]> git.mxchange.org Git - flightgear.git/commitdiff
Separate instruments from cockpit displays.
authorJames Turner <zakalawe@mac.com>
Wed, 26 Sep 2012 19:30:24 +0000 (20:30 +0100)
committerJames Turner <zakalawe@mac.com>
Fri, 5 Oct 2012 17:13:50 +0000 (18:13 +0100)
33 files changed:
src/Canvas/canvas.hxx
src/Cockpit/CMakeLists.txt
src/Cockpit/NavDisplay.cxx [new file with mode: 0644]
src/Cockpit/NavDisplay.hxx [new file with mode: 0644]
src/Cockpit/agradar.cxx [new file with mode: 0644]
src/Cockpit/agradar.hxx [new file with mode: 0644]
src/Cockpit/cockpitDisplayManager.cxx [new file with mode: 0644]
src/Cockpit/cockpitDisplayManager.hxx [new file with mode: 0644]
src/Cockpit/groundradar.cxx [new file with mode: 0644]
src/Cockpit/groundradar.hxx [new file with mode: 0644]
src/Cockpit/od_gauge.cxx [new file with mode: 0644]
src/Cockpit/od_gauge.hxx [new file with mode: 0644]
src/Cockpit/render_area_2d.cxx [new file with mode: 0644]
src/Cockpit/render_area_2d.hxx [new file with mode: 0644]
src/Cockpit/wxradar.cxx [new file with mode: 0644]
src/Cockpit/wxradar.hxx [new file with mode: 0644]
src/Instrumentation/CMakeLists.txt
src/Instrumentation/NavDisplay.cxx [deleted file]
src/Instrumentation/NavDisplay.hxx [deleted file]
src/Instrumentation/agradar.cxx [deleted file]
src/Instrumentation/agradar.hxx [deleted file]
src/Instrumentation/dclgps.hxx
src/Instrumentation/groundradar.cxx [deleted file]
src/Instrumentation/groundradar.hxx [deleted file]
src/Instrumentation/instrument_mgr.cxx
src/Instrumentation/od_gauge.cxx [deleted file]
src/Instrumentation/od_gauge.hxx [deleted file]
src/Instrumentation/rad_alt.hxx
src/Instrumentation/render_area_2d.cxx [deleted file]
src/Instrumentation/render_area_2d.hxx [deleted file]
src/Instrumentation/wxradar.cxx [deleted file]
src/Instrumentation/wxradar.hxx [deleted file]
src/Main/fg_init.cxx

index 25cd8e81676cf205d235b0011cb7ee528a1f3e69..2071452997cb88364f9c091a5523b4dd274df985 100644 (file)
@@ -23,7 +23,7 @@
 #include "property_based_element.hxx"
 
 #include <Canvas/canvas_fwd.hpp>
-#include <Instrumentation/od_gauge.hxx>
+#include <Cockpit/od_gauge.hxx>
 
 #include <simgear/props/propertyObject.hxx>
 #include <osg/NodeCallback>
index d68022e7771784e5f78ad17b94516e00fd164cb0..42b03bac23be3ad7e549a989cb114f7cce38e14e 100644 (file)
@@ -1,15 +1,29 @@
 include(FlightGearComponent)
 
 set(SOURCES
+    cockpitDisplayManager.cxx
        panel.cxx
        panel_io.cxx
        built_in/FGMagRibbon.cxx
+    agradar.cxx
+    groundradar.cxx
+    od_gauge.cxx
+    render_area_2d.cxx
+    wxradar.cxx
+    NavDisplay.cxx
        )
        
 set(HEADERS
+    cockpitDisplayManager.hxx
        panel.hxx
        panel_io.hxx
        built_in/FGMagRibbon.hxx
+    agradar.hxx
+    groundradar.hxx
+    od_gauge.hxx
+    render_area_2d.hxx
+    wxradar.hxx
+    NavDisplay.hxx
        )
 
 
diff --git a/src/Cockpit/NavDisplay.cxx b/src/Cockpit/NavDisplay.cxx
new file mode 100644 (file)
index 0000000..15452a2
--- /dev/null
@@ -0,0 +1,1433 @@
+// navigation display texture
+//
+// Written by James Turner, forked from wxradar code
+//
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+//
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "NavDisplay.hxx"
+
+#include <cassert>
+#include <boost/foreach.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
+#include <algorithm>
+
+#include <osg/Array>
+#include <osg/Geometry>
+#include <osg/Matrixf>
+#include <osg/PrimitiveSet>
+#include <osg/StateSet>
+#include <osg/LineWidth>
+#include <osg/Version>
+
+#include <simgear/constants.h>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/scene/model/model.hxx>
+#include <simgear/structure/exception.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/misc/strutils.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+
+#include <sstream>
+#include <iomanip>
+#include <iostream>             // for cout, endl
+
+using std::stringstream;
+using std::endl;
+using std::setprecision;
+using std::fixed;
+using std::setw;
+using std::setfill;
+using std::cout;
+using std::endl;
+using std::map;
+using std::string;
+
+#include <Main/fg_props.hxx>
+#include <Main/globals.hxx>
+#include "panel.hxx"
+#include <Navaids/routePath.hxx>
+#include <Autopilot/route_mgr.hxx>
+#include <Navaids/navrecord.hxx>
+#include <Navaids/navlist.hxx>
+#include <Navaids/fix.hxx>
+#include <Airports/simple.hxx>
+#include <Airports/runways.hxx>
+#include "od_gauge.hxx"
+
+static const char *DEFAULT_FONT = "typewriter.txf";
+
+static
+osg::Matrixf degRotation(float angle)
+{
+    return osg::Matrixf::rotate(angle * SG_DEGREES_TO_RADIANS, 0.0f, 0.0f, -1.0f);
+}
+
+static osg::Vec4 readColor(SGPropertyNode* colorNode, const osg::Vec4& c)
+{
+    osg::Vec4 result;
+    result.r() = colorNode->getDoubleValue("red",   c.r());
+    result.g() = colorNode->getDoubleValue("green", c.g());
+    result.b() = colorNode->getDoubleValue("blue",  c.b());
+    result.a() = colorNode->getDoubleValue("alpha", c.a());
+    return result;
+}
+
+static osgText::Text::AlignmentType readAlignment(const std::string& t)
+{
+    if (t == "left-top") {
+        return osgText::Text::LEFT_TOP;
+    } else if (t == "left-center") {
+        return osgText::Text::LEFT_CENTER;
+    } else if (t == "left-bottom") {
+        return osgText::Text::LEFT_BOTTOM;
+    } else if (t == "center-top") {
+        return osgText::Text::CENTER_TOP;
+    } else if (t == "center-center") {
+        return osgText::Text::CENTER_CENTER;
+    } else if (t == "center-bottom") {
+        return osgText::Text::CENTER_BOTTOM;
+    } else if (t == "right-top") {
+        return osgText::Text::RIGHT_TOP;
+    } else if (t == "right-center") {
+        return osgText::Text::RIGHT_CENTER;
+    } else if (t == "right-bottom") {
+        return osgText::Text::RIGHT_BOTTOM;
+    } else if (t == "left-baseline") {
+        return osgText::Text::LEFT_BASE_LINE;
+    } else if (t == "center-baseline") {
+        return osgText::Text::CENTER_BASE_LINE;
+    } else if (t == "right-baseline") {
+        return osgText::Text::RIGHT_BASE_LINE;
+    }
+    
+    return osgText::Text::BASE_LINE;
+}
+
+static string formatPropertyValue(SGPropertyNode* nd, const string& format)
+{
+    assert(nd);
+    static char buf[512];
+    if (format.find('d') != string::npos) {
+        ::snprintf(buf, 512, format.c_str(), nd->getIntValue());
+        return buf;
+    }
+    
+    if (format.find('s') != string::npos) {
+        ::snprintf(buf, 512, format.c_str(), nd->getStringValue());
+        return buf;
+    }
+    
+// assume it's a double/float
+    ::snprintf(buf, 512, format.c_str(), nd->getDoubleValue());
+    return buf;
+}
+
+static osg::Vec2 mult(const osg::Vec2& v, const osg::Matrixf& m)
+{
+    osg::Vec3 r = m.preMult(osg::Vec3(v.x(), v.y(), 0.0));
+    return osg::Vec2(r.x(), r.y());
+}
+
+class NavDisplay::CacheListener : public SGPropertyChangeListener
+{
+public:
+    CacheListener(NavDisplay *nd) : 
+        _nd(nd)
+    {}
+    
+    virtual void valueChanged (SGPropertyNode * prop)
+    {
+        _nd->invalidatePositionedCache();
+    }
+private:
+    NavDisplay* _nd;
+};
+
+class NavDisplay::ForceUpdateListener : public SGPropertyChangeListener
+{
+public:
+  ForceUpdateListener(NavDisplay *nd) : 
+    _nd(nd)
+  {}
+  
+  virtual void valueChanged (SGPropertyNode * prop)
+  {
+    _nd->forceUpdate();
+  }
+private:
+  NavDisplay* _nd;
+};
+
+///////////////////////////////////////////////////////////////////
+
+class SymbolRule
+{
+public:
+  SymbolRule()
+    {
+        
+    }
+    
+    bool initFromNode(SGPropertyNode* node, NavDisplay* owner)
+    {
+        if (!node->getChild("type")) {
+            return false;
+        }
+        
+        type = node->getStringValue("type");
+        boost::to_lower(type);
+        SGPropertyNode* enableNode = node->getChild("enable");
+        if (enableNode) { 
+            enable.reset(sgReadCondition(fgGetNode("/"), enableNode));
+        }
+        
+        int n=0;
+        while (node->hasChild("state", n)) {
+            string m = node->getChild("state", n++)->getStringValue();
+            if (m[0] == '!') {
+                excluded_states.insert(m.substr(1));
+            } else {
+                required_states.insert(m);
+            }
+        } // of matches parsing
+        
+          
+        return true;
+    }
+    
+    void setDefinition(SymbolDef* d)
+    {
+        definition = d;
+    }
+    
+    SymbolDef* getDefinition() const
+    { return definition; }
+    
+    bool matches(const string_set& states) const
+    {
+        BOOST_FOREACH(const string& s, required_states) {
+            if (states.count(s) == 0) {
+                return false;
+            }
+        }
+        
+        BOOST_FOREACH(const string& s, excluded_states) {
+            if (states.count(s) != 0) {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
+  // return if the enabled state changed (needs a cache update)
+    bool checkEnabled()
+    {
+        if (enable.get()) {
+            bool wasEnabled = enabled;
+            enabled = enable->test();
+            return (enabled != wasEnabled);
+        } else {
+            enabled = true;
+            return false;
+        }
+    }
+    
+    bool enabled; // cached enabled state
+    std::string type;
+    
+  // record instances for limiting by count
+    int instanceCount;
+private:
+    SymbolDef* definition;
+    
+    std::auto_ptr<SGCondition> enable;
+    string_set required_states;
+    string_set excluded_states;
+};
+
+class SymbolDef
+{
+public:
+    SymbolDef() : limitCount(0) { }
+  
+    bool initFromNode(SGPropertyNode* node, NavDisplay* owner)
+    {
+        if (node->getChild("type")) {
+            SymbolRule* builtinRule = new SymbolRule;
+            builtinRule->initFromNode(node, owner);
+            builtinRule->setDefinition(this);
+            owner->addRule(builtinRule);
+        }
+        
+        if (node->hasChild("width")) {
+            float w = node->getFloatValue("width");
+            float h = node->getFloatValue("height", w);
+            xy0.x() = -w * 0.5;
+            xy0.y() = -h * 0.5;
+            xy1.x() = w * 0.5;
+            xy1.y() = h * 0.5;
+        } else {
+            xy0.x()  = node->getFloatValue("x0", 0.0);
+            xy0.y()  = node->getFloatValue("y0", 0.0);
+            xy1.x()  = node->getFloatValue("x1", 5);
+            xy1.y()  = node->getFloatValue("y1", 5);
+        }
+      
+        double texSize = node->getFloatValue("texture-size", owner->textureSize());
+        
+        uv0.x()  = node->getFloatValue("u0", 0) / texSize;
+        uv0.y()  = node->getFloatValue("v0", 0) / texSize;
+        uv1.x()  = node->getFloatValue("u1", 1) / texSize;
+        uv1.y()  = node->getFloatValue("v1", 1) / texSize;
+        
+        color = readColor(node->getChild("color"), osg::Vec4(1, 1, 1, 1));
+        priority = node->getIntValue("priority", 0);
+        zOrder = node->getIntValue("zOrder", 0);
+        rotateToHeading = node->getBoolValue("rotate-to-heading", false);
+        roundPos = node->getBoolValue("round-position", true);
+        hasText = false;
+        if (node->hasChild("text")) {
+            hasText = true;
+            alignment = readAlignment(node->getStringValue("text-align"));
+            textTemplate = node->getStringValue("text");
+            textOffset.x() = node->getFloatValue("text-offset-x", 0);
+            textOffset.y() = node->getFloatValue("text-offset-y", 0);
+            textColor = readColor(node->getChild("text-color"), color);
+          
+            SGPropertyNode* enableNode = node->getChild("text-enable");
+            if (enableNode) { 
+              textEnable.reset(sgReadCondition(fgGetNode("/"), enableNode));
+            }
+        }
+        
+        drawLine = node->getBoolValue("draw-line", false);
+        lineColor = readColor(node->getChild("line-color"), color);
+        drawRouteLeg = node->getBoolValue("draw-leg", false);
+        
+        stretchSymbol = node->getBoolValue("stretch-symbol", false);
+        if (stretchSymbol) {
+            stretchY2 = node->getFloatValue("y2");
+            stretchY3 = node->getFloatValue("y3");
+            stretchV2 = node->getFloatValue("v2") / texSize;
+            stretchV3 = node->getFloatValue("v3") / texSize;
+        }
+      
+        SGPropertyNode* limitNode = node->getChild("limit");
+        if (limitNode) {
+          limitCount = limitNode->getIntValue();
+        }
+
+        return true;
+    }
+    
+    osg::Vec2 xy0, xy1;
+    osg::Vec2 uv0, uv1;
+    osg::Vec4 color;
+    
+    int priority;
+    int zOrder;
+    bool rotateToHeading;
+    bool roundPos; ///< should position be rounded to integer values
+    bool hasText;
+    std::auto_ptr<SGCondition> textEnable;
+    bool textEnabled; ///< cache condition result
+    osg::Vec4 textColor;
+    osg::Vec2 textOffset;
+    osgText::Text::AlignmentType alignment;
+    string textTemplate;
+    
+    bool drawLine;
+    osg::Vec4 lineColor;
+    
+// symbol stretching creates three quads (instead of one) - a start,
+// middle and end quad, positioned along the line of the symbol.
+// X (and U) axis values determined by the values above, so we only need
+// to define the Y (and V) values to build the other quads.
+    bool stretchSymbol;
+    double stretchY2, stretchY3;
+    double stretchV2, stretchV3;
+    
+    bool drawRouteLeg;
+    
+    int limitCount, instanceCount;
+};
+
+class SymbolInstance
+{
+public:
+    SymbolInstance(const osg::Vec2& p, double h, SymbolDef* def, SGPropertyNode* vars) :
+        pos(p),
+        headingDeg(h),
+        definition(def),
+        props(vars)
+    { }
+    
+    osg::Vec2 pos; // projected position
+    osg::Vec2 endPos;
+    double headingDeg;
+    SymbolDef* definition;
+    SGPropertyNode_ptr props;
+    
+    string text() const
+    {
+        assert(definition->hasText);
+        string r;        
+        size_t lastPos = 0;
+        
+        while (true) {
+            size_t pos = definition->textTemplate.find('{', lastPos);
+            if (pos == string::npos) { // no more replacements
+                r.append(definition->textTemplate.substr(lastPos));
+                break;
+            }
+            
+            r.append(definition->textTemplate.substr(lastPos, pos - lastPos));
+            
+            size_t endReplacement = definition->textTemplate.find('}', pos+1);
+            if (endReplacement <= pos) {
+                return "bad replacement";
+            }
+
+            string spec = definition->textTemplate.substr(pos + 1, endReplacement - (pos + 1));
+        // look for formatter in spec
+            size_t colonPos = spec.find(':');
+            if (colonPos == string::npos) {
+            // simple replacement
+                r.append(props->getStringValue(spec));
+            } else {
+                string format = spec.substr(colonPos + 1);
+                string prop = spec.substr(0, colonPos);
+                r.append(formatPropertyValue(props->getNode(prop), format));
+            }
+            
+            lastPos = endReplacement + 1;
+        }
+        
+        return r;
+    }
+};
+
+//////////////////////////////////////////////////////////////////
+
+NavDisplay::NavDisplay(SGPropertyNode *node) :
+    _name(node->getStringValue("name", "nd")),
+    _num(node->getIntValue("number", 0)),
+    _time(0.0),
+    _updateInterval(node->getDoubleValue("update-interval-sec", 0.1)),
+    _forceUpdate(true),
+    _odg(0),
+    _scale(0),
+    _view_heading(0),
+    _font_size(0),
+    _font_spacing(0),
+    _rangeNm(0),
+    _maxSymbols(100)
+{
+    _Instrument = fgGetNode(string("/instrumentation/" + _name).c_str(), _num, true);
+    _font_node = _Instrument->getNode("font", true);
+
+#define INITFONT(p, val, type) if (!_font_node->hasValue(p)) _font_node->set##type##Value(p, val)
+    INITFONT("name", DEFAULT_FONT, String);
+    INITFONT("size", 8, Float);
+    INITFONT("line-spacing", 0.25, Float);
+    INITFONT("color/red", 0, Float);
+    INITFONT("color/green", 0.8, Float);
+    INITFONT("color/blue", 0, Float);
+    INITFONT("color/alpha", 1, Float);
+#undef INITFONT
+
+    _textureSize = _Instrument->getNode("symbol-texture-size", true)->getIntValue();
+    SGPropertyNode* symbolsNode = node->getNode("symbols");
+    SGPropertyNode* symbol;
+
+    map<string, SymbolDef*> definitionDict;
+    for (int i = 0; (symbol = symbolsNode->getChild("symbol", i)) != NULL; ++i) {
+        SymbolDef* def = new SymbolDef;
+        if (!def->initFromNode(symbol, this)) {
+          delete def;
+          continue;
+        }
+        
+        const char* id = symbol->getStringValue("id");
+        if (id && strlen(id)) {
+            definitionDict[id] = def;
+        }
+        
+        _definitions.push_back(def);
+    } // of symbol definition parsing
+    
+    SGPropertyNode* rulesNode = node->getNode("rules");
+    if (rulesNode) {
+        SGPropertyNode* rule;
+        
+        for (int i = 0; (rule = rulesNode->getChild("rule", i)) != NULL; ++i) {
+            SymbolRule* r = new SymbolRule;
+            if (!r->initFromNode(rule, this)) {
+                delete r;
+                continue;
+            }
+            
+            const char* id = symbol->getStringValue("symbol");
+            if (id && strlen(id) && (definitionDict.find(id) != definitionDict.end())) {
+                r->setDefinition(definitionDict[id]);
+            } else {
+                SG_LOG(SG_INSTR, SG_WARN, "symbol rule has missing/unknown definition id:" << id);
+                delete r;
+                continue;
+            }
+            
+            addRule(r);
+        } // of symbol rule parsing
+    }
+    
+}
+
+
+NavDisplay::~NavDisplay()
+{
+  delete _odg;
+}
+
+void
+NavDisplay::init ()
+{
+    _cachedItemsValid = false;
+    _cacheListener.reset(new CacheListener(this));
+    _forceUpdateListener.reset(new ForceUpdateListener(this));
+  
+    _serviceable_node = _Instrument->getNode("serviceable", true);
+    _rangeNode = _Instrument->getNode("range", true);
+    if (!_rangeNode->hasValue()) {
+      _rangeNode->setDoubleValue(40.0);
+    }
+    _rangeNode->addChangeListener(_cacheListener.get());
+    _rangeNode->addChangeListener(_forceUpdateListener.get());
+  
+    _xCenterNode = _Instrument->getNode("x-center");
+    if (!_xCenterNode->hasValue()) {
+      _xCenterNode->setDoubleValue(0.5);
+    }
+    _xCenterNode->addChangeListener(_forceUpdateListener.get());
+    _yCenterNode = _Instrument->getNode("y-center");
+    if (!_yCenterNode->hasValue()) {
+      _yCenterNode->setDoubleValue(0.5);
+    }
+    _yCenterNode->addChangeListener(_forceUpdateListener.get());
+  
+    // texture name to use in 2D and 3D instruments
+    _texture_path = _Instrument->getStringValue("radar-texture-path",
+        "Aircraft/Instruments/Textures/od_wxradar.rgb");
+
+    string path = _Instrument->getStringValue("symbol-texture-path",
+        "Aircraft/Instruments/Textures/nd-symbols.png");
+    SGPath tpath = globals->resolve_aircraft_path(path);
+    if (!tpath.exists()) {
+      SG_LOG(SG_INSTR, SG_WARN, "ND symbol texture not found:" << path);
+    }
+  
+    // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect
+    _symbolTexture = SGLoadTexture2D(tpath, NULL, false, false);
+
+    _odg = new FGODGauge;
+    _odg->setSize(_Instrument->getIntValue("texture-size", 512));
+
+    _route = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
+    
+    _navRadio1Node = fgGetNode("/instrumentation/nav[0]", true);
+    _navRadio2Node = fgGetNode("/instrumentation/nav[1]", true);
+    
+    _excessDataNode = _Instrument->getChild("excess-data", 0, true);
+    _excessDataNode->setBoolValue(false);
+    _testModeNode = _Instrument->getChild("test-mode", 0, true);
+    _testModeNode->setBoolValue(false);
+  
+    _viewHeadingNode = _Instrument->getChild("view-heading-deg", 0, true);
+// OSG geometry setup
+    _radarGeode = new osg::Geode;
+
+    _geom = new osg::Geometry;
+    _geom->setUseDisplayList(false);
+    
+    osg::StateSet *stateSet = _geom->getOrCreateStateSet();
+    stateSet->setTextureAttributeAndModes(0, _symbolTexture.get());
+    stateSet->setDataVariance(osg::Object::STATIC);
+  
+    // Initially allocate space for 128 quads
+    _vertices = new osg::Vec2Array;
+    _vertices->setDataVariance(osg::Object::DYNAMIC);
+    _vertices->reserve(128 * 4);
+    _geom->setVertexArray(_vertices);
+    _texCoords = new osg::Vec2Array;
+    _texCoords->setDataVariance(osg::Object::DYNAMIC);
+    _texCoords->reserve(128 * 4);
+    _geom->setTexCoordArray(0, _texCoords);
+    
+    _quadColors = new osg::Vec4Array;
+    _quadColors->setDataVariance(osg::Object::DYNAMIC);
+    _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
+    _geom->setColorArray(_quadColors);
+    
+    _symbolPrimSet = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
+    _symbolPrimSet->setDataVariance(osg::Object::DYNAMIC);
+    _geom->addPrimitiveSet(_symbolPrimSet);
+    
+    _geom->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f),
+        osg::Vec3f(256.0f, 256.0f, 0.0f)));
+  
+    _radarGeode->addDrawable(_geom);
+    _odg->allocRT();
+    // Texture in the 2D panel system
+    FGTextureManager::addTexture(_texture_path.c_str(), _odg->getTexture());
+
+    _lineGeometry = new osg::Geometry;
+    _lineGeometry->setUseDisplayList(false);
+    stateSet = _lineGeometry->getOrCreateStateSet();    
+    osg::LineWidth *lw = new osg::LineWidth();
+    lw->setWidth(2.0);
+    stateSet->setAttribute(lw);
+    
+    _lineVertices = new osg::Vec2Array;
+    _lineVertices->setDataVariance(osg::Object::DYNAMIC);
+    _lineVertices->reserve(128 * 4);
+    _lineGeometry->setVertexArray(_lineVertices);
+    
+                  
+    _lineColors = new osg::Vec4Array;
+    _lineColors->setDataVariance(osg::Object::DYNAMIC);
+    _lineGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
+    _lineGeometry->setColorArray(_lineColors);
+    
+    _linePrimSet = new osg::DrawArrays(osg::PrimitiveSet::LINES);
+    _linePrimSet->setDataVariance(osg::Object::DYNAMIC);
+    _lineGeometry->addPrimitiveSet(_linePrimSet);
+    
+    _lineGeometry->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f),
+                                            osg::Vec3f(256.0f, 256.0f, 0.0f)));
+
+    _radarGeode->addDrawable(_lineGeometry);              
+                  
+    _textGeode = new osg::Geode;
+
+    osg::Camera *camera = _odg->getCamera();
+    camera->addChild(_radarGeode.get());
+    camera->addChild(_textGeode.get());
+    osg::Texture2D* tex = _odg->getTexture();
+    camera->setProjectionMatrixAsOrtho2D(0, tex->getTextureWidth(), 
+        0, tex->getTextureHeight());
+    
+    updateFont();
+}
+
+void
+NavDisplay::update (double delta_time_sec)
+{
+  if (!fgGetBool("sim/sceneryloaded", false)) {
+    return;
+  }
+
+  if (!_odg || !_serviceable_node->getBoolValue()) {
+    _Instrument->setStringValue("status", "");
+    return;
+  }
+  
+  if (_forceUpdate) {
+    _forceUpdate = false;
+    _time = 0.0;
+  } else {
+    _time += delta_time_sec;
+    if (_time < _updateInterval){
+      return;
+    }
+    _time -= _updateInterval;
+  }
+
+  _rangeNm = _rangeNode->getFloatValue();
+  if (_testModeNode->getBoolValue()) {
+    _view_heading = 90;
+  } else if (_Instrument->getBoolValue("aircraft-heading-up", true)) {
+    _view_heading = fgGetDouble("/orientation/heading-deg");
+  } else {
+    _view_heading = _Instrument->getFloatValue("heading-up-deg", 0.0);
+  }
+  _viewHeadingNode->setDoubleValue(_view_heading);
+  
+  double xCenterFrac = _xCenterNode->getDoubleValue();
+  double yCenterFrac = _yCenterNode->getDoubleValue();
+  int pixelSize = _odg->size();
+  
+  int rangePixels = _Instrument->getIntValue("range-pixels", -1);
+  if (rangePixels < 0) {
+    // hacky - assume (as is very common) that x-frac doesn't vary, and
+    // y-frac is used to position the center at either the top or bottom of
+    // the pixel area. Measure from the center to the furthest edge (top or bottom)
+    rangePixels = pixelSize * std::max(fabs(1.0 - yCenterFrac), fabs(yCenterFrac));
+  }
+  
+  _scale = rangePixels / _rangeNm;
+  _Instrument->setDoubleValue("scale", _scale);
+  
+  
+  _centerTrans = osg::Matrixf::translate(xCenterFrac * pixelSize, 
+      yCenterFrac * pixelSize, 0.0);
+
+// scale from nm to display units, rotate so aircraft heading is up
+// (as opposed to north), and compensate for centering
+  _projectMat = osg::Matrixf::scale(_scale, _scale, 1.0) * 
+      degRotation(-_view_heading) * _centerTrans;
+  
+  _pos = globals->get_aircraft_position();
+
+    // invalidate the cache of positioned items, if we travelled more than 1nm
+    if (_cachedItemsValid) {
+        SGVec3d cartNow(SGVec3d::fromGeod(_pos));
+        double movedNm = dist(_cachedPos, cartNow) * SG_METER_TO_NM;
+        _cachedItemsValid = (movedNm < 1.0);
+    }
+    
+  _vertices->clear();
+  _lineVertices->clear();
+  _lineColors->clear();
+  _quadColors->clear();
+  _texCoords->clear();
+  _textGeode->removeDrawables(0, _textGeode->getNumDrawables());
+  
+  BOOST_FOREACH(SymbolInstance* si, _symbols) {
+      delete si;
+  }
+  _symbols.clear();
+  
+  BOOST_FOREACH(SymbolDef* d, _definitions) {
+    d->instanceCount = 0;
+    d->textEnabled = d->textEnable.get() ? d->textEnable->test() : true;
+  }
+  
+  bool enableChanged = false;
+  BOOST_FOREACH(SymbolRule* r, _rules) {
+      enableChanged |= r->checkEnabled();
+  }
+  
+  if (enableChanged) {
+    SG_LOG(SG_INSTR, SG_INFO, "NS rule enables changed, rebuilding cache");
+    _cachedItemsValid = false;
+  }
+  
+  if (_testModeNode->getBoolValue()) {
+    addTestSymbols();
+  } else {
+    processRoute();
+    processNavRadios();
+    processAI();
+    findItems();
+    limitDisplayedSymbols();
+  }
+
+  addSymbolsToScene();
+  
+  _symbolPrimSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size());
+  _symbolPrimSet->dirty();
+  _linePrimSet->set(osg::PrimitiveSet::LINES, 0, _lineVertices->size());
+  _linePrimSet->dirty();
+}
+
+
+void
+NavDisplay::updateFont()
+{
+    float red = _font_node->getFloatValue("color/red");
+    float green = _font_node->getFloatValue("color/green");
+    float blue = _font_node->getFloatValue("color/blue");
+    float alpha = _font_node->getFloatValue("color/alpha");
+    _font_color.set(red, green, blue, alpha);
+
+    _font_size = _font_node->getFloatValue("size");
+    _font_spacing = _font_size * _font_node->getFloatValue("line-spacing");
+    string path = _font_node->getStringValue("name", DEFAULT_FONT);
+
+    SGPath tpath;
+    if (path[0] != '/') {
+        tpath = globals->get_fg_root();
+        tpath.append("Fonts");
+        tpath.append(path);
+    } else {
+        tpath = path;
+    }
+
+    osg::ref_ptr<osgDB::ReaderWriter::Options> fontOptions = new osgDB::ReaderWriter::Options("monochrome");
+    osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str(), fontOptions.get());
+
+    if (font != 0) {
+        _font = font;
+        _font->setMinFilterHint(osg::Texture::NEAREST);
+        _font->setMagFilterHint(osg::Texture::NEAREST);
+        _font->setGlyphImageMargin(0);
+        _font->setGlyphImageMarginRatio(0);
+    }
+}
+
+void NavDisplay::addSymbolToScene(SymbolInstance* sym)
+{
+    SymbolDef* def = sym->definition;
+    
+    osg::Vec2 verts[4];
+    verts[0] = def->xy0;
+    verts[1] = osg::Vec2(def->xy1.x(), def->xy0.y());
+    verts[2] = def->xy1;
+    verts[3] = osg::Vec2(def->xy0.x(), def->xy1.y());
+    
+    if (def->rotateToHeading) {
+        osg::Matrixf m(degRotation(sym->headingDeg - _view_heading));
+        for (int i=0; i<4; ++i) {
+            verts[i] = mult(verts[i], m);
+        }
+    }
+    
+    osg::Vec2 pos = sym->pos;
+    if (def->roundPos) {
+        pos = osg::Vec2((int) pos.x(), (int) pos.y());
+    }
+    
+    _texCoords->push_back(def->uv0);
+    _texCoords->push_back(osg::Vec2(def->uv1.x(), def->uv0.y()));
+    _texCoords->push_back(def->uv1);
+    _texCoords->push_back(osg::Vec2(def->uv0.x(), def->uv1.y()));
+
+    for (int i=0; i<4; ++i) {
+        _vertices->push_back(verts[i] + pos);
+        _quadColors->push_back(def->color);
+    }
+    
+    if (def->stretchSymbol) {
+        osg::Vec2 stretchVerts[4];
+        stretchVerts[0] = osg::Vec2(def->xy0.x(), def->stretchY2);
+        stretchVerts[1] = osg::Vec2(def->xy1.x(), def->stretchY2);
+        stretchVerts[2] = osg::Vec2(def->xy1.x(), def->stretchY3);
+        stretchVerts[3] = osg::Vec2(def->xy0.x(), def->stretchY3);
+        
+        osg::Matrixf m(degRotation(sym->headingDeg - _view_heading));
+        for (int i=0; i<4; ++i) {
+            stretchVerts[i] = mult(stretchVerts[i], m);
+        }
+        
+    // stretched quad
+        _vertices->push_back(verts[2] + pos);
+        _vertices->push_back(stretchVerts[1] + sym->endPos);
+        _vertices->push_back(stretchVerts[0] + sym->endPos);
+        _vertices->push_back(verts[3] + pos);
+        
+        _texCoords->push_back(def->uv1);
+        _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV2));
+        _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV2));
+        _texCoords->push_back(osg::Vec2(def->uv0.x(), def->uv1.y()));
+        
+        for (int i=0; i<4; ++i) {
+            _quadColors->push_back(def->color);
+        }
+        
+    // quad three, for the end portion
+        for (int i=0; i<4; ++i) {
+            _vertices->push_back(stretchVerts[i] + sym->endPos);
+            _quadColors->push_back(def->color);
+        }
+        
+        _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV2));
+        _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV2));
+        _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV3));
+        _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV3));
+    }
+    
+    if (def->drawLine) {
+        addLine(sym->pos, sym->endPos, def->lineColor);
+    }
+    
+    if (!def->hasText || !def->textEnabled) {
+        return;
+    }
+    
+    osgText::Text* t = new osgText::Text;
+    t->setFont(_font.get());
+    t->setFontResolution(12, 12);
+    t->setCharacterSize(_font_size);
+    t->setLineSpacing(_font_spacing);
+    t->setColor(def->textColor);
+    t->setAlignment(def->alignment);
+    t->setText(sym->text());
+
+
+    osg::Vec2 textPos = def->textOffset + pos;
+// ensure we use ints here, or text visual quality goes bad
+    t->setPosition(osg::Vec3((int)textPos.x(), (int)textPos.y(), 0));
+    _textGeode->addDrawable(t);
+}
+
+class OrderByPriority
+{
+public:
+    bool operator()(SymbolInstance* a, SymbolInstance* b)
+    {
+        return a->definition->priority > b->definition->priority;
+    }    
+};
+
+void NavDisplay::limitDisplayedSymbols()
+{
+// gloabl symbol limit
+    _maxSymbols= _Instrument->getIntValue("max-symbols", _maxSymbols);
+    if ((int) _symbols.size() <= _maxSymbols) {
+        _excessDataNode->setBoolValue(false);
+        return;
+    }
+    
+    std::sort(_symbols.begin(), _symbols.end(), OrderByPriority());
+    _symbols.resize(_maxSymbols);
+    _excessDataNode->setBoolValue(true);
+}
+
+class OrderByZ
+{
+public:
+    bool operator()(SymbolInstance* a, SymbolInstance* b)
+    {
+        return a->definition->zOrder > b->definition->zOrder;
+    }
+};
+
+void NavDisplay::addSymbolsToScene()
+{
+    std::sort(_symbols.begin(), _symbols.end(), OrderByZ());
+    BOOST_FOREACH(SymbolInstance* sym, _symbols) {
+        addSymbolToScene(sym);
+    }
+}
+
+void NavDisplay::addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec4& color)
+{    
+    _lineVertices->push_back(a);
+    _lineVertices->push_back(b);
+    _lineColors->push_back(color);
+    _lineColors->push_back(color);
+}
+
+osg::Vec2 NavDisplay::projectBearingRange(double bearingDeg, double rangeNm) const
+{
+    osg::Vec3 p(0, rangeNm, 0.0);
+    p = degRotation(bearingDeg).preMult(p);
+    p = _projectMat.preMult(p);
+    return osg::Vec2(p.x(), p.y());
+}
+
+osg::Vec2 NavDisplay::projectGeod(const SGGeod& geod) const
+{
+    double rangeM, bearing, az2;
+    SGGeodesy::inverse(_pos, geod, bearing, az2, rangeM);
+    return projectBearingRange(bearing, rangeM * SG_METER_TO_NM);
+}
+
+class Filter : public FGPositioned::Filter
+{
+public:
+    Filter(NavDisplay* nd) : _owner(nd) { }
+  
+    double minRunwayLengthFt;
+  
+    virtual bool pass(FGPositioned* aPos) const
+    {
+        if (aPos->type() == FGPositioned::FIX) {
+            string ident(aPos->ident());
+            // ignore fixes which end in digits
+            if ((ident.size() > 4) && isdigit(ident[3]) && isdigit(ident[4])) {
+                return false;
+            }
+        }
+
+        if (aPos->type() == FGPositioned::AIRPORT) {
+          FGAirport* apt = (FGAirport*) aPos;
+          if (!apt->hasHardRunwayOfLengthFt(minRunwayLengthFt)) {
+            return false;
+          }
+        }
+      
+      // check against current rule states
+        return _owner->isPositionedShown(aPos);
+    }
+
+    virtual FGPositioned::Type minType() const {
+        return FGPositioned::AIRPORT;
+    }
+
+    virtual FGPositioned::Type maxType() const {
+        return FGPositioned::OBSTACLE;
+    }
+  
+private:
+    NavDisplay* _owner;
+};
+
+void NavDisplay::findItems()
+{
+    if (!_cachedItemsValid) {
+        Filter filt(this);
+        filt.minRunwayLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 2000);
+        _itemsInRange = FGPositioned::findClosestN(_pos, _maxSymbols, _rangeNm, &filt);
+        _cachedItemsValid = true;
+        _cachedPos = SGVec3d::fromGeod(_pos);
+    }
+    
+  // sort by distance from pos, so symbol limits are accurate
+    FGPositioned::sortByRange(_itemsInRange, _pos);
+  
+    BOOST_FOREACH(FGPositioned* pos, _itemsInRange) {
+        foundPositionedItem(pos);
+    }
+}
+
+void NavDisplay::processRoute()
+{
+    _routeSources.clear();
+    flightgear::FlightPlan* fp = _route->flightPlan();
+    RoutePath path(fp);
+    int current = _route->currentIndex();
+    
+    for (int l=0; l<fp->numLegs(); ++l) {
+        flightgear::FlightPlan::Leg* leg = fp->legAtIndex(l);
+        flightgear::WayptRef wpt(leg->waypoint());
+        _routeSources.insert(wpt->source());
+        
+        string_set state;
+        state.insert("on-active-route");
+        
+        if (l < current) {
+            state.insert("passed");
+        }
+        
+        if (l == current) {
+            state.insert("current-wp");
+        }
+        
+        if (l > current) {
+            state.insert("future");
+        }
+        
+        if (l == (current + 1)) {
+            state.insert("next-wp");
+        }
+        
+        SymbolRuleVector rules;
+        findRules("waypoint" , state, rules);
+        if (rules.empty()) {
+            return; // no rules matched, we can skip this item
+        }
+
+        SGGeod g = path.positionForIndex(l);
+        SGPropertyNode* vars = _route->wayptNodeAtIndex(l);
+        if (!vars) {
+          continue; // shouldn't happen, but let's guard against it
+        }
+      
+        double heading;
+        computeWayptPropsAndHeading(wpt, g, vars, heading);
+
+        osg::Vec2 projected = projectGeod(g);
+        BOOST_FOREACH(SymbolRule* r, rules) {
+            addSymbolInstance(projected, heading, r->getDefinition(), vars);
+            
+            if (r->getDefinition()->drawRouteLeg) {
+                SGGeodVec gv(path.pathForIndex(l));
+                if (!gv.empty()) {
+                    osg::Vec2 pr = projectGeod(gv[0]);
+                    for (unsigned int i=1; i<gv.size(); ++i) {
+                        osg::Vec2 p = projectGeod(gv[i]);
+                        addLine(pr, p, r->getDefinition()->lineColor);
+                        pr = p;
+                    }
+                }
+            } // of leg drawing enabled
+        } // of matching rules iteration
+    } // of waypoints iteration
+}
+
+void NavDisplay::computeWayptPropsAndHeading(flightgear::Waypt* wpt, const SGGeod& pos, SGPropertyNode* nd, double& heading)
+{
+    double rangeM, az2;
+    SGGeodesy::inverse(_pos, pos, heading, az2, rangeM);
+    nd->setIntValue("radial", heading);
+    nd->setDoubleValue("distance-nm", rangeM * SG_METER_TO_NM);
+    
+    heading = nd->getDoubleValue("leg-bearing-true-deg");
+}
+
+void NavDisplay::processNavRadios()
+{
+    _nav1Station = processNavRadio(_navRadio1Node);
+    _nav2Station = processNavRadio(_navRadio2Node);
+    
+    foundPositionedItem(_nav1Station);
+    foundPositionedItem(_nav2Station);
+}
+
+FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio)
+{
+  double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0);
+  FGNavRecord* nav = FGNavList::findByFreq(mhz, _pos, FGNavList::navFilter());
+    if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) {
+        // station was not found
+        return NULL;
+    }
+    
+    
+    return nav;
+}
+
+bool NavDisplay::anyRuleForType(const string& type) const
+{
+    BOOST_FOREACH(SymbolRule* r, _rules) {
+        if (!r->enabled) {
+            continue;
+        }
+    
+        if (r->type == type) {
+            return true;
+        }
+    }
+    
+    return false;
+}
+
+void NavDisplay::findRules(const string& type, const string_set& states, SymbolRuleVector& rules)
+{
+    BOOST_FOREACH(SymbolRule* candidate, _rules) {
+        if (!candidate->enabled || (candidate->type != type)) {
+            continue;
+        }
+        
+        if (candidate->matches(states)) {
+            rules.push_back(candidate);
+        }
+    }
+}
+
+bool NavDisplay::isPositionedShown(FGPositioned* pos)
+{
+  SymbolRuleVector rules;
+  isPositionedShownInner(pos, rules);
+  return !rules.empty();
+}
+
+void NavDisplay::isPositionedShownInner(FGPositioned* pos, SymbolRuleVector& rules)
+{
+  string type = FGPositioned::nameForType(pos->type());
+  boost::to_lower(type);
+  if (!anyRuleForType(type)) {
+    return; // not diplayed at all, we're done
+  }
+  
+  string_set states;
+  computePositionedState(pos, states);
+  
+  findRules(type, states, rules);
+}
+
+void NavDisplay::foundPositionedItem(FGPositioned* pos)
+{
+    if (!pos) {
+        return;
+    }
+    
+    SymbolRuleVector rules;
+    isPositionedShownInner(pos, rules);
+    if (rules.empty()) {
+      return;
+    }
+  
+    SGPropertyNode_ptr vars(new SGPropertyNode);
+    double heading;
+    computePositionedPropsAndHeading(pos, vars, heading);
+    
+    osg::Vec2 projected = projectGeod(pos->geod());
+    if (pos->type() == FGPositioned::RUNWAY) {
+        FGRunway* rwy = (FGRunway*) pos;
+        projected = projectGeod(rwy->threshold());
+    }
+    
+    BOOST_FOREACH(SymbolRule* r, rules) {
+        SymbolInstance* ins = addSymbolInstance(projected, heading, r->getDefinition(), vars);
+        if ((ins)&&(pos->type() == FGPositioned::RUNWAY)) {
+            FGRunway* rwy = (FGRunway*) pos;
+            ins->endPos = projectGeod(rwy->end());
+        }
+    }
+}
+
+void NavDisplay::computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyNode* nd, double& heading)
+{
+    nd->setStringValue("id", pos->ident());
+    nd->setStringValue("name", pos->name());
+    nd->setDoubleValue("elevation-ft", pos->elevation());
+    nd->setIntValue("heading-deg", 0);
+    heading = 0.0;
+    
+    switch (pos->type()) {
+    case FGPositioned::VOR:
+    case FGPositioned::LOC: 
+    case FGPositioned::TACAN: {
+        FGNavRecord* nav = static_cast<FGNavRecord*>(pos);
+        nd->setDoubleValue("frequency-mhz", nav->get_freq());
+        
+        if (pos == _nav1Station) {
+            heading = _navRadio1Node->getDoubleValue("radials/target-radial-deg");
+        } else if (pos == _nav2Station) {
+            heading = _navRadio2Node->getDoubleValue("radials/target-radial-deg");
+        }
+        
+        nd->setIntValue("heading-deg", heading);
+        break;
+    }
+
+    case FGPositioned::AIRPORT:
+    case FGPositioned::SEAPORT:
+    case FGPositioned::HELIPORT:
+        
+        break;
+        
+    case FGPositioned::RUNWAY: {
+        FGRunway* rwy = static_cast<FGRunway*>(pos);
+        heading = rwy->headingDeg();
+        nd->setDoubleValue("heading-deg", heading);
+        nd->setIntValue("length-ft", rwy->lengthFt());
+        nd->setStringValue("airport", rwy->airport()->ident());
+        break;
+    }
+
+    default:
+        break; 
+    }
+}
+
+void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states)
+{
+    if (_routeSources.count(pos) != 0) {
+        states.insert("on-active-route");
+    }
+    
+    flightgear::FlightPlan* fp = _route->flightPlan();
+    switch (pos->type()) {
+    case FGPositioned::VOR:
+    case FGPositioned::LOC:
+        if (pos == _nav1Station) {
+            states.insert("tuned");
+            states.insert("nav1");
+        }
+        
+        if (pos == _nav2Station) {
+            states.insert("tuned");
+            states.insert("nav2");
+        }
+        break;
+    
+    case FGPositioned::AIRPORT:
+    case FGPositioned::SEAPORT:
+    case FGPositioned::HELIPORT:
+        // mark alternates!
+        // once the FMS system has some way to tell us about them, of course
+        
+        if (pos == fp->departureAirport()) {
+            states.insert("departure");
+        }
+        
+        if (pos == fp->destinationAirport()) {
+            states.insert("destination");
+        }
+        break;
+    
+    case FGPositioned::RUNWAY:
+        if (pos == fp->departureRunway()) {
+            states.insert("departure");
+        }
+        
+        if (pos == fp->destinationRunway()) {
+            states.insert("destination");
+        }
+        break;
+    
+    case FGPositioned::OBSTACLE:
+    #if 0    
+        FGObstacle* obs = (FGObstacle*) pos;
+        if (obj->isLit()) {
+            states.insert("lit");
+        }
+        
+        if (obj->getHeightAGLFt() >= 1000) {
+            states.insert("greater-1000-ft");
+        }
+    #endif
+        break;
+    
+    default:
+        break;
+    } // FGPositioned::Type switch
+}
+
+static string mapAINodeToType(SGPropertyNode* model)
+{
+  // assume all multiplayer items are aircraft for the moment. Not ideal.
+  if (!strcmp(model->getName(), "multiplayer")) {
+    return "ai-aircraft";
+  }
+  
+  return string("ai-") + model->getName();
+}
+
+void NavDisplay::processAI()
+{
+    SGPropertyNode *ai = fgGetNode("/ai/models", true);
+    for (int i = ai->nChildren() - 1; i >= 0; i--) {
+        SGPropertyNode *model = ai->getChild(i);
+        if (!model->nChildren()) {
+            continue;
+        }
+        
+    // prefix types with 'ai-', to avoid any chance of namespace collisions
+    // with fg-positioned.
+        string_set ss;
+        computeAIStates(model, ss);        
+        SymbolRuleVector rules;
+        findRules(mapAINodeToType(model), ss, rules);
+        if (rules.empty()) {
+            return; // no rules matched, we can skip this item
+        }
+
+        double heading = model->getDoubleValue("orientation/true-heading-deg");
+        SGGeod aiModelPos = SGGeod::fromDegFt(model->getDoubleValue("position/longitude-deg"), 
+                                            model->getDoubleValue("position/latitude-deg"), 
+                                            model->getDoubleValue("position/altitude-ft"));
+    // compute some additional props
+        int fl = (aiModelPos.getElevationFt() / 1000);
+        model->setIntValue("flight-level", fl * 10);
+                                            
+        osg::Vec2 projected = projectGeod(aiModelPos);
+        BOOST_FOREACH(SymbolRule* r, rules) {
+            addSymbolInstance(projected, heading, r->getDefinition(), (SGPropertyNode*) model);
+        }
+    } // of ai models iteration
+}
+
+void NavDisplay::computeAIStates(const SGPropertyNode* ai, string_set& states)
+{
+    int threatLevel = ai->getIntValue("tcas/threat-level",-1);
+    if (threatLevel < 1)
+      threatLevel = 0;
+  
+    states.insert("tcas");
+  
+    std::ostringstream os;
+    os << "tcas-threat-level-" << threatLevel;
+    states.insert(os.str());
+
+    double vspeed = ai->getDoubleValue("velocities/vertical-speed-fps");
+    if (vspeed < -3.0) {
+        states.insert("descending");
+    } else if (vspeed > 3.0) {
+        states.insert("climbing");
+    }
+}
+
+SymbolInstance* NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars)
+{
+    if (isProjectedClipped(proj)) {
+        return NULL;
+    }
+    
+    if ((def->limitCount > 0) && (def->instanceCount >= def->limitCount)) {
+      return NULL;
+    }
+  
+    ++def->instanceCount;
+    SymbolInstance* sym = new SymbolInstance(proj, heading, def, vars);
+    _symbols.push_back(sym);
+    return sym;
+}
+
+bool NavDisplay::isProjectedClipped(const osg::Vec2& projected) const
+{
+    double size = _odg->size();
+    return (projected.x() < 0.0) ||
+        (projected.y() < 0.0) ||
+        (projected.x() >= size) ||
+            (projected.y() >= size);
+}
+
+void NavDisplay::addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars)
+{
+  string_set stateSet;
+  BOOST_FOREACH(std::string s, simgear::strutils::split(states, ",")) {
+    stateSet.insert(s);
+  }
+  
+  SymbolRuleVector rules;
+  findRules(type, stateSet, rules);
+  if (rules.empty()) {
+    return; // no rules matched, we can skip this item
+  }
+    
+  osg::Vec2 projected = projectGeod(pos);
+  BOOST_FOREACH(SymbolRule* r, rules) {
+    addSymbolInstance(projected, heading, r->getDefinition(), vars);
+  }
+}
+
+void NavDisplay::addTestSymbols()
+{
+  _pos = SGGeod::fromDeg(-122.3748889, 37.6189722); // KSFO
+  
+  SGGeod a1;
+  double dummy;
+  SGGeodesy::direct(_pos, 45.0, 20.0 * SG_NM_TO_METER, a1, dummy);
+  
+  addTestSymbol("airport", "", a1, 0.0, NULL);
+  
+  SGGeodesy::direct(_pos, 95.0, 40.0 * SG_NM_TO_METER, a1, dummy);
+  
+  addTestSymbol("vor", "", a1, 0.0, NULL);
+  
+  SGGeodesy::direct(_pos, 120, 80.0 * SG_NM_TO_METER, a1, dummy);
+  
+  addTestSymbol("airport", "destination", a1, 0.0, NULL);
+  
+  SGGeodesy::direct(_pos, 80.0, 20.0 * SG_NM_TO_METER, a1, dummy);  
+  addTestSymbol("fix", "", a1, 0.0, NULL);
+
+  
+  SGGeodesy::direct(_pos, 140.0, 20.0 * SG_NM_TO_METER, a1, dummy);  
+  addTestSymbol("fix", "", a1, 0.0, NULL);
+  
+  SGGeodesy::direct(_pos, 110.0, 10.0 * SG_NM_TO_METER, a1, dummy);  
+  addTestSymbol("fix", "", a1, 0.0, NULL);
+  
+  SGGeodesy::direct(_pos, 110.0, 5.0 * SG_NM_TO_METER, a1, dummy);  
+  addTestSymbol("fix", "", a1, 0.0, NULL);
+}
+
+void NavDisplay::addRule(SymbolRule* r)
+{
+    _rules.push_back(r);
+}
+
diff --git a/src/Cockpit/NavDisplay.hxx b/src/Cockpit/NavDisplay.hxx
new file mode 100644 (file)
index 0000000..870c3cc
--- /dev/null
@@ -0,0 +1,202 @@
+// Wx Radar background texture
+//
+// Written by Harald JOHNSEN, started May 2005.
+// With major amendments by Vivian MEAZZA May 2007
+// Ported to OSG by Tim MOORE Jun 2007
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef _INST_ND_HXX
+#define _INST_ND_HXX
+
+#include <osg/ref_ptr>
+#include <osg/Geode>
+#include <osg/Texture2D>
+#include <osgText/Text>
+
+#include <simgear/props/props.hxx>
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <Navaids/positioned.hxx>
+
+class FGODGauge;
+class FGRouteMgr;
+class FGNavRecord;
+
+class SymbolInstance;
+class SymbolDef;
+class SymbolRule;
+
+namespace flightgear
+{
+    class Waypt;
+}
+
+typedef std::set<std::string> string_set;
+typedef std::vector<SymbolRule*> SymbolRuleVector;
+typedef std::vector<SymbolDef*> SymbolDefVector;
+
+class NavDisplay : public SGSubsystem
+{
+public:
+
+    NavDisplay(SGPropertyNode *node);
+    virtual ~NavDisplay();
+
+    virtual void init();
+    virtual void update(double dt);
+
+    void invalidatePositionedCache()
+    {
+        _cachedItemsValid = false;
+    }
+    
+    double textureSize() const
+    { return _textureSize; }
+    
+    void forceUpdate()
+    { _forceUpdate = true; }
+    
+    bool anyRuleForType(const std::string& type) const;
+    bool isPositionedShown(FGPositioned* pos);
+protected:
+    std::string _name;
+    int _num;
+    double _time;
+    double _updateInterval;
+    bool _forceUpdate;
+    
+    SGPropertyNode_ptr _serviceable_node;
+    SGPropertyNode_ptr _Instrument;
+    SGPropertyNode_ptr _radar_mode_control_node;
+    SGPropertyNode_ptr _user_heading_node;
+    SGPropertyNode_ptr _testModeNode;
+  
+    FGODGauge *_odg;
+
+    // Convenience function for creating a property node with a
+    // default value
+    template<typename DefaultType>
+    SGPropertyNode *getInstrumentNode(const char *name, DefaultType value);
+
+private:
+    friend class SymbolRule;
+    friend class SymbolDef;
+  
+    void addRule(SymbolRule*);
+  
+    void addSymbolsToScene();
+    void addSymbolToScene(SymbolInstance* sym);
+    void limitDisplayedSymbols();
+    
+    void findItems();
+    void isPositionedShownInner(FGPositioned* pos, SymbolRuleVector& rules);
+    void foundPositionedItem(FGPositioned* pos);
+    void computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyNode* nd, double& heading);
+    void computePositionedState(FGPositioned* pos, string_set& states);
+    void processRoute();
+    void computeWayptPropsAndHeading(flightgear::Waypt* wpt, const SGGeod& pos, SGPropertyNode* nd, double& heading);
+    void processNavRadios();
+    FGNavRecord* processNavRadio(const SGPropertyNode_ptr& radio);
+    void processAI();
+    void computeAIStates(const SGPropertyNode* ai, string_set& states);
+    
+    void findRules(const std::string& type, const string_set& states, SymbolRuleVector& rules);
+    
+    SymbolInstance* addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars);
+    void addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec4& color);
+    osg::Vec2 projectBearingRange(double bearingDeg, double rangeNm) const;
+    osg::Vec2 projectGeod(const SGGeod& geod) const;
+    bool isProjectedClipped(const osg::Vec2& projected) const;
+    void updateFont();
+    
+    void addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars);
+    void addTestSymbols();
+  
+    std::string _texture_path;
+    unsigned int _textureSize;
+
+    float _scale;   // factor to convert nm to display units
+    float _view_heading;
+    
+    SGPropertyNode_ptr _Radar_controls;
+
+
+    
+    SGPropertyNode_ptr _font_node;
+    SGPropertyNode_ptr _ai_enabled_node;
+    SGPropertyNode_ptr _navRadio1Node;
+    SGPropertyNode_ptr _navRadio2Node;
+    SGPropertyNode_ptr _xCenterNode, _yCenterNode;
+    SGPropertyNode_ptr _viewHeadingNode;
+  
+    osg::ref_ptr<osg::Texture2D> _symbolTexture;
+    osg::ref_ptr<osg::Geode> _radarGeode;
+    osg::ref_ptr<osg::Geode> _textGeode;
+  
+    osg::Geometry *_geom;
+  
+    osg::DrawArrays* _symbolPrimSet;
+    osg::Vec2Array *_vertices;
+    osg::Vec2Array *_texCoords;
+    osg::Vec4Array* _quadColors;
+    
+    osg::Geometry* _lineGeometry;
+    osg::DrawArrays* _linePrimSet;
+    osg::Vec2Array* _lineVertices;
+    osg::Vec4Array* _lineColors;
+  
+  
+    osg::Matrixf _centerTrans;
+    osg::Matrixf _projectMat;
+    
+    osg::ref_ptr<osgText::Font> _font;
+    osg::Vec4 _font_color;
+    float _font_size;
+    float _font_spacing;
+
+    FGRouteMgr* _route;
+    SGGeod _pos;
+    double _rangeNm;
+    SGPropertyNode_ptr _rangeNode;
+    
+    SymbolDefVector _definitions;
+    SymbolRuleVector _rules;
+    FGNavRecord* _nav1Station;
+    FGNavRecord* _nav2Station;
+    std::vector<SymbolInstance*> _symbols;
+    std::set<FGPositioned*> _routeSources;
+    
+    bool _cachedItemsValid;
+    SGVec3d _cachedPos;
+    FGPositioned::List _itemsInRange;
+    SGPropertyNode_ptr _excessDataNode;
+    int _maxSymbols;
+  
+    class CacheListener;
+    std::auto_ptr<CacheListener> _cacheListener;
+    
+    class ForceUpdateListener;
+    std::auto_ptr<ForceUpdateListener> _forceUpdateListener;
+};
+
+#endif // _INST_ND_HXX
diff --git a/src/Cockpit/agradar.cxx b/src/Cockpit/agradar.cxx
new file mode 100644 (file)
index 0000000..768fdea
--- /dev/null
@@ -0,0 +1,325 @@
+// Air Ground Radar
+//
+// Written by Vivian MEAZZA, started Feb 2008.
+//
+//
+// Copyright (C) 2008  Vivian Meazza
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+//
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <Main/fg_props.hxx>
+#include <Main/globals.hxx>
+#include "agradar.hxx"
+
+
+agRadar::agRadar(SGPropertyNode *node) : wxRadarBg(node) 
+{
+
+    _name = node->getStringValue("name", "air-ground-radar");
+    _num = node->getIntValue("number", 0);
+
+}
+
+agRadar::~agRadar ()
+{
+}
+
+void
+agRadar::init ()
+{
+    _user_hdg_deg_node      = fgGetNode("/orientation/heading-deg", true);
+    _user_pitch_deg_node    = fgGetNode("/orientation/pitch-deg", true);
+    _user_roll_deg_node     = fgGetNode("/orientation/roll-deg", true);
+
+    _terrain_warning_node   = fgGetNode("/sim/alarms/terrain-warning", true);
+    _terrain_warning_node->setBoolValue(false);
+
+    wxRadarBg::init();
+
+    // those properties are used by a radar instrument of a MFD
+    // input switch = OFF | TST | STBY | ON
+    // input mode = WX | WXA | MAP | TW
+    // output status = STBY | TEST | WX | WXA | MAP | blank
+    // input lightning = true | false
+    // input TRK = +/- n degrees
+    // input TILT = +/- n degree
+    // input autotilt = true | false
+    // input range = n nm (20/40/80)
+    // input display-mode = arc | rose | map | plan
+
+    _Instrument->setFloatValue("trk", 0.0);
+    _Instrument->setFloatValue("tilt",-2.5);
+    _Instrument->setStringValue("status","");
+    _Instrument->setIntValue("mode-control", 5); 
+
+    _Instrument->setBoolValue("stabilisation/roll", false);
+    _Instrument->setBoolValue("stabilisation/pitch", false);
+    
+    _xOffsetMNode = getInstrumentNode("antenna/x-offset-m", 0.0);
+    _yOffsetMNode = getInstrumentNode("antenna/y-offset-m", 0.0);
+    _zOffsetMNode = getInstrumentNode("antenna/z-offset-m", 0.0);
+
+    _elevLimitDegNode = getInstrumentNode("terrain-warning/elev-limit-deg", 2.0);
+    _elevStepDegNode = getInstrumentNode("terrain-warning/elev-step-deg", 1.0);
+    _azLimitDegNode = getInstrumentNode("terrain-warning/az-limit-deg", 1.0);
+    _azStepDegNode = getInstrumentNode("terrain-warning/az-step-deg", 1.5);
+    _maxRangeMNode = getInstrumentNode("terrain-warning/max-range-m", 4000.0);
+    _minRangeMNode = getInstrumentNode("terrain-warning/min-range-m", 250.0);
+    _tiltNode = getInstrumentNode("terrain-warning/tilt", -2.0);
+
+    _brgDegNode = getInstrumentNode("terrain-warning/hit/brg-deg", 0.0);
+    _rangeMNode = getInstrumentNode("terrain-warning/hit/range-m", 0.0);
+    _elevationMNode = getInstrumentNode("terrain-warning/hit/elevation-m", 0.0);
+    _materialNode = getInstrumentNode("terrain-warning/hit/material", "");
+    _bumpinessNode = getInstrumentNode("terrain-warning/hit/bumpiness", 0.0);
+
+    _rollStabNode = getInstrumentNode("terrain-warning/stabilisation/roll",
+                                      true);
+    _pitchStabNode = getInstrumentNode("terrain-warning/stabilisation/pitch",
+                                       false);
+//    cout << "init done" << endl;
+
+}
+
+void
+agRadar::update (double delta_time_sec)
+{
+    if (!_sceneryLoaded->getBoolValue())
+        return;
+
+    if ( !_odg || ! _serviceable_node->getBoolValue() ) {
+        _Instrument->setStringValue("status","");
+        return;
+    }
+
+    _time += delta_time_sec;
+
+    if (_time < _interval)
+        return;
+
+    _time = 0.0;
+
+    update_terrain();
+//    wxRadarBg::update(delta_time_sec);
+}
+
+void
+agRadar::setUserPos()
+{
+    userpos.setLatitudeDeg(_user_lat_node->getDoubleValue());
+    userpos.setLongitudeDeg(_user_lon_node->getDoubleValue());
+    userpos.setElevationM(_user_alt_node->getDoubleValue() * SG_FEET_TO_METER);
+}
+
+SGVec3d
+agRadar::getCartUserPos() const {
+    SGVec3d cartUserPos = SGVec3d::fromGeod(userpos);
+    return cartUserPos;
+}
+
+SGVec3d
+agRadar::getCartAntennaPos() const {
+
+    float yaw   = _user_hdg_deg_node->getDoubleValue();
+    float pitch = _user_pitch_deg_node->getDoubleValue();
+    float roll  = _user_roll_deg_node->getDoubleValue();
+
+    double x_offset_m =_xOffsetMNode->getDoubleValue();
+    double y_offset_m =_yOffsetMNode->getDoubleValue();
+    double z_offset_m =_zOffsetMNode->getDoubleValue();
+
+    // convert geodetic positions to geocentered
+    SGVec3d cartuserPos = getCartUserPos();
+
+    // Transform to the right coordinate frame, configuration is done in
+    // the x-forward, y-right, z-up coordinates (feet), computation
+    // in the simulation usual body x-forward, y-right, z-down coordinates
+    // (meters) )
+    SGVec3d _off(x_offset_m, y_offset_m, -z_offset_m);
+
+    // Transform the user position to the horizontal local coordinate system.
+    SGQuatd hlTrans = SGQuatd::fromLonLat(userpos);
+
+    // and postrotate the orientation of the user model wrt the horizontal
+    // local frame
+    hlTrans *= SGQuatd::fromYawPitchRollDeg(yaw,pitch,roll);
+
+    // The offset converted to the usual body fixed coordinate system
+    // rotated to the earth-fixed coordinates axis
+    SGVec3d off = hlTrans.backTransform(_off);
+
+    // Add the position offset of the user model to get the geocentered position
+    SGVec3d offsetPos = cartuserPos + off;
+
+    return offsetPos;
+}
+
+void
+agRadar::setAntennaPos() {
+    SGGeodesy::SGCartToGeod(getCartAntennaPos(), antennapos);
+}
+
+void
+agRadar::setUserVec(double az, double el)
+{
+    float yaw   = _user_hdg_deg_node->getDoubleValue();
+    float pitch = _user_pitch_deg_node->getDoubleValue();
+    float roll  = _user_roll_deg_node->getDoubleValue();
+    double tilt = _Instrument->getDoubleValue("tilt");
+    double trk  = _Instrument->getDoubleValue("trk");
+    bool roll_stab   = _Instrument->getBoolValue("stabilisation/roll");
+    bool pitch_stab  = _Instrument->getBoolValue("stabilisation/pitch");
+
+    SGQuatd offset = SGQuatd::fromYawPitchRollDeg(az + trk, el + tilt, 0);
+
+    // Transform the antenna position to the horizontal local coordinate system.
+    SGQuatd hlTrans = SGQuatd::fromLonLat(antennapos);
+
+    // and postrotate the orientation of the radar wrt the horizontal
+    // local frame
+    hlTrans *= SGQuatd::fromYawPitchRollDeg(yaw,
+                                            pitch_stab ? 0 :pitch,
+                                            roll_stab ? 0 : roll);
+    hlTrans *= offset;
+
+    // now rotate the rotation vector back into the
+    // earth centered frames coordinates
+    SGVec3d angleaxis(1,0,0);
+    uservec = hlTrans.backTransform(angleaxis);
+
+}
+
+bool
+agRadar::getMaterial(){
+
+    const simgear::BVHMaterial* mat = 0;
+    if (globals->get_scenery()->get_elevation_m(hitpos, _elevation_m, &mat)){
+        //_ht_agl_ft = pos.getElevationFt() - _elevation_m * SG_METER_TO_FEET;
+        const SGMaterial* material = dynamic_cast<const SGMaterial*>(mat);
+        if (material) {
+            const std::vector<std::string>& names = material->get_names();
+
+            _solid = material->get_solid();
+            _load_resistance = material->get_load_resistance();
+            _frictionFactor = material->get_friction_factor();
+            _bumpinessFactor = material->get_bumpiness();
+
+            if (!names.empty()) 
+                _mat_name = names[0];
+            else
+                _mat_name = "";
+
+        }
+        /*cout << "material " << mat_name 
+        << " solid " << _solid 
+        << " load " << _load_resistance
+        << " frictionFactor " << frictionFactor 
+        << " _bumpinessFactor " << _bumpinessFactor
+        << endl;*/
+        return true;
+    } else {
+        return false;
+    }
+
+}
+
+void
+agRadar::update_terrain()
+{
+    int mode = _radar_mode_control_node->getIntValue();
+
+    double el_limit = 1;
+    double el_step = 1;
+    double az_limit = 50;
+    double az_step = 10;
+    double max_range = 40000;
+    double min_range = 250;
+    double tilt = -2.5;
+    bool roll_stab   = _rollStabNode->getBoolValue();
+    bool pitch_stab  = _pitchStabNode->getBoolValue();
+    const char* status = "";
+    bool hdg_mkr = true;
+
+    if (mode == 5){
+        status = "TW";
+        hdg_mkr = false;
+        tilt        = _tiltNode->getDoubleValue();
+        el_limit    = _elevLimitDegNode->getDoubleValue();
+        el_step     = _elevStepDegNode->getDoubleValue();
+        az_limit    = _azLimitDegNode->getDoubleValue();
+        az_step     = _azStepDegNode->getDoubleValue();
+        max_range   = _maxRangeMNode->getDoubleValue();
+        min_range   = _minRangeMNode->getDoubleValue();
+    }
+
+    _Instrument->setDoubleValue("tilt", tilt);
+    _Instrument->setBoolValue("stabilisation/roll", roll_stab);
+    _Instrument->setBoolValue("stabilisation/pitch", pitch_stab);
+    _Instrument->setStringValue("status", status);
+    _Instrument->setDoubleValue("limit-deg", az_limit);
+    _Instrument->setBoolValue("heading-marker", hdg_mkr);
+    setUserPos();
+    setAntennaPos();
+    SGVec3d cartantennapos = getCartAntennaPos();
+    
+    for(double brg = -az_limit; brg <= az_limit; brg += az_step){
+        for(double elev = el_limit; elev >= - el_limit; elev -= el_step){
+            setUserVec(brg, elev);
+            SGVec3d nearestHit;
+            globals->get_scenery()->get_cart_ground_intersection(cartantennapos, uservec, nearestHit);
+            SGGeodesy::SGCartToGeod(nearestHit, hitpos);
+
+            double course1, course2, distance;
+
+            SGGeodesy::inverse(hitpos, antennapos, course1, course2, distance);
+
+            if (distance >= min_range && distance <= max_range) {
+                _terrain_warning_node->setBoolValue(true);
+                getMaterial();
+                _brgDegNode->setDoubleValue(course2);
+                _rangeMNode->setDoubleValue(distance);
+                _materialNode->setStringValue(_mat_name.c_str());
+                _bumpinessNode->setDoubleValue(_bumpinessFactor);
+                _elevationMNode->setDoubleValue(_elevation_m);
+            } else {
+                _terrain_warning_node->setBoolValue(false);
+                _brgDegNode->setDoubleValue(0);
+                _rangeMNode->setDoubleValue(0);
+                _materialNode->setStringValue("");
+                _bumpinessNode->setDoubleValue(0);
+                _elevationMNode->setDoubleValue(0);
+            }
+
+            //cout  << "usr hdg " << _user_hdg_deg_node->getDoubleValue()
+            //    << " ant brg " << course2 
+            //    << " elev " << _Instrument->getDoubleValue("tilt")
+            //    << " gnd rng nm " << distance * SG_METER_TO_NM
+            //    << " ht " << hitpos.getElevationFt()
+            //    << " mat " << _mat_name
+            //    << " solid " << _solid
+            //    << " bumpiness " << _bumpinessFactor
+            //    << endl;
+
+        }
+    }
+}
+
+
diff --git a/src/Cockpit/agradar.hxx b/src/Cockpit/agradar.hxx
new file mode 100644 (file)
index 0000000..dda152e
--- /dev/null
@@ -0,0 +1,94 @@
+// Air Ground Radar
+//
+// Written by Vivian MEAZZA, started Feb 2008.
+// 
+//
+// Copyright (C) 2008  Vivain MEAZZA - vivian.meazza@lineone.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+//
+
+#ifndef _INST_AGRADAR_HXX
+#define _INST_AGRADAR_HXX
+
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <Scenery/scenery.hxx>
+#include <simgear/scene/material/mat.hxx>
+
+#include "wxradar.hxx"
+
+class agRadar : public wxRadarBg{
+public:
+
+    agRadar ( SGPropertyNode *node );
+    agRadar ();
+    virtual ~agRadar ();
+
+    virtual void init ();
+    virtual void update (double dt);
+
+    void setUserPos();
+    void setUserVec(double az, double el);
+    void update_terrain();
+    void setAntennaPos();
+
+    bool getMaterial();
+
+    double _load_resistance;    // ground load resistanc N/m^2
+    double _frictionFactor;     // dimensionless modifier for Coefficient of Friction
+    double _bumpinessFactor;    // dimensionless modifier for Bumpiness
+    double _elevation_m;        // ground elevation in meters
+    bool   _solid;              // if true ground is solid for FDMs
+
+    std::string _mat_name; // ground material
+
+    SGVec3d getCartUserPos() const;
+    SGVec3d getCartAntennaPos()const;
+
+    SGVec3d uservec;
+
+    SGPropertyNode_ptr _user_hdg_deg_node;
+    SGPropertyNode_ptr _user_roll_deg_node;
+    SGPropertyNode_ptr _user_pitch_deg_node;
+    SGPropertyNode_ptr _terrain_warning_node;
+
+    SGPropertyNode_ptr _xOffsetMNode;
+    SGPropertyNode_ptr _yOffsetMNode;
+    SGPropertyNode_ptr _zOffsetMNode;
+
+    SGPropertyNode_ptr _elevLimitDegNode;
+    SGPropertyNode_ptr _elevStepDegNode;
+    SGPropertyNode_ptr _azLimitDegNode;
+    SGPropertyNode_ptr _azStepDegNode;
+    SGPropertyNode_ptr _maxRangeMNode;
+    SGPropertyNode_ptr _minRangeMNode;
+    SGPropertyNode_ptr _tiltNode;
+
+    SGPropertyNode_ptr _brgDegNode;
+    SGPropertyNode_ptr _rangeMNode;
+    SGPropertyNode_ptr _elevationMNode;
+    SGPropertyNode_ptr _materialNode;
+    SGPropertyNode_ptr _bumpinessNode;
+
+    SGPropertyNode_ptr _rollStabNode;
+    SGPropertyNode_ptr _pitchStabNode;
+
+    SGGeod userpos;
+    SGGeod hitpos;
+    SGGeod antennapos;
+};
+
+#endif // _INST_AGRADAR_HXX
diff --git a/src/Cockpit/cockpitDisplayManager.cxx b/src/Cockpit/cockpitDisplayManager.cxx
new file mode 100644 (file)
index 0000000..18e6e7d
--- /dev/null
@@ -0,0 +1,126 @@
+// cockpitDisplayManager.cxx -- manage cockpit displays, typically
+// rendered using a sub-camera or render-texture
+//
+// Copyright (C) 2012 James Turner  zakalawe@mac.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "cockpitDisplayManager.hxx"
+
+#include <simgear/structure/exception.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/sg_inlines.h>
+#include <simgear/props/props_io.hxx>
+
+#include <boost/foreach.hpp>
+
+#include <Main/fg_props.hxx>
+#include <Main/globals.hxx>
+
+#include "agradar.hxx"
+#include "NavDisplay.hxx"
+#include "groundradar.hxx"
+#include "wxradar.hxx"
+
+namespace flightgear
+{
+
+CockpitDisplayManager::CockpitDisplayManager () 
+{    
+}
+
+CockpitDisplayManager::~CockpitDisplayManager ()
+{
+}
+
+SGSubsystem::InitStatus CockpitDisplayManager::incrementalInit()
+{
+  init();
+  return INIT_DONE;
+}
+
+void CockpitDisplayManager::init()
+{
+  SGPropertyNode_ptr config_props = new SGPropertyNode;
+  SGPropertyNode* path_n = fgGetNode("/sim/instrumentation/path");
+  if (!path_n) {
+    SG_LOG(SG_COCKPIT, SG_WARN, "No instrumentation model specified for this model!");
+    return;
+  }
+
+  SGPath config = globals->resolve_aircraft_path(path_n->getStringValue());
+  SG_LOG( SG_COCKPIT, SG_INFO, "Reading cockpit displays from " << config.str() );
+
+  try {
+    readProperties( config.str(), config_props );
+    if (!build(config_props)) {
+      throw sg_exception(
+                    "Detected an internal inconsistency in the instrumentation\n"
+                    "system specification file.  See earlier errors for details.");
+    }
+  } catch (const sg_exception& e) {
+    SG_LOG(SG_COCKPIT, SG_ALERT, "Failed to load instrumentation system model: "
+                    << config.str() << ":" << e.getFormattedMessage() );
+  }
+
+  // bind() created instruments before init.
+  BOOST_FOREACH(std::string s, _displays) {
+      get_subsystem(s)->bind();
+  }
+  
+  SGSubsystemGroup::init();
+}
+
+bool CockpitDisplayManager::build (SGPropertyNode* config_props)
+{
+    for ( int i = 0; i < config_props->nChildren(); ++i ) {
+        SGPropertyNode *node = config_props->getChild(i);
+        std::string name = node->getName();
+
+        std::ostringstream subsystemname;
+        subsystemname << "instrument-" << i << '-'
+                << node->getStringValue("name", name.c_str());
+        int index = node->getIntValue("number", 0);
+        if (index > 0)
+            subsystemname << '['<< index << ']';
+        std::string id = subsystemname.str();
+      
+        if ( name == "radar" ) {
+            set_subsystem( id, new wxRadarBg ( node ) );
+
+        } else if ( name == "groundradar" ) {
+            set_subsystem( id, new GroundRadar( node ) );
+
+        } else if ( name == "air-ground-radar" ) {
+            set_subsystem( id, new agRadar( node ) );
+      
+        } else if ( name == "navigation-display" ) {
+            set_subsystem( id, new NavDisplay( node ) );
+            
+        } else {
+            // probably a regular instrument
+            continue;
+        }
+        // only add to our list if we build a display
+        _displays.push_back(id);
+    }
+    return true;
+}
+
+} // of namespace flightgear
diff --git a/src/Cockpit/cockpitDisplayManager.hxx b/src/Cockpit/cockpitDisplayManager.hxx
new file mode 100644 (file)
index 0000000..53febaf
--- /dev/null
@@ -0,0 +1,54 @@
+// cockpitDisplayManager.hxx -- manage cockpit displays, typically
+// rendered using a sub-camera or render-texture
+//
+// Copyright (C) 2012 James Turner  zakalawe@mac.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef COCKPIT_DISPLAY_MGR_HXX
+#define COCKPIT_DISPLAY_MGR_HXX 1
+
+#include <simgear/compiler.h>
+
+#include <vector>
+#include <simgear/structure/subsystem_mgr.hxx>
+
+class SGPropertyNode;
+
+namespace flightgear
+{
+    
+/**
+ * Manage aircraft displays.
+ */
+class CockpitDisplayManager : public SGSubsystemGroup
+{
+public:
+
+    CockpitDisplayManager ();
+    virtual ~CockpitDisplayManager ();
+    
+    virtual void init();
+    virtual InitStatus incrementalInit();
+
+private:
+    bool build (SGPropertyNode* config_props);
+        
+    std::vector<std::string> _displays;
+};
+
+} // of namespace lfightgear
+
+#endif // COCKPIT_DISPLAY_MGR_HXX
diff --git a/src/Cockpit/groundradar.cxx b/src/Cockpit/groundradar.cxx
new file mode 100644 (file)
index 0000000..b7d2834
--- /dev/null
@@ -0,0 +1,354 @@
+//  groundradar.cxx - Background layer for the ATC radar.
+//
+//  Copyright (C) 2007 Csaba Halasz.
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License as
+//  published by the Free Software Foundation; either version 2 of the
+//  License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful, but
+//  WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <cassert>
+
+#include <osg/Node>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Camera>
+#include <osg/Texture2D>
+#include <osgViewer/Viewer>
+
+#include <osgText/Text>
+#include <osgDB/Registry>
+#include <osgDB/ReaderWriter>
+#include <osgUtil/Tessellator>
+
+#include <Main/fg_props.hxx>
+#include <Main/globals.hxx>
+#include <Viewer/renderer.hxx>
+#include <Cockpit/panel.hxx>
+#include <Airports/simple.hxx>
+#include <Airports/runways.hxx>
+#include <Airports/pavement.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/beziercurve.hxx>
+
+#include "groundradar.hxx"
+
+static const char* airport_source_node_name = "airport-id-source";
+static const char* default_airport_node_name = "/sim/airport/closest-airport-id";
+static const char* texture_node_name = "texture-name";
+static const char* default_texture_name = "Aircraft/Instruments/Textures/od_groundradar.rgb";
+static const char* range_source_node_name = "range-source";
+static const char* default_range_node_name = "/instrumentation/radar/range";
+
+struct SingleFrameCallback : public osg::Camera::DrawCallback
+{
+    virtual void operator () (const osg::Camera& camera) const
+    {
+        const_cast<osg::Camera&>(camera).setNodeMask(0);
+    }
+};
+
+GroundRadar::GroundRadar(SGPropertyNode *node)
+{
+    _airport_node = fgGetNode(node->getStringValue(airport_source_node_name, default_airport_node_name), true);
+    _range_node = fgGetNode(node->getStringValue(range_source_node_name, default_range_node_name), true);
+    createTexture(node->getStringValue(texture_node_name, default_texture_name));
+    updateTexture();
+    _airport_node->addChangeListener(this);
+    _range_node->addChangeListener(this);
+}
+
+GroundRadar::~GroundRadar()
+{
+    _airport_node->removeChangeListener(this);
+    _range_node->removeChangeListener(this);
+}
+
+void GroundRadar::update (double /* dt */)
+{
+  
+}
+
+void GroundRadar::valueChanged(SGPropertyNode*)
+{
+    updateTexture();
+}
+
+inline static osg::Vec3 fromPolar(double fi, double r)
+{
+    return osg::Vec3(sin(fi * SGD_DEGREES_TO_RADIANS) * r, cos(fi * SGD_DEGREES_TO_RADIANS) * r, 0);
+}
+
+void GroundRadar::createTexture(const char* texture_name)
+{
+    setSize(TextureHalfSize + TextureHalfSize);
+    allocRT();
+
+    _geode = new osg::Geode();
+    osg::StateSet* stateset = _geode->getOrCreateStateSet();
+    stateset->setMode(GL_BLEND, osg::StateAttribute::OFF);
+
+    osg::Vec4Array* taxi_color = new osg::Vec4Array;
+    taxi_color->push_back(osg::Vec4(0.0f, 0.5f, 0.0f, 1.0f));
+    osg::Vec4Array* rwy_color = new osg::Vec4Array;
+    rwy_color->push_back(osg::Vec4(0.0f, 0.5f, 0.5f, 1.0f));
+
+    osg::Geometry *taxi_geom = new osg::Geometry();
+    taxi_geom->setColorArray(taxi_color);
+    taxi_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
+    taxi_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 0)); // Taxiways
+    _geode->addDrawable(taxi_geom);
+
+    osg::Geometry *pvt_geom = new osg::Geometry();
+    pvt_geom->setColorArray(taxi_color);
+    pvt_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
+    // no primitive set for the moment. It needs tessellation
+    _geode->addDrawable(pvt_geom);
+
+    osg::Geometry *rwy_geom = new osg::Geometry();
+    rwy_geom->setColorArray(rwy_color);
+    rwy_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
+    rwy_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 0)); // Runways
+    _geode->addDrawable(rwy_geom);
+
+    osg::Camera* camera = getCamera();
+    camera->setPostDrawCallback(new SingleFrameCallback());
+    camera->addChild(_geode.get());
+    camera->setNodeMask(0);
+    camera->setProjectionMatrixAsOrtho2D(0, getTexture()->getTextureWidth(), 0, getTexture()->getTextureHeight());
+
+    // Texture in the 2D panel system
+    FGTextureManager::addTexture(texture_name, getTexture());
+}
+
+void GroundRadar::addRunwayVertices(const FGRunwayBase* aRunway, double aTowerLat, double aTowerLon, double aScale, osg::Vec3Array* aVertices)
+{
+  double az1, az2, dist_m;
+  geo_inverse_wgs_84(aTowerLat, aTowerLon, aRunway->latitude(), aRunway->longitude(), &az1, &az2, &dist_m);
+
+  osg::Vec3 center = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
+  osg::Vec3 leftcenter = fromPolar(aRunway->headingDeg(), aRunway->lengthM() * aScale / 2) + center;
+  osg::Vec3 lefttop = fromPolar(aRunway->headingDeg() - 90, aRunway->widthM() * aScale / 2) + leftcenter;
+  osg::Vec3 leftbottom = leftcenter * 2 - lefttop;
+  osg::Vec3 rightbottom = center * 2 - lefttop;
+  osg::Vec3 righttop = center * 2 - leftbottom;
+
+  aVertices->push_back(lefttop);
+  aVertices->push_back(leftbottom);
+  aVertices->push_back(rightbottom);
+  aVertices->push_back(righttop);
+}
+
+osg::Geometry *GroundRadar::addPavementGeometry(const FGPavement* aPavement, double aTowerLat, double aTowerLon, double aScale)
+{
+
+  osg::ref_ptr<osgUtil::Tessellator> tess = new osgUtil::Tessellator;
+  osg::ref_ptr<osg::Geometry> polygon = new osg::Geometry;
+  osg::ref_ptr<osg::Vec3Array> pts = new osg::Vec3Array;
+
+  double az1, az2, dist_m;
+  const FGPavement::NodeList &nodeLst = aPavement->getNodeList();
+  FGPavement::NodeList::const_iterator it = nodeLst.begin(),
+                                        loopBegin = it;
+  while ( it != nodeLst.end() )
+  {
+    bool close = (*it)->mClose;
+    geo_inverse_wgs_84(aTowerLat, aTowerLon, (*it)->mPos.getLatitudeDeg(), (*it)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
+    osg::Vec3 p1 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
+    const FGPavement::BezierNode *bn = dynamic_cast<const FGPavement::BezierNode *>( it->ptr() );
+    if ( bn != 0 )
+    {
+      geo_inverse_wgs_84(aTowerLat, aTowerLon, bn->mControl.getLatitudeDeg(), bn->mControl.getLongitudeDeg(), &az1, &az2, &dist_m);
+      osg::Vec3 p2 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0),
+                p3;
+      ++it;
+      if ( it == nodeLst.end() || close )
+      {
+        geo_inverse_wgs_84(aTowerLat, aTowerLon, (*loopBegin)->mPos.getLatitudeDeg(), (*loopBegin)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
+      }
+      else
+      {
+        geo_inverse_wgs_84(aTowerLat, aTowerLon, (*it)->mPos.getLatitudeDeg(), (*it)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
+      }
+      p3 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
+      simgear::BezierCurve<osg::Vec3> bCurv( p1, p2, p3 );
+      simgear::BezierCurve<osg::Vec3>::PointList &ptList = bCurv.pointList();
+      for ( simgear::BezierCurve<osg::Vec3>::PointList::iterator ii = ptList.begin(); ii != ptList.end(); ++ii )
+      {
+        pts->push_back( *ii );
+      }
+      pts->pop_back(); // Last point belongs to next segment
+    }
+    else
+    {
+      pts->push_back( p1 );
+      ++it;
+    }
+
+    if ( close ) // One loop for the moment
+      break;
+  }
+  geo_inverse_wgs_84(aTowerLat, aTowerLon, (*loopBegin)->mPos.getLatitudeDeg(), (*loopBegin)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
+  osg::Vec3 p1 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
+  pts->push_back( p1 );
+  polygon->setVertexArray( pts.get() );
+
+  polygon->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, 0, pts->size() ) );
+
+  tess->setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
+  tess->setBoundaryOnly( false );
+  tess->setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
+  tess->retessellatePolygons( *polygon );
+  return polygon.release();
+}
+
+void GroundRadar::updateTexture()
+{
+    osg::ref_ptr<osg::Vec3Array> rwy_vertices = new osg::Vec3Array;
+    osg::ref_ptr<osg::Vec3Array> taxi_vertices = new osg::Vec3Array;
+    osg::ref_ptr<osg::Vec3Array> pvt_vertices = new osg::Vec3Array;
+
+    const string airport_name = _airport_node->getStringValue();
+
+    const FGAirport* airport = fgFindAirportID(airport_name);
+    if (airport == 0)
+        return;
+
+    const SGGeod& tower_location = airport->getTowerLocation();
+    const double tower_lat = tower_location.getLatitudeDeg();
+    const double tower_lon = tower_location.getLongitudeDeg();
+    double scale = SG_METER_TO_NM * 200 / _range_node->getDoubleValue();
+  
+    const FGAirport* apt = fgFindAirportID(airport_name);
+    assert(apt);
+
+    for (unsigned int i=0; i<apt->numTaxiways(); ++i)
+    {
+      FGTaxiway* txwy(apt->getTaxiwayByIndex(i));
+      addRunwayVertices(txwy, tower_lat, tower_lon, scale, taxi_vertices.get());
+    }
+    osg::Geometry *taxi_geom = dynamic_cast<osg::Geometry *>(_geode->getDrawable(0));
+    taxi_geom->setVertexArray(taxi_vertices.get());
+    osg::DrawArrays* taxi = dynamic_cast<osg::DrawArrays*>(taxi_geom->getPrimitiveSet(0));
+    taxi->setCount(taxi_vertices->size());
+
+    osg::Geometry *pvt_geom = dynamic_cast<osg::Geometry *>(_geode->getDrawable(1));
+    osg::Geometry::PrimitiveSetList &pvt_prim_list = pvt_geom->getPrimitiveSetList();
+    pvt_prim_list.clear();
+    for (unsigned int i=0; i<apt->numPavements(); ++i)
+    {
+      FGPavement* pvt(apt->getPavementByIndex(i));
+      osg::ref_ptr<osg::Geometry> geom = addPavementGeometry(pvt, tower_lat, tower_lon, scale);
+      osg::Geometry::PrimitiveSetList &prim_list = geom->getPrimitiveSetList();
+      osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array *>(geom->getVertexArray());
+      size_t before = pvt_vertices->size(),
+            count = vertices->size();
+      for (size_t i = 0; i < count; ++i )
+      {
+        pvt_vertices->push_back( (*vertices)[i] );
+      }
+      for (osg::Geometry::PrimitiveSetList::iterator ii = prim_list.begin(); ii != prim_list.end(); ++ii )
+      {
+        osg::DrawArrays *da;
+        osg::DrawElementsUByte *de1;
+        osg::DrawElementsUShort *de2;
+        osg::DrawElementsUInt *de3;
+        if ((da = dynamic_cast<osg::DrawArrays *>(ii->get())) != 0)
+        {
+          osg::DrawArrays *ps = new osg::DrawArrays(*da);
+          ps->setFirst(da->getFirst() + before);
+          pvt_prim_list.push_back(ps);
+        }
+        else if ((de1 = dynamic_cast<osg::DrawElementsUByte *>(ii->get())) != 0)
+        {
+          if (before + count <= 255)
+          {
+            osg::DrawElementsUByte *ps = new osg::DrawElementsUByte(*de1);
+            for (size_t j = 0; j < ps->size(); ++j)
+            {
+              (*ps)[j] += before;
+            }
+            pvt_prim_list.push_back(ps);
+          }
+          else if (before + count <= 65535)
+          {
+            osg::DrawElementsUShort *ps = new osg::DrawElementsUShort(de1->getMode(), de1->begin(), de1->end());
+            for (size_t j = 0; j < ps->size(); ++j)
+            {
+              (*ps)[j] += before;
+            }
+            pvt_prim_list.push_back(ps);
+          }
+          else
+          {
+            osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(de1->getMode(), de1->begin(), de1->end());
+            for (size_t j = 0; j < ps->size(); ++j)
+            {
+              (*ps)[j] += before;
+            }
+            pvt_prim_list.push_back(ps);
+          }
+        }
+        else if ((de2 = dynamic_cast<osg::DrawElementsUShort *>(ii->get())) != 0)
+        {
+          if (before + count <= 65535)
+          {
+            osg::DrawElementsUShort *ps = new osg::DrawElementsUShort(*de2);
+            for (size_t j = 0; j < ps->size(); ++j)
+            {
+              (*ps)[j] += before;
+            }
+            pvt_prim_list.push_back(ps);
+          }
+          else
+          {
+            osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(de2->getMode(), de2->begin(), de2->end());
+            for (size_t j = 0; j < ps->size(); ++j)
+            {
+              (*ps)[j] += before;
+            }
+            pvt_prim_list.push_back(ps);
+          }
+        }
+        else if ((de3 = dynamic_cast<osg::DrawElementsUInt *>(ii->get())) != 0)
+        {
+          osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(*de3);
+          for (size_t j = 0; j < ps->size(); ++j)
+          {
+            (*ps)[j] += before;
+          }
+          pvt_prim_list.push_back(ps);
+        }
+      }
+    }
+    pvt_geom->setVertexArray(pvt_vertices.get());
+
+    for (unsigned int i=0; i<apt->numRunways(); ++i)
+    {
+      FGRunway* runway(apt->getRunwayByIndex(i));
+      if (runway->isReciprocal()) continue;
+      
+      addRunwayVertices(runway, tower_lat, tower_lon, scale, rwy_vertices.get());
+    }
+    osg::Geometry *rwy_geom = dynamic_cast<osg::Geometry *>(_geode->getDrawable(2));
+    rwy_geom->setVertexArray(rwy_vertices.get());
+    osg::DrawArrays* rwy = dynamic_cast<osg::DrawArrays*>(rwy_geom->getPrimitiveSet(0));
+    rwy->setCount(rwy_vertices->size());
+
+    getCamera()->setNodeMask(0xffffffff);
+}
+
+// end of GroundRadar.cxx
diff --git a/src/Cockpit/groundradar.hxx b/src/Cockpit/groundradar.hxx
new file mode 100644 (file)
index 0000000..ec439c2
--- /dev/null
@@ -0,0 +1,59 @@
+//  groundradar.hxx - Background layer for the ATC radar.
+//
+//  Copyright (C) 2007 Csaba Halasz.
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License as
+//  published by the Free Software Foundation; either version 2 of the
+//  License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful, but
+//  WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+
+#ifndef __INST_GROUNDRADAR_HXX
+#define __INST_GROUNDRADAR_HXX
+
+#include <osg/ref_ptr>
+#include <osg/Geometry>
+
+#include <simgear/props/props.hxx>
+#include <simgear/structure/subsystem_mgr.hxx>
+
+#include "od_gauge.hxx"
+
+// forward decls
+class FGRunwayBase;
+class FGPavement;
+
+////////////////////////////////////////////////////////////////////////
+// Built-in layer for the atc radar.
+////////////////////////////////////////////////////////////////////////
+
+class GroundRadar : public SGSubsystem, public SGPropertyChangeListener, private FGODGauge
+{
+public:
+    static const int TextureHalfSize = 256;
+    GroundRadar(SGPropertyNode* node);
+    virtual ~GroundRadar();
+    void updateTexture();
+    virtual void valueChanged(SGPropertyNode*);
+    virtual void update (double dt);
+protected:
+    void createTexture(const char* texture_name);
+    
+    void addRunwayVertices(const FGRunwayBase* aRunway, double aTowerLat, double aTowerLon, double aScale, osg::Vec3Array* aVertices);
+    osg::Geometry *addPavementGeometry(const FGPavement* aPavement, double aTowerLat, double aTowerLon, double aScale);
+    
+    osg::ref_ptr<osg::Geode> _geode;
+    SGPropertyNode_ptr _airport_node;
+    SGPropertyNode_ptr _range_node;
+};
+
+#endif // __INST_GROUNDRADAR_HXX
diff --git a/src/Cockpit/od_gauge.cxx b/src/Cockpit/od_gauge.cxx
new file mode 100644 (file)
index 0000000..c6be269
--- /dev/null
@@ -0,0 +1,479 @@
+// Owner Drawn Gauge helper class
+//
+// Written by Harald JOHNSEN, started May 2005.
+//
+// Copyright (C) 2005  Harald JOHNSEN
+//
+// Ported to OSG by Tim Moore - Jun 2007
+//
+// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
+// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
+// the texture in the scene by certain filter criteria
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+//
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <osg/Texture2D>
+#include <osg/AlphaFunc>
+#include <osg/BlendFunc>
+#include <osg/Camera>
+#include <osg/Geode>
+#include <osg/NodeVisitor>
+#include <osg/Matrix>
+#include <osg/PolygonMode>
+#include <osg/ShadeModel>
+#include <osg/StateSet>
+#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
+
+#include <osgDB/FileNameUtils>
+
+#include <simgear/scene/material/EffectGeode.hxx>
+#include <simgear/scene/util/RenderConstants.hxx>
+
+#include <Main/globals.hxx>
+#include <Viewer/renderer.hxx>
+#include <Scenery/scenery.hxx>
+#include "od_gauge.hxx"
+
+#include <cassert>
+
+//------------------------------------------------------------------------------
+FGODGauge::FGODGauge():
+  _size_x( -1 ),
+  _size_y( -1 ),
+  _view_width( -1 ),
+  _view_height( -1 ),
+  _use_image_coords( false ),
+  _use_stencil( false ),
+  _use_mipmapping( false ),
+  _coverage_samples( 0 ),
+  _color_samples( 0 ),
+  rtAvailable( false )
+{
+}
+
+//------------------------------------------------------------------------------
+FGODGauge::~FGODGauge()
+{
+  if( camera.valid() )
+    globals->get_renderer()->removeCamera(camera.get());
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::setSize(int size_x, int size_y)
+{
+  _size_x = size_x;
+  _size_y = size_y < 0 ? size_x : size_y;
+
+  if( texture.valid() )
+    texture->setTextureSize(_size_x, _size_x);
+}
+
+//----------------------------------------------------------------------------
+void FGODGauge::setViewSize(int width, int height)
+{
+  _view_width = width;
+  _view_height = height < 0 ? width : height;
+
+  if( camera )
+    updateCoordinateFrame();
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::useImageCoords(bool use)
+{
+  if( use == _use_image_coords )
+    return;
+
+  _use_image_coords = use;
+
+  if( texture )
+    updateCoordinateFrame();
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::useStencil(bool use)
+{
+  if( use == _use_stencil )
+    return;
+
+  _use_stencil = use;
+
+  if( texture )
+    updateStencil();
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::setSampling( bool mipmapping,
+                             int coverage_samples,
+                             int color_samples )
+{
+  if(    _use_mipmapping == mipmapping
+      && _coverage_samples == coverage_samples
+      && _color_samples == color_samples )
+    return;
+
+  _use_mipmapping = mipmapping;
+
+  if( color_samples > coverage_samples )
+  {
+    SG_LOG
+    (
+      SG_GL,
+      SG_WARN,
+      "FGODGauge::setSampling: color_samples > coverage_samples not allowed!"
+    );
+    color_samples = coverage_samples;
+  }
+
+  _coverage_samples = coverage_samples;
+  _color_samples = color_samples;
+
+  updateSampling();
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::setRender(bool render)
+{
+  // Only the far camera should trigger this texture to be rendered.
+  camera->setNodeMask(render ? simgear::BACKGROUND_BIT : 0);
+}
+
+//------------------------------------------------------------------------------
+bool FGODGauge::serviceable(void) 
+{
+  return rtAvailable;
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::allocRT(osg::NodeCallback* camera_cull_callback)
+{
+  camera = new osg::Camera;
+  camera->setDataVariance(osg::Object::DYNAMIC);
+  camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
+  camera->setRenderOrder(osg::Camera::PRE_RENDER);
+  camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f));
+  camera->setClearStencil(0);
+  camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT,
+                                             osg::Camera::FRAME_BUFFER );
+
+  if( camera_cull_callback )
+    camera->setCullCallback(camera_cull_callback);
+
+  setRender(true);
+  updateCoordinateFrame();
+  updateStencil();
+
+  osg::StateSet* stateSet = camera->getOrCreateStateSet();
+  stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
+  stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
+  stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
+  stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
+  stateSet->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK,
+          osg::PolygonMode::FILL),
+          osg::StateAttribute::ON);
+  stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER,
+          0.0f),
+          osg::StateAttribute::ON);
+  stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
+  stateSet->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA,
+          osg::BlendFunc::ONE_MINUS_SRC_ALPHA),
+          osg::StateAttribute::ON);
+  if( !texture )
+  {
+    texture = new osg::Texture2D;
+    texture->setTextureSize(_size_x, _size_y);
+    texture->setInternalFormat(GL_RGBA);
+  }
+
+  updateSampling();
+
+  globals->get_renderer()->addCamera(camera.get(), false);
+  rtAvailable = true;
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::updateCoordinateFrame()
+{
+  assert( camera );
+
+  if( _view_width < 0 )
+    _view_width = _size_x;
+  if( _view_height < 0 )
+    _view_height = _size_y;
+
+  camera->setViewport(0, 0, _size_x, _size_y);
+
+  if( _use_image_coords )
+    camera->setProjectionMatrix(
+      osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
+    );
+  else
+    camera->setProjectionMatrix(
+      osg::Matrix::ortho2D( -_view_width/2.,  _view_width/2.,
+                            -_view_height/2., _view_height/2. )
+    );
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::updateStencil()
+{
+  assert( camera );
+
+  GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
+
+  if( _use_stencil)
+  {
+    camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
+                     GL_DEPTH_STENCIL_EXT );
+    mask |= GL_STENCIL_BUFFER_BIT;
+  }
+  else
+  {
+    camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER);
+  }
+
+  camera->setClearMask(mask);
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::updateSampling()
+{
+  assert( camera );
+  assert( texture );
+
+  texture->setFilter(
+    osg::Texture2D::MIN_FILTER,
+    _use_mipmapping ? osg::Texture2D::LINEAR_MIPMAP_LINEAR
+                    : osg::Texture2D::LINEAR
+  );
+  camera->attach(
+    osg::Camera::COLOR_BUFFER,
+    texture.get(),
+    0, 0,
+    _use_mipmapping,
+    _coverage_samples,
+    _color_samples
+  );
+}
+
+/**
+ * Replace a texture in the airplane model with the gauge texture.
+ */
+class ReplaceStaticTextureVisitor:
+  public osg::NodeVisitor
+{
+  public:
+
+    ReplaceStaticTextureVisitor( const char* name,
+                                 osg::Texture2D* new_texture ):
+        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+        _tex_name( osgDB::getSimpleFileName(name) ),
+        _new_texture(new_texture)
+    {}
+
+    ReplaceStaticTextureVisitor( const SGPropertyNode* placement,
+                                 osg::Texture2D* new_texture,
+                                 osg::NodeCallback* cull_callback = 0 ):
+        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+        _tex_name( osgDB::getSimpleFileName(
+          placement->getStringValue("texture"))
+        ),
+        _node_name( placement->getStringValue("node") ),
+        _parent_name( placement->getStringValue("parent") ),
+        _new_texture(new_texture),
+        _cull_callback(cull_callback)
+    {
+      if(    _tex_name.empty()
+          && _node_name.empty()
+          && _parent_name.empty() )
+        SG_LOG
+        (
+          SG_GL,
+          SG_WARN,
+          "No filter criterion for replacing texture. "
+          " Every texture will be replaced!"
+        );
+    }
+
+    /**
+     * Get a list of groups which have been inserted into the scene graph to
+     * replace the given texture
+     */
+    canvas::Placements& getPlacements()
+    {
+      return _placements;
+    }
+
+    virtual void apply(osg::Geode& node)
+    {
+      simgear::EffectGeode* eg = dynamic_cast<simgear::EffectGeode*>(&node);
+      if( !eg )
+        return;
+
+      osg::StateSet* ss = eg->getEffect()->getDefaultStateSet();
+      if( !ss )
+        return;
+
+      osg::Group *parent = node.getParent(0);
+      if( !_node_name.empty() && parent->getName() != _node_name )
+        return;
+
+      if( !_parent_name.empty() )
+      {
+        // Traverse nodes upwards starting at the parent node (skip current
+        // node)
+        const osg::NodePath& np = getNodePath();
+        bool found = false;
+        for( int i = static_cast<int>(np.size()) - 2; i >= 0; --i )
+        {
+          const osg::Node* path_segment = np[i];
+          const osg::Node* path_parent = path_segment->getParent(0);
+
+          // A node without a name is always the parent of the root node of
+          // the model just containing the file name
+          if( path_parent && path_parent->getName().empty() )
+            return;
+
+          if( path_segment->getName() == _parent_name )
+          {
+            found = true;
+            break;
+          }
+        }
+
+        if( !found )
+          return;
+      }
+
+      for( size_t unit = 0; unit < ss->getNumTextureAttributeLists(); ++unit )
+      {
+        osg::Texture2D* tex = dynamic_cast<osg::Texture2D*>
+        (
+          ss->getTextureAttribute(unit, osg::StateAttribute::TEXTURE)
+        );
+
+        if( !tex || !tex->getImage() || tex == _new_texture )
+          continue;
+
+        if( !_tex_name.empty() )
+        {
+          std::string tex_name = tex->getImage()->getFileName();
+          std::string tex_name_simple = osgDB::getSimpleFileName(tex_name);
+          if( !osgDB::equalCaseInsensitive(_tex_name, tex_name_simple) )
+            continue;
+        }
+
+        // insert a new group between the geode an it's parent which overrides
+        // the texture
+        osg::ref_ptr<osg::Group> group = new osg::Group;
+        group->setName("canvas texture group");
+        group->addChild(eg);
+        parent->removeChild(eg);
+        parent->addChild(group);
+
+        if( _cull_callback )
+          group->setCullCallback(_cull_callback);
+
+        _placements.push_back(
+          canvas::PlacementPtr(new ObjectPlacement(group))
+        );
+
+        osg::StateSet* stateSet = group->getOrCreateStateSet();
+        stateSet->setTextureAttribute( unit, _new_texture,
+                                             osg::StateAttribute::OVERRIDE );
+        stateSet->setTextureMode( unit, GL_TEXTURE_2D,
+                                        osg::StateAttribute::ON );
+
+        SG_LOG
+        (
+          SG_GL,
+          SG_INFO,
+             "Replaced texture '" << _tex_name << "'"
+          << " for object '" << parent->getName() << "'"
+          << (!_parent_name.empty() ? " with parent '" + _parent_name + "'"
+                                    : "")
+        );
+        return;
+      }
+    }
+
+  protected:
+
+    class ObjectPlacement:
+      public canvas::Placement
+    {
+      public:
+        ObjectPlacement(osg::ref_ptr<osg::Group> group):
+          _group(group)
+        {}
+
+        /**
+         * Remove placement from the scene
+         */
+        virtual ~ObjectPlacement()
+        {
+          assert( _group->getNumChildren() == 1 );
+          osg::Node *child = _group->getChild(0);
+
+          if( _group->getNumParents() )
+          {
+            osg::Group *parent = _group->getParent(0);
+            parent->addChild(child);
+            parent->removeChild(_group);
+          }
+
+          _group->removeChild(child);
+        }
+
+      private:
+        osg::ref_ptr<osg::Group> _group;
+    };
+
+    std::string _tex_name,      ///<! Name of texture to be replaced
+                _node_name,     ///<! Only replace if node name matches
+                _parent_name;   ///<! Only replace if any parent node matches
+                                ///   given name (all the tree upwards)
+    osg::Texture2D     *_new_texture;
+    osg::NodeCallback  *_cull_callback;
+
+    canvas::Placements _placements;
+};
+
+//------------------------------------------------------------------------------
+canvas::Placements FGODGauge::set_texture( const char* name,
+                                           osg::Texture2D* new_texture )
+{
+  osg::Group* root = globals->get_scenery()->get_aircraft_branch();
+  ReplaceStaticTextureVisitor visitor(name, new_texture);
+  root->accept(visitor);
+  return visitor.getPlacements();
+}
+
+//------------------------------------------------------------------------------
+canvas::Placements FGODGauge::set_texture( const SGPropertyNode* placement,
+                                           osg::Texture2D* new_texture,
+                                           osg::NodeCallback* cull_callback )
+{
+  osg::Group* root = globals->get_scenery()->get_aircraft_branch();
+  ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback);
+  root->accept(visitor);
+  return visitor.getPlacements();
+}
diff --git a/src/Cockpit/od_gauge.hxx b/src/Cockpit/od_gauge.hxx
new file mode 100644 (file)
index 0000000..7c4ee00
--- /dev/null
@@ -0,0 +1,177 @@
+// Owner Drawn Gauge helper class
+//
+// Written by Harald JOHNSEN, started May 2005.
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// Ported to OSG by Tim Moore - Jun 2007
+//
+// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
+// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
+// the texture in the scene by certain filter criteria
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+//
+
+#ifndef _OD_GAUGE_HXX
+#define _OD_GAUGE_HXX
+
+#include <Canvas/canvas_fwd.hpp>
+#include <Canvas/placement.hxx>
+
+#include <osg/NodeCallback>
+#include <osg/Group>
+
+namespace osg {
+  class Camera;
+  class Texture2D;
+}
+
+class SGPropertyNode;
+
+/**
+ * Owner Drawn Gauge helper class.
+ */
+class FGODGauge
+{
+  public:
+    FGODGauge();
+    virtual ~FGODGauge();
+
+    /**
+     * Set the size of the render target.
+     *
+     * @param size_x    X size
+     * @param size_y    Y size. Defaults to size_x if not specified
+     */
+    void setSize(int size_x, int size_y = -1);
+
+    /**
+     * Set the size of the viewport
+     *
+     * @param width
+     * @param height    Defaults to width if not specified
+     */
+    void setViewSize(int width, int height = -1);
+
+    /**
+     * DEPRECATED
+     *
+     * Get size of squared texture
+     */
+    int size() const { return _size_x; }
+    
+    /**
+     * Set whether to use image coordinates or not.
+     *
+     * Default: origin == center of texture
+     * Image Coords: origin == top left corner
+     */
+    void useImageCoords(bool use = true);
+
+    /**
+     * Enable/Disable using a stencil buffer
+     */
+    void useStencil(bool use = true);
+
+    /**
+     * Set sampling parameters for mipmapping and coverage sampling
+     * antialiasing.
+     *
+     * @note color_samples is not allowed to be higher than coverage_samples
+     *
+     */
+    void setSampling( bool mipmapping,
+                      int coverage_samples = 0,
+                      int color_samples = 0 );
+
+    /**
+     * Enable/Disable updating the texture (If disabled the contents of the
+     * texture remains with the outcome of the last rendering pass)
+     */
+    void setRender(bool render);
+
+    /**
+     * Say if we can render to a texture.
+     * @return true if rtt is available
+     */
+    bool serviceable(void);
+
+    /**
+     * Replace an opengl texture name inside the aircraft scene graph.
+     * This is to replace a static texture by a dynamic one
+     * @param name texture filename
+     * @param new_texture dynamic texture to replace the old one
+     * @return A list of groups which override the given texture
+     */
+    static
+    canvas::Placements set_texture( const char * name,
+                                    osg::Texture2D* new_texture );
+
+    /**
+     * Replace an opengl texture name inside the aircraft scene graph.
+     * This is to replace a static texture by a dynamic one. The replacement
+     * is base on certain filtering criteria which have to be stored in string
+     * value childs of the placement node. Recognized nodes are:
+     *   - texture  Match the name of the texture
+     *   - node     Match the name of the object
+     *   - parent   Match any of the object parents names (all the tree upwards)
+     * @param placement the node containing the replacement criteria
+     * @param new_texture dynamic texture to replace the old one
+     * @param an optional cull callback which will be installed on any matching
+     *        object
+     * @return A list of groups which override the given texture
+     */
+    static
+    canvas::Placements set_texture( const SGPropertyNode* placement,
+                                    osg::Texture2D* new_texture,
+                                    osg::NodeCallback* cull_callback = 0 );
+
+    /**
+     * Get the OSG camera for drawing this gauge.
+     */
+    osg::Camera* getCamera() const { return camera.get(); }
+
+    osg::Texture2D* getTexture() const { return texture.get(); }
+    //void setTexture(osg::Texture2D* t) { texture = t; }
+
+    // Real initialization function. Bad name.
+    void allocRT(osg::NodeCallback* camera_cull_callback = 0);
+
+  private:
+    int _size_x,
+        _size_y,
+        _view_width,
+        _view_height;
+    bool _use_image_coords,
+         _use_stencil,
+         _use_mipmapping;
+
+    // Multisampling parameters
+    int  _coverage_samples,
+         _color_samples;
+
+    bool rtAvailable;
+    osg::ref_ptr<osg::Camera> camera;
+    osg::ref_ptr<osg::Texture2D> texture;
+
+    void updateCoordinateFrame();
+    void updateStencil();
+    void updateSampling();
+
+};
+
+#endif // _OD_GAUGE_HXX
diff --git a/src/Cockpit/render_area_2d.cxx b/src/Cockpit/render_area_2d.cxx
new file mode 100644 (file)
index 0000000..8ad68a7
--- /dev/null
@@ -0,0 +1,426 @@
+// RenderArea2D.cxx - a class to manage 2D polygon-based drawing
+//                    for a complex instrument (eg. GPS).
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "render_area_2d.hxx"
+
+RA2DPrimitive::RA2DPrimitive() {
+    invert = false;
+    debug = false;
+}
+                                         
+RenderArea2D::RenderArea2D(int logx, int logy, int sizex, int sizey, int posx, int posy) {
+    _logx = logx;
+    _logy = logy;
+    _sizex = sizex;
+    _sizey = sizey;
+    _posx = posx;
+    _posy = posy;
+    _clipx1 = 0;
+    _clipx2 = _logx - 1;
+    _clipy1 = 0;
+    _clipy2 = _logy - 1;
+    
+    // Default to black background / white text.
+    _backgroundColor[0] = 0.0;
+    _backgroundColor[1] = 0.0;
+    _backgroundColor[2] = 0.0;
+    _backgroundColor[3] = 1.0;
+    _pixelColor[0] = 1.0;
+    _pixelColor[1] = 1.0;
+    _pixelColor[2] = 1.0;
+    _pixelColor[3] = 1.0;
+    
+    _ra2d_debug = false;
+}
+
+void RenderArea2D::Draw(osg::State& state) {
+    
+    static osg::ref_ptr<osg::StateSet> renderArea2DStateSet;
+    if(!renderArea2DStateSet.valid()) {
+        renderArea2DStateSet = new osg::StateSet;
+        renderArea2DStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
+        renderArea2DStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
+    }
+    
+    state.pushStateSet(renderArea2DStateSet.get());
+    state.apply();
+    state.setActiveTextureUnit(0);
+    state.setClientActiveTextureUnit(0);
+    
+    // DCL - the 2 lines below are copied verbatim from the hotspot drawing code.
+    // I am not sure if they are needed here or not.
+    glPushAttrib(GL_ENABLE_BIT);
+    glDisable(GL_COLOR_MATERIAL);
+    
+    // FIXME - disabling all clip planes causes bleed-through through the splash screen.
+    glDisable(GL_CLIP_PLANE0);
+    glDisable(GL_CLIP_PLANE1);
+    glDisable(GL_CLIP_PLANE2);
+    glDisable(GL_CLIP_PLANE3);
+
+    DoDrawBackground();
+    
+    for(unsigned int i = 0; i < drawing_list.size(); ++i) {
+        RA2DPrimitive prim = drawing_list[i];
+        switch(prim.type) {
+        case RA2D_LINE:
+            DoDrawLine(prim.x1, prim.y1, prim.x2, prim.y2);
+            break;
+        case RA2D_QUAD:
+            if(prim.debug) {
+                //cout << "Clipping = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n';
+                //cout << "Drawing quad " << prim.x1 << ", " << prim.y1 << " to " << prim.x2 << ", " << prim.y2 << '\n';
+            }
+            DoDrawQuad(prim.x1, prim.y1, prim.x2, prim.y2, prim.invert);
+            break;
+        case RA2D_PIXEL:
+            DoDrawPixel(prim.x1, prim.y1, prim.invert);
+            break;
+        }
+    }
+    
+    drawing_list.clear();
+    
+    glPopAttrib();
+    
+    state.popStateSet();
+    state.apply();
+    state.setActiveTextureUnit(0);
+    state.setClientActiveTextureUnit(0);
+}
+
+void RenderArea2D::Flush() {
+    drawing_list.clear();
+}
+
+void RenderArea2D::SetPixelColor(const float* rgba) {
+    _pixelColor[0] = rgba[0];
+    _pixelColor[1] = rgba[1];
+    _pixelColor[2] = rgba[2];
+    _pixelColor[3] = rgba[3];
+}
+
+// Set clipping region in logical units
+void RenderArea2D::SetClipRegion(int x1, int y1, int x2, int y2) {
+    _clipx1 = x1;
+    _clipx2 = x2;
+    _clipy1 = y1;
+    _clipy2 = y2;
+    //cout << "Set clip region, clip region = "  << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n';
+}
+
+// Set clip region to be the same as the rendered area (default)
+void RenderArea2D::ResetClipRegion() {
+    _clipx1 = 0;
+    _clipx2 = _logx - 1;
+    _clipy1 = 0;
+    _clipy2 = _logy - 1;
+    //cout << "Reset clip region, clip region = "  << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n';
+}
+
+void RenderArea2D::SetPosition(int posx, int posy) {
+    _posx = posx;
+    _posy = posy;
+}
+
+void RenderArea2D::SetLogicalSize(int logx, int logy) {
+    _logx = logx;
+    _logy = logy;
+}
+
+void RenderArea2D::SetActualSize(int sizex, int sizey) {
+    _sizex = sizex;
+    _sizey = sizey;
+}
+
+void RenderArea2D::DrawPixel(int x, int y, bool invert) {
+    // Clip
+    if(x < _clipx1 || x > _clipx2 || y < _clipy1 || y > _clipy2) return;
+
+    RA2DPrimitive prim;
+    prim.x1 = x;
+    prim.y1 = y;
+    prim.x2 = 0;
+    prim.y2 = 0;
+    prim.type = RA2D_PIXEL;
+    prim.invert = invert;
+    drawing_list.push_back(prim);
+}
+
+void RenderArea2D::DoDrawPixel(int x, int y, bool invert) {
+    // Clip.  In theory this shouldn't be necessary, since all input is clipped before adding
+    // to the drawing list, but it ensures that any errors in clipping lines etc will only 
+    // spill over the clip area within the instrument, and still be clipped from straying
+    // outside the instrument.
+    if(x < _clipx1 || x > _clipx2 || y < _clipy1 || y > _clipy2) return;
+    
+    // Scale to position within background
+    float fx1 = (float)x, fy1 = (float)y;
+    float rx = (float)_sizex / (float)_logx;
+    float ry = (float)_sizey / (float)_logy;
+    fx1 *= rx;
+    fy1 *= ry;
+    float fx2 = fx1 + rx;
+    float fy2 = fy1 + ry;
+    
+    // Translate to final position
+    fx1 += (float)_posx;
+    fx2 += (float)_posx;
+    fy1 += (float)_posy;
+    fy2 += (float)_posy;
+    
+    //cout << "DP: " << fx1 << ", " << fy1 << " ... " << fx2 << ", " << fy2 << '\n';
+    
+    SetRenderColor(invert ? _backgroundColor : _pixelColor);
+    SGVec2f corners[4] = {
+        SGVec2f(fx1, fy1),
+        SGVec2f(fx2, fy1),
+        SGVec2f(fx2, fy2),
+        SGVec2f(fx1, fy2)
+    };
+    RenderQuad(corners);
+}
+
+void RenderArea2D::DrawLine(int x1, int y1, int x2, int y2) {
+    // We need to clip the line to the current region before storing it in the drawing 
+    // list, since when we come to actually draw it the clip region may have changed.
+
+    // Liang-Barsky clipping algorithm
+    int p[4], q[4];
+    float u1 = 0.0f, u2 = 1.0f;
+    p[0] = -(x2 - x1); q[0] = (x1 - _clipx1);
+    p[1] =  (x2 - x1); q[1] = (_clipx2 - x1);
+    p[2] = -(y2 - y1); q[2] = (y1 - _clipy1);
+    p[3] =  (y2 - y1); q[3] = (_clipy2 - y1);
+    
+    for(int i=0; i<4; ++i) {
+        if(p[i] == 0) {
+            if(q[i] < 0) {
+                // Then we have a trivial rejection of a line parallel to a clip plane
+                // completely outside the clip region.
+                return;
+            }
+        } else if(p[i] < 0) {
+            float r = (float)q[i]/(float)p[i];
+            u1 = (u1 > r ? u1 : r);
+        } else {        // p[i] > 0
+            float r = (float)q[i]/(float)p[i];
+            u2 = (u2 < r ? u2 : r);
+        }
+        if(u1 > u2) {
+            // Then the line is completely outside the clip area.
+            return;
+        }
+    }
+    
+    float fx1 = x1 + u1 * (float)(x2 - x1);
+    float fy1 = y1 + u1 * (float)(y2 - y1);
+    float fx2 = x1 + u2 * (float)(x2 - x1);
+    float fy2 = y1 + u2 * (float)(y2 - y1);
+    x1 = (int)(fx1 + 0.5);
+    y1 = (int)(fy1 + 0.5);
+    x2 = (int)(fx2 + 0.5);
+    y2 = (int)(fy2 + 0.5);
+    
+    RA2DPrimitive prim;
+    prim.x1 = x1;
+    prim.y1 = y1;
+    prim.x2 = x2;
+    prim.y2 = y2;
+    prim.type = RA2D_LINE;
+    prim.invert = false;
+    drawing_list.push_back(prim);
+}
+
+void RenderArea2D::DoDrawLine(int x1, int y1, int x2, int y2) {
+    // Crude implementation of Bresenham line drawing algorithm.
+    
+    // Our lines are non directional, so first order the points x-direction-wise to leave only 4 octants to consider.
+    if(x2 < x1) {
+        int tmp_x = x1;
+        int tmp_y = y1;
+        x1 = x2;
+        y1 = y2;
+        x2 = tmp_x;
+        y2 = tmp_y;
+    }
+    
+    bool flip_y = (y1 > y2 ? true : false);
+    int dx = x2 - x1;
+    int dy = (flip_y ? y1 - y2 : y2 - y1); 
+    if(dx > dy) {
+        // push the x dir
+        int y = y1;
+        int yn = dx/2;
+        for(int x=x1; x<=x2; ++x) {
+            DoDrawPixel(x, y);
+            yn += dy;
+            if(yn >= dx) {
+                yn -= dx;
+                y = (flip_y ? y - 1 : y + 1);
+            }
+        }
+    } else {
+        // push the y dir
+        int x = x1;
+        int xn = dy/2;
+        // Must be a more elegant way to roll the next two cases into one!
+        if(flip_y) {
+            for(int y=y1; y>=y2; --y) {
+                DoDrawPixel(x, y);
+                xn += dx;
+                if(xn >= dy) {
+                    xn -= dy;
+                    x++;
+                }
+            }
+        } else {
+            for(int y=y1; y<=y2; ++y) {
+                DoDrawPixel(x, y);
+                xn += dx;
+                if(xn >= dy) {
+                    xn -= dy;
+                    x++;
+                }
+            }
+        }
+    }
+}
+
+void RenderArea2D::DrawQuad(int x1, int y1, int x2, int y2, bool invert) {
+    // Force the input to be ordered with x1 < x2 and y1 < y2.
+    if(x1 > x2) {
+        int x = x2;
+        x2 = x1;
+        x1 = x;
+    }
+    if(y1 > y2) {
+        int y = y2;
+        y2 = y1;
+        y1 = y;
+    }
+    
+    // Clip the input to the current drawing region.
+    x1 = x1 < _clipx1 ? _clipx1 : x1;
+    if(x1 > _clipx2) { return; }
+    x2 = x2 > _clipx2 ? _clipx2 : x2;
+    if(x2 < _clipx1) { return; }
+    y1 = y1 < _clipy1 ? _clipy1 : y1;
+    if(y1 > _clipy2) { return; }
+    y2 = y2 > _clipy2 ? _clipy2 : y2;
+    if(y2 < _clipy1) { return; }
+    
+    RA2DPrimitive prim;
+    prim.x1 = x1;
+    prim.y1 = y1;
+    prim.x2 = x2;
+    prim.y2 = y2;
+    prim.type = RA2D_QUAD;
+    prim.invert = invert;
+    if(_ra2d_debug) prim.debug = true;
+    drawing_list.push_back(prim);
+}
+
+void RenderArea2D::DoDrawQuad(int x1, int y1, int x2, int y2, bool invert) {
+    // Scale to position within background
+    float fx1 = (float)x1, fy1 = (float)y1;
+    float fx2 = (float)x2, fy2 = (float)y2;
+    float rx = (float)_sizex / (float)_logx;
+    float ry = (float)_sizey / (float)_logy;
+    fx1 *= rx;
+    fy1 *= ry;
+    fx2 *= rx;
+    fy2 *= ry;
+    
+    fx2 += rx;
+    fy2 += ry;
+    
+    // Translate to final position
+    fx1 += (float)_posx;
+    fx2 += (float)_posx;
+    fy1 += (float)_posy;
+    fy2 += (float)_posy;
+    
+    //cout << "DP: " << fx1 << ", " << fy1 << " ... " << fx2 << ", " << fy2 << '\n';
+    
+    SetRenderColor(invert ? _backgroundColor : _pixelColor);
+    SGVec2f corners[4] = {
+        SGVec2f(fx1, fy1),
+        SGVec2f(fx2, fy1),
+        SGVec2f(fx2, fy2),
+        SGVec2f(fx1, fy2)
+    };
+    RenderQuad(corners);
+}
+
+void RenderArea2D::DrawBackground() {
+    // Currently a NO-OP
+}
+
+void RenderArea2D::DoDrawBackground() {
+    SetRenderColor(_backgroundColor);
+    SGVec2f corners[4] = {
+        SGVec2f(_posx, _posy),
+        SGVec2f(_posx + _sizex, _posy),
+        SGVec2f(_posx + _sizex, _posy + _sizey),
+        SGVec2f(_posx, _posy + _sizey)                                                                  
+    };
+  
+    RenderQuad(corners);
+}
+
+// -----------------------------------------
+//
+// Actual drawing routines copied from Atlas
+//
+// -----------------------------------------
+
+void RenderArea2D::SetRenderColor( const float *rgba ) {
+    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, rgba);
+    glColor4fv( rgba );
+}
+
+void RenderArea2D::RenderQuad( const SGVec2f *p) {
+    glBegin(GL_QUADS);
+        glNormal3f(0.0f, 0.0f, 0.0f);
+        glVertex2fv( p[0].data() );
+        glVertex2fv( p[1].data() );
+        glVertex2fv( p[2].data() );
+        glVertex2fv( p[3].data() );
+    glEnd();
+}
+
+void RenderArea2D::RenderQuad( const SGVec2f *p, const SGVec4f *color ) {
+    glBegin(GL_QUADS);
+        glNormal3f(0.0f, 0.0f, 0.0f);
+        glColor4fv( color[0].data() ); glVertex2fv( p[0].data() );
+        glColor4fv( color[1].data() ); glVertex2fv( p[1].data() );
+        glColor4fv( color[2].data() ); glVertex2fv( p[2].data() );
+        glColor4fv( color[3].data() ); glVertex2fv( p[3].data() );
+    glEnd();
+}
diff --git a/src/Cockpit/render_area_2d.hxx b/src/Cockpit/render_area_2d.hxx
new file mode 100644 (file)
index 0000000..83d74ef
--- /dev/null
@@ -0,0 +1,116 @@
+// RenderArea2D.hxx - a class to manage 2D polygon-based drawing
+//                    for a complex instrument (eg. GPS).
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#ifndef _RENDER_AREA_2D
+#define _RENDER_AREA_2D
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <osg/ref_ptr>
+#include <osg/State>
+#include <osg/StateSet>
+
+#include <simgear/compiler.h>
+#include <simgear/math/SGMath.hxx>
+
+#include <vector>
+
+using std::vector;
+
+enum RA2DDrawingType {
+       RA2D_LINE,
+       RA2D_QUAD,
+       RA2D_PIXEL
+};
+
+struct RA2DPrimitive {
+       RA2DPrimitive();
+
+       RA2DDrawingType type;
+       int x1, y1, x2, y2;
+       bool invert;
+       bool debug;
+};
+
+class RenderArea2D {
+       
+public:
+       RenderArea2D(int logx, int logy, int sizex, int sizey, int posx, int posy);
+       ~RenderArea2D();
+       
+       void SetPixelColor(const float* rgba);
+       void SetBackgroundColor(const float* rgba);
+       void SetPosition(int posx, int posy);
+       void SetLogicalSize(int logx, int logy);
+       void SetActualSize(int sizex, int sizey);
+       
+       // Set clipping region in logical units
+       void SetClipRegion(int x1, int y1, int x2, int y2);
+       // Set clip region to be the same as the rendered area (default)
+       void ResetClipRegion();
+       
+       // The DrawXXX functions place the shapes in the buffer, specified 
+       // in logical units, and clipped to the current clip region.
+       void DrawPixel(int x, int y, bool invert = false);
+       void DrawLine(int x1, int y1, int x2, int y2);
+       void DrawQuad(int x1, int y1, int x2, int y2, bool invert = false);
+       void DrawBackground();
+       
+       // Call Draw to have the buffer contents drawn and then cleared.
+       void Draw(osg::State& state);
+       
+       // Clear the buffer contents
+       void Flush();
+       
+       // Turn debugging on or off.
+       inline void SetDebugging(bool b) { _ra2d_debug = b; }
+
+private:
+       int _logx, _logy;       // logical size of rendered area
+       int _sizex, _sizey;             // Actual size of rendered area
+       int _posx, _posy;       // Position of rendered area
+       int _clipx1, _clipx2, _clipy1, _clipy2; // Current clipping region in *logical* units
+       
+       float _backgroundColor[4];
+       float _pixelColor[4];
+       
+       // Drawing specified in logical units
+       void DoDrawPixel(int x, int y, bool invert = false);
+       void DoDrawLine(int x1, int y1, int x2, int y2);
+       void DoDrawQuad(int x1, int y1, int x2, int y2, bool invert = false);
+       void DoDrawBackground();
+       
+       // Actual rendering routines copied from Atlas
+       void SetRenderColor( const float *rgb );
+       void RenderQuad( const SGVec2f *p);
+       void RenderQuad( const SGVec2f *p, const SGVec4f *color );
+       
+       vector<RA2DPrimitive> drawing_list;
+       
+       // Control whether to output debugging output
+       bool _ra2d_debug;
+};
+
+#endif
diff --git a/src/Cockpit/wxradar.cxx b/src/Cockpit/wxradar.cxx
new file mode 100644 (file)
index 0000000..2d3ce04
--- /dev/null
@@ -0,0 +1,1115 @@
+// Wx Radar background texture
+//
+// Written by Harald JOHNSEN, started May 2005.
+// With major amendments by Vivian MEAZZA May 2007
+// Ported to OSG by Tim Moore Jun 2007
+//
+//
+// Copyright (C) 2005  Harald JOHNSEN
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+//
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <osg/Array>
+#include <osg/Geometry>
+#include <osg/Matrixf>
+#include <osg/PrimitiveSet>
+#include <osg/StateSet>
+#include <osg/Version>
+#include <osgDB/ReaderWriter>
+#include <osgDB/WriteFile>
+
+#include <simgear/constants.h>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/scene/model/model.hxx>
+#include <simgear/structure/exception.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+
+#include <sstream>
+#include <iomanip>
+
+using std::stringstream;
+using std::endl;
+using std::setprecision;
+using std::fixed;
+using std::setw;
+using std::setfill;
+using std::string;
+
+#include <Main/fg_props.hxx>
+#include <Main/globals.hxx>
+
+#include "panel.hxx" // for FGTextureManager
+#include "od_gauge.hxx"
+#include "wxradar.hxx"
+
+#include <iostream>             // for cout, endl
+
+using std::cout;
+using std::endl;
+
+static const float UNIT = 1.0f / 8.0f;  // 8 symbols in a row/column in the texture
+static const char *DEFAULT_FONT = "typewriter.txf";
+
+wxRadarBg::wxRadarBg(SGPropertyNode *node) :
+    _name(node->getStringValue("name", "radar")),
+    _num(node->getIntValue("number", 0)),
+    _time(0.0),
+    _interval(node->getDoubleValue("update-interval-sec", 1.0)),
+    _elapsed_time(0),
+    _persistance(0),
+    _odg(0),
+    _range_nm(0),
+    _scale(0),
+    _angle_offset(0),
+    _view_heading(0),
+    _x_offset(0),
+    _y_offset(0),
+    _radar_ref_rng(0),
+    _lat(0),
+    _lon(0),
+    _antenna_ht(node->getDoubleValue("antenna-ht-ft", 0.0)),
+    _resultTexture(0),
+    _wxEcho(0),
+    _font_size(0),
+    _font_spacing(0)
+{
+    string branch;
+    branch = "/instrumentation/" + _name;
+    _Instrument = fgGetNode(branch.c_str(), _num, true);
+
+    const char *tacan_source = node->getStringValue("tacan-source", "/instrumentation/tacan");
+    _Tacan = fgGetNode(tacan_source, true);
+
+    _font_node = _Instrument->getNode("font", true);
+
+#define INITFONT(p, val, type) if (!_font_node->hasValue(p)) _font_node->set##type##Value(p, val)
+    INITFONT("name", DEFAULT_FONT, String);
+    INITFONT("size", 8, Float);
+    INITFONT("line-spacing", 0.25, Float);
+    INITFONT("color/red", 0, Float);
+    INITFONT("color/green", 0.8, Float);
+    INITFONT("color/blue", 0, Float);
+    INITFONT("color/alpha", 1, Float);
+#undef INITFONT
+
+    _font_node->addChangeListener(this, true);
+}
+
+
+wxRadarBg::~wxRadarBg ()
+{
+    _font_node->removeChangeListener(this);
+    delete _odg;
+}
+
+
+void
+wxRadarBg::init ()
+{
+    _serviceable_node = _Instrument->getNode("serviceable", true);
+    _sceneryLoaded = fgGetNode("/sim/sceneryloaded", true);
+
+    // texture name to use in 2D and 3D instruments
+    _texture_path = _Instrument->getStringValue("radar-texture-path",
+        "Aircraft/Instruments/Textures/od_wxradar.rgb");
+    _resultTexture = FGTextureManager::createTexture(_texture_path.c_str(), false);
+
+    string path = _Instrument->getStringValue("echo-texture-path",
+        "Aircraft/Instruments/Textures/wxecho.rgb");
+    SGPath tpath = globals->resolve_aircraft_path(path);
+
+    // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect
+    _wxEcho = SGLoadTexture2D(tpath, NULL, false, false);
+
+
+    _Instrument->setFloatValue("trk", 0.0);
+    _Instrument->setFloatValue("tilt", 0.0);
+    _Instrument->setStringValue("status", "");
+    // those properties are used by a radar instrument of a MFD
+    // input switch = OFF | TST | STBY | ON
+    // input mode = WX | WXA | MAP
+    // output status = STBY | TEST | WX | WXA | MAP | blank
+    // input lightning = true | false
+    // input TRK = +/- n degrees
+    // input TILT = +/- n degree
+    // input autotilt = true | false
+    // input range = n nm (20/40/80)
+    // input display-mode = arc | rose | map | plan
+
+    _odg = new FGODGauge;
+    _odg->setSize(512);
+
+    _ai_enabled_node = fgGetNode("/sim/ai/enabled", true);
+
+    _user_lat_node = fgGetNode("/position/latitude-deg", true);
+    _user_lon_node = fgGetNode("/position/longitude-deg", true);
+    _user_alt_node = fgGetNode("/position/altitude-ft", true);
+
+    _user_speed_east_fps_node   = fgGetNode("/velocities/speed-east-fps", true);
+    _user_speed_north_fps_node  = fgGetNode("/velocities/speed-north-fps", true);
+
+    _tacan_serviceable_node = _Tacan->getNode("serviceable", true);
+    _tacan_distance_node    = _Tacan->getNode("indicated-distance-nm", true);
+    _tacan_name_node        = _Tacan->getNode("name", true);
+    _tacan_bearing_node     = _Tacan->getNode("indicated-bearing-true-deg", true);
+    _tacan_in_range_node    = _Tacan->getNode("in-range", true);
+
+    _radar_mode_control_node = _Instrument->getNode("mode-control", true);
+    _radar_coverage_node     = _Instrument->getNode("limit-deg", true);
+    _radar_ref_rng_node      = _Instrument->getNode("reference-range-nm", true);
+    _radar_hdg_marker_node   = _Instrument->getNode("heading-marker", true);
+
+    SGPropertyNode *n = _Instrument->getNode("display-controls", true);
+    _radar_weather_node     = n->getNode("WX", true);
+    _radar_position_node    = n->getNode("pos", true);
+    _radar_data_node        = n->getNode("data", true);
+    _radar_symbol_node      = n->getNode("symbol", true);
+    _radar_centre_node      = n->getNode("centre", true);
+    _radar_rotate_node      = n->getNode("rotate", true);
+    _radar_tcas_node        = n->getNode("tcas", true);
+    _radar_absalt_node      = n->getNode("abs-altitude", true);
+
+    _radar_centre_node->setBoolValue(false);
+    if (!_radar_coverage_node->hasValue())
+        _radar_coverage_node->setFloatValue(120);
+    if (!_radar_ref_rng_node->hasValue())
+        _radar_ref_rng_node->setDoubleValue(35);
+    if (!_radar_hdg_marker_node->hasValue())
+        _radar_hdg_marker_node->setBoolValue(true);
+
+    _x_offset = 0;
+    _y_offset = 0;
+
+    // OSG geometry setup. The polygons for the radar returns will be
+    // stored in a single Geometry. The geometry will have several
+    // primitive sets so we can have different kinds of polys and
+    // choose a different overall color for each set.
+    _radarGeode = new osg::Geode;
+    osg::StateSet *stateSet = _radarGeode->getOrCreateStateSet();
+    stateSet->setTextureAttributeAndModes(0, _wxEcho.get());
+    _geom = new osg::Geometry;
+    _geom->setUseDisplayList(false);
+    // Initially allocate space for 128 quads
+    _vertices = new osg::Vec2Array;
+    _vertices->setDataVariance(osg::Object::DYNAMIC);
+    _vertices->reserve(128 * 4);
+    _geom->setVertexArray(_vertices);
+    _texCoords = new osg::Vec2Array;
+    _texCoords->setDataVariance(osg::Object::DYNAMIC);
+    _texCoords->reserve(128 * 4);
+    _geom->setTexCoordArray(0, _texCoords);
+    osg::Vec3Array *colors = new osg::Vec3Array;
+    colors->push_back(osg::Vec3(1.0f, 1.0f, 1.0f)); // color of echos
+    colors->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); // arc mask
+    colors->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); // rest of mask
+    _geom->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE_SET);
+    _geom->setColorArray(colors);
+    osg::PrimitiveSet *pset = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
+    pset->setDataVariance(osg::Object::DYNAMIC);
+    _geom->addPrimitiveSet(pset);
+    pset = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
+    pset->setDataVariance(osg::Object::DYNAMIC);
+    _geom->addPrimitiveSet(pset);
+    pset = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES);
+    pset->setDataVariance(osg::Object::DYNAMIC);
+    _geom->addPrimitiveSet(pset);
+    _geom->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f),
+        osg::Vec3f(256.0f, 256.0f, 0.0f)));
+    _radarGeode->addDrawable(_geom);
+    _odg->allocRT();
+    // Texture in the 2D panel system
+    FGTextureManager::addTexture(_texture_path.c_str(), _odg->getTexture());
+
+    _textGeode = new osg::Geode;
+
+    osg::Camera *camera = _odg->getCamera();
+    camera->addChild(_radarGeode.get());
+    camera->addChild(_textGeode.get());
+
+    updateFont();
+    _time = 0.0;
+}
+
+
+// Local coordinates for each echo
+const osg::Vec3f echoCoords[4] = {
+    osg::Vec3f(-.7f, -.7f, 0.0f), osg::Vec3f(.7f, -.7f, 0.0f),
+    osg::Vec3f(.7f, .7f, 0.0f), osg::Vec3f(-.7f, .7f, 0.0f)
+};
+
+
+const osg::Vec2f echoTexCoords[4] = {
+    osg::Vec2f(0.0f, 0.0f), osg::Vec2f(UNIT, 0.0f),
+    osg::Vec2f(UNIT, UNIT), osg::Vec2f(0.0f, UNIT)
+};
+
+
+// helper
+static void
+addQuad(osg::Vec2Array *vertices, osg::Vec2Array *texCoords,
+        const osg::Matrixf& transform, const osg::Vec2f& texBase)
+{
+    for (int i = 0; i < 4; i++) {
+        const osg::Vec3f coords = transform.preMult(echoCoords[i]);
+        texCoords->push_back(texBase + echoTexCoords[i]);
+        vertices->push_back(osg::Vec2f(coords.x(), coords.y()));
+    }
+}
+
+
+// Rotate by a heading value
+static inline
+osg::Matrixf wxRotate(float angle)
+{
+    return osg::Matrixf::rotate(angle, 0.0f, 0.0f, -1.0f);
+}
+
+
+void
+wxRadarBg::update (double delta_time_sec)
+{
+    if (!_sceneryLoaded->getBoolValue())
+        return;
+
+    if (!_odg || !_serviceable_node->getBoolValue()) {
+        _Instrument->setStringValue("status", "");
+        return;
+    }
+
+    _time += delta_time_sec;
+    if (_time < _interval)
+        return;
+
+    _time -= _interval;
+
+    string mode = _Instrument->getStringValue("display-mode", "arc");
+    if (mode == "map") {
+        if (_display_mode != MAP) {
+            _display_mode = MAP;
+            center_map();
+        }
+    } else if (mode == "plan") {
+        _display_mode = PLAN;}
+    else if (mode == "bscan") {
+        _display_mode = BSCAN;
+    } else {
+        _display_mode = ARC;
+    }
+
+    string switchKnob = _Instrument->getStringValue("switch", "on");
+    if (switchKnob == "off") {
+        _Instrument->setStringValue("status", "");
+    } else if (switchKnob == "stby") {
+        _Instrument->setStringValue("status", "STBY");
+    } else if (switchKnob == "tst") {
+        _Instrument->setStringValue("status", "TST");
+        // find something interesting to do...
+    } else {
+        float r = _Instrument->getFloatValue("range", 40.0);
+        if (r != _range_nm) {
+            center_map();
+            _range_nm = r;
+        }
+
+        _radar_ref_rng = _radar_ref_rng_node->getDoubleValue();
+        _view_heading = fgGetDouble("/orientation/heading-deg") * SG_DEGREES_TO_RADIANS;
+        _centerTrans.makeTranslate(0.0f, 0.0f, 0.0f);
+
+        _scale = 200.0 / _range_nm;
+        _angle_offset = 0;
+
+        if (_display_mode == ARC) {
+            _scale = 2*200.0f / _range_nm;
+            _angle_offset = -_view_heading;
+            _centerTrans.makeTranslate(0.0f, -200.0f, 0.0f);
+
+        } else if (_display_mode == MAP) {
+            apply_map_offset();
+
+            bool centre = _radar_centre_node->getBoolValue();
+            if (centre) {
+                center_map();
+                _radar_centre_node->setBoolValue(false);
+            }
+
+            //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: displacement "
+            //        << _x_offset <<", "<<_y_offset
+            //        << " user_speed_east_fps * SG_FPS_TO_KT "
+            //        << user_speed_east_fps * SG_FPS_TO_KT
+            //        << " user_speed_north_fps * SG_FPS_TO_KT "
+            //        << user_speed_north_fps * SG_FPS_TO_KT
+            //        << " dt " << delta_time_sec);
+
+            _centerTrans.makeTranslate(_x_offset, _y_offset, 0.0f);
+
+        } else if (_display_mode == PLAN) {
+            if (_radar_rotate_node->getBoolValue()) {
+                _angle_offset = -_view_heading;
+            }
+        } else if (_display_mode == BSCAN) {
+            _angle_offset = -_view_heading;
+        } else {
+            // rose
+        }
+
+        _vertices->clear();
+        _texCoords->clear();
+        _textGeode->removeDrawables(0, _textGeode->getNumDrawables());
+
+#if 0
+        //TODO FIXME Mask below (only used for ARC mode) isn't properly aligned, i.e.
+        // it assumes the a/c position at the center of the display - though it's somewhere at
+        // bottom part for ARC mode.
+        // The mask hadn't worked at all for a while (probably since the OSG port) due to
+        // another bug (which is fixed now). Now, the mask is disabled completely until s.o.
+        // adapted the coordinates below. And the mask is only really useful to limit displayed
+        // weather blobs (not support yet).
+        // Aircraft echos are already limited properly through wxradar's "limit-deg" property.
+        {
+            osg::DrawArrays *maskPSet
+                = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(1));
+            osg::DrawArrays *trimaskPSet
+                = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(2));
+
+            if (_display_mode == ARC) {
+                // erase what is out of sight of antenna
+                /*
+                |\     /|
+                | \   / |
+                |  \ /  |
+                ---------
+                |       |
+                |       |
+                ---------
+                */
+                float xOffset = 256.0f;
+                float yOffset = 200.0f;
+
+                int firstQuadVert = _vertices->size();
+                _texCoords->push_back(osg::Vec2f(0.5f, 0.25f));
+                _vertices->push_back(osg::Vec2f(-xOffset, 0.0 + yOffset));
+                _texCoords->push_back(osg::Vec2f(1.0f, 0.25f));
+                _vertices->push_back(osg::Vec2f(xOffset, 0.0 + yOffset));
+                _texCoords->push_back(osg::Vec2f(1.0f, 0.5f));
+                _vertices->push_back(osg::Vec2f(xOffset, 256.0 + yOffset));
+                _texCoords->push_back(osg::Vec2f(0.5f, 0.5f));
+                _vertices->push_back(osg::Vec2f(-xOffset, 256.0 + yOffset));
+                maskPSet->set(osg::PrimitiveSet::QUADS, firstQuadVert, 4);
+                firstQuadVert += 4;
+
+                // The triangles aren't supposed to be textured, but there's
+                // no need to set up a different Geometry, switch modes,
+                // etc. I happen to know that there's a white pixel in the
+                // texture at 1.0, 0.0 :)
+                float centerY = tan(30 * SG_DEGREES_TO_RADIANS);
+                _vertices->push_back(osg::Vec2f(0.0, 0.0));
+                _vertices->push_back(osg::Vec2f(-256.0, 0.0));
+                _vertices->push_back(osg::Vec2f(-256.0, 256.0 * centerY));
+
+                _vertices->push_back(osg::Vec2f(0.0, 0.0));
+                _vertices->push_back(osg::Vec2f(256.0, 0.0));
+                _vertices->push_back(osg::Vec2f(256.0, 256.0 * centerY));
+
+                _vertices->push_back(osg::Vec2f(-256, 0.0));
+                _vertices->push_back(osg::Vec2f(256.0, 0.0));
+                _vertices->push_back(osg::Vec2f(-256.0, -256.0));
+
+                _vertices->push_back(osg::Vec2f(256, 0.0));
+                _vertices->push_back(osg::Vec2f(256.0, -256.0));
+                _vertices->push_back(osg::Vec2f(-256.0, -256.0));
+
+                const osg::Vec2f whiteSpot(1.0f, 0.0f);
+                for (int i = 0; i < 3 * 4; i++)
+                    _texCoords->push_back(whiteSpot);
+
+                trimaskPSet->set(osg::PrimitiveSet::TRIANGLES, firstQuadVert, 3 * 4);
+
+            } else
+            {
+                maskPSet->set(osg::PrimitiveSet::QUADS, 0, 0);
+                trimaskPSet->set(osg::PrimitiveSet::TRIANGLES, 0, 0);
+            }
+
+            maskPSet->dirty();
+            trimaskPSet->dirty();
+        }
+#endif
+
+        // remember index of next vertex
+        int vIndex = _vertices->size();
+
+        update_weather();
+
+        osg::DrawArrays *quadPSet
+            = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(0));
+
+        update_aircraft();
+        update_tacan();
+        update_heading_marker();
+
+        // draw all new vertices are quads
+        quadPSet->set(osg::PrimitiveSet::QUADS, vIndex, _vertices->size()-vIndex);
+        quadPSet->dirty();
+    }
+}
+
+
+void
+wxRadarBg::update_weather()
+{
+    string modeButton = _Instrument->getStringValue("mode", "WX");
+// FIXME: implementation of radar echoes missing
+//    _radarEchoBuffer = *sgEnviro.get_radar_echo();
+
+    // pretend we have a scan angle bigger then the FOV
+    // TODO:check real fov, enlarge if < nn, and do clipping if > mm
+//    const float fovFactor = 1.45f;
+    _Instrument->setStringValue("status", modeButton.c_str());
+
+// FIXME: implementation of radar echoes missing
+#if 0
+    list_of_SGWxRadarEcho *radarEcho = &_radarEchoBuffer;
+    list_of_SGWxRadarEcho::iterator iradarEcho, end = radarEcho->end();
+    const float LWClevel[] = { 0.1f, 0.5f, 2.1f };
+
+    // draw the cloud radar echo
+    bool drawClouds = _radar_weather_node->getBoolValue();
+    if (drawClouds) {
+
+        // we do that in 3 passes, one for each color level
+        // this is to 'merge' same colors together
+        for (int level = 0; level <= 2; level++) {
+            float col = level * UNIT;
+
+            for (iradarEcho = radarEcho->begin(); iradarEcho != end; ++iradarEcho) {
+                int cloudId = iradarEcho->cloudId;
+                bool upgrade = (cloudId >> 5) & 1;
+                float lwc = iradarEcho->LWC + (upgrade ? 1.0f : 0.0f);
+
+                // skip ns
+                if (iradarEcho->LWC >= 0.5 && iradarEcho->LWC <= 0.6)
+                    continue;
+
+                if (iradarEcho->lightning || lwc < LWClevel[level])
+                    continue;
+
+                float radius = sqrt(iradarEcho->dist) * SG_METER_TO_NM * _scale;
+                float size = iradarEcho->radius * 2.0 * SG_METER_TO_NM * _scale;
+
+                if (radius - size > 180)
+                    continue;
+
+                float angle = (iradarEcho->heading - _angle_offset) //* fovFactor
+                    + 0.5 * SG_PI;
+
+                // Rotate echo into position, and rotate echo to have
+                // a constant orientation towards the
+                // airplane. Compass headings increase in clockwise
+                // direction, while graphics rotations follow
+                // right-hand (counter-clockwise) rule.
+                const osg::Vec2f texBase(col, (UNIT * (float) (4 + (cloudId & 3))));
+
+                osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
+                    * osg::Matrixf::translate(0.0f, radius, 0.0f)
+                    * wxRotate(angle) * _centerTrans);
+                addQuad(_vertices, _texCoords, m, texBase);
+
+                //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: drawing clouds"
+                //        << " ID=" << cloudId
+                //        << " x=" << x
+                //        << " y="<< y
+                //        << " radius=" << radius
+                //        << " view_heading=" << _view_heading * SG_RADIANS_TO_DEGREES
+                //        << " heading=" << iradarEcho->heading * SG_RADIANS_TO_DEGREES
+                //        << " angle=" << angle * SG_RADIANS_TO_DEGREES);
+            }
+        }
+    }
+
+    // draw lightning echos
+    bool drawLightning = _Instrument->getBoolValue("lightning", true);
+    if (drawLightning) {
+        const osg::Vec2f texBase(3 * UNIT, 4 * UNIT);
+
+        for (iradarEcho = radarEcho->begin(); iradarEcho != end; ++iradarEcho) {
+            if (!iradarEcho->lightning)
+                continue;
+
+            float size = UNIT * 0.5f;
+            float radius = iradarEcho->dist * _scale;
+            float angle = iradarEcho->heading * SG_DEGREES_TO_RADIANS
+                - _angle_offset;
+
+            osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
+                * wxRotate(-angle)
+                * osg::Matrixf::translate(0.0f, radius, 0.0f)
+                * wxRotate(angle) * _centerTrans);
+            addQuad(_vertices, _texCoords, m, texBase);
+        }
+    }
+#endif
+}
+
+
+void
+wxRadarBg::update_data(const SGPropertyNode *ac, double altitude, double heading,
+                       double radius, double bearing, bool selected)
+{
+    osgText::Text *callsign = new osgText::Text;
+    callsign->setFont(_font.get());
+    callsign->setFontResolution(12, 12);
+    callsign->setCharacterSize(_font_size);
+    callsign->setColor(selected ? osg::Vec4(1, 1, 1, 1) : _font_color);
+    osg::Matrixf m(wxRotate(-bearing)
+        * osg::Matrixf::translate(0.0f, radius, 0.0f)
+        * wxRotate(bearing) * _centerTrans);
+
+    osg::Vec3 pos = m.preMult(osg::Vec3(16, 16, 0));
+    // cast to int's, otherwise text comes out ugly
+    callsign->setPosition(osg::Vec3((int)pos.x(), (int)pos.y(), 0));
+    callsign->setAlignment(osgText::Text::LEFT_BOTTOM_BASE_LINE);
+    callsign->setLineSpacing(_font_spacing);
+
+    const char *identity = ac->getStringValue("transponder-id");
+    if (!identity[0])
+        identity = ac->getStringValue("callsign");
+
+    stringstream text;
+    text << identity << endl
+        << setprecision(0) << fixed
+        << setw(3) << setfill('0') << heading * SG_RADIANS_TO_DEGREES << "\xB0 "
+        << setw(0) << altitude << "ft" << endl
+        << ac->getDoubleValue("velocities/true-airspeed-kt") << "kts";
+
+    callsign->setText(text.str());
+    _textGeode->addDrawable(callsign);
+}
+
+
+void
+wxRadarBg::update_aircraft()
+{
+    double diff;
+    double age_factor = 1.0;
+    double test_rng;
+    double test_brg;
+    double range;
+    double bearing;
+    float echo_radius;
+    double angle;
+
+    if (!ground_echoes.empty()){
+        ground_echoes_iterator = ground_echoes.begin();
+
+        while(ground_echoes_iterator != ground_echoes.end()) {
+            diff = _elapsed_time - (*ground_echoes_iterator)->elapsed_time;
+
+            if( diff > _persistance) {
+                ground_echoes.erase(ground_echoes_iterator++);
+            } else {
+//                double test_brg = (*ground_echoes_iterator)->bearing;
+//                double bearing = test_brg * SG_DEGREES_TO_RADIANS;
+//                float angle = calcRelBearing(bearing, _view_heading);
+                double bumpinessFactor  = (*ground_echoes_iterator)->bumpiness;
+                float heading = fgGetDouble("/orientation/heading-deg");
+                if ( _display_mode == BSCAN ){
+                    test_rng = (*ground_echoes_iterator)->elevation * 6;
+                    test_brg = (*ground_echoes_iterator)->bearing;
+                    angle = calcRelBearingDeg(test_brg, heading) * 6;
+                    range = sqrt(test_rng * test_rng + angle * angle);
+                    bearing = atan2(angle, test_rng);
+                    //cout << "angle " << angle <<" bearing "
+                    //    << bearing / SG_DEGREES_TO_RADIANS <<  endl;
+                    echo_radius = (0.1 + (1.9 * bumpinessFactor)) * 240 * age_factor;
+                } else {
+                    test_rng = (*ground_echoes_iterator)->range;
+                    range = test_rng * SG_METER_TO_NM;
+                    test_brg = (*ground_echoes_iterator)->bearing;
+                    bearing = test_brg * SG_DEGREES_TO_RADIANS;
+                    echo_radius = (0.1 + (1.9 * bumpinessFactor)) * 120 * age_factor;
+                    bearing += _angle_offset;
+                }
+
+                float radius = range * _scale;
+                //double heading = 90 * SG_DEGREES_TO_RADIANS;
+                //heading += _angle_offset;
+
+                age_factor = 1;
+
+                if (diff != 0)
+                    age_factor = 1 - (0.5 * diff/_persistance);
+
+                float size = echo_radius * UNIT;
+
+                const osg::Vec2f texBase(3 * UNIT, 3 * UNIT);
+                osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
+                    * osg::Matrixf::translate(0.0f, radius, 0.0f)
+                    * wxRotate(bearing) * _centerTrans);
+                addQuad(_vertices, _texCoords, m, texBase);
+
+                ++ground_echoes_iterator;
+
+                //cout << "test bearing " << test_brg 
+                //<< " test_rng " << test_rng * SG_METER_TO_NM
+                //<< " persistance " << _persistance
+                //<< endl;
+            }
+
+        }
+
+    }
+    if (!_ai_enabled_node->getBoolValue())
+        return;
+
+    bool draw_tcas     = _radar_tcas_node->getBoolValue();
+    bool draw_absolute = _radar_absalt_node->getBoolValue();
+    bool draw_echoes   = _radar_position_node->getBoolValue();
+    bool draw_symbols  = _radar_symbol_node->getBoolValue();
+    bool draw_data     = _radar_data_node->getBoolValue();
+    if (!draw_echoes && !draw_symbols && !draw_data)
+        return;
+
+    double user_lat = _user_lat_node->getDoubleValue();
+    double user_lon = _user_lon_node->getDoubleValue();
+    double user_alt = _user_alt_node->getDoubleValue();
+
+    float limit = _radar_coverage_node->getFloatValue();
+    if (limit > 180)
+        limit = 180;
+    else if (limit < 0)
+        limit = 0;
+    limit *= SG_DEGREES_TO_RADIANS;
+
+    int selected_id = fgGetInt("/instrumentation/radar/selected-id", -1);
+
+    const SGPropertyNode *selected_ac = 0;
+    const SGPropertyNode *ai = fgGetNode("/ai/models", true);
+
+    for (int i = ai->nChildren() - 1; i >= -1; i--) {
+        const SGPropertyNode *model;
+
+        if (i < 0) { // last iteration: selected model
+            model = selected_ac;
+        } else {
+            model = ai->getChild(i);
+            if (!model->nChildren())
+                continue;
+            if ((model->getIntValue("id") == selected_id)&&
+                (!draw_tcas)) {
+                selected_ac = model;  // save selected model for last iteration
+                continue;
+            }
+        }
+        if (!model)
+            continue;
+
+        double echo_radius, sigma;
+        const string name = model->getName();
+
+        //cout << "name "<<name << endl;
+        if (name == "aircraft" || name == "tanker")
+            echo_radius = 1, sigma = 1;
+        else if (name == "multiplayer" || name == "wingman" || name == "static")
+            echo_radius = 1.5, sigma = 1;
+        else if (name == "ship" || name == "carrier" || name == "escort" ||name == "storm")
+            echo_radius = 1.5, sigma = 100;
+        else if (name == "thermal")
+            echo_radius = 2, sigma = 100;
+        else if (name == "rocket")
+            echo_radius = 0.1, sigma = 0.1;
+        else if (name == "ballistic")
+            echo_radius = 0.001, sigma = 0.001;
+        else
+            continue;
+
+        double lat = model->getDoubleValue("position/latitude-deg");
+        double lon = model->getDoubleValue("position/longitude-deg");
+        double alt = model->getDoubleValue("position/altitude-ft");
+        double heading = model->getDoubleValue("orientation/true-heading-deg");
+
+        double range, bearing;
+        calcRangeBearing(user_lat, user_lon, lat, lon, range, bearing);
+        //cout << _antenna_ht << _interval<< endl;
+        bool isVisible = withinRadarHorizon(user_alt, alt, range);
+
+        if (!isVisible)
+            continue;
+
+        if (!inRadarRange(sigma, range))
+            continue;
+
+        bearing *= SG_DEGREES_TO_RADIANS;
+        heading *= SG_DEGREES_TO_RADIANS;
+
+        float radius = range * _scale;
+        float angle = calcRelBearing(bearing, _view_heading);
+
+        if (angle > limit || angle < -limit)
+            continue;
+
+        bearing += _angle_offset;
+        heading += _angle_offset;
+
+        bool is_tcas_contact = false;
+        if (draw_tcas)
+        {
+            is_tcas_contact = update_tcas(model,range,user_alt,alt,bearing,radius,draw_absolute);
+        }
+
+        // pos mode
+        if (draw_echoes && (!is_tcas_contact)) {
+            float size = echo_radius * 120 * UNIT;
+
+            const osg::Vec2f texBase(3 * UNIT, 3 * UNIT);
+            osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
+                * osg::Matrixf::translate(0.0f, radius, 0.0f)
+                * wxRotate(bearing) * _centerTrans);
+            addQuad(_vertices, _texCoords, m, texBase);
+        }
+
+        // data mode
+        if (draw_symbols && (!draw_tcas)) {
+            const osg::Vec2f texBase(0, 3 * UNIT);
+            float size = 600 * UNIT;
+            osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
+                * wxRotate(heading - bearing)
+                * osg::Matrixf::translate(0.0f, radius, 0.0f)
+                * wxRotate(bearing) * _centerTrans);
+            addQuad(_vertices, _texCoords, m, texBase);
+        }
+
+        if ((draw_data || i < 0)&&  // selected one (i == -1) is always drawn
+            ((!draw_tcas)||(is_tcas_contact)||(draw_echoes)))
+            update_data(model, alt, heading, radius, bearing, i < 0);
+    }
+}
+
+/** Update TCAS display.
+ * Return true when processed as TCAS contact, false otherwise. */
+bool
+wxRadarBg::update_tcas(const SGPropertyNode *model,double range,double user_alt,double alt,
+                       double bearing,double radius,bool absMode)
+{
+    int threatLevel=0;
+    {
+        // update TCAS symbol
+        osg::Vec2f texBase;
+        threatLevel = model->getIntValue("tcas/threat-level",-1);
+        if (threatLevel == -1)
+        {
+            // no TCAS information (i.e. no transponder) => not visible to TCAS
+            return false;
+        }
+        int row = 7 - threatLevel;
+        int col = 4;
+        double vspeed = model->getDoubleValue("velocities/vertical-speed-fps");
+        if (vspeed < -3.0) // descending
+            col+=1;
+        else
+        if (vspeed > 3.0) // climbing
+            col+=2;
+        texBase = osg::Vec2f(col*UNIT,row * UNIT);
+        float size = 200 * UNIT;
+            osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
+                * wxRotate(-bearing)
+                * osg::Matrixf::translate(0.0f, radius, 0.0f)
+                * wxRotate(bearing) * _centerTrans);
+            addQuad(_vertices, _texCoords, m, texBase);
+    }
+
+    {
+        // update TCAS data
+        osgText::Text *altStr = new osgText::Text;
+        altStr->setFont(_font.get());
+        altStr->setFontResolution(12, 12);
+        altStr->setCharacterSize(_font_size);
+        altStr->setColor(_tcas_colors[threatLevel]);
+        osg::Matrixf m(wxRotate(-bearing)
+            * osg::Matrixf::translate(0.0f, radius, 0.0f)
+            * wxRotate(bearing) * _centerTrans);
+    
+        osg::Vec3 pos = m.preMult(osg::Vec3(16, 16, 0));
+        // cast to int's, otherwise text comes out ugly
+        altStr->setLineSpacing(_font_spacing);
+    
+        stringstream text;
+        altStr->setAlignment(osgText::Text::LEFT_CENTER);
+        int altDif = (alt-user_alt+50)/100;
+        char sign = 0;
+        int dy=0;
+        if (altDif>=0)
+        {
+            sign='+';
+            dy=2;
+        }
+        else
+        if (altDif<0)
+        {
+            sign='-';
+            altDif = -altDif;
+            dy=-30;
+        }
+        altStr->setPosition(osg::Vec3((int)pos.x()-30, (int)pos.y()+dy, 0));
+        if (absMode)
+        {
+            // absolute altitude display
+            text << setprecision(0) << fixed
+                 << setw(3) << setfill('0') << alt/100 << endl;
+        }
+        else // relative altitude display
+        if (sign)
+        {
+            text << sign
+                 << setprecision(0) << fixed
+                 << setw(2) << setfill('0') << altDif << endl;
+        }
+    
+        altStr->setText(text.str());
+        _textGeode->addDrawable(altStr);
+    }
+
+    return true;
+}
+
+void
+wxRadarBg::update_tacan()
+{
+    // draw TACAN symbol
+    int mode = _radar_mode_control_node->getIntValue();
+    bool inRange = _tacan_in_range_node->getBoolValue();
+
+    if (mode != 1 || !inRange)
+        return;
+
+    float size = 600 * UNIT;
+    float radius = _tacan_distance_node->getFloatValue() * _scale;
+    float angle = _tacan_bearing_node->getFloatValue() * SG_DEGREES_TO_RADIANS
+        + _angle_offset;
+
+    const osg::Vec2f texBase(1 * UNIT, 3 * UNIT);
+    osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
+        * wxRotate(-angle)
+        * osg::Matrixf::translate(0.0f, radius, 0.0f)
+        * wxRotate(angle) * _centerTrans);
+    addQuad(_vertices, _texCoords, m, texBase);
+
+    //SG_LOG(SG_INSTR, SG_DEBUG, "Radar:     drawing TACAN"
+    //        << " dist=" << radius
+    //        << " view_heading=" << _view_heading * SG_RADIANS_TO_DEGREES
+    //        << " bearing=" << angle * SG_RADIANS_TO_DEGREES
+    //        << " x=" << x << " y="<< y
+    //        << " size=" << size);
+}
+
+
+void
+wxRadarBg::update_heading_marker()
+{
+    if (!_radar_hdg_marker_node->getBoolValue())
+        return;
+
+    const osg::Vec2f texBase(2 * UNIT, 3 * UNIT);
+    float size = 600 * UNIT;
+    osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
+        * wxRotate(_view_heading + _angle_offset));
+
+    m *= _centerTrans;
+    addQuad(_vertices, _texCoords, m, texBase);
+
+    //SG_LOG(SG_INSTR, SG_DEBUG, "Radar:   drawing heading marker"
+    //        << " x,y " << x <<","<< y
+    //        << " dist" << dist
+    //        << " view_heading" << _view_heading * SG_RADIANS_TO_DEGREES
+    //        << " heading " << iradarEcho->heading * SG_RADIANS_TO_DEGREES
+    //        << " angle " << angle * SG_RADIANS_TO_DEGREES);
+}
+
+
+void
+wxRadarBg::center_map()
+{
+    _lat = _user_lat_node->getDoubleValue();
+    _lon = _user_lon_node->getDoubleValue();
+    _x_offset = _y_offset = 0;
+}
+
+
+void
+wxRadarBg::apply_map_offset()
+{
+    double lat = _user_lat_node->getDoubleValue();
+    double lon = _user_lon_node->getDoubleValue();
+    double bearing, distance, az2;
+    geo_inverse_wgs_84(_lat, _lon, lat, lon, &bearing, &az2, &distance);
+    distance *= SG_METER_TO_NM * _scale;
+    bearing *= SG_DEGREES_TO_RADIANS;
+    _x_offset += sin(bearing) * distance;
+    _y_offset += cos(bearing) * distance;
+    _lat = lat;
+    _lon = lon;
+}
+
+
+bool
+wxRadarBg::withinRadarHorizon(double user_alt, double alt, double range_nm)
+{
+    // Radar Horizon  = 1.23(ht^1/2 + hr^1/2),
+    //don't allow negative altitudes (an approximation - yes altitudes can be negative)
+    // Allow antenna ht to be set, but only on ground
+    _antenna_ht = _Instrument->getDoubleValue("antenna-ht-ft");
+
+    if (user_alt <= 0)
+        user_alt = _antenna_ht;
+
+    if (alt <= 0)
+        alt = 0; // to allow some vertical extent of target
+
+    double radarhorizon = 1.23 * (sqrt(alt) + sqrt(user_alt));
+//    SG_LOG(SG_INSTR, SG_ALERT, "Radar: radar horizon " << radarhorizon);
+    return radarhorizon >= range_nm;
+}
+
+
+bool
+wxRadarBg::inRadarRange(double sigma, double range_nm)
+{
+    //The Radar Equation:
+    //
+    // MaxRange^4 = (TxPower * AntGain^2 * lambda^2 * sigma)/((constant) * MDS)
+    //
+    // Where (constant) = (4*pi)3 and MDS is the Minimum Detectable Signal power.
+    //
+    // For a given radar we can assume that the only variable is sigma,
+    // the target radar cross section.
+    //
+    // Here, we will use a normalised rcs (sigma) for a standard taget and assume that this
+    // will provide a maximum range of 35nm;
+    //
+    // TODO - make the maximum range adjustable at runtime
+
+    double constant = _radar_ref_rng;
+
+    if (constant <= 0)
+        constant = 35;
+
+    double maxrange = constant * pow(sigma, 0.25);
+    //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: max range " << maxrange);
+    return maxrange >= range_nm;
+}
+
+
+void
+wxRadarBg::calcRangeBearing(double lat, double lon, double lat2, double lon2,
+                            double &range, double &bearing) const
+{
+    // calculate the bearing and range of the second pos from the first
+    double az2, distance;
+    geo_inverse_wgs_84(lat, lon, lat2, lon2, &bearing, &az2, &distance);
+    range = distance *= SG_METER_TO_NM;
+}
+
+
+float
+wxRadarBg::calcRelBearing(float bearing, float heading)
+{
+    float angle = bearing - heading;
+
+    if (angle >= SG_PI)
+        angle -= 2.0 * SG_PI;
+
+    if (angle < -SG_PI)
+        angle += 2.0 * SG_PI;
+
+    return angle;
+}
+
+float
+wxRadarBg::calcRelBearingDeg(float bearing, float heading)
+{
+    float angle = bearing - heading;
+
+    if (angle >= 180)
+        return angle -= 360;
+
+    if (angle < -180)
+        return angle += 360;
+
+    return angle;
+}
+
+
+void
+wxRadarBg::updateFont()
+{
+    float red = _font_node->getFloatValue("color/red");
+    float green = _font_node->getFloatValue("color/green");
+    float blue = _font_node->getFloatValue("color/blue");
+    float alpha = _font_node->getFloatValue("color/alpha");
+    _font_color.set(red, green, blue, alpha);
+
+    _font_size = _font_node->getFloatValue("size");
+    _font_spacing = _font_size * _font_node->getFloatValue("line-spacing");
+    string path = _font_node->getStringValue("name", DEFAULT_FONT);
+
+    SGPath tpath;
+    if (path[0] != '/') {
+        tpath = globals->get_fg_root();
+        tpath.append("Fonts");
+        tpath.append(path);
+    } else {
+        tpath = path;
+    }
+
+#if (FG_OSG_VERSION >= 21000)
+    osg::ref_ptr<osgDB::ReaderWriter::Options> fontOptions = new osgDB::ReaderWriter::Options("monochrome");
+    osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str(), fontOptions.get());
+#else
+    osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str());
+#endif
+
+    if (font != 0) {
+        _font = font;
+        _font->setMinFilterHint(osg::Texture::NEAREST);
+        _font->setMagFilterHint(osg::Texture::NEAREST);
+        _font->setGlyphImageMargin(0);
+        _font->setGlyphImageMarginRatio(0);
+    }
+
+    for (int i=0;i<4;i++)
+    {
+        const float defaultColors[4][3] = {{0,1,1},{0,1,1},{1,0.5,0},{1,0,0}};
+        SGPropertyNode_ptr color_node = _font_node->getNode("tcas/color",i,true);
+        float red   = color_node->getFloatValue("red",defaultColors[i][0]);
+        float green = color_node->getFloatValue("green",defaultColors[i][1]);
+        float blue  = color_node->getFloatValue("blue",defaultColors[i][2]);
+        float alpha = color_node->getFloatValue("alpha",1);
+        _tcas_colors[i]=osg::Vec4(red, green, blue, alpha);
+    }
+}
+
+void
+wxRadarBg::valueChanged(SGPropertyNode*)
+{
+    updateFont();
+    _time = _interval;
+}
+
diff --git a/src/Cockpit/wxradar.hxx b/src/Cockpit/wxradar.hxx
new file mode 100644 (file)
index 0000000..7aff777
--- /dev/null
@@ -0,0 +1,202 @@
+// Wx Radar background texture
+//
+// Written by Harald JOHNSEN, started May 2005.
+// With major amendments by Vivian MEAZZA May 2007
+// Ported to OSG by Tim MOORE Jun 2007
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef _INST_WXRADAR_HXX
+#define _INST_WXRADAR_HXX
+
+#include <osg/ref_ptr>
+#include <osg/Geode>
+#include <osg/Texture2D>
+#include <osgText/Text>
+
+#include <simgear/props/props.hxx>
+#include <simgear/structure/subsystem_mgr.hxx>
+
+#include <vector>
+#include <string>
+
+class FGODGauge;
+
+class wxRadarBg : public SGSubsystem, public SGPropertyChangeListener {
+public:
+
+    wxRadarBg(SGPropertyNode *node);
+    wxRadarBg();
+    virtual ~wxRadarBg();
+
+    virtual void init();
+    virtual void update(double dt);
+    virtual void valueChanged(SGPropertyNode *);
+
+protected:
+    std::string _name;
+    int _num;
+    double _time;
+    double _interval;
+    double _elapsed_time;
+    double _persistance;
+
+    SGPropertyNode_ptr _serviceable_node;
+    SGPropertyNode_ptr _sceneryLoaded;
+    SGPropertyNode_ptr _Instrument;
+    SGPropertyNode_ptr _radar_mode_control_node;
+
+    SGPropertyNode_ptr _user_lat_node;
+    SGPropertyNode_ptr _user_lon_node;
+    SGPropertyNode_ptr _user_heading_node;
+    SGPropertyNode_ptr _user_alt_node;
+
+    FGODGauge *_odg;
+
+    typedef struct {
+        double bearing;
+        double range;
+        double elevation;
+        double bumpiness;
+        double elapsed_time;
+    }ground_echo;
+
+    typedef std::vector <ground_echo*> ground_echo_vector_type;
+    typedef ground_echo_vector_type::iterator ground_echo_vector_iterator;
+
+    ground_echo_vector_type       ground_echoes;
+    ground_echo_vector_iterator   ground_echoes_iterator;
+
+    // Convenience function for creating a property node with a
+    // default value
+    template<typename DefaultType>
+    SGPropertyNode *getInstrumentNode(const char *name, DefaultType value);
+
+private:
+    std::string _texture_path;
+
+    typedef enum { ARC, MAP, PLAN, ROSE, BSCAN} DisplayMode;
+    DisplayMode _display_mode;
+
+    float _range_nm;
+    float _scale;   // factor to convert nm to display units
+    float _angle_offset;
+    float _view_heading;
+    float _x_offset, _y_offset;
+
+    double _radar_ref_rng;
+    double _lat, _lon;
+    double _antenna_ht;
+
+    SGPropertyNode_ptr _Tacan;
+    SGPropertyNode_ptr _Radar_controls;
+
+    SGPropertyNode_ptr _user_speed_east_fps_node;
+    SGPropertyNode_ptr _user_speed_north_fps_node;
+
+    SGPropertyNode_ptr _tacan_serviceable_node;
+    SGPropertyNode_ptr _tacan_distance_node;
+    SGPropertyNode_ptr _tacan_name_node;
+    SGPropertyNode_ptr _tacan_bearing_node;
+    SGPropertyNode_ptr _tacan_in_range_node;
+
+    SGPropertyNode_ptr _radar_weather_node;
+    SGPropertyNode_ptr _radar_position_node;
+    SGPropertyNode_ptr _radar_data_node;
+    SGPropertyNode_ptr _radar_symbol_node;
+
+    SGPropertyNode_ptr _radar_centre_node;
+    SGPropertyNode_ptr _radar_coverage_node;
+    SGPropertyNode_ptr _radar_ref_rng_node;
+    SGPropertyNode_ptr _radar_hdg_marker_node;
+    SGPropertyNode_ptr _radar_rotate_node;
+    SGPropertyNode_ptr _radar_tcas_node;
+    SGPropertyNode_ptr _radar_absalt_node;
+
+    SGPropertyNode_ptr _font_node;
+    SGPropertyNode_ptr _ai_enabled_node;
+
+    osg::ref_ptr<osg::Texture2D> _resultTexture;
+    osg::ref_ptr<osg::Texture2D> _wxEcho;
+    osg::ref_ptr<osg::Geode> _radarGeode;
+    osg::ref_ptr<osg::Geode> _textGeode;
+    osg::Geometry *_geom;
+    osg::Vec2Array *_vertices;
+    osg::Vec2Array *_texCoords;
+    osg::Matrixf _centerTrans;
+    osg::ref_ptr<osgText::Font> _font;
+    osg::Vec4 _font_color;
+    osg::Vec4 _tcas_colors[4];
+    float _font_size;
+    float _font_spacing;
+
+// FIXME: implementation of radar echoes missing
+//    list_of_SGWxRadarEcho _radarEchoBuffer;
+
+    void update_weather();
+    void update_aircraft();
+    void update_tacan();
+    void update_heading_marker();
+    void update_data(const SGPropertyNode *ac, double alt, double heading,
+        double radius, double bearing, bool selected);
+    bool update_tcas(const SGPropertyNode *model,double range,double user_alt,double alt,
+                     double bearing,double radius, bool absMode);
+    void center_map();
+    void apply_map_offset();
+    void updateFont();
+    void calcRangeBearing(double lat, double lon, double lat2, double lon2,
+        double &range, double &bearing) const;
+
+    bool withinRadarHorizon(double user_alt, double alt, double range);
+    bool inRadarRange(double sigma, double range);
+
+    float calcRelBearing(float bearing, float heading);
+    float calcRelBearingDeg(float bearing, float heading);
+};
+
+
+template<> inline
+SGPropertyNode *wxRadarBg::getInstrumentNode(const char *name, bool value)
+{
+    SGPropertyNode *result = _Instrument->getNode(name, true);
+    if (!result->hasValue())
+        result->setBoolValue(value);
+    return result;
+}
+
+
+template<> inline
+SGPropertyNode *wxRadarBg::getInstrumentNode(const char *name, double value)
+{
+    SGPropertyNode *result = _Instrument->getNode(name, true);
+    if (!result->hasValue())
+        result->setDoubleValue(value);
+    return result;
+}
+
+
+template<> inline
+SGPropertyNode *wxRadarBg::getInstrumentNode(const char *name, const char *value)
+{
+    SGPropertyNode *result = _Instrument->getNode(name, true);
+    if (result->hasValue())
+        result->setStringValue(value);
+    return result;
+}
+
+
+#endif // _INST_WXRADAR_HXX
index 4060b83b9d17f8b71d060e3a194d1c8c63c5b335..ba0ba8b7b55c25eda3fae2b4cf62c90573355281 100644 (file)
@@ -2,7 +2,6 @@ include(FlightGearComponent)
 
 set(SOURCES
     adf.cxx
-    agradar.cxx
     airspeed_indicator.cxx
     altimeter.cxx
     attitude_indicator.cxx
@@ -10,7 +9,6 @@ set(SOURCES
     dclgps.cxx
     dme.cxx
     gps.cxx
-    groundradar.cxx
     gsdi.cxx
     gyro.cxx
     heading_indicator.cxx
@@ -26,9 +24,7 @@ set(SOURCES
     mrg.cxx
     navradio.cxx
     newnavradio.cxx
-    od_gauge.cxx
     rad_alt.cxx
-    render_area_2d.cxx
     rnav_waypt_controller.cxx
     slip_skid_ball.cxx
     tacan.cxx
@@ -36,8 +32,6 @@ set(SOURCES
     transponder.cxx
     turn_indicator.cxx
     vertical_speed_indicator.cxx
-    wxradar.cxx
-    NavDisplay.cxx
     HUD/HUD.cxx
     HUD/HUD_dial.cxx
     HUD/HUD_gauge.cxx
@@ -69,7 +63,6 @@ set(SOURCES
 
 set(HEADERS
     adf.hxx
-    agradar.hxx
     airspeed_indicator.hxx
     altimeter.hxx
     attitude_indicator.hxx
@@ -77,7 +70,6 @@ set(HEADERS
     dclgps.hxx
     dme.hxx
     gps.hxx
-    groundradar.hxx
     gsdi.hxx
     gyro.hxx
     heading_indicator.hxx
@@ -93,9 +85,7 @@ set(HEADERS
     mrg.hxx
     navradio.hxx
     newnavradio.hxx
-    od_gauge.hxx
     rad_alt.hxx
-    render_area_2d.hxx
     rnav_waypt_controller.hxx
     slip_skid_ball.hxx
     tacan.hxx
@@ -103,8 +93,6 @@ set(HEADERS
     transponder.hxx
     turn_indicator.hxx
     vertical_speed_indicator.hxx
-    wxradar.hxx
-    NavDisplay.hxx
     HUD/HUD.hxx
     HUD/HUD_private.hxx
     KLN89/kln89.hxx
diff --git a/src/Instrumentation/NavDisplay.cxx b/src/Instrumentation/NavDisplay.cxx
deleted file mode 100644 (file)
index ecf9bdf..0000000
+++ /dev/null
@@ -1,1435 +0,0 @@
-// navigation display texture
-//
-// Written by James Turner, forked from wxradar code
-//
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//
-//
-
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#endif
-
-#include "NavDisplay.hxx"
-
-#include <cassert>
-#include <boost/foreach.hpp>
-#include <boost/algorithm/string/case_conv.hpp>
-#include <algorithm>
-
-#include <osg/Array>
-#include <osg/Geometry>
-#include <osg/Matrixf>
-#include <osg/PrimitiveSet>
-#include <osg/StateSet>
-#include <osg/LineWidth>
-#include <osg/Version>
-
-#include <simgear/constants.h>
-#include <simgear/misc/sg_path.hxx>
-#include <simgear/scene/model/model.hxx>
-#include <simgear/structure/exception.hxx>
-#include <simgear/misc/sg_path.hxx>
-#include <simgear/misc/strutils.hxx>
-#include <simgear/math/sg_geodesy.hxx>
-
-#include <sstream>
-#include <iomanip>
-#include <iostream>             // for cout, endl
-
-using std::stringstream;
-using std::endl;
-using std::setprecision;
-using std::fixed;
-using std::setw;
-using std::setfill;
-using std::cout;
-using std::endl;
-using std::map;
-using std::string;
-
-#include <Main/fg_props.hxx>
-#include <Main/globals.hxx>
-#include <Cockpit/panel.hxx>
-#include <Navaids/routePath.hxx>
-#include <Autopilot/route_mgr.hxx>
-#include <Navaids/navrecord.hxx>
-#include <Navaids/navlist.hxx>
-#include <Navaids/fix.hxx>
-#include <Airports/simple.hxx>
-#include <Airports/runways.hxx>
-
-#include "instrument_mgr.hxx"
-#include "od_gauge.hxx"
-
-static const char *DEFAULT_FONT = "typewriter.txf";
-
-static
-osg::Matrixf degRotation(float angle)
-{
-    return osg::Matrixf::rotate(angle * SG_DEGREES_TO_RADIANS, 0.0f, 0.0f, -1.0f);
-}
-
-static osg::Vec4 readColor(SGPropertyNode* colorNode, const osg::Vec4& c)
-{
-    osg::Vec4 result;
-    result.r() = colorNode->getDoubleValue("red",   c.r());
-    result.g() = colorNode->getDoubleValue("green", c.g());
-    result.b() = colorNode->getDoubleValue("blue",  c.b());
-    result.a() = colorNode->getDoubleValue("alpha", c.a());
-    return result;
-}
-
-static osgText::Text::AlignmentType readAlignment(const std::string& t)
-{
-    if (t == "left-top") {
-        return osgText::Text::LEFT_TOP;
-    } else if (t == "left-center") {
-        return osgText::Text::LEFT_CENTER;
-    } else if (t == "left-bottom") {
-        return osgText::Text::LEFT_BOTTOM;
-    } else if (t == "center-top") {
-        return osgText::Text::CENTER_TOP;
-    } else if (t == "center-center") {
-        return osgText::Text::CENTER_CENTER;
-    } else if (t == "center-bottom") {
-        return osgText::Text::CENTER_BOTTOM;
-    } else if (t == "right-top") {
-        return osgText::Text::RIGHT_TOP;
-    } else if (t == "right-center") {
-        return osgText::Text::RIGHT_CENTER;
-    } else if (t == "right-bottom") {
-        return osgText::Text::RIGHT_BOTTOM;
-    } else if (t == "left-baseline") {
-        return osgText::Text::LEFT_BASE_LINE;
-    } else if (t == "center-baseline") {
-        return osgText::Text::CENTER_BASE_LINE;
-    } else if (t == "right-baseline") {
-        return osgText::Text::RIGHT_BASE_LINE;
-    }
-    
-    return osgText::Text::BASE_LINE;
-}
-
-static string formatPropertyValue(SGPropertyNode* nd, const string& format)
-{
-    assert(nd);
-    static char buf[512];
-    if (format.find('d') != string::npos) {
-        ::snprintf(buf, 512, format.c_str(), nd->getIntValue());
-        return buf;
-    }
-    
-    if (format.find('s') != string::npos) {
-        ::snprintf(buf, 512, format.c_str(), nd->getStringValue());
-        return buf;
-    }
-    
-// assume it's a double/float
-    ::snprintf(buf, 512, format.c_str(), nd->getDoubleValue());
-    return buf;
-}
-
-static osg::Vec2 mult(const osg::Vec2& v, const osg::Matrixf& m)
-{
-    osg::Vec3 r = m.preMult(osg::Vec3(v.x(), v.y(), 0.0));
-    return osg::Vec2(r.x(), r.y());
-}
-
-class NavDisplay::CacheListener : public SGPropertyChangeListener
-{
-public:
-    CacheListener(NavDisplay *nd) : 
-        _nd(nd)
-    {}
-    
-    virtual void valueChanged (SGPropertyNode * prop)
-    {
-        _nd->invalidatePositionedCache();
-    }
-private:
-    NavDisplay* _nd;
-};
-
-class NavDisplay::ForceUpdateListener : public SGPropertyChangeListener
-{
-public:
-  ForceUpdateListener(NavDisplay *nd) : 
-    _nd(nd)
-  {}
-  
-  virtual void valueChanged (SGPropertyNode * prop)
-  {
-    _nd->forceUpdate();
-  }
-private:
-  NavDisplay* _nd;
-};
-
-///////////////////////////////////////////////////////////////////
-
-class SymbolRule
-{
-public:
-  SymbolRule()
-    {
-        
-    }
-    
-    bool initFromNode(SGPropertyNode* node, NavDisplay* owner)
-    {
-        if (!node->getChild("type")) {
-            return false;
-        }
-        
-        type = node->getStringValue("type");
-        boost::to_lower(type);
-        SGPropertyNode* enableNode = node->getChild("enable");
-        if (enableNode) { 
-            enable.reset(sgReadCondition(fgGetNode("/"), enableNode));
-        }
-        
-        int n=0;
-        while (node->hasChild("state", n)) {
-            string m = node->getChild("state", n++)->getStringValue();
-            if (m[0] == '!') {
-                excluded_states.insert(m.substr(1));
-            } else {
-                required_states.insert(m);
-            }
-        } // of matches parsing
-        
-          
-        return true;
-    }
-    
-    void setDefinition(SymbolDef* d)
-    {
-        definition = d;
-    }
-    
-    SymbolDef* getDefinition() const
-    { return definition; }
-    
-    bool matches(const string_set& states) const
-    {
-        BOOST_FOREACH(const string& s, required_states) {
-            if (states.count(s) == 0) {
-                return false;
-            }
-        }
-        
-        BOOST_FOREACH(const string& s, excluded_states) {
-            if (states.count(s) != 0) {
-                return false;
-            }
-        }
-        
-        return true;
-    }
-    
-  // return if the enabled state changed (needs a cache update)
-    bool checkEnabled()
-    {
-        if (enable.get()) {
-            bool wasEnabled = enabled;
-            enabled = enable->test();
-            return (enabled != wasEnabled);
-        } else {
-            enabled = true;
-            return false;
-        }
-    }
-    
-    bool enabled; // cached enabled state
-    std::string type;
-    
-  // record instances for limiting by count
-    int instanceCount;
-private:
-    SymbolDef* definition;
-    
-    std::auto_ptr<SGCondition> enable;
-    string_set required_states;
-    string_set excluded_states;
-};
-
-class SymbolDef
-{
-public:
-    SymbolDef() : limitCount(0) { }
-  
-    bool initFromNode(SGPropertyNode* node, NavDisplay* owner)
-    {
-        if (node->getChild("type")) {
-            SymbolRule* builtinRule = new SymbolRule;
-            builtinRule->initFromNode(node, owner);
-            builtinRule->setDefinition(this);
-            owner->addRule(builtinRule);
-        }
-        
-        if (node->hasChild("width")) {
-            float w = node->getFloatValue("width");
-            float h = node->getFloatValue("height", w);
-            xy0.x() = -w * 0.5;
-            xy0.y() = -h * 0.5;
-            xy1.x() = w * 0.5;
-            xy1.y() = h * 0.5;
-        } else {
-            xy0.x()  = node->getFloatValue("x0", 0.0);
-            xy0.y()  = node->getFloatValue("y0", 0.0);
-            xy1.x()  = node->getFloatValue("x1", 5);
-            xy1.y()  = node->getFloatValue("y1", 5);
-        }
-      
-        double texSize = node->getFloatValue("texture-size", owner->textureSize());
-        
-        uv0.x()  = node->getFloatValue("u0", 0) / texSize;
-        uv0.y()  = node->getFloatValue("v0", 0) / texSize;
-        uv1.x()  = node->getFloatValue("u1", 1) / texSize;
-        uv1.y()  = node->getFloatValue("v1", 1) / texSize;
-        
-        color = readColor(node->getChild("color"), osg::Vec4(1, 1, 1, 1));
-        priority = node->getIntValue("priority", 0);
-        zOrder = node->getIntValue("zOrder", 0);
-        rotateToHeading = node->getBoolValue("rotate-to-heading", false);
-        roundPos = node->getBoolValue("round-position", true);
-        hasText = false;
-        if (node->hasChild("text")) {
-            hasText = true;
-            alignment = readAlignment(node->getStringValue("text-align"));
-            textTemplate = node->getStringValue("text");
-            textOffset.x() = node->getFloatValue("text-offset-x", 0);
-            textOffset.y() = node->getFloatValue("text-offset-y", 0);
-            textColor = readColor(node->getChild("text-color"), color);
-          
-            SGPropertyNode* enableNode = node->getChild("text-enable");
-            if (enableNode) { 
-              textEnable.reset(sgReadCondition(fgGetNode("/"), enableNode));
-            }
-        }
-        
-        drawLine = node->getBoolValue("draw-line", false);
-        lineColor = readColor(node->getChild("line-color"), color);
-        drawRouteLeg = node->getBoolValue("draw-leg", false);
-        
-        stretchSymbol = node->getBoolValue("stretch-symbol", false);
-        if (stretchSymbol) {
-            stretchY2 = node->getFloatValue("y2");
-            stretchY3 = node->getFloatValue("y3");
-            stretchV2 = node->getFloatValue("v2") / texSize;
-            stretchV3 = node->getFloatValue("v3") / texSize;
-        }
-      
-        SGPropertyNode* limitNode = node->getChild("limit");
-        if (limitNode) {
-          limitCount = limitNode->getIntValue();
-        }
-
-        return true;
-    }
-    
-    osg::Vec2 xy0, xy1;
-    osg::Vec2 uv0, uv1;
-    osg::Vec4 color;
-    
-    int priority;
-    int zOrder;
-    bool rotateToHeading;
-    bool roundPos; ///< should position be rounded to integer values
-    bool hasText;
-    std::auto_ptr<SGCondition> textEnable;
-    bool textEnabled; ///< cache condition result
-    osg::Vec4 textColor;
-    osg::Vec2 textOffset;
-    osgText::Text::AlignmentType alignment;
-    string textTemplate;
-    
-    bool drawLine;
-    osg::Vec4 lineColor;
-    
-// symbol stretching creates three quads (instead of one) - a start,
-// middle and end quad, positioned along the line of the symbol.
-// X (and U) axis values determined by the values above, so we only need
-// to define the Y (and V) values to build the other quads.
-    bool stretchSymbol;
-    double stretchY2, stretchY3;
-    double stretchV2, stretchV3;
-    
-    bool drawRouteLeg;
-    
-    int limitCount, instanceCount;
-};
-
-class SymbolInstance
-{
-public:
-    SymbolInstance(const osg::Vec2& p, double h, SymbolDef* def, SGPropertyNode* vars) :
-        pos(p),
-        headingDeg(h),
-        definition(def),
-        props(vars)
-    { }
-    
-    osg::Vec2 pos; // projected position
-    osg::Vec2 endPos;
-    double headingDeg;
-    SymbolDef* definition;
-    SGPropertyNode_ptr props;
-    
-    string text() const
-    {
-        assert(definition->hasText);
-        string r;        
-        size_t lastPos = 0;
-        
-        while (true) {
-            size_t pos = definition->textTemplate.find('{', lastPos);
-            if (pos == string::npos) { // no more replacements
-                r.append(definition->textTemplate.substr(lastPos));
-                break;
-            }
-            
-            r.append(definition->textTemplate.substr(lastPos, pos - lastPos));
-            
-            size_t endReplacement = definition->textTemplate.find('}', pos+1);
-            if (endReplacement <= pos) {
-                return "bad replacement";
-            }
-
-            string spec = definition->textTemplate.substr(pos + 1, endReplacement - (pos + 1));
-        // look for formatter in spec
-            size_t colonPos = spec.find(':');
-            if (colonPos == string::npos) {
-            // simple replacement
-                r.append(props->getStringValue(spec));
-            } else {
-                string format = spec.substr(colonPos + 1);
-                string prop = spec.substr(0, colonPos);
-                r.append(formatPropertyValue(props->getNode(prop), format));
-            }
-            
-            lastPos = endReplacement + 1;
-        }
-        
-        return r;
-    }
-};
-
-//////////////////////////////////////////////////////////////////
-
-NavDisplay::NavDisplay(SGPropertyNode *node) :
-    _name(node->getStringValue("name", "nd")),
-    _num(node->getIntValue("number", 0)),
-    _time(0.0),
-    _updateInterval(node->getDoubleValue("update-interval-sec", 0.1)),
-    _forceUpdate(true),
-    _odg(0),
-    _scale(0),
-    _view_heading(0),
-    _font_size(0),
-    _font_spacing(0),
-    _rangeNm(0),
-    _maxSymbols(100)
-{
-    _Instrument = fgGetNode(string("/instrumentation/" + _name).c_str(), _num, true);
-    _font_node = _Instrument->getNode("font", true);
-
-#define INITFONT(p, val, type) if (!_font_node->hasValue(p)) _font_node->set##type##Value(p, val)
-    INITFONT("name", DEFAULT_FONT, String);
-    INITFONT("size", 8, Float);
-    INITFONT("line-spacing", 0.25, Float);
-    INITFONT("color/red", 0, Float);
-    INITFONT("color/green", 0.8, Float);
-    INITFONT("color/blue", 0, Float);
-    INITFONT("color/alpha", 1, Float);
-#undef INITFONT
-
-    _textureSize = _Instrument->getNode("symbol-texture-size", true)->getIntValue();
-    SGPropertyNode* symbolsNode = node->getNode("symbols");
-    SGPropertyNode* symbol;
-
-    map<string, SymbolDef*> definitionDict;
-    for (int i = 0; (symbol = symbolsNode->getChild("symbol", i)) != NULL; ++i) {
-        SymbolDef* def = new SymbolDef;
-        if (!def->initFromNode(symbol, this)) {
-          delete def;
-          continue;
-        }
-        
-        const char* id = symbol->getStringValue("id");
-        if (id && strlen(id)) {
-            definitionDict[id] = def;
-        }
-        
-        _definitions.push_back(def);
-    } // of symbol definition parsing
-    
-    SGPropertyNode* rulesNode = node->getNode("rules");
-    if (rulesNode) {
-        SGPropertyNode* rule;
-        
-        for (int i = 0; (rule = rulesNode->getChild("rule", i)) != NULL; ++i) {
-            SymbolRule* r = new SymbolRule;
-            if (!r->initFromNode(rule, this)) {
-                delete r;
-                continue;
-            }
-            
-            const char* id = symbol->getStringValue("symbol");
-            if (id && strlen(id) && (definitionDict.find(id) != definitionDict.end())) {
-                r->setDefinition(definitionDict[id]);
-            } else {
-                SG_LOG(SG_INSTR, SG_WARN, "symbol rule has missing/unknown definition id:" << id);
-                delete r;
-                continue;
-            }
-            
-            addRule(r);
-        } // of symbol rule parsing
-    }
-    
-}
-
-
-NavDisplay::~NavDisplay()
-{
-  delete _odg;
-}
-
-void
-NavDisplay::init ()
-{
-    _cachedItemsValid = false;
-    _cacheListener.reset(new CacheListener(this));
-    _forceUpdateListener.reset(new ForceUpdateListener(this));
-  
-    _serviceable_node = _Instrument->getNode("serviceable", true);
-    _rangeNode = _Instrument->getNode("range", true);
-    if (!_rangeNode->hasValue()) {
-      _rangeNode->setDoubleValue(40.0);
-    }
-    _rangeNode->addChangeListener(_cacheListener.get());
-    _rangeNode->addChangeListener(_forceUpdateListener.get());
-  
-    _xCenterNode = _Instrument->getNode("x-center");
-    if (!_xCenterNode->hasValue()) {
-      _xCenterNode->setDoubleValue(0.5);
-    }
-    _xCenterNode->addChangeListener(_forceUpdateListener.get());
-    _yCenterNode = _Instrument->getNode("y-center");
-    if (!_yCenterNode->hasValue()) {
-      _yCenterNode->setDoubleValue(0.5);
-    }
-    _yCenterNode->addChangeListener(_forceUpdateListener.get());
-  
-    // texture name to use in 2D and 3D instruments
-    _texture_path = _Instrument->getStringValue("radar-texture-path",
-        "Aircraft/Instruments/Textures/od_wxradar.rgb");
-
-    string path = _Instrument->getStringValue("symbol-texture-path",
-        "Aircraft/Instruments/Textures/nd-symbols.png");
-    SGPath tpath = globals->resolve_aircraft_path(path);
-    if (!tpath.exists()) {
-      SG_LOG(SG_INSTR, SG_WARN, "ND symbol texture not found:" << path);
-    }
-  
-    // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect
-    _symbolTexture = SGLoadTexture2D(tpath, NULL, false, false);
-
-    _odg = new FGODGauge;
-    _odg->setSize(_Instrument->getIntValue("texture-size", 512));
-
-    _route = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
-    
-    _navRadio1Node = fgGetNode("/instrumentation/nav[0]", true);
-    _navRadio2Node = fgGetNode("/instrumentation/nav[1]", true);
-    
-    _excessDataNode = _Instrument->getChild("excess-data", 0, true);
-    _excessDataNode->setBoolValue(false);
-    _testModeNode = _Instrument->getChild("test-mode", 0, true);
-    _testModeNode->setBoolValue(false);
-  
-    _viewHeadingNode = _Instrument->getChild("view-heading-deg", 0, true);
-// OSG geometry setup
-    _radarGeode = new osg::Geode;
-
-    _geom = new osg::Geometry;
-    _geom->setUseDisplayList(false);
-    
-    osg::StateSet *stateSet = _geom->getOrCreateStateSet();
-    stateSet->setTextureAttributeAndModes(0, _symbolTexture.get());
-    stateSet->setDataVariance(osg::Object::STATIC);
-  
-    // Initially allocate space for 128 quads
-    _vertices = new osg::Vec2Array;
-    _vertices->setDataVariance(osg::Object::DYNAMIC);
-    _vertices->reserve(128 * 4);
-    _geom->setVertexArray(_vertices);
-    _texCoords = new osg::Vec2Array;
-    _texCoords->setDataVariance(osg::Object::DYNAMIC);
-    _texCoords->reserve(128 * 4);
-    _geom->setTexCoordArray(0, _texCoords);
-    
-    _quadColors = new osg::Vec4Array;
-    _quadColors->setDataVariance(osg::Object::DYNAMIC);
-    _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
-    _geom->setColorArray(_quadColors);
-    
-    _symbolPrimSet = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
-    _symbolPrimSet->setDataVariance(osg::Object::DYNAMIC);
-    _geom->addPrimitiveSet(_symbolPrimSet);
-    
-    _geom->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f),
-        osg::Vec3f(256.0f, 256.0f, 0.0f)));
-  
-    _radarGeode->addDrawable(_geom);
-    _odg->allocRT();
-    // Texture in the 2D panel system
-    FGTextureManager::addTexture(_texture_path.c_str(), _odg->getTexture());
-
-    _lineGeometry = new osg::Geometry;
-    _lineGeometry->setUseDisplayList(false);
-    stateSet = _lineGeometry->getOrCreateStateSet();    
-    osg::LineWidth *lw = new osg::LineWidth();
-    lw->setWidth(2.0);
-    stateSet->setAttribute(lw);
-    
-    _lineVertices = new osg::Vec2Array;
-    _lineVertices->setDataVariance(osg::Object::DYNAMIC);
-    _lineVertices->reserve(128 * 4);
-    _lineGeometry->setVertexArray(_lineVertices);
-    
-                  
-    _lineColors = new osg::Vec4Array;
-    _lineColors->setDataVariance(osg::Object::DYNAMIC);
-    _lineGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
-    _lineGeometry->setColorArray(_lineColors);
-    
-    _linePrimSet = new osg::DrawArrays(osg::PrimitiveSet::LINES);
-    _linePrimSet->setDataVariance(osg::Object::DYNAMIC);
-    _lineGeometry->addPrimitiveSet(_linePrimSet);
-    
-    _lineGeometry->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f),
-                                            osg::Vec3f(256.0f, 256.0f, 0.0f)));
-
-    _radarGeode->addDrawable(_lineGeometry);              
-                  
-    _textGeode = new osg::Geode;
-
-    osg::Camera *camera = _odg->getCamera();
-    camera->addChild(_radarGeode.get());
-    camera->addChild(_textGeode.get());
-    osg::Texture2D* tex = _odg->getTexture();
-    camera->setProjectionMatrixAsOrtho2D(0, tex->getTextureWidth(), 
-        0, tex->getTextureHeight());
-    
-    updateFont();
-}
-
-void
-NavDisplay::update (double delta_time_sec)
-{
-  if (!fgGetBool("sim/sceneryloaded", false)) {
-    return;
-  }
-
-  if (!_odg || !_serviceable_node->getBoolValue()) {
-    _Instrument->setStringValue("status", "");
-    return;
-  }
-  
-  if (_forceUpdate) {
-    _forceUpdate = false;
-    _time = 0.0;
-  } else {
-    _time += delta_time_sec;
-    if (_time < _updateInterval){
-      return;
-    }
-    _time -= _updateInterval;
-  }
-
-  _rangeNm = _rangeNode->getFloatValue();
-  if (_testModeNode->getBoolValue()) {
-    _view_heading = 90;
-  } else if (_Instrument->getBoolValue("aircraft-heading-up", true)) {
-    _view_heading = fgGetDouble("/orientation/heading-deg");
-  } else {
-    _view_heading = _Instrument->getFloatValue("heading-up-deg", 0.0);
-  }
-  _viewHeadingNode->setDoubleValue(_view_heading);
-  
-  double xCenterFrac = _xCenterNode->getDoubleValue();
-  double yCenterFrac = _yCenterNode->getDoubleValue();
-  int pixelSize = _odg->size();
-  
-  int rangePixels = _Instrument->getIntValue("range-pixels", -1);
-  if (rangePixels < 0) {
-    // hacky - assume (as is very common) that x-frac doesn't vary, and
-    // y-frac is used to position the center at either the top or bottom of
-    // the pixel area. Measure from the center to the furthest edge (top or bottom)
-    rangePixels = pixelSize * std::max(fabs(1.0 - yCenterFrac), fabs(yCenterFrac));
-  }
-  
-  _scale = rangePixels / _rangeNm;
-  _Instrument->setDoubleValue("scale", _scale);
-  
-  
-  _centerTrans = osg::Matrixf::translate(xCenterFrac * pixelSize, 
-      yCenterFrac * pixelSize, 0.0);
-
-// scale from nm to display units, rotate so aircraft heading is up
-// (as opposed to north), and compensate for centering
-  _projectMat = osg::Matrixf::scale(_scale, _scale, 1.0) * 
-      degRotation(-_view_heading) * _centerTrans;
-  
-  _pos = globals->get_aircraft_position();
-
-    // invalidate the cache of positioned items, if we travelled more than 1nm
-    if (_cachedItemsValid) {
-        SGVec3d cartNow(SGVec3d::fromGeod(_pos));
-        double movedNm = dist(_cachedPos, cartNow) * SG_METER_TO_NM;
-        _cachedItemsValid = (movedNm < 1.0);
-    }
-    
-  _vertices->clear();
-  _lineVertices->clear();
-  _lineColors->clear();
-  _quadColors->clear();
-  _texCoords->clear();
-  _textGeode->removeDrawables(0, _textGeode->getNumDrawables());
-  
-  BOOST_FOREACH(SymbolInstance* si, _symbols) {
-      delete si;
-  }
-  _symbols.clear();
-  
-  BOOST_FOREACH(SymbolDef* d, _definitions) {
-    d->instanceCount = 0;
-    d->textEnabled = d->textEnable.get() ? d->textEnable->test() : true;
-  }
-  
-  bool enableChanged = false;
-  BOOST_FOREACH(SymbolRule* r, _rules) {
-      enableChanged |= r->checkEnabled();
-  }
-  
-  if (enableChanged) {
-    SG_LOG(SG_INSTR, SG_INFO, "NS rule enables changed, rebuilding cache");
-    _cachedItemsValid = false;
-  }
-  
-  if (_testModeNode->getBoolValue()) {
-    addTestSymbols();
-  } else {
-    processRoute();
-    processNavRadios();
-    processAI();
-    findItems();
-    limitDisplayedSymbols();
-  }
-
-  addSymbolsToScene();
-  
-  _symbolPrimSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size());
-  _symbolPrimSet->dirty();
-  _linePrimSet->set(osg::PrimitiveSet::LINES, 0, _lineVertices->size());
-  _linePrimSet->dirty();
-}
-
-
-void
-NavDisplay::updateFont()
-{
-    float red = _font_node->getFloatValue("color/red");
-    float green = _font_node->getFloatValue("color/green");
-    float blue = _font_node->getFloatValue("color/blue");
-    float alpha = _font_node->getFloatValue("color/alpha");
-    _font_color.set(red, green, blue, alpha);
-
-    _font_size = _font_node->getFloatValue("size");
-    _font_spacing = _font_size * _font_node->getFloatValue("line-spacing");
-    string path = _font_node->getStringValue("name", DEFAULT_FONT);
-
-    SGPath tpath;
-    if (path[0] != '/') {
-        tpath = globals->get_fg_root();
-        tpath.append("Fonts");
-        tpath.append(path);
-    } else {
-        tpath = path;
-    }
-
-    osg::ref_ptr<osgDB::ReaderWriter::Options> fontOptions = new osgDB::ReaderWriter::Options("monochrome");
-    osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str(), fontOptions.get());
-
-    if (font != 0) {
-        _font = font;
-        _font->setMinFilterHint(osg::Texture::NEAREST);
-        _font->setMagFilterHint(osg::Texture::NEAREST);
-        _font->setGlyphImageMargin(0);
-        _font->setGlyphImageMarginRatio(0);
-    }
-}
-
-void NavDisplay::addSymbolToScene(SymbolInstance* sym)
-{
-    SymbolDef* def = sym->definition;
-    
-    osg::Vec2 verts[4];
-    verts[0] = def->xy0;
-    verts[1] = osg::Vec2(def->xy1.x(), def->xy0.y());
-    verts[2] = def->xy1;
-    verts[3] = osg::Vec2(def->xy0.x(), def->xy1.y());
-    
-    if (def->rotateToHeading) {
-        osg::Matrixf m(degRotation(sym->headingDeg - _view_heading));
-        for (int i=0; i<4; ++i) {
-            verts[i] = mult(verts[i], m);
-        }
-    }
-    
-    osg::Vec2 pos = sym->pos;
-    if (def->roundPos) {
-        pos = osg::Vec2((int) pos.x(), (int) pos.y());
-    }
-    
-    _texCoords->push_back(def->uv0);
-    _texCoords->push_back(osg::Vec2(def->uv1.x(), def->uv0.y()));
-    _texCoords->push_back(def->uv1);
-    _texCoords->push_back(osg::Vec2(def->uv0.x(), def->uv1.y()));
-
-    for (int i=0; i<4; ++i) {
-        _vertices->push_back(verts[i] + pos);
-        _quadColors->push_back(def->color);
-    }
-    
-    if (def->stretchSymbol) {
-        osg::Vec2 stretchVerts[4];
-        stretchVerts[0] = osg::Vec2(def->xy0.x(), def->stretchY2);
-        stretchVerts[1] = osg::Vec2(def->xy1.x(), def->stretchY2);
-        stretchVerts[2] = osg::Vec2(def->xy1.x(), def->stretchY3);
-        stretchVerts[3] = osg::Vec2(def->xy0.x(), def->stretchY3);
-        
-        osg::Matrixf m(degRotation(sym->headingDeg - _view_heading));
-        for (int i=0; i<4; ++i) {
-            stretchVerts[i] = mult(stretchVerts[i], m);
-        }
-        
-    // stretched quad
-        _vertices->push_back(verts[2] + pos);
-        _vertices->push_back(stretchVerts[1] + sym->endPos);
-        _vertices->push_back(stretchVerts[0] + sym->endPos);
-        _vertices->push_back(verts[3] + pos);
-        
-        _texCoords->push_back(def->uv1);
-        _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV2));
-        _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV2));
-        _texCoords->push_back(osg::Vec2(def->uv0.x(), def->uv1.y()));
-        
-        for (int i=0; i<4; ++i) {
-            _quadColors->push_back(def->color);
-        }
-        
-    // quad three, for the end portion
-        for (int i=0; i<4; ++i) {
-            _vertices->push_back(stretchVerts[i] + sym->endPos);
-            _quadColors->push_back(def->color);
-        }
-        
-        _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV2));
-        _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV2));
-        _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV3));
-        _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV3));
-    }
-    
-    if (def->drawLine) {
-        addLine(sym->pos, sym->endPos, def->lineColor);
-    }
-    
-    if (!def->hasText || !def->textEnabled) {
-        return;
-    }
-    
-    osgText::Text* t = new osgText::Text;
-    t->setFont(_font.get());
-    t->setFontResolution(12, 12);
-    t->setCharacterSize(_font_size);
-    t->setLineSpacing(_font_spacing);
-    t->setColor(def->textColor);
-    t->setAlignment(def->alignment);
-    t->setText(sym->text());
-
-
-    osg::Vec2 textPos = def->textOffset + pos;
-// ensure we use ints here, or text visual quality goes bad
-    t->setPosition(osg::Vec3((int)textPos.x(), (int)textPos.y(), 0));
-    _textGeode->addDrawable(t);
-}
-
-class OrderByPriority
-{
-public:
-    bool operator()(SymbolInstance* a, SymbolInstance* b)
-    {
-        return a->definition->priority > b->definition->priority;
-    }    
-};
-
-void NavDisplay::limitDisplayedSymbols()
-{
-// gloabl symbol limit
-    _maxSymbols= _Instrument->getIntValue("max-symbols", _maxSymbols);
-    if ((int) _symbols.size() <= _maxSymbols) {
-        _excessDataNode->setBoolValue(false);
-        return;
-    }
-    
-    std::sort(_symbols.begin(), _symbols.end(), OrderByPriority());
-    _symbols.resize(_maxSymbols);
-    _excessDataNode->setBoolValue(true);
-}
-
-class OrderByZ
-{
-public:
-    bool operator()(SymbolInstance* a, SymbolInstance* b)
-    {
-        return a->definition->zOrder > b->definition->zOrder;
-    }
-};
-
-void NavDisplay::addSymbolsToScene()
-{
-    std::sort(_symbols.begin(), _symbols.end(), OrderByZ());
-    BOOST_FOREACH(SymbolInstance* sym, _symbols) {
-        addSymbolToScene(sym);
-    }
-}
-
-void NavDisplay::addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec4& color)
-{    
-    _lineVertices->push_back(a);
-    _lineVertices->push_back(b);
-    _lineColors->push_back(color);
-    _lineColors->push_back(color);
-}
-
-osg::Vec2 NavDisplay::projectBearingRange(double bearingDeg, double rangeNm) const
-{
-    osg::Vec3 p(0, rangeNm, 0.0);
-    p = degRotation(bearingDeg).preMult(p);
-    p = _projectMat.preMult(p);
-    return osg::Vec2(p.x(), p.y());
-}
-
-osg::Vec2 NavDisplay::projectGeod(const SGGeod& geod) const
-{
-    double rangeM, bearing, az2;
-    SGGeodesy::inverse(_pos, geod, bearing, az2, rangeM);
-    return projectBearingRange(bearing, rangeM * SG_METER_TO_NM);
-}
-
-class Filter : public FGPositioned::Filter
-{
-public:
-    Filter(NavDisplay* nd) : _owner(nd) { }
-  
-    double minRunwayLengthFt;
-  
-    virtual bool pass(FGPositioned* aPos) const
-    {
-        if (aPos->type() == FGPositioned::FIX) {
-            string ident(aPos->ident());
-            // ignore fixes which end in digits
-            if ((ident.size() > 4) && isdigit(ident[3]) && isdigit(ident[4])) {
-                return false;
-            }
-        }
-
-        if (aPos->type() == FGPositioned::AIRPORT) {
-          FGAirport* apt = (FGAirport*) aPos;
-          if (!apt->hasHardRunwayOfLengthFt(minRunwayLengthFt)) {
-            return false;
-          }
-        }
-      
-      // check against current rule states
-        return _owner->isPositionedShown(aPos);
-    }
-
-    virtual FGPositioned::Type minType() const {
-        return FGPositioned::AIRPORT;
-    }
-
-    virtual FGPositioned::Type maxType() const {
-        return FGPositioned::OBSTACLE;
-    }
-  
-private:
-    NavDisplay* _owner;
-};
-
-void NavDisplay::findItems()
-{
-    if (!_cachedItemsValid) {
-        Filter filt(this);
-        filt.minRunwayLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 2000);
-        _itemsInRange = FGPositioned::findClosestN(_pos, _maxSymbols, _rangeNm, &filt);
-        _cachedItemsValid = true;
-        _cachedPos = SGVec3d::fromGeod(_pos);
-    }
-    
-  // sort by distance from pos, so symbol limits are accurate
-    FGPositioned::sortByRange(_itemsInRange, _pos);
-  
-    BOOST_FOREACH(FGPositioned* pos, _itemsInRange) {
-        foundPositionedItem(pos);
-    }
-}
-
-void NavDisplay::processRoute()
-{
-    _routeSources.clear();
-    flightgear::FlightPlan* fp = _route->flightPlan();
-    RoutePath path(fp);
-    int current = _route->currentIndex();
-    
-    for (int l=0; l<fp->numLegs(); ++l) {
-        flightgear::FlightPlan::Leg* leg = fp->legAtIndex(l);
-        flightgear::WayptRef wpt(leg->waypoint());
-        _routeSources.insert(wpt->source());
-        
-        string_set state;
-        state.insert("on-active-route");
-        
-        if (l < current) {
-            state.insert("passed");
-        }
-        
-        if (l == current) {
-            state.insert("current-wp");
-        }
-        
-        if (l > current) {
-            state.insert("future");
-        }
-        
-        if (l == (current + 1)) {
-            state.insert("next-wp");
-        }
-        
-        SymbolRuleVector rules;
-        findRules("waypoint" , state, rules);
-        if (rules.empty()) {
-            return; // no rules matched, we can skip this item
-        }
-
-        SGGeod g = path.positionForIndex(l);
-        SGPropertyNode* vars = _route->wayptNodeAtIndex(l);
-        if (!vars) {
-          continue; // shouldn't happen, but let's guard against it
-        }
-      
-        double heading;
-        computeWayptPropsAndHeading(wpt, g, vars, heading);
-
-        osg::Vec2 projected = projectGeod(g);
-        BOOST_FOREACH(SymbolRule* r, rules) {
-            addSymbolInstance(projected, heading, r->getDefinition(), vars);
-            
-            if (r->getDefinition()->drawRouteLeg) {
-                SGGeodVec gv(path.pathForIndex(l));
-                if (!gv.empty()) {
-                    osg::Vec2 pr = projectGeod(gv[0]);
-                    for (unsigned int i=1; i<gv.size(); ++i) {
-                        osg::Vec2 p = projectGeod(gv[i]);
-                        addLine(pr, p, r->getDefinition()->lineColor);
-                        pr = p;
-                    }
-                }
-            } // of leg drawing enabled
-        } // of matching rules iteration
-    } // of waypoints iteration
-}
-
-void NavDisplay::computeWayptPropsAndHeading(flightgear::Waypt* wpt, const SGGeod& pos, SGPropertyNode* nd, double& heading)
-{
-    double rangeM, az2;
-    SGGeodesy::inverse(_pos, pos, heading, az2, rangeM);
-    nd->setIntValue("radial", heading);
-    nd->setDoubleValue("distance-nm", rangeM * SG_METER_TO_NM);
-    
-    heading = nd->getDoubleValue("leg-bearing-true-deg");
-}
-
-void NavDisplay::processNavRadios()
-{
-    _nav1Station = processNavRadio(_navRadio1Node);
-    _nav2Station = processNavRadio(_navRadio2Node);
-    
-    foundPositionedItem(_nav1Station);
-    foundPositionedItem(_nav2Station);
-}
-
-FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio)
-{
-  double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0);
-  FGNavRecord* nav = FGNavList::findByFreq(mhz, _pos, FGNavList::navFilter());
-    if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) {
-        // station was not found
-        return NULL;
-    }
-    
-    
-    return nav;
-}
-
-bool NavDisplay::anyRuleForType(const string& type) const
-{
-    BOOST_FOREACH(SymbolRule* r, _rules) {
-        if (!r->enabled) {
-            continue;
-        }
-    
-        if (r->type == type) {
-            return true;
-        }
-    }
-    
-    return false;
-}
-
-void NavDisplay::findRules(const string& type, const string_set& states, SymbolRuleVector& rules)
-{
-    BOOST_FOREACH(SymbolRule* candidate, _rules) {
-        if (!candidate->enabled || (candidate->type != type)) {
-            continue;
-        }
-        
-        if (candidate->matches(states)) {
-            rules.push_back(candidate);
-        }
-    }
-}
-
-bool NavDisplay::isPositionedShown(FGPositioned* pos)
-{
-  SymbolRuleVector rules;
-  isPositionedShownInner(pos, rules);
-  return !rules.empty();
-}
-
-void NavDisplay::isPositionedShownInner(FGPositioned* pos, SymbolRuleVector& rules)
-{
-  string type = FGPositioned::nameForType(pos->type());
-  boost::to_lower(type);
-  if (!anyRuleForType(type)) {
-    return; // not diplayed at all, we're done
-  }
-  
-  string_set states;
-  computePositionedState(pos, states);
-  
-  findRules(type, states, rules);
-}
-
-void NavDisplay::foundPositionedItem(FGPositioned* pos)
-{
-    if (!pos) {
-        return;
-    }
-    
-    SymbolRuleVector rules;
-    isPositionedShownInner(pos, rules);
-    if (rules.empty()) {
-      return;
-    }
-  
-    SGPropertyNode_ptr vars(new SGPropertyNode);
-    double heading;
-    computePositionedPropsAndHeading(pos, vars, heading);
-    
-    osg::Vec2 projected = projectGeod(pos->geod());
-    if (pos->type() == FGPositioned::RUNWAY) {
-        FGRunway* rwy = (FGRunway*) pos;
-        projected = projectGeod(rwy->threshold());
-    }
-    
-    BOOST_FOREACH(SymbolRule* r, rules) {
-        SymbolInstance* ins = addSymbolInstance(projected, heading, r->getDefinition(), vars);
-        if ((ins)&&(pos->type() == FGPositioned::RUNWAY)) {
-            FGRunway* rwy = (FGRunway*) pos;
-            ins->endPos = projectGeod(rwy->end());
-        }
-    }
-}
-
-void NavDisplay::computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyNode* nd, double& heading)
-{
-    nd->setStringValue("id", pos->ident());
-    nd->setStringValue("name", pos->name());
-    nd->setDoubleValue("elevation-ft", pos->elevation());
-    nd->setIntValue("heading-deg", 0);
-    heading = 0.0;
-    
-    switch (pos->type()) {
-    case FGPositioned::VOR:
-    case FGPositioned::LOC: 
-    case FGPositioned::TACAN: {
-        FGNavRecord* nav = static_cast<FGNavRecord*>(pos);
-        nd->setDoubleValue("frequency-mhz", nav->get_freq());
-        
-        if (pos == _nav1Station) {
-            heading = _navRadio1Node->getDoubleValue("radials/target-radial-deg");
-        } else if (pos == _nav2Station) {
-            heading = _navRadio2Node->getDoubleValue("radials/target-radial-deg");
-        }
-        
-        nd->setIntValue("heading-deg", heading);
-        break;
-    }
-
-    case FGPositioned::AIRPORT:
-    case FGPositioned::SEAPORT:
-    case FGPositioned::HELIPORT:
-        
-        break;
-        
-    case FGPositioned::RUNWAY: {
-        FGRunway* rwy = static_cast<FGRunway*>(pos);
-        heading = rwy->headingDeg();
-        nd->setDoubleValue("heading-deg", heading);
-        nd->setIntValue("length-ft", rwy->lengthFt());
-        nd->setStringValue("airport", rwy->airport()->ident());
-        break;
-    }
-
-    default:
-        break; 
-    }
-}
-
-void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states)
-{
-    if (_routeSources.count(pos) != 0) {
-        states.insert("on-active-route");
-    }
-    
-    flightgear::FlightPlan* fp = _route->flightPlan();
-    switch (pos->type()) {
-    case FGPositioned::VOR:
-    case FGPositioned::LOC:
-        if (pos == _nav1Station) {
-            states.insert("tuned");
-            states.insert("nav1");
-        }
-        
-        if (pos == _nav2Station) {
-            states.insert("tuned");
-            states.insert("nav2");
-        }
-        break;
-    
-    case FGPositioned::AIRPORT:
-    case FGPositioned::SEAPORT:
-    case FGPositioned::HELIPORT:
-        // mark alternates!
-        // once the FMS system has some way to tell us about them, of course
-        
-        if (pos == fp->departureAirport()) {
-            states.insert("departure");
-        }
-        
-        if (pos == fp->destinationAirport()) {
-            states.insert("destination");
-        }
-        break;
-    
-    case FGPositioned::RUNWAY:
-        if (pos == fp->departureRunway()) {
-            states.insert("departure");
-        }
-        
-        if (pos == fp->destinationRunway()) {
-            states.insert("destination");
-        }
-        break;
-    
-    case FGPositioned::OBSTACLE:
-    #if 0    
-        FGObstacle* obs = (FGObstacle*) pos;
-        if (obj->isLit()) {
-            states.insert("lit");
-        }
-        
-        if (obj->getHeightAGLFt() >= 1000) {
-            states.insert("greater-1000-ft");
-        }
-    #endif
-        break;
-    
-    default:
-        break;
-    } // FGPositioned::Type switch
-}
-
-static string mapAINodeToType(SGPropertyNode* model)
-{
-  // assume all multiplayer items are aircraft for the moment. Not ideal.
-  if (!strcmp(model->getName(), "multiplayer")) {
-    return "ai-aircraft";
-  }
-  
-  return string("ai-") + model->getName();
-}
-
-void NavDisplay::processAI()
-{
-    SGPropertyNode *ai = fgGetNode("/ai/models", true);
-    for (int i = ai->nChildren() - 1; i >= 0; i--) {
-        SGPropertyNode *model = ai->getChild(i);
-        if (!model->nChildren()) {
-            continue;
-        }
-        
-    // prefix types with 'ai-', to avoid any chance of namespace collisions
-    // with fg-positioned.
-        string_set ss;
-        computeAIStates(model, ss);        
-        SymbolRuleVector rules;
-        findRules(mapAINodeToType(model), ss, rules);
-        if (rules.empty()) {
-            return; // no rules matched, we can skip this item
-        }
-
-        double heading = model->getDoubleValue("orientation/true-heading-deg");
-        SGGeod aiModelPos = SGGeod::fromDegFt(model->getDoubleValue("position/longitude-deg"), 
-                                            model->getDoubleValue("position/latitude-deg"), 
-                                            model->getDoubleValue("position/altitude-ft"));
-    // compute some additional props
-        int fl = (aiModelPos.getElevationFt() / 1000);
-        model->setIntValue("flight-level", fl * 10);
-                                            
-        osg::Vec2 projected = projectGeod(aiModelPos);
-        BOOST_FOREACH(SymbolRule* r, rules) {
-            addSymbolInstance(projected, heading, r->getDefinition(), (SGPropertyNode*) model);
-        }
-    } // of ai models iteration
-}
-
-void NavDisplay::computeAIStates(const SGPropertyNode* ai, string_set& states)
-{
-    int threatLevel = ai->getIntValue("tcas/threat-level",-1);
-    if (threatLevel < 1)
-      threatLevel = 0;
-  
-    states.insert("tcas");
-  
-    std::ostringstream os;
-    os << "tcas-threat-level-" << threatLevel;
-    states.insert(os.str());
-
-    double vspeed = ai->getDoubleValue("velocities/vertical-speed-fps");
-    if (vspeed < -3.0) {
-        states.insert("descending");
-    } else if (vspeed > 3.0) {
-        states.insert("climbing");
-    }
-}
-
-SymbolInstance* NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars)
-{
-    if (isProjectedClipped(proj)) {
-        return NULL;
-    }
-    
-    if ((def->limitCount > 0) && (def->instanceCount >= def->limitCount)) {
-      return NULL;
-    }
-  
-    ++def->instanceCount;
-    SymbolInstance* sym = new SymbolInstance(proj, heading, def, vars);
-    _symbols.push_back(sym);
-    return sym;
-}
-
-bool NavDisplay::isProjectedClipped(const osg::Vec2& projected) const
-{
-    double size = _odg->size();
-    return (projected.x() < 0.0) ||
-        (projected.y() < 0.0) ||
-        (projected.x() >= size) ||
-            (projected.y() >= size);
-}
-
-void NavDisplay::addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars)
-{
-  string_set stateSet;
-  BOOST_FOREACH(std::string s, simgear::strutils::split(states, ",")) {
-    stateSet.insert(s);
-  }
-  
-  SymbolRuleVector rules;
-  findRules(type, stateSet, rules);
-  if (rules.empty()) {
-    return; // no rules matched, we can skip this item
-  }
-    
-  osg::Vec2 projected = projectGeod(pos);
-  BOOST_FOREACH(SymbolRule* r, rules) {
-    addSymbolInstance(projected, heading, r->getDefinition(), vars);
-  }
-}
-
-void NavDisplay::addTestSymbols()
-{
-  _pos = SGGeod::fromDeg(-122.3748889, 37.6189722); // KSFO
-  
-  SGGeod a1;
-  double dummy;
-  SGGeodesy::direct(_pos, 45.0, 20.0 * SG_NM_TO_METER, a1, dummy);
-  
-  addTestSymbol("airport", "", a1, 0.0, NULL);
-  
-  SGGeodesy::direct(_pos, 95.0, 40.0 * SG_NM_TO_METER, a1, dummy);
-  
-  addTestSymbol("vor", "", a1, 0.0, NULL);
-  
-  SGGeodesy::direct(_pos, 120, 80.0 * SG_NM_TO_METER, a1, dummy);
-  
-  addTestSymbol("airport", "destination", a1, 0.0, NULL);
-  
-  SGGeodesy::direct(_pos, 80.0, 20.0 * SG_NM_TO_METER, a1, dummy);  
-  addTestSymbol("fix", "", a1, 0.0, NULL);
-
-  
-  SGGeodesy::direct(_pos, 140.0, 20.0 * SG_NM_TO_METER, a1, dummy);  
-  addTestSymbol("fix", "", a1, 0.0, NULL);
-  
-  SGGeodesy::direct(_pos, 110.0, 10.0 * SG_NM_TO_METER, a1, dummy);  
-  addTestSymbol("fix", "", a1, 0.0, NULL);
-  
-  SGGeodesy::direct(_pos, 110.0, 5.0 * SG_NM_TO_METER, a1, dummy);  
-  addTestSymbol("fix", "", a1, 0.0, NULL);
-}
-
-void NavDisplay::addRule(SymbolRule* r)
-{
-    _rules.push_back(r);
-}
-
diff --git a/src/Instrumentation/NavDisplay.hxx b/src/Instrumentation/NavDisplay.hxx
deleted file mode 100644 (file)
index 870c3cc..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-// Wx Radar background texture
-//
-// Written by Harald JOHNSEN, started May 2005.
-// With major amendments by Vivian MEAZZA May 2007
-// Ported to OSG by Tim MOORE Jun 2007
-//
-// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-
-#ifndef _INST_ND_HXX
-#define _INST_ND_HXX
-
-#include <osg/ref_ptr>
-#include <osg/Geode>
-#include <osg/Texture2D>
-#include <osgText/Text>
-
-#include <simgear/props/props.hxx>
-#include <simgear/structure/subsystem_mgr.hxx>
-#include <simgear/math/sg_geodesy.hxx>
-
-#include <vector>
-#include <string>
-#include <memory>
-
-#include <Navaids/positioned.hxx>
-
-class FGODGauge;
-class FGRouteMgr;
-class FGNavRecord;
-
-class SymbolInstance;
-class SymbolDef;
-class SymbolRule;
-
-namespace flightgear
-{
-    class Waypt;
-}
-
-typedef std::set<std::string> string_set;
-typedef std::vector<SymbolRule*> SymbolRuleVector;
-typedef std::vector<SymbolDef*> SymbolDefVector;
-
-class NavDisplay : public SGSubsystem
-{
-public:
-
-    NavDisplay(SGPropertyNode *node);
-    virtual ~NavDisplay();
-
-    virtual void init();
-    virtual void update(double dt);
-
-    void invalidatePositionedCache()
-    {
-        _cachedItemsValid = false;
-    }
-    
-    double textureSize() const
-    { return _textureSize; }
-    
-    void forceUpdate()
-    { _forceUpdate = true; }
-    
-    bool anyRuleForType(const std::string& type) const;
-    bool isPositionedShown(FGPositioned* pos);
-protected:
-    std::string _name;
-    int _num;
-    double _time;
-    double _updateInterval;
-    bool _forceUpdate;
-    
-    SGPropertyNode_ptr _serviceable_node;
-    SGPropertyNode_ptr _Instrument;
-    SGPropertyNode_ptr _radar_mode_control_node;
-    SGPropertyNode_ptr _user_heading_node;
-    SGPropertyNode_ptr _testModeNode;
-  
-    FGODGauge *_odg;
-
-    // Convenience function for creating a property node with a
-    // default value
-    template<typename DefaultType>
-    SGPropertyNode *getInstrumentNode(const char *name, DefaultType value);
-
-private:
-    friend class SymbolRule;
-    friend class SymbolDef;
-  
-    void addRule(SymbolRule*);
-  
-    void addSymbolsToScene();
-    void addSymbolToScene(SymbolInstance* sym);
-    void limitDisplayedSymbols();
-    
-    void findItems();
-    void isPositionedShownInner(FGPositioned* pos, SymbolRuleVector& rules);
-    void foundPositionedItem(FGPositioned* pos);
-    void computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyNode* nd, double& heading);
-    void computePositionedState(FGPositioned* pos, string_set& states);
-    void processRoute();
-    void computeWayptPropsAndHeading(flightgear::Waypt* wpt, const SGGeod& pos, SGPropertyNode* nd, double& heading);
-    void processNavRadios();
-    FGNavRecord* processNavRadio(const SGPropertyNode_ptr& radio);
-    void processAI();
-    void computeAIStates(const SGPropertyNode* ai, string_set& states);
-    
-    void findRules(const std::string& type, const string_set& states, SymbolRuleVector& rules);
-    
-    SymbolInstance* addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars);
-    void addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec4& color);
-    osg::Vec2 projectBearingRange(double bearingDeg, double rangeNm) const;
-    osg::Vec2 projectGeod(const SGGeod& geod) const;
-    bool isProjectedClipped(const osg::Vec2& projected) const;
-    void updateFont();
-    
-    void addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars);
-    void addTestSymbols();
-  
-    std::string _texture_path;
-    unsigned int _textureSize;
-
-    float _scale;   // factor to convert nm to display units
-    float _view_heading;
-    
-    SGPropertyNode_ptr _Radar_controls;
-
-
-    
-    SGPropertyNode_ptr _font_node;
-    SGPropertyNode_ptr _ai_enabled_node;
-    SGPropertyNode_ptr _navRadio1Node;
-    SGPropertyNode_ptr _navRadio2Node;
-    SGPropertyNode_ptr _xCenterNode, _yCenterNode;
-    SGPropertyNode_ptr _viewHeadingNode;
-  
-    osg::ref_ptr<osg::Texture2D> _symbolTexture;
-    osg::ref_ptr<osg::Geode> _radarGeode;
-    osg::ref_ptr<osg::Geode> _textGeode;
-  
-    osg::Geometry *_geom;
-  
-    osg::DrawArrays* _symbolPrimSet;
-    osg::Vec2Array *_vertices;
-    osg::Vec2Array *_texCoords;
-    osg::Vec4Array* _quadColors;
-    
-    osg::Geometry* _lineGeometry;
-    osg::DrawArrays* _linePrimSet;
-    osg::Vec2Array* _lineVertices;
-    osg::Vec4Array* _lineColors;
-  
-  
-    osg::Matrixf _centerTrans;
-    osg::Matrixf _projectMat;
-    
-    osg::ref_ptr<osgText::Font> _font;
-    osg::Vec4 _font_color;
-    float _font_size;
-    float _font_spacing;
-
-    FGRouteMgr* _route;
-    SGGeod _pos;
-    double _rangeNm;
-    SGPropertyNode_ptr _rangeNode;
-    
-    SymbolDefVector _definitions;
-    SymbolRuleVector _rules;
-    FGNavRecord* _nav1Station;
-    FGNavRecord* _nav2Station;
-    std::vector<SymbolInstance*> _symbols;
-    std::set<FGPositioned*> _routeSources;
-    
-    bool _cachedItemsValid;
-    SGVec3d _cachedPos;
-    FGPositioned::List _itemsInRange;
-    SGPropertyNode_ptr _excessDataNode;
-    int _maxSymbols;
-  
-    class CacheListener;
-    std::auto_ptr<CacheListener> _cacheListener;
-    
-    class ForceUpdateListener;
-    std::auto_ptr<ForceUpdateListener> _forceUpdateListener;
-};
-
-#endif // _INST_ND_HXX
diff --git a/src/Instrumentation/agradar.cxx b/src/Instrumentation/agradar.cxx
deleted file mode 100644 (file)
index 768fdea..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-// Air Ground Radar
-//
-// Written by Vivian MEAZZA, started Feb 2008.
-//
-//
-// Copyright (C) 2008  Vivian Meazza
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//
-//
-
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#endif
-
-#include <Main/fg_props.hxx>
-#include <Main/globals.hxx>
-#include "agradar.hxx"
-
-
-agRadar::agRadar(SGPropertyNode *node) : wxRadarBg(node) 
-{
-
-    _name = node->getStringValue("name", "air-ground-radar");
-    _num = node->getIntValue("number", 0);
-
-}
-
-agRadar::~agRadar ()
-{
-}
-
-void
-agRadar::init ()
-{
-    _user_hdg_deg_node      = fgGetNode("/orientation/heading-deg", true);
-    _user_pitch_deg_node    = fgGetNode("/orientation/pitch-deg", true);
-    _user_roll_deg_node     = fgGetNode("/orientation/roll-deg", true);
-
-    _terrain_warning_node   = fgGetNode("/sim/alarms/terrain-warning", true);
-    _terrain_warning_node->setBoolValue(false);
-
-    wxRadarBg::init();
-
-    // those properties are used by a radar instrument of a MFD
-    // input switch = OFF | TST | STBY | ON
-    // input mode = WX | WXA | MAP | TW
-    // output status = STBY | TEST | WX | WXA | MAP | blank
-    // input lightning = true | false
-    // input TRK = +/- n degrees
-    // input TILT = +/- n degree
-    // input autotilt = true | false
-    // input range = n nm (20/40/80)
-    // input display-mode = arc | rose | map | plan
-
-    _Instrument->setFloatValue("trk", 0.0);
-    _Instrument->setFloatValue("tilt",-2.5);
-    _Instrument->setStringValue("status","");
-    _Instrument->setIntValue("mode-control", 5); 
-
-    _Instrument->setBoolValue("stabilisation/roll", false);
-    _Instrument->setBoolValue("stabilisation/pitch", false);
-    
-    _xOffsetMNode = getInstrumentNode("antenna/x-offset-m", 0.0);
-    _yOffsetMNode = getInstrumentNode("antenna/y-offset-m", 0.0);
-    _zOffsetMNode = getInstrumentNode("antenna/z-offset-m", 0.0);
-
-    _elevLimitDegNode = getInstrumentNode("terrain-warning/elev-limit-deg", 2.0);
-    _elevStepDegNode = getInstrumentNode("terrain-warning/elev-step-deg", 1.0);
-    _azLimitDegNode = getInstrumentNode("terrain-warning/az-limit-deg", 1.0);
-    _azStepDegNode = getInstrumentNode("terrain-warning/az-step-deg", 1.5);
-    _maxRangeMNode = getInstrumentNode("terrain-warning/max-range-m", 4000.0);
-    _minRangeMNode = getInstrumentNode("terrain-warning/min-range-m", 250.0);
-    _tiltNode = getInstrumentNode("terrain-warning/tilt", -2.0);
-
-    _brgDegNode = getInstrumentNode("terrain-warning/hit/brg-deg", 0.0);
-    _rangeMNode = getInstrumentNode("terrain-warning/hit/range-m", 0.0);
-    _elevationMNode = getInstrumentNode("terrain-warning/hit/elevation-m", 0.0);
-    _materialNode = getInstrumentNode("terrain-warning/hit/material", "");
-    _bumpinessNode = getInstrumentNode("terrain-warning/hit/bumpiness", 0.0);
-
-    _rollStabNode = getInstrumentNode("terrain-warning/stabilisation/roll",
-                                      true);
-    _pitchStabNode = getInstrumentNode("terrain-warning/stabilisation/pitch",
-                                       false);
-//    cout << "init done" << endl;
-
-}
-
-void
-agRadar::update (double delta_time_sec)
-{
-    if (!_sceneryLoaded->getBoolValue())
-        return;
-
-    if ( !_odg || ! _serviceable_node->getBoolValue() ) {
-        _Instrument->setStringValue("status","");
-        return;
-    }
-
-    _time += delta_time_sec;
-
-    if (_time < _interval)
-        return;
-
-    _time = 0.0;
-
-    update_terrain();
-//    wxRadarBg::update(delta_time_sec);
-}
-
-void
-agRadar::setUserPos()
-{
-    userpos.setLatitudeDeg(_user_lat_node->getDoubleValue());
-    userpos.setLongitudeDeg(_user_lon_node->getDoubleValue());
-    userpos.setElevationM(_user_alt_node->getDoubleValue() * SG_FEET_TO_METER);
-}
-
-SGVec3d
-agRadar::getCartUserPos() const {
-    SGVec3d cartUserPos = SGVec3d::fromGeod(userpos);
-    return cartUserPos;
-}
-
-SGVec3d
-agRadar::getCartAntennaPos() const {
-
-    float yaw   = _user_hdg_deg_node->getDoubleValue();
-    float pitch = _user_pitch_deg_node->getDoubleValue();
-    float roll  = _user_roll_deg_node->getDoubleValue();
-
-    double x_offset_m =_xOffsetMNode->getDoubleValue();
-    double y_offset_m =_yOffsetMNode->getDoubleValue();
-    double z_offset_m =_zOffsetMNode->getDoubleValue();
-
-    // convert geodetic positions to geocentered
-    SGVec3d cartuserPos = getCartUserPos();
-
-    // Transform to the right coordinate frame, configuration is done in
-    // the x-forward, y-right, z-up coordinates (feet), computation
-    // in the simulation usual body x-forward, y-right, z-down coordinates
-    // (meters) )
-    SGVec3d _off(x_offset_m, y_offset_m, -z_offset_m);
-
-    // Transform the user position to the horizontal local coordinate system.
-    SGQuatd hlTrans = SGQuatd::fromLonLat(userpos);
-
-    // and postrotate the orientation of the user model wrt the horizontal
-    // local frame
-    hlTrans *= SGQuatd::fromYawPitchRollDeg(yaw,pitch,roll);
-
-    // The offset converted to the usual body fixed coordinate system
-    // rotated to the earth-fixed coordinates axis
-    SGVec3d off = hlTrans.backTransform(_off);
-
-    // Add the position offset of the user model to get the geocentered position
-    SGVec3d offsetPos = cartuserPos + off;
-
-    return offsetPos;
-}
-
-void
-agRadar::setAntennaPos() {
-    SGGeodesy::SGCartToGeod(getCartAntennaPos(), antennapos);
-}
-
-void
-agRadar::setUserVec(double az, double el)
-{
-    float yaw   = _user_hdg_deg_node->getDoubleValue();
-    float pitch = _user_pitch_deg_node->getDoubleValue();
-    float roll  = _user_roll_deg_node->getDoubleValue();
-    double tilt = _Instrument->getDoubleValue("tilt");
-    double trk  = _Instrument->getDoubleValue("trk");
-    bool roll_stab   = _Instrument->getBoolValue("stabilisation/roll");
-    bool pitch_stab  = _Instrument->getBoolValue("stabilisation/pitch");
-
-    SGQuatd offset = SGQuatd::fromYawPitchRollDeg(az + trk, el + tilt, 0);
-
-    // Transform the antenna position to the horizontal local coordinate system.
-    SGQuatd hlTrans = SGQuatd::fromLonLat(antennapos);
-
-    // and postrotate the orientation of the radar wrt the horizontal
-    // local frame
-    hlTrans *= SGQuatd::fromYawPitchRollDeg(yaw,
-                                            pitch_stab ? 0 :pitch,
-                                            roll_stab ? 0 : roll);
-    hlTrans *= offset;
-
-    // now rotate the rotation vector back into the
-    // earth centered frames coordinates
-    SGVec3d angleaxis(1,0,0);
-    uservec = hlTrans.backTransform(angleaxis);
-
-}
-
-bool
-agRadar::getMaterial(){
-
-    const simgear::BVHMaterial* mat = 0;
-    if (globals->get_scenery()->get_elevation_m(hitpos, _elevation_m, &mat)){
-        //_ht_agl_ft = pos.getElevationFt() - _elevation_m * SG_METER_TO_FEET;
-        const SGMaterial* material = dynamic_cast<const SGMaterial*>(mat);
-        if (material) {
-            const std::vector<std::string>& names = material->get_names();
-
-            _solid = material->get_solid();
-            _load_resistance = material->get_load_resistance();
-            _frictionFactor = material->get_friction_factor();
-            _bumpinessFactor = material->get_bumpiness();
-
-            if (!names.empty()) 
-                _mat_name = names[0];
-            else
-                _mat_name = "";
-
-        }
-        /*cout << "material " << mat_name 
-        << " solid " << _solid 
-        << " load " << _load_resistance
-        << " frictionFactor " << frictionFactor 
-        << " _bumpinessFactor " << _bumpinessFactor
-        << endl;*/
-        return true;
-    } else {
-        return false;
-    }
-
-}
-
-void
-agRadar::update_terrain()
-{
-    int mode = _radar_mode_control_node->getIntValue();
-
-    double el_limit = 1;
-    double el_step = 1;
-    double az_limit = 50;
-    double az_step = 10;
-    double max_range = 40000;
-    double min_range = 250;
-    double tilt = -2.5;
-    bool roll_stab   = _rollStabNode->getBoolValue();
-    bool pitch_stab  = _pitchStabNode->getBoolValue();
-    const char* status = "";
-    bool hdg_mkr = true;
-
-    if (mode == 5){
-        status = "TW";
-        hdg_mkr = false;
-        tilt        = _tiltNode->getDoubleValue();
-        el_limit    = _elevLimitDegNode->getDoubleValue();
-        el_step     = _elevStepDegNode->getDoubleValue();
-        az_limit    = _azLimitDegNode->getDoubleValue();
-        az_step     = _azStepDegNode->getDoubleValue();
-        max_range   = _maxRangeMNode->getDoubleValue();
-        min_range   = _minRangeMNode->getDoubleValue();
-    }
-
-    _Instrument->setDoubleValue("tilt", tilt);
-    _Instrument->setBoolValue("stabilisation/roll", roll_stab);
-    _Instrument->setBoolValue("stabilisation/pitch", pitch_stab);
-    _Instrument->setStringValue("status", status);
-    _Instrument->setDoubleValue("limit-deg", az_limit);
-    _Instrument->setBoolValue("heading-marker", hdg_mkr);
-    setUserPos();
-    setAntennaPos();
-    SGVec3d cartantennapos = getCartAntennaPos();
-    
-    for(double brg = -az_limit; brg <= az_limit; brg += az_step){
-        for(double elev = el_limit; elev >= - el_limit; elev -= el_step){
-            setUserVec(brg, elev);
-            SGVec3d nearestHit;
-            globals->get_scenery()->get_cart_ground_intersection(cartantennapos, uservec, nearestHit);
-            SGGeodesy::SGCartToGeod(nearestHit, hitpos);
-
-            double course1, course2, distance;
-
-            SGGeodesy::inverse(hitpos, antennapos, course1, course2, distance);
-
-            if (distance >= min_range && distance <= max_range) {
-                _terrain_warning_node->setBoolValue(true);
-                getMaterial();
-                _brgDegNode->setDoubleValue(course2);
-                _rangeMNode->setDoubleValue(distance);
-                _materialNode->setStringValue(_mat_name.c_str());
-                _bumpinessNode->setDoubleValue(_bumpinessFactor);
-                _elevationMNode->setDoubleValue(_elevation_m);
-            } else {
-                _terrain_warning_node->setBoolValue(false);
-                _brgDegNode->setDoubleValue(0);
-                _rangeMNode->setDoubleValue(0);
-                _materialNode->setStringValue("");
-                _bumpinessNode->setDoubleValue(0);
-                _elevationMNode->setDoubleValue(0);
-            }
-
-            //cout  << "usr hdg " << _user_hdg_deg_node->getDoubleValue()
-            //    << " ant brg " << course2 
-            //    << " elev " << _Instrument->getDoubleValue("tilt")
-            //    << " gnd rng nm " << distance * SG_METER_TO_NM
-            //    << " ht " << hitpos.getElevationFt()
-            //    << " mat " << _mat_name
-            //    << " solid " << _solid
-            //    << " bumpiness " << _bumpinessFactor
-            //    << endl;
-
-        }
-    }
-}
-
-
diff --git a/src/Instrumentation/agradar.hxx b/src/Instrumentation/agradar.hxx
deleted file mode 100644 (file)
index dda152e..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-// Air Ground Radar
-//
-// Written by Vivian MEAZZA, started Feb 2008.
-// 
-//
-// Copyright (C) 2008  Vivain MEAZZA - vivian.meazza@lineone.net
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//
-//
-
-#ifndef _INST_AGRADAR_HXX
-#define _INST_AGRADAR_HXX
-
-#include <simgear/structure/subsystem_mgr.hxx>
-#include <Scenery/scenery.hxx>
-#include <simgear/scene/material/mat.hxx>
-
-#include "wxradar.hxx"
-
-class agRadar : public wxRadarBg{
-public:
-
-    agRadar ( SGPropertyNode *node );
-    agRadar ();
-    virtual ~agRadar ();
-
-    virtual void init ();
-    virtual void update (double dt);
-
-    void setUserPos();
-    void setUserVec(double az, double el);
-    void update_terrain();
-    void setAntennaPos();
-
-    bool getMaterial();
-
-    double _load_resistance;    // ground load resistanc N/m^2
-    double _frictionFactor;     // dimensionless modifier for Coefficient of Friction
-    double _bumpinessFactor;    // dimensionless modifier for Bumpiness
-    double _elevation_m;        // ground elevation in meters
-    bool   _solid;              // if true ground is solid for FDMs
-
-    std::string _mat_name; // ground material
-
-    SGVec3d getCartUserPos() const;
-    SGVec3d getCartAntennaPos()const;
-
-    SGVec3d uservec;
-
-    SGPropertyNode_ptr _user_hdg_deg_node;
-    SGPropertyNode_ptr _user_roll_deg_node;
-    SGPropertyNode_ptr _user_pitch_deg_node;
-    SGPropertyNode_ptr _terrain_warning_node;
-
-    SGPropertyNode_ptr _xOffsetMNode;
-    SGPropertyNode_ptr _yOffsetMNode;
-    SGPropertyNode_ptr _zOffsetMNode;
-
-    SGPropertyNode_ptr _elevLimitDegNode;
-    SGPropertyNode_ptr _elevStepDegNode;
-    SGPropertyNode_ptr _azLimitDegNode;
-    SGPropertyNode_ptr _azStepDegNode;
-    SGPropertyNode_ptr _maxRangeMNode;
-    SGPropertyNode_ptr _minRangeMNode;
-    SGPropertyNode_ptr _tiltNode;
-
-    SGPropertyNode_ptr _brgDegNode;
-    SGPropertyNode_ptr _rangeMNode;
-    SGPropertyNode_ptr _elevationMNode;
-    SGPropertyNode_ptr _materialNode;
-    SGPropertyNode_ptr _bumpinessNode;
-
-    SGPropertyNode_ptr _rollStabNode;
-    SGPropertyNode_ptr _pitchStabNode;
-
-    SGGeod userpos;
-    SGGeod hitpos;
-    SGGeod antennapos;
-};
-
-#endif // _INST_AGRADAR_HXX
index 1eeadfd6760107e2ef63983ed5a2aea0faa1f695..405c5a70b28dbb760973ed0496fe5a19c3b3e183 100644 (file)
@@ -26,7 +26,8 @@
 #ifndef _DCLGPS_HXX
 #define _DCLGPS_HXX
 
-#include "render_area_2d.hxx"
+#include <Cockpit/render_area_2d.hxx>
+
 #include <string>
 #include <list>
 #include <vector>
diff --git a/src/Instrumentation/groundradar.cxx b/src/Instrumentation/groundradar.cxx
deleted file mode 100644 (file)
index b7d2834..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-//  groundradar.cxx - Background layer for the ATC radar.
-//
-//  Copyright (C) 2007 Csaba Halasz.
-//
-//  This program is free software; you can redistribute it and/or
-//  modify it under the terms of the GNU General Public License as
-//  published by the Free Software Foundation; either version 2 of the
-//  License, or (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful, but
-//  WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-//  General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program; if not, write to the Free Software
-//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#endif
-
-#include <cassert>
-
-#include <osg/Node>
-#include <osg/Geode>
-#include <osg/Geometry>
-#include <osg/Camera>
-#include <osg/Texture2D>
-#include <osgViewer/Viewer>
-
-#include <osgText/Text>
-#include <osgDB/Registry>
-#include <osgDB/ReaderWriter>
-#include <osgUtil/Tessellator>
-
-#include <Main/fg_props.hxx>
-#include <Main/globals.hxx>
-#include <Viewer/renderer.hxx>
-#include <Cockpit/panel.hxx>
-#include <Airports/simple.hxx>
-#include <Airports/runways.hxx>
-#include <Airports/pavement.hxx>
-#include <simgear/math/sg_geodesy.hxx>
-#include <simgear/math/beziercurve.hxx>
-
-#include "groundradar.hxx"
-
-static const char* airport_source_node_name = "airport-id-source";
-static const char* default_airport_node_name = "/sim/airport/closest-airport-id";
-static const char* texture_node_name = "texture-name";
-static const char* default_texture_name = "Aircraft/Instruments/Textures/od_groundradar.rgb";
-static const char* range_source_node_name = "range-source";
-static const char* default_range_node_name = "/instrumentation/radar/range";
-
-struct SingleFrameCallback : public osg::Camera::DrawCallback
-{
-    virtual void operator () (const osg::Camera& camera) const
-    {
-        const_cast<osg::Camera&>(camera).setNodeMask(0);
-    }
-};
-
-GroundRadar::GroundRadar(SGPropertyNode *node)
-{
-    _airport_node = fgGetNode(node->getStringValue(airport_source_node_name, default_airport_node_name), true);
-    _range_node = fgGetNode(node->getStringValue(range_source_node_name, default_range_node_name), true);
-    createTexture(node->getStringValue(texture_node_name, default_texture_name));
-    updateTexture();
-    _airport_node->addChangeListener(this);
-    _range_node->addChangeListener(this);
-}
-
-GroundRadar::~GroundRadar()
-{
-    _airport_node->removeChangeListener(this);
-    _range_node->removeChangeListener(this);
-}
-
-void GroundRadar::update (double /* dt */)
-{
-  
-}
-
-void GroundRadar::valueChanged(SGPropertyNode*)
-{
-    updateTexture();
-}
-
-inline static osg::Vec3 fromPolar(double fi, double r)
-{
-    return osg::Vec3(sin(fi * SGD_DEGREES_TO_RADIANS) * r, cos(fi * SGD_DEGREES_TO_RADIANS) * r, 0);
-}
-
-void GroundRadar::createTexture(const char* texture_name)
-{
-    setSize(TextureHalfSize + TextureHalfSize);
-    allocRT();
-
-    _geode = new osg::Geode();
-    osg::StateSet* stateset = _geode->getOrCreateStateSet();
-    stateset->setMode(GL_BLEND, osg::StateAttribute::OFF);
-
-    osg::Vec4Array* taxi_color = new osg::Vec4Array;
-    taxi_color->push_back(osg::Vec4(0.0f, 0.5f, 0.0f, 1.0f));
-    osg::Vec4Array* rwy_color = new osg::Vec4Array;
-    rwy_color->push_back(osg::Vec4(0.0f, 0.5f, 0.5f, 1.0f));
-
-    osg::Geometry *taxi_geom = new osg::Geometry();
-    taxi_geom->setColorArray(taxi_color);
-    taxi_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
-    taxi_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 0)); // Taxiways
-    _geode->addDrawable(taxi_geom);
-
-    osg::Geometry *pvt_geom = new osg::Geometry();
-    pvt_geom->setColorArray(taxi_color);
-    pvt_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
-    // no primitive set for the moment. It needs tessellation
-    _geode->addDrawable(pvt_geom);
-
-    osg::Geometry *rwy_geom = new osg::Geometry();
-    rwy_geom->setColorArray(rwy_color);
-    rwy_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
-    rwy_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 0)); // Runways
-    _geode->addDrawable(rwy_geom);
-
-    osg::Camera* camera = getCamera();
-    camera->setPostDrawCallback(new SingleFrameCallback());
-    camera->addChild(_geode.get());
-    camera->setNodeMask(0);
-    camera->setProjectionMatrixAsOrtho2D(0, getTexture()->getTextureWidth(), 0, getTexture()->getTextureHeight());
-
-    // Texture in the 2D panel system
-    FGTextureManager::addTexture(texture_name, getTexture());
-}
-
-void GroundRadar::addRunwayVertices(const FGRunwayBase* aRunway, double aTowerLat, double aTowerLon, double aScale, osg::Vec3Array* aVertices)
-{
-  double az1, az2, dist_m;
-  geo_inverse_wgs_84(aTowerLat, aTowerLon, aRunway->latitude(), aRunway->longitude(), &az1, &az2, &dist_m);
-
-  osg::Vec3 center = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
-  osg::Vec3 leftcenter = fromPolar(aRunway->headingDeg(), aRunway->lengthM() * aScale / 2) + center;
-  osg::Vec3 lefttop = fromPolar(aRunway->headingDeg() - 90, aRunway->widthM() * aScale / 2) + leftcenter;
-  osg::Vec3 leftbottom = leftcenter * 2 - lefttop;
-  osg::Vec3 rightbottom = center * 2 - lefttop;
-  osg::Vec3 righttop = center * 2 - leftbottom;
-
-  aVertices->push_back(lefttop);
-  aVertices->push_back(leftbottom);
-  aVertices->push_back(rightbottom);
-  aVertices->push_back(righttop);
-}
-
-osg::Geometry *GroundRadar::addPavementGeometry(const FGPavement* aPavement, double aTowerLat, double aTowerLon, double aScale)
-{
-
-  osg::ref_ptr<osgUtil::Tessellator> tess = new osgUtil::Tessellator;
-  osg::ref_ptr<osg::Geometry> polygon = new osg::Geometry;
-  osg::ref_ptr<osg::Vec3Array> pts = new osg::Vec3Array;
-
-  double az1, az2, dist_m;
-  const FGPavement::NodeList &nodeLst = aPavement->getNodeList();
-  FGPavement::NodeList::const_iterator it = nodeLst.begin(),
-                                        loopBegin = it;
-  while ( it != nodeLst.end() )
-  {
-    bool close = (*it)->mClose;
-    geo_inverse_wgs_84(aTowerLat, aTowerLon, (*it)->mPos.getLatitudeDeg(), (*it)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
-    osg::Vec3 p1 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
-    const FGPavement::BezierNode *bn = dynamic_cast<const FGPavement::BezierNode *>( it->ptr() );
-    if ( bn != 0 )
-    {
-      geo_inverse_wgs_84(aTowerLat, aTowerLon, bn->mControl.getLatitudeDeg(), bn->mControl.getLongitudeDeg(), &az1, &az2, &dist_m);
-      osg::Vec3 p2 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0),
-                p3;
-      ++it;
-      if ( it == nodeLst.end() || close )
-      {
-        geo_inverse_wgs_84(aTowerLat, aTowerLon, (*loopBegin)->mPos.getLatitudeDeg(), (*loopBegin)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
-      }
-      else
-      {
-        geo_inverse_wgs_84(aTowerLat, aTowerLon, (*it)->mPos.getLatitudeDeg(), (*it)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
-      }
-      p3 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
-      simgear::BezierCurve<osg::Vec3> bCurv( p1, p2, p3 );
-      simgear::BezierCurve<osg::Vec3>::PointList &ptList = bCurv.pointList();
-      for ( simgear::BezierCurve<osg::Vec3>::PointList::iterator ii = ptList.begin(); ii != ptList.end(); ++ii )
-      {
-        pts->push_back( *ii );
-      }
-      pts->pop_back(); // Last point belongs to next segment
-    }
-    else
-    {
-      pts->push_back( p1 );
-      ++it;
-    }
-
-    if ( close ) // One loop for the moment
-      break;
-  }
-  geo_inverse_wgs_84(aTowerLat, aTowerLon, (*loopBegin)->mPos.getLatitudeDeg(), (*loopBegin)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
-  osg::Vec3 p1 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
-  pts->push_back( p1 );
-  polygon->setVertexArray( pts.get() );
-
-  polygon->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, 0, pts->size() ) );
-
-  tess->setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
-  tess->setBoundaryOnly( false );
-  tess->setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
-  tess->retessellatePolygons( *polygon );
-  return polygon.release();
-}
-
-void GroundRadar::updateTexture()
-{
-    osg::ref_ptr<osg::Vec3Array> rwy_vertices = new osg::Vec3Array;
-    osg::ref_ptr<osg::Vec3Array> taxi_vertices = new osg::Vec3Array;
-    osg::ref_ptr<osg::Vec3Array> pvt_vertices = new osg::Vec3Array;
-
-    const string airport_name = _airport_node->getStringValue();
-
-    const FGAirport* airport = fgFindAirportID(airport_name);
-    if (airport == 0)
-        return;
-
-    const SGGeod& tower_location = airport->getTowerLocation();
-    const double tower_lat = tower_location.getLatitudeDeg();
-    const double tower_lon = tower_location.getLongitudeDeg();
-    double scale = SG_METER_TO_NM * 200 / _range_node->getDoubleValue();
-  
-    const FGAirport* apt = fgFindAirportID(airport_name);
-    assert(apt);
-
-    for (unsigned int i=0; i<apt->numTaxiways(); ++i)
-    {
-      FGTaxiway* txwy(apt->getTaxiwayByIndex(i));
-      addRunwayVertices(txwy, tower_lat, tower_lon, scale, taxi_vertices.get());
-    }
-    osg::Geometry *taxi_geom = dynamic_cast<osg::Geometry *>(_geode->getDrawable(0));
-    taxi_geom->setVertexArray(taxi_vertices.get());
-    osg::DrawArrays* taxi = dynamic_cast<osg::DrawArrays*>(taxi_geom->getPrimitiveSet(0));
-    taxi->setCount(taxi_vertices->size());
-
-    osg::Geometry *pvt_geom = dynamic_cast<osg::Geometry *>(_geode->getDrawable(1));
-    osg::Geometry::PrimitiveSetList &pvt_prim_list = pvt_geom->getPrimitiveSetList();
-    pvt_prim_list.clear();
-    for (unsigned int i=0; i<apt->numPavements(); ++i)
-    {
-      FGPavement* pvt(apt->getPavementByIndex(i));
-      osg::ref_ptr<osg::Geometry> geom = addPavementGeometry(pvt, tower_lat, tower_lon, scale);
-      osg::Geometry::PrimitiveSetList &prim_list = geom->getPrimitiveSetList();
-      osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array *>(geom->getVertexArray());
-      size_t before = pvt_vertices->size(),
-            count = vertices->size();
-      for (size_t i = 0; i < count; ++i )
-      {
-        pvt_vertices->push_back( (*vertices)[i] );
-      }
-      for (osg::Geometry::PrimitiveSetList::iterator ii = prim_list.begin(); ii != prim_list.end(); ++ii )
-      {
-        osg::DrawArrays *da;
-        osg::DrawElementsUByte *de1;
-        osg::DrawElementsUShort *de2;
-        osg::DrawElementsUInt *de3;
-        if ((da = dynamic_cast<osg::DrawArrays *>(ii->get())) != 0)
-        {
-          osg::DrawArrays *ps = new osg::DrawArrays(*da);
-          ps->setFirst(da->getFirst() + before);
-          pvt_prim_list.push_back(ps);
-        }
-        else if ((de1 = dynamic_cast<osg::DrawElementsUByte *>(ii->get())) != 0)
-        {
-          if (before + count <= 255)
-          {
-            osg::DrawElementsUByte *ps = new osg::DrawElementsUByte(*de1);
-            for (size_t j = 0; j < ps->size(); ++j)
-            {
-              (*ps)[j] += before;
-            }
-            pvt_prim_list.push_back(ps);
-          }
-          else if (before + count <= 65535)
-          {
-            osg::DrawElementsUShort *ps = new osg::DrawElementsUShort(de1->getMode(), de1->begin(), de1->end());
-            for (size_t j = 0; j < ps->size(); ++j)
-            {
-              (*ps)[j] += before;
-            }
-            pvt_prim_list.push_back(ps);
-          }
-          else
-          {
-            osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(de1->getMode(), de1->begin(), de1->end());
-            for (size_t j = 0; j < ps->size(); ++j)
-            {
-              (*ps)[j] += before;
-            }
-            pvt_prim_list.push_back(ps);
-          }
-        }
-        else if ((de2 = dynamic_cast<osg::DrawElementsUShort *>(ii->get())) != 0)
-        {
-          if (before + count <= 65535)
-          {
-            osg::DrawElementsUShort *ps = new osg::DrawElementsUShort(*de2);
-            for (size_t j = 0; j < ps->size(); ++j)
-            {
-              (*ps)[j] += before;
-            }
-            pvt_prim_list.push_back(ps);
-          }
-          else
-          {
-            osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(de2->getMode(), de2->begin(), de2->end());
-            for (size_t j = 0; j < ps->size(); ++j)
-            {
-              (*ps)[j] += before;
-            }
-            pvt_prim_list.push_back(ps);
-          }
-        }
-        else if ((de3 = dynamic_cast<osg::DrawElementsUInt *>(ii->get())) != 0)
-        {
-          osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(*de3);
-          for (size_t j = 0; j < ps->size(); ++j)
-          {
-            (*ps)[j] += before;
-          }
-          pvt_prim_list.push_back(ps);
-        }
-      }
-    }
-    pvt_geom->setVertexArray(pvt_vertices.get());
-
-    for (unsigned int i=0; i<apt->numRunways(); ++i)
-    {
-      FGRunway* runway(apt->getRunwayByIndex(i));
-      if (runway->isReciprocal()) continue;
-      
-      addRunwayVertices(runway, tower_lat, tower_lon, scale, rwy_vertices.get());
-    }
-    osg::Geometry *rwy_geom = dynamic_cast<osg::Geometry *>(_geode->getDrawable(2));
-    rwy_geom->setVertexArray(rwy_vertices.get());
-    osg::DrawArrays* rwy = dynamic_cast<osg::DrawArrays*>(rwy_geom->getPrimitiveSet(0));
-    rwy->setCount(rwy_vertices->size());
-
-    getCamera()->setNodeMask(0xffffffff);
-}
-
-// end of GroundRadar.cxx
diff --git a/src/Instrumentation/groundradar.hxx b/src/Instrumentation/groundradar.hxx
deleted file mode 100644 (file)
index fb603be..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-//  groundradar.hxx - Background layer for the ATC radar.
-//
-//  Copyright (C) 2007 Csaba Halasz.
-//
-//  This program is free software; you can redistribute it and/or
-//  modify it under the terms of the GNU General Public License as
-//  published by the Free Software Foundation; either version 2 of the
-//  License, or (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful, but
-//  WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-//  General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program; if not, write to the Free Software
-//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-
-
-#ifndef __INST_GROUNDRADAR_HXX
-#define __INST_GROUNDRADAR_HXX
-
-#include <osg/ref_ptr>
-#include <osg/Geometry>
-#include <simgear/props/props.hxx>
-#include "od_gauge.hxx"
-#include <simgear/structure/subsystem_mgr.hxx>
-
-// forward decls
-class FGRunwayBase;
-
-////////////////////////////////////////////////////////////////////////
-// Built-in layer for the atc radar.
-////////////////////////////////////////////////////////////////////////
-
-class GroundRadar : public SGSubsystem, public SGPropertyChangeListener, private FGODGauge
-{
-public:
-    static const int TextureHalfSize = 256;
-    GroundRadar(SGPropertyNode* node);
-    virtual ~GroundRadar();
-    void updateTexture();
-    virtual void valueChanged(SGPropertyNode*);
-    virtual void update (double dt);
-protected:
-    void createTexture(const char* texture_name);
-    
-    void addRunwayVertices(const FGRunwayBase* aRunway, double aTowerLat, double aTowerLon, double aScale, osg::Vec3Array* aVertices);
-    osg::Geometry *addPavementGeometry(const FGPavement* aPavement, double aTowerLat, double aTowerLon, double aScale);
-    
-    osg::ref_ptr<osg::Geode> _geode;
-    SGPropertyNode_ptr _airport_node;
-    SGPropertyNode_ptr _range_node;
-};
-
-#endif // __INST_GROUNDRADAR_HXX
index 86b09e13e9351d391fd6c99e705c62e681f96c97..b9e30154dfb15f2a2f3d6437760ffe2a3d675d09 100644 (file)
 #include "turn_indicator.hxx"
 #include "vertical_speed_indicator.hxx"
 #include "inst_vertical_speed_indicator.hxx"
-#include "wxradar.hxx"
 #include "tacan.hxx"
 #include "mk_viii.hxx"
 #include "mrg.hxx"
-#include "groundradar.hxx"
-#include "agradar.hxx"
 #include "rad_alt.hxx"
 #include "tcas.hxx"
-#include "NavDisplay.hxx"
 
 FGInstrumentMgr::FGInstrumentMgr () :
   _explicitGps(false)
@@ -124,7 +120,6 @@ bool FGInstrumentMgr::build (SGPropertyNode* config_props)
         if (index > 0)
             subsystemname << '['<< index << ']';
         string id = subsystemname.str();
-        _instruments.push_back(id);
       
         if ( name == "adf" ) {
             set_subsystem( id, new ADF( node ), 0.15 );
@@ -189,9 +184,6 @@ bool FGInstrumentMgr::build (SGPropertyNode* config_props)
         } else if ( name == "vertical-speed-indicator" ) {
             set_subsystem( id, new VerticalSpeedIndicator( node ) );
 
-        } else if ( name == "radar" ) {
-            set_subsystem( id, new wxRadarBg ( node ) );
-
         } else if ( name == "inst-vertical-speed-indicator" ) {
             set_subsystem( id, new InstVerticalSpeedIndicator( node ) );
 
@@ -204,27 +196,30 @@ bool FGInstrumentMgr::build (SGPropertyNode* config_props)
         } else if ( name == "master-reference-gyro" ) {
             set_subsystem( id, new MasterReferenceGyro( node ) );
 
-        } else if ( name == "groundradar" ) {
-            set_subsystem( id, new GroundRadar( node ) );
-
-        } else if ( name == "air-ground-radar" ) {
-            set_subsystem( id, new agRadar( node ) );
-
+        } else if (( name == "groundradar" ) ||
+                   ( name == "radar" ) ||
+                   ( name == "air-ground-radar" ) ||
+                   ( name == "navigation-display" ))
+        {
+        // these instruments are handled by the CockpitDisplayManager
+        // catch them here so we can still warn about bogus names in
+        // the instruments file
+          continue;
         } else if ( name == "radar-altimeter" ) {
             set_subsystem( id, new radAlt( node ) );
 
         } else if ( name == "tcas" ) {
             set_subsystem( id, new TCAS( node ), 0.2);
-        
-        } else if ( name == "navigation-display" ) {
-            set_subsystem( id, new NavDisplay( node ) );
             
         } else {
             SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: "
                     << name );
             return false;
         }
-    }
+      
+      // only push to our array if we actually built an insturment
+        _instruments.push_back(id);
+    } // of instruments iteration
     return true;
 }
 
diff --git a/src/Instrumentation/od_gauge.cxx b/src/Instrumentation/od_gauge.cxx
deleted file mode 100644 (file)
index c6be269..0000000
+++ /dev/null
@@ -1,479 +0,0 @@
-// Owner Drawn Gauge helper class
-//
-// Written by Harald JOHNSEN, started May 2005.
-//
-// Copyright (C) 2005  Harald JOHNSEN
-//
-// Ported to OSG by Tim Moore - Jun 2007
-//
-// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
-// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
-// the texture in the scene by certain filter criteria
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//
-//
-
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#endif
-
-#include <osg/Texture2D>
-#include <osg/AlphaFunc>
-#include <osg/BlendFunc>
-#include <osg/Camera>
-#include <osg/Geode>
-#include <osg/NodeVisitor>
-#include <osg/Matrix>
-#include <osg/PolygonMode>
-#include <osg/ShadeModel>
-#include <osg/StateSet>
-#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
-
-#include <osgDB/FileNameUtils>
-
-#include <simgear/scene/material/EffectGeode.hxx>
-#include <simgear/scene/util/RenderConstants.hxx>
-
-#include <Main/globals.hxx>
-#include <Viewer/renderer.hxx>
-#include <Scenery/scenery.hxx>
-#include "od_gauge.hxx"
-
-#include <cassert>
-
-//------------------------------------------------------------------------------
-FGODGauge::FGODGauge():
-  _size_x( -1 ),
-  _size_y( -1 ),
-  _view_width( -1 ),
-  _view_height( -1 ),
-  _use_image_coords( false ),
-  _use_stencil( false ),
-  _use_mipmapping( false ),
-  _coverage_samples( 0 ),
-  _color_samples( 0 ),
-  rtAvailable( false )
-{
-}
-
-//------------------------------------------------------------------------------
-FGODGauge::~FGODGauge()
-{
-  if( camera.valid() )
-    globals->get_renderer()->removeCamera(camera.get());
-}
-
-//------------------------------------------------------------------------------
-void FGODGauge::setSize(int size_x, int size_y)
-{
-  _size_x = size_x;
-  _size_y = size_y < 0 ? size_x : size_y;
-
-  if( texture.valid() )
-    texture->setTextureSize(_size_x, _size_x);
-}
-
-//----------------------------------------------------------------------------
-void FGODGauge::setViewSize(int width, int height)
-{
-  _view_width = width;
-  _view_height = height < 0 ? width : height;
-
-  if( camera )
-    updateCoordinateFrame();
-}
-
-//------------------------------------------------------------------------------
-void FGODGauge::useImageCoords(bool use)
-{
-  if( use == _use_image_coords )
-    return;
-
-  _use_image_coords = use;
-
-  if( texture )
-    updateCoordinateFrame();
-}
-
-//------------------------------------------------------------------------------
-void FGODGauge::useStencil(bool use)
-{
-  if( use == _use_stencil )
-    return;
-
-  _use_stencil = use;
-
-  if( texture )
-    updateStencil();
-}
-
-//------------------------------------------------------------------------------
-void FGODGauge::setSampling( bool mipmapping,
-                             int coverage_samples,
-                             int color_samples )
-{
-  if(    _use_mipmapping == mipmapping
-      && _coverage_samples == coverage_samples
-      && _color_samples == color_samples )
-    return;
-
-  _use_mipmapping = mipmapping;
-
-  if( color_samples > coverage_samples )
-  {
-    SG_LOG
-    (
-      SG_GL,
-      SG_WARN,
-      "FGODGauge::setSampling: color_samples > coverage_samples not allowed!"
-    );
-    color_samples = coverage_samples;
-  }
-
-  _coverage_samples = coverage_samples;
-  _color_samples = color_samples;
-
-  updateSampling();
-}
-
-//------------------------------------------------------------------------------
-void FGODGauge::setRender(bool render)
-{
-  // Only the far camera should trigger this texture to be rendered.
-  camera->setNodeMask(render ? simgear::BACKGROUND_BIT : 0);
-}
-
-//------------------------------------------------------------------------------
-bool FGODGauge::serviceable(void) 
-{
-  return rtAvailable;
-}
-
-//------------------------------------------------------------------------------
-void FGODGauge::allocRT(osg::NodeCallback* camera_cull_callback)
-{
-  camera = new osg::Camera;
-  camera->setDataVariance(osg::Object::DYNAMIC);
-  camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
-  camera->setRenderOrder(osg::Camera::PRE_RENDER);
-  camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f));
-  camera->setClearStencil(0);
-  camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT,
-                                             osg::Camera::FRAME_BUFFER );
-
-  if( camera_cull_callback )
-    camera->setCullCallback(camera_cull_callback);
-
-  setRender(true);
-  updateCoordinateFrame();
-  updateStencil();
-
-  osg::StateSet* stateSet = camera->getOrCreateStateSet();
-  stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
-  stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
-  stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
-  stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
-  stateSet->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK,
-          osg::PolygonMode::FILL),
-          osg::StateAttribute::ON);
-  stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER,
-          0.0f),
-          osg::StateAttribute::ON);
-  stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
-  stateSet->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA,
-          osg::BlendFunc::ONE_MINUS_SRC_ALPHA),
-          osg::StateAttribute::ON);
-  if( !texture )
-  {
-    texture = new osg::Texture2D;
-    texture->setTextureSize(_size_x, _size_y);
-    texture->setInternalFormat(GL_RGBA);
-  }
-
-  updateSampling();
-
-  globals->get_renderer()->addCamera(camera.get(), false);
-  rtAvailable = true;
-}
-
-//------------------------------------------------------------------------------
-void FGODGauge::updateCoordinateFrame()
-{
-  assert( camera );
-
-  if( _view_width < 0 )
-    _view_width = _size_x;
-  if( _view_height < 0 )
-    _view_height = _size_y;
-
-  camera->setViewport(0, 0, _size_x, _size_y);
-
-  if( _use_image_coords )
-    camera->setProjectionMatrix(
-      osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
-    );
-  else
-    camera->setProjectionMatrix(
-      osg::Matrix::ortho2D( -_view_width/2.,  _view_width/2.,
-                            -_view_height/2., _view_height/2. )
-    );
-}
-
-//------------------------------------------------------------------------------
-void FGODGauge::updateStencil()
-{
-  assert( camera );
-
-  GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
-
-  if( _use_stencil)
-  {
-    camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
-                     GL_DEPTH_STENCIL_EXT );
-    mask |= GL_STENCIL_BUFFER_BIT;
-  }
-  else
-  {
-    camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER);
-  }
-
-  camera->setClearMask(mask);
-}
-
-//------------------------------------------------------------------------------
-void FGODGauge::updateSampling()
-{
-  assert( camera );
-  assert( texture );
-
-  texture->setFilter(
-    osg::Texture2D::MIN_FILTER,
-    _use_mipmapping ? osg::Texture2D::LINEAR_MIPMAP_LINEAR
-                    : osg::Texture2D::LINEAR
-  );
-  camera->attach(
-    osg::Camera::COLOR_BUFFER,
-    texture.get(),
-    0, 0,
-    _use_mipmapping,
-    _coverage_samples,
-    _color_samples
-  );
-}
-
-/**
- * Replace a texture in the airplane model with the gauge texture.
- */
-class ReplaceStaticTextureVisitor:
-  public osg::NodeVisitor
-{
-  public:
-
-    ReplaceStaticTextureVisitor( const char* name,
-                                 osg::Texture2D* new_texture ):
-        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
-        _tex_name( osgDB::getSimpleFileName(name) ),
-        _new_texture(new_texture)
-    {}
-
-    ReplaceStaticTextureVisitor( const SGPropertyNode* placement,
-                                 osg::Texture2D* new_texture,
-                                 osg::NodeCallback* cull_callback = 0 ):
-        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
-        _tex_name( osgDB::getSimpleFileName(
-          placement->getStringValue("texture"))
-        ),
-        _node_name( placement->getStringValue("node") ),
-        _parent_name( placement->getStringValue("parent") ),
-        _new_texture(new_texture),
-        _cull_callback(cull_callback)
-    {
-      if(    _tex_name.empty()
-          && _node_name.empty()
-          && _parent_name.empty() )
-        SG_LOG
-        (
-          SG_GL,
-          SG_WARN,
-          "No filter criterion for replacing texture. "
-          " Every texture will be replaced!"
-        );
-    }
-
-    /**
-     * Get a list of groups which have been inserted into the scene graph to
-     * replace the given texture
-     */
-    canvas::Placements& getPlacements()
-    {
-      return _placements;
-    }
-
-    virtual void apply(osg::Geode& node)
-    {
-      simgear::EffectGeode* eg = dynamic_cast<simgear::EffectGeode*>(&node);
-      if( !eg )
-        return;
-
-      osg::StateSet* ss = eg->getEffect()->getDefaultStateSet();
-      if( !ss )
-        return;
-
-      osg::Group *parent = node.getParent(0);
-      if( !_node_name.empty() && parent->getName() != _node_name )
-        return;
-
-      if( !_parent_name.empty() )
-      {
-        // Traverse nodes upwards starting at the parent node (skip current
-        // node)
-        const osg::NodePath& np = getNodePath();
-        bool found = false;
-        for( int i = static_cast<int>(np.size()) - 2; i >= 0; --i )
-        {
-          const osg::Node* path_segment = np[i];
-          const osg::Node* path_parent = path_segment->getParent(0);
-
-          // A node without a name is always the parent of the root node of
-          // the model just containing the file name
-          if( path_parent && path_parent->getName().empty() )
-            return;
-
-          if( path_segment->getName() == _parent_name )
-          {
-            found = true;
-            break;
-          }
-        }
-
-        if( !found )
-          return;
-      }
-
-      for( size_t unit = 0; unit < ss->getNumTextureAttributeLists(); ++unit )
-      {
-        osg::Texture2D* tex = dynamic_cast<osg::Texture2D*>
-        (
-          ss->getTextureAttribute(unit, osg::StateAttribute::TEXTURE)
-        );
-
-        if( !tex || !tex->getImage() || tex == _new_texture )
-          continue;
-
-        if( !_tex_name.empty() )
-        {
-          std::string tex_name = tex->getImage()->getFileName();
-          std::string tex_name_simple = osgDB::getSimpleFileName(tex_name);
-          if( !osgDB::equalCaseInsensitive(_tex_name, tex_name_simple) )
-            continue;
-        }
-
-        // insert a new group between the geode an it's parent which overrides
-        // the texture
-        osg::ref_ptr<osg::Group> group = new osg::Group;
-        group->setName("canvas texture group");
-        group->addChild(eg);
-        parent->removeChild(eg);
-        parent->addChild(group);
-
-        if( _cull_callback )
-          group->setCullCallback(_cull_callback);
-
-        _placements.push_back(
-          canvas::PlacementPtr(new ObjectPlacement(group))
-        );
-
-        osg::StateSet* stateSet = group->getOrCreateStateSet();
-        stateSet->setTextureAttribute( unit, _new_texture,
-                                             osg::StateAttribute::OVERRIDE );
-        stateSet->setTextureMode( unit, GL_TEXTURE_2D,
-                                        osg::StateAttribute::ON );
-
-        SG_LOG
-        (
-          SG_GL,
-          SG_INFO,
-             "Replaced texture '" << _tex_name << "'"
-          << " for object '" << parent->getName() << "'"
-          << (!_parent_name.empty() ? " with parent '" + _parent_name + "'"
-                                    : "")
-        );
-        return;
-      }
-    }
-
-  protected:
-
-    class ObjectPlacement:
-      public canvas::Placement
-    {
-      public:
-        ObjectPlacement(osg::ref_ptr<osg::Group> group):
-          _group(group)
-        {}
-
-        /**
-         * Remove placement from the scene
-         */
-        virtual ~ObjectPlacement()
-        {
-          assert( _group->getNumChildren() == 1 );
-          osg::Node *child = _group->getChild(0);
-
-          if( _group->getNumParents() )
-          {
-            osg::Group *parent = _group->getParent(0);
-            parent->addChild(child);
-            parent->removeChild(_group);
-          }
-
-          _group->removeChild(child);
-        }
-
-      private:
-        osg::ref_ptr<osg::Group> _group;
-    };
-
-    std::string _tex_name,      ///<! Name of texture to be replaced
-                _node_name,     ///<! Only replace if node name matches
-                _parent_name;   ///<! Only replace if any parent node matches
-                                ///   given name (all the tree upwards)
-    osg::Texture2D     *_new_texture;
-    osg::NodeCallback  *_cull_callback;
-
-    canvas::Placements _placements;
-};
-
-//------------------------------------------------------------------------------
-canvas::Placements FGODGauge::set_texture( const char* name,
-                                           osg::Texture2D* new_texture )
-{
-  osg::Group* root = globals->get_scenery()->get_aircraft_branch();
-  ReplaceStaticTextureVisitor visitor(name, new_texture);
-  root->accept(visitor);
-  return visitor.getPlacements();
-}
-
-//------------------------------------------------------------------------------
-canvas::Placements FGODGauge::set_texture( const SGPropertyNode* placement,
-                                           osg::Texture2D* new_texture,
-                                           osg::NodeCallback* cull_callback )
-{
-  osg::Group* root = globals->get_scenery()->get_aircraft_branch();
-  ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback);
-  root->accept(visitor);
-  return visitor.getPlacements();
-}
diff --git a/src/Instrumentation/od_gauge.hxx b/src/Instrumentation/od_gauge.hxx
deleted file mode 100644 (file)
index 7c4ee00..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-// Owner Drawn Gauge helper class
-//
-// Written by Harald JOHNSEN, started May 2005.
-//
-// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
-//
-// Ported to OSG by Tim Moore - Jun 2007
-//
-// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
-// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
-// the texture in the scene by certain filter criteria
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//
-//
-
-#ifndef _OD_GAUGE_HXX
-#define _OD_GAUGE_HXX
-
-#include <Canvas/canvas_fwd.hpp>
-#include <Canvas/placement.hxx>
-
-#include <osg/NodeCallback>
-#include <osg/Group>
-
-namespace osg {
-  class Camera;
-  class Texture2D;
-}
-
-class SGPropertyNode;
-
-/**
- * Owner Drawn Gauge helper class.
- */
-class FGODGauge
-{
-  public:
-    FGODGauge();
-    virtual ~FGODGauge();
-
-    /**
-     * Set the size of the render target.
-     *
-     * @param size_x    X size
-     * @param size_y    Y size. Defaults to size_x if not specified
-     */
-    void setSize(int size_x, int size_y = -1);
-
-    /**
-     * Set the size of the viewport
-     *
-     * @param width
-     * @param height    Defaults to width if not specified
-     */
-    void setViewSize(int width, int height = -1);
-
-    /**
-     * DEPRECATED
-     *
-     * Get size of squared texture
-     */
-    int size() const { return _size_x; }
-    
-    /**
-     * Set whether to use image coordinates or not.
-     *
-     * Default: origin == center of texture
-     * Image Coords: origin == top left corner
-     */
-    void useImageCoords(bool use = true);
-
-    /**
-     * Enable/Disable using a stencil buffer
-     */
-    void useStencil(bool use = true);
-
-    /**
-     * Set sampling parameters for mipmapping and coverage sampling
-     * antialiasing.
-     *
-     * @note color_samples is not allowed to be higher than coverage_samples
-     *
-     */
-    void setSampling( bool mipmapping,
-                      int coverage_samples = 0,
-                      int color_samples = 0 );
-
-    /**
-     * Enable/Disable updating the texture (If disabled the contents of the
-     * texture remains with the outcome of the last rendering pass)
-     */
-    void setRender(bool render);
-
-    /**
-     * Say if we can render to a texture.
-     * @return true if rtt is available
-     */
-    bool serviceable(void);
-
-    /**
-     * Replace an opengl texture name inside the aircraft scene graph.
-     * This is to replace a static texture by a dynamic one
-     * @param name texture filename
-     * @param new_texture dynamic texture to replace the old one
-     * @return A list of groups which override the given texture
-     */
-    static
-    canvas::Placements set_texture( const char * name,
-                                    osg::Texture2D* new_texture );
-
-    /**
-     * Replace an opengl texture name inside the aircraft scene graph.
-     * This is to replace a static texture by a dynamic one. The replacement
-     * is base on certain filtering criteria which have to be stored in string
-     * value childs of the placement node. Recognized nodes are:
-     *   - texture  Match the name of the texture
-     *   - node     Match the name of the object
-     *   - parent   Match any of the object parents names (all the tree upwards)
-     * @param placement the node containing the replacement criteria
-     * @param new_texture dynamic texture to replace the old one
-     * @param an optional cull callback which will be installed on any matching
-     *        object
-     * @return A list of groups which override the given texture
-     */
-    static
-    canvas::Placements set_texture( const SGPropertyNode* placement,
-                                    osg::Texture2D* new_texture,
-                                    osg::NodeCallback* cull_callback = 0 );
-
-    /**
-     * Get the OSG camera for drawing this gauge.
-     */
-    osg::Camera* getCamera() const { return camera.get(); }
-
-    osg::Texture2D* getTexture() const { return texture.get(); }
-    //void setTexture(osg::Texture2D* t) { texture = t; }
-
-    // Real initialization function. Bad name.
-    void allocRT(osg::NodeCallback* camera_cull_callback = 0);
-
-  private:
-    int _size_x,
-        _size_y,
-        _view_width,
-        _view_height;
-    bool _use_image_coords,
-         _use_stencil,
-         _use_mipmapping;
-
-    // Multisampling parameters
-    int  _coverage_samples,
-         _color_samples;
-
-    bool rtAvailable;
-    osg::ref_ptr<osg::Camera> camera;
-    osg::ref_ptr<osg::Texture2D> texture;
-
-    void updateCoordinateFrame();
-    void updateStencil();
-    void updateSampling();
-
-};
-
-#endif // _OD_GAUGE_HXX
index 7ab1e657f784ad254d29ab95d0f915aa8f09a345..5babe31605339beaa9a8a0830c8ca6a2c351c8cf 100644 (file)
@@ -28,7 +28,7 @@
 #include <Scenery/scenery.hxx>
 #include <simgear/scene/material/mat.hxx>
 
-#include "agradar.hxx"
+#include <Cockpit/agradar.hxx>
 
 class radAlt : public agRadar{
 public:
diff --git a/src/Instrumentation/render_area_2d.cxx b/src/Instrumentation/render_area_2d.cxx
deleted file mode 100644 (file)
index 8ad68a7..0000000
+++ /dev/null
@@ -1,426 +0,0 @@
-// RenderArea2D.cxx - a class to manage 2D polygon-based drawing
-//                    for a complex instrument (eg. GPS).
-//
-// Written by David Luff, started 2005.
-//
-// Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//
-// $Id$
-
-
-#ifdef HAVE_CONFIG_H
-#  include <config.h>
-#endif
-
-#include "render_area_2d.hxx"
-
-RA2DPrimitive::RA2DPrimitive() {
-    invert = false;
-    debug = false;
-}
-                                         
-RenderArea2D::RenderArea2D(int logx, int logy, int sizex, int sizey, int posx, int posy) {
-    _logx = logx;
-    _logy = logy;
-    _sizex = sizex;
-    _sizey = sizey;
-    _posx = posx;
-    _posy = posy;
-    _clipx1 = 0;
-    _clipx2 = _logx - 1;
-    _clipy1 = 0;
-    _clipy2 = _logy - 1;
-    
-    // Default to black background / white text.
-    _backgroundColor[0] = 0.0;
-    _backgroundColor[1] = 0.0;
-    _backgroundColor[2] = 0.0;
-    _backgroundColor[3] = 1.0;
-    _pixelColor[0] = 1.0;
-    _pixelColor[1] = 1.0;
-    _pixelColor[2] = 1.0;
-    _pixelColor[3] = 1.0;
-    
-    _ra2d_debug = false;
-}
-
-void RenderArea2D::Draw(osg::State& state) {
-    
-    static osg::ref_ptr<osg::StateSet> renderArea2DStateSet;
-    if(!renderArea2DStateSet.valid()) {
-        renderArea2DStateSet = new osg::StateSet;
-        renderArea2DStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
-        renderArea2DStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
-    }
-    
-    state.pushStateSet(renderArea2DStateSet.get());
-    state.apply();
-    state.setActiveTextureUnit(0);
-    state.setClientActiveTextureUnit(0);
-    
-    // DCL - the 2 lines below are copied verbatim from the hotspot drawing code.
-    // I am not sure if they are needed here or not.
-    glPushAttrib(GL_ENABLE_BIT);
-    glDisable(GL_COLOR_MATERIAL);
-    
-    // FIXME - disabling all clip planes causes bleed-through through the splash screen.
-    glDisable(GL_CLIP_PLANE0);
-    glDisable(GL_CLIP_PLANE1);
-    glDisable(GL_CLIP_PLANE2);
-    glDisable(GL_CLIP_PLANE3);
-
-    DoDrawBackground();
-    
-    for(unsigned int i = 0; i < drawing_list.size(); ++i) {
-        RA2DPrimitive prim = drawing_list[i];
-        switch(prim.type) {
-        case RA2D_LINE:
-            DoDrawLine(prim.x1, prim.y1, prim.x2, prim.y2);
-            break;
-        case RA2D_QUAD:
-            if(prim.debug) {
-                //cout << "Clipping = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n';
-                //cout << "Drawing quad " << prim.x1 << ", " << prim.y1 << " to " << prim.x2 << ", " << prim.y2 << '\n';
-            }
-            DoDrawQuad(prim.x1, prim.y1, prim.x2, prim.y2, prim.invert);
-            break;
-        case RA2D_PIXEL:
-            DoDrawPixel(prim.x1, prim.y1, prim.invert);
-            break;
-        }
-    }
-    
-    drawing_list.clear();
-    
-    glPopAttrib();
-    
-    state.popStateSet();
-    state.apply();
-    state.setActiveTextureUnit(0);
-    state.setClientActiveTextureUnit(0);
-}
-
-void RenderArea2D::Flush() {
-    drawing_list.clear();
-}
-
-void RenderArea2D::SetPixelColor(const float* rgba) {
-    _pixelColor[0] = rgba[0];
-    _pixelColor[1] = rgba[1];
-    _pixelColor[2] = rgba[2];
-    _pixelColor[3] = rgba[3];
-}
-
-// Set clipping region in logical units
-void RenderArea2D::SetClipRegion(int x1, int y1, int x2, int y2) {
-    _clipx1 = x1;
-    _clipx2 = x2;
-    _clipy1 = y1;
-    _clipy2 = y2;
-    //cout << "Set clip region, clip region = "  << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n';
-}
-
-// Set clip region to be the same as the rendered area (default)
-void RenderArea2D::ResetClipRegion() {
-    _clipx1 = 0;
-    _clipx2 = _logx - 1;
-    _clipy1 = 0;
-    _clipy2 = _logy - 1;
-    //cout << "Reset clip region, clip region = "  << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n';
-}
-
-void RenderArea2D::SetPosition(int posx, int posy) {
-    _posx = posx;
-    _posy = posy;
-}
-
-void RenderArea2D::SetLogicalSize(int logx, int logy) {
-    _logx = logx;
-    _logy = logy;
-}
-
-void RenderArea2D::SetActualSize(int sizex, int sizey) {
-    _sizex = sizex;
-    _sizey = sizey;
-}
-
-void RenderArea2D::DrawPixel(int x, int y, bool invert) {
-    // Clip
-    if(x < _clipx1 || x > _clipx2 || y < _clipy1 || y > _clipy2) return;
-
-    RA2DPrimitive prim;
-    prim.x1 = x;
-    prim.y1 = y;
-    prim.x2 = 0;
-    prim.y2 = 0;
-    prim.type = RA2D_PIXEL;
-    prim.invert = invert;
-    drawing_list.push_back(prim);
-}
-
-void RenderArea2D::DoDrawPixel(int x, int y, bool invert) {
-    // Clip.  In theory this shouldn't be necessary, since all input is clipped before adding
-    // to the drawing list, but it ensures that any errors in clipping lines etc will only 
-    // spill over the clip area within the instrument, and still be clipped from straying
-    // outside the instrument.
-    if(x < _clipx1 || x > _clipx2 || y < _clipy1 || y > _clipy2) return;
-    
-    // Scale to position within background
-    float fx1 = (float)x, fy1 = (float)y;
-    float rx = (float)_sizex / (float)_logx;
-    float ry = (float)_sizey / (float)_logy;
-    fx1 *= rx;
-    fy1 *= ry;
-    float fx2 = fx1 + rx;
-    float fy2 = fy1 + ry;
-    
-    // Translate to final position
-    fx1 += (float)_posx;
-    fx2 += (float)_posx;
-    fy1 += (float)_posy;
-    fy2 += (float)_posy;
-    
-    //cout << "DP: " << fx1 << ", " << fy1 << " ... " << fx2 << ", " << fy2 << '\n';
-    
-    SetRenderColor(invert ? _backgroundColor : _pixelColor);
-    SGVec2f corners[4] = {
-        SGVec2f(fx1, fy1),
-        SGVec2f(fx2, fy1),
-        SGVec2f(fx2, fy2),
-        SGVec2f(fx1, fy2)
-    };
-    RenderQuad(corners);
-}
-
-void RenderArea2D::DrawLine(int x1, int y1, int x2, int y2) {
-    // We need to clip the line to the current region before storing it in the drawing 
-    // list, since when we come to actually draw it the clip region may have changed.
-
-    // Liang-Barsky clipping algorithm
-    int p[4], q[4];
-    float u1 = 0.0f, u2 = 1.0f;
-    p[0] = -(x2 - x1); q[0] = (x1 - _clipx1);
-    p[1] =  (x2 - x1); q[1] = (_clipx2 - x1);
-    p[2] = -(y2 - y1); q[2] = (y1 - _clipy1);
-    p[3] =  (y2 - y1); q[3] = (_clipy2 - y1);
-    
-    for(int i=0; i<4; ++i) {
-        if(p[i] == 0) {
-            if(q[i] < 0) {
-                // Then we have a trivial rejection of a line parallel to a clip plane
-                // completely outside the clip region.
-                return;
-            }
-        } else if(p[i] < 0) {
-            float r = (float)q[i]/(float)p[i];
-            u1 = (u1 > r ? u1 : r);
-        } else {        // p[i] > 0
-            float r = (float)q[i]/(float)p[i];
-            u2 = (u2 < r ? u2 : r);
-        }
-        if(u1 > u2) {
-            // Then the line is completely outside the clip area.
-            return;
-        }
-    }
-    
-    float fx1 = x1 + u1 * (float)(x2 - x1);
-    float fy1 = y1 + u1 * (float)(y2 - y1);
-    float fx2 = x1 + u2 * (float)(x2 - x1);
-    float fy2 = y1 + u2 * (float)(y2 - y1);
-    x1 = (int)(fx1 + 0.5);
-    y1 = (int)(fy1 + 0.5);
-    x2 = (int)(fx2 + 0.5);
-    y2 = (int)(fy2 + 0.5);
-    
-    RA2DPrimitive prim;
-    prim.x1 = x1;
-    prim.y1 = y1;
-    prim.x2 = x2;
-    prim.y2 = y2;
-    prim.type = RA2D_LINE;
-    prim.invert = false;
-    drawing_list.push_back(prim);
-}
-
-void RenderArea2D::DoDrawLine(int x1, int y1, int x2, int y2) {
-    // Crude implementation of Bresenham line drawing algorithm.
-    
-    // Our lines are non directional, so first order the points x-direction-wise to leave only 4 octants to consider.
-    if(x2 < x1) {
-        int tmp_x = x1;
-        int tmp_y = y1;
-        x1 = x2;
-        y1 = y2;
-        x2 = tmp_x;
-        y2 = tmp_y;
-    }
-    
-    bool flip_y = (y1 > y2 ? true : false);
-    int dx = x2 - x1;
-    int dy = (flip_y ? y1 - y2 : y2 - y1); 
-    if(dx > dy) {
-        // push the x dir
-        int y = y1;
-        int yn = dx/2;
-        for(int x=x1; x<=x2; ++x) {
-            DoDrawPixel(x, y);
-            yn += dy;
-            if(yn >= dx) {
-                yn -= dx;
-                y = (flip_y ? y - 1 : y + 1);
-            }
-        }
-    } else {
-        // push the y dir
-        int x = x1;
-        int xn = dy/2;
-        // Must be a more elegant way to roll the next two cases into one!
-        if(flip_y) {
-            for(int y=y1; y>=y2; --y) {
-                DoDrawPixel(x, y);
-                xn += dx;
-                if(xn >= dy) {
-                    xn -= dy;
-                    x++;
-                }
-            }
-        } else {
-            for(int y=y1; y<=y2; ++y) {
-                DoDrawPixel(x, y);
-                xn += dx;
-                if(xn >= dy) {
-                    xn -= dy;
-                    x++;
-                }
-            }
-        }
-    }
-}
-
-void RenderArea2D::DrawQuad(int x1, int y1, int x2, int y2, bool invert) {
-    // Force the input to be ordered with x1 < x2 and y1 < y2.
-    if(x1 > x2) {
-        int x = x2;
-        x2 = x1;
-        x1 = x;
-    }
-    if(y1 > y2) {
-        int y = y2;
-        y2 = y1;
-        y1 = y;
-    }
-    
-    // Clip the input to the current drawing region.
-    x1 = x1 < _clipx1 ? _clipx1 : x1;
-    if(x1 > _clipx2) { return; }
-    x2 = x2 > _clipx2 ? _clipx2 : x2;
-    if(x2 < _clipx1) { return; }
-    y1 = y1 < _clipy1 ? _clipy1 : y1;
-    if(y1 > _clipy2) { return; }
-    y2 = y2 > _clipy2 ? _clipy2 : y2;
-    if(y2 < _clipy1) { return; }
-    
-    RA2DPrimitive prim;
-    prim.x1 = x1;
-    prim.y1 = y1;
-    prim.x2 = x2;
-    prim.y2 = y2;
-    prim.type = RA2D_QUAD;
-    prim.invert = invert;
-    if(_ra2d_debug) prim.debug = true;
-    drawing_list.push_back(prim);
-}
-
-void RenderArea2D::DoDrawQuad(int x1, int y1, int x2, int y2, bool invert) {
-    // Scale to position within background
-    float fx1 = (float)x1, fy1 = (float)y1;
-    float fx2 = (float)x2, fy2 = (float)y2;
-    float rx = (float)_sizex / (float)_logx;
-    float ry = (float)_sizey / (float)_logy;
-    fx1 *= rx;
-    fy1 *= ry;
-    fx2 *= rx;
-    fy2 *= ry;
-    
-    fx2 += rx;
-    fy2 += ry;
-    
-    // Translate to final position
-    fx1 += (float)_posx;
-    fx2 += (float)_posx;
-    fy1 += (float)_posy;
-    fy2 += (float)_posy;
-    
-    //cout << "DP: " << fx1 << ", " << fy1 << " ... " << fx2 << ", " << fy2 << '\n';
-    
-    SetRenderColor(invert ? _backgroundColor : _pixelColor);
-    SGVec2f corners[4] = {
-        SGVec2f(fx1, fy1),
-        SGVec2f(fx2, fy1),
-        SGVec2f(fx2, fy2),
-        SGVec2f(fx1, fy2)
-    };
-    RenderQuad(corners);
-}
-
-void RenderArea2D::DrawBackground() {
-    // Currently a NO-OP
-}
-
-void RenderArea2D::DoDrawBackground() {
-    SetRenderColor(_backgroundColor);
-    SGVec2f corners[4] = {
-        SGVec2f(_posx, _posy),
-        SGVec2f(_posx + _sizex, _posy),
-        SGVec2f(_posx + _sizex, _posy + _sizey),
-        SGVec2f(_posx, _posy + _sizey)                                                                  
-    };
-  
-    RenderQuad(corners);
-}
-
-// -----------------------------------------
-//
-// Actual drawing routines copied from Atlas
-//
-// -----------------------------------------
-
-void RenderArea2D::SetRenderColor( const float *rgba ) {
-    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, rgba);
-    glColor4fv( rgba );
-}
-
-void RenderArea2D::RenderQuad( const SGVec2f *p) {
-    glBegin(GL_QUADS);
-        glNormal3f(0.0f, 0.0f, 0.0f);
-        glVertex2fv( p[0].data() );
-        glVertex2fv( p[1].data() );
-        glVertex2fv( p[2].data() );
-        glVertex2fv( p[3].data() );
-    glEnd();
-}
-
-void RenderArea2D::RenderQuad( const SGVec2f *p, const SGVec4f *color ) {
-    glBegin(GL_QUADS);
-        glNormal3f(0.0f, 0.0f, 0.0f);
-        glColor4fv( color[0].data() ); glVertex2fv( p[0].data() );
-        glColor4fv( color[1].data() ); glVertex2fv( p[1].data() );
-        glColor4fv( color[2].data() ); glVertex2fv( p[2].data() );
-        glColor4fv( color[3].data() ); glVertex2fv( p[3].data() );
-    glEnd();
-}
diff --git a/src/Instrumentation/render_area_2d.hxx b/src/Instrumentation/render_area_2d.hxx
deleted file mode 100644 (file)
index 83d74ef..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-// RenderArea2D.hxx - a class to manage 2D polygon-based drawing
-//                    for a complex instrument (eg. GPS).
-//
-// Written by David Luff, started 2005.
-//
-// Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//
-// $Id$
-
-#ifndef _RENDER_AREA_2D
-#define _RENDER_AREA_2D
-
-#ifdef HAVE_CONFIG_H
-#  include <config.h>
-#endif
-
-#include <osg/ref_ptr>
-#include <osg/State>
-#include <osg/StateSet>
-
-#include <simgear/compiler.h>
-#include <simgear/math/SGMath.hxx>
-
-#include <vector>
-
-using std::vector;
-
-enum RA2DDrawingType {
-       RA2D_LINE,
-       RA2D_QUAD,
-       RA2D_PIXEL
-};
-
-struct RA2DPrimitive {
-       RA2DPrimitive();
-
-       RA2DDrawingType type;
-       int x1, y1, x2, y2;
-       bool invert;
-       bool debug;
-};
-
-class RenderArea2D {
-       
-public:
-       RenderArea2D(int logx, int logy, int sizex, int sizey, int posx, int posy);
-       ~RenderArea2D();
-       
-       void SetPixelColor(const float* rgba);
-       void SetBackgroundColor(const float* rgba);
-       void SetPosition(int posx, int posy);
-       void SetLogicalSize(int logx, int logy);
-       void SetActualSize(int sizex, int sizey);
-       
-       // Set clipping region in logical units
-       void SetClipRegion(int x1, int y1, int x2, int y2);
-       // Set clip region to be the same as the rendered area (default)
-       void ResetClipRegion();
-       
-       // The DrawXXX functions place the shapes in the buffer, specified 
-       // in logical units, and clipped to the current clip region.
-       void DrawPixel(int x, int y, bool invert = false);
-       void DrawLine(int x1, int y1, int x2, int y2);
-       void DrawQuad(int x1, int y1, int x2, int y2, bool invert = false);
-       void DrawBackground();
-       
-       // Call Draw to have the buffer contents drawn and then cleared.
-       void Draw(osg::State& state);
-       
-       // Clear the buffer contents
-       void Flush();
-       
-       // Turn debugging on or off.
-       inline void SetDebugging(bool b) { _ra2d_debug = b; }
-
-private:
-       int _logx, _logy;       // logical size of rendered area
-       int _sizex, _sizey;             // Actual size of rendered area
-       int _posx, _posy;       // Position of rendered area
-       int _clipx1, _clipx2, _clipy1, _clipy2; // Current clipping region in *logical* units
-       
-       float _backgroundColor[4];
-       float _pixelColor[4];
-       
-       // Drawing specified in logical units
-       void DoDrawPixel(int x, int y, bool invert = false);
-       void DoDrawLine(int x1, int y1, int x2, int y2);
-       void DoDrawQuad(int x1, int y1, int x2, int y2, bool invert = false);
-       void DoDrawBackground();
-       
-       // Actual rendering routines copied from Atlas
-       void SetRenderColor( const float *rgb );
-       void RenderQuad( const SGVec2f *p);
-       void RenderQuad( const SGVec2f *p, const SGVec4f *color );
-       
-       vector<RA2DPrimitive> drawing_list;
-       
-       // Control whether to output debugging output
-       bool _ra2d_debug;
-};
-
-#endif
diff --git a/src/Instrumentation/wxradar.cxx b/src/Instrumentation/wxradar.cxx
deleted file mode 100644 (file)
index c773c6c..0000000
+++ /dev/null
@@ -1,1116 +0,0 @@
-// Wx Radar background texture
-//
-// Written by Harald JOHNSEN, started May 2005.
-// With major amendments by Vivian MEAZZA May 2007
-// Ported to OSG by Tim Moore Jun 2007
-//
-//
-// Copyright (C) 2005  Harald JOHNSEN
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//
-//
-
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#endif
-
-#include <osg/Array>
-#include <osg/Geometry>
-#include <osg/Matrixf>
-#include <osg/PrimitiveSet>
-#include <osg/StateSet>
-#include <osg/Version>
-#include <osgDB/ReaderWriter>
-#include <osgDB/WriteFile>
-
-#include <simgear/constants.h>
-#include <simgear/misc/sg_path.hxx>
-#include <simgear/scene/model/model.hxx>
-#include <simgear/structure/exception.hxx>
-#include <simgear/misc/sg_path.hxx>
-#include <simgear/math/sg_geodesy.hxx>
-
-#include <sstream>
-#include <iomanip>
-
-using std::stringstream;
-using std::endl;
-using std::setprecision;
-using std::fixed;
-using std::setw;
-using std::setfill;
-using std::string;
-
-#include <Main/fg_props.hxx>
-#include <Main/globals.hxx>
-#include <Cockpit/panel.hxx>
-
-#include "instrument_mgr.hxx"
-#include "od_gauge.hxx"
-#include "wxradar.hxx"
-
-#include <iostream>             // for cout, endl
-
-using std::cout;
-using std::endl;
-
-static const float UNIT = 1.0f / 8.0f;  // 8 symbols in a row/column in the texture
-static const char *DEFAULT_FONT = "typewriter.txf";
-
-wxRadarBg::wxRadarBg(SGPropertyNode *node) :
-    _name(node->getStringValue("name", "radar")),
-    _num(node->getIntValue("number", 0)),
-    _time(0.0),
-    _interval(node->getDoubleValue("update-interval-sec", 1.0)),
-    _elapsed_time(0),
-    _persistance(0),
-    _odg(0),
-    _range_nm(0),
-    _scale(0),
-    _angle_offset(0),
-    _view_heading(0),
-    _x_offset(0),
-    _y_offset(0),
-    _radar_ref_rng(0),
-    _lat(0),
-    _lon(0),
-    _antenna_ht(node->getDoubleValue("antenna-ht-ft", 0.0)),
-    _resultTexture(0),
-    _wxEcho(0),
-    _font_size(0),
-    _font_spacing(0)
-{
-    string branch;
-    branch = "/instrumentation/" + _name;
-    _Instrument = fgGetNode(branch.c_str(), _num, true);
-
-    const char *tacan_source = node->getStringValue("tacan-source", "/instrumentation/tacan");
-    _Tacan = fgGetNode(tacan_source, true);
-
-    _font_node = _Instrument->getNode("font", true);
-
-#define INITFONT(p, val, type) if (!_font_node->hasValue(p)) _font_node->set##type##Value(p, val)
-    INITFONT("name", DEFAULT_FONT, String);
-    INITFONT("size", 8, Float);
-    INITFONT("line-spacing", 0.25, Float);
-    INITFONT("color/red", 0, Float);
-    INITFONT("color/green", 0.8, Float);
-    INITFONT("color/blue", 0, Float);
-    INITFONT("color/alpha", 1, Float);
-#undef INITFONT
-
-    _font_node->addChangeListener(this, true);
-}
-
-
-wxRadarBg::~wxRadarBg ()
-{
-    _font_node->removeChangeListener(this);
-    delete _odg;
-}
-
-
-void
-wxRadarBg::init ()
-{
-    _serviceable_node = _Instrument->getNode("serviceable", true);
-    _sceneryLoaded = fgGetNode("/sim/sceneryloaded", true);
-
-    // texture name to use in 2D and 3D instruments
-    _texture_path = _Instrument->getStringValue("radar-texture-path",
-        "Aircraft/Instruments/Textures/od_wxradar.rgb");
-    _resultTexture = FGTextureManager::createTexture(_texture_path.c_str(), false);
-
-    string path = _Instrument->getStringValue("echo-texture-path",
-        "Aircraft/Instruments/Textures/wxecho.rgb");
-    SGPath tpath = globals->resolve_aircraft_path(path);
-
-    // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect
-    _wxEcho = SGLoadTexture2D(tpath, NULL, false, false);
-
-
-    _Instrument->setFloatValue("trk", 0.0);
-    _Instrument->setFloatValue("tilt", 0.0);
-    _Instrument->setStringValue("status", "");
-    // those properties are used by a radar instrument of a MFD
-    // input switch = OFF | TST | STBY | ON
-    // input mode = WX | WXA | MAP
-    // output status = STBY | TEST | WX | WXA | MAP | blank
-    // input lightning = true | false
-    // input TRK = +/- n degrees
-    // input TILT = +/- n degree
-    // input autotilt = true | false
-    // input range = n nm (20/40/80)
-    // input display-mode = arc | rose | map | plan
-
-    _odg = new FGODGauge;
-    _odg->setSize(512);
-
-    _ai_enabled_node = fgGetNode("/sim/ai/enabled", true);
-
-    _user_lat_node = fgGetNode("/position/latitude-deg", true);
-    _user_lon_node = fgGetNode("/position/longitude-deg", true);
-    _user_alt_node = fgGetNode("/position/altitude-ft", true);
-
-    _user_speed_east_fps_node   = fgGetNode("/velocities/speed-east-fps", true);
-    _user_speed_north_fps_node  = fgGetNode("/velocities/speed-north-fps", true);
-
-    _tacan_serviceable_node = _Tacan->getNode("serviceable", true);
-    _tacan_distance_node    = _Tacan->getNode("indicated-distance-nm", true);
-    _tacan_name_node        = _Tacan->getNode("name", true);
-    _tacan_bearing_node     = _Tacan->getNode("indicated-bearing-true-deg", true);
-    _tacan_in_range_node    = _Tacan->getNode("in-range", true);
-
-    _radar_mode_control_node = _Instrument->getNode("mode-control", true);
-    _radar_coverage_node     = _Instrument->getNode("limit-deg", true);
-    _radar_ref_rng_node      = _Instrument->getNode("reference-range-nm", true);
-    _radar_hdg_marker_node   = _Instrument->getNode("heading-marker", true);
-
-    SGPropertyNode *n = _Instrument->getNode("display-controls", true);
-    _radar_weather_node     = n->getNode("WX", true);
-    _radar_position_node    = n->getNode("pos", true);
-    _radar_data_node        = n->getNode("data", true);
-    _radar_symbol_node      = n->getNode("symbol", true);
-    _radar_centre_node      = n->getNode("centre", true);
-    _radar_rotate_node      = n->getNode("rotate", true);
-    _radar_tcas_node        = n->getNode("tcas", true);
-    _radar_absalt_node      = n->getNode("abs-altitude", true);
-
-    _radar_centre_node->setBoolValue(false);
-    if (!_radar_coverage_node->hasValue())
-        _radar_coverage_node->setFloatValue(120);
-    if (!_radar_ref_rng_node->hasValue())
-        _radar_ref_rng_node->setDoubleValue(35);
-    if (!_radar_hdg_marker_node->hasValue())
-        _radar_hdg_marker_node->setBoolValue(true);
-
-    _x_offset = 0;
-    _y_offset = 0;
-
-    // OSG geometry setup. The polygons for the radar returns will be
-    // stored in a single Geometry. The geometry will have several
-    // primitive sets so we can have different kinds of polys and
-    // choose a different overall color for each set.
-    _radarGeode = new osg::Geode;
-    osg::StateSet *stateSet = _radarGeode->getOrCreateStateSet();
-    stateSet->setTextureAttributeAndModes(0, _wxEcho.get());
-    _geom = new osg::Geometry;
-    _geom->setUseDisplayList(false);
-    // Initially allocate space for 128 quads
-    _vertices = new osg::Vec2Array;
-    _vertices->setDataVariance(osg::Object::DYNAMIC);
-    _vertices->reserve(128 * 4);
-    _geom->setVertexArray(_vertices);
-    _texCoords = new osg::Vec2Array;
-    _texCoords->setDataVariance(osg::Object::DYNAMIC);
-    _texCoords->reserve(128 * 4);
-    _geom->setTexCoordArray(0, _texCoords);
-    osg::Vec3Array *colors = new osg::Vec3Array;
-    colors->push_back(osg::Vec3(1.0f, 1.0f, 1.0f)); // color of echos
-    colors->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); // arc mask
-    colors->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); // rest of mask
-    _geom->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE_SET);
-    _geom->setColorArray(colors);
-    osg::PrimitiveSet *pset = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
-    pset->setDataVariance(osg::Object::DYNAMIC);
-    _geom->addPrimitiveSet(pset);
-    pset = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
-    pset->setDataVariance(osg::Object::DYNAMIC);
-    _geom->addPrimitiveSet(pset);
-    pset = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES);
-    pset->setDataVariance(osg::Object::DYNAMIC);
-    _geom->addPrimitiveSet(pset);
-    _geom->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f),
-        osg::Vec3f(256.0f, 256.0f, 0.0f)));
-    _radarGeode->addDrawable(_geom);
-    _odg->allocRT();
-    // Texture in the 2D panel system
-    FGTextureManager::addTexture(_texture_path.c_str(), _odg->getTexture());
-
-    _textGeode = new osg::Geode;
-
-    osg::Camera *camera = _odg->getCamera();
-    camera->addChild(_radarGeode.get());
-    camera->addChild(_textGeode.get());
-
-    updateFont();
-    _time = 0.0;
-}
-
-
-// Local coordinates for each echo
-const osg::Vec3f echoCoords[4] = {
-    osg::Vec3f(-.7f, -.7f, 0.0f), osg::Vec3f(.7f, -.7f, 0.0f),
-    osg::Vec3f(.7f, .7f, 0.0f), osg::Vec3f(-.7f, .7f, 0.0f)
-};
-
-
-const osg::Vec2f echoTexCoords[4] = {
-    osg::Vec2f(0.0f, 0.0f), osg::Vec2f(UNIT, 0.0f),
-    osg::Vec2f(UNIT, UNIT), osg::Vec2f(0.0f, UNIT)
-};
-
-
-// helper
-static void
-addQuad(osg::Vec2Array *vertices, osg::Vec2Array *texCoords,
-        const osg::Matrixf& transform, const osg::Vec2f& texBase)
-{
-    for (int i = 0; i < 4; i++) {
-        const osg::Vec3f coords = transform.preMult(echoCoords[i]);
-        texCoords->push_back(texBase + echoTexCoords[i]);
-        vertices->push_back(osg::Vec2f(coords.x(), coords.y()));
-    }
-}
-
-
-// Rotate by a heading value
-static inline
-osg::Matrixf wxRotate(float angle)
-{
-    return osg::Matrixf::rotate(angle, 0.0f, 0.0f, -1.0f);
-}
-
-
-void
-wxRadarBg::update (double delta_time_sec)
-{
-    if (!_sceneryLoaded->getBoolValue())
-        return;
-
-    if (!_odg || !_serviceable_node->getBoolValue()) {
-        _Instrument->setStringValue("status", "");
-        return;
-    }
-
-    _time += delta_time_sec;
-    if (_time < _interval)
-        return;
-
-    _time -= _interval;
-
-    string mode = _Instrument->getStringValue("display-mode", "arc");
-    if (mode == "map") {
-        if (_display_mode != MAP) {
-            _display_mode = MAP;
-            center_map();
-        }
-    } else if (mode == "plan") {
-        _display_mode = PLAN;}
-    else if (mode == "bscan") {
-        _display_mode = BSCAN;
-    } else {
-        _display_mode = ARC;
-    }
-
-    string switchKnob = _Instrument->getStringValue("switch", "on");
-    if (switchKnob == "off") {
-        _Instrument->setStringValue("status", "");
-    } else if (switchKnob == "stby") {
-        _Instrument->setStringValue("status", "STBY");
-    } else if (switchKnob == "tst") {
-        _Instrument->setStringValue("status", "TST");
-        // find something interesting to do...
-    } else {
-        float r = _Instrument->getFloatValue("range", 40.0);
-        if (r != _range_nm) {
-            center_map();
-            _range_nm = r;
-        }
-
-        _radar_ref_rng = _radar_ref_rng_node->getDoubleValue();
-        _view_heading = fgGetDouble("/orientation/heading-deg") * SG_DEGREES_TO_RADIANS;
-        _centerTrans.makeTranslate(0.0f, 0.0f, 0.0f);
-
-        _scale = 200.0 / _range_nm;
-        _angle_offset = 0;
-
-        if (_display_mode == ARC) {
-            _scale = 2*200.0f / _range_nm;
-            _angle_offset = -_view_heading;
-            _centerTrans.makeTranslate(0.0f, -200.0f, 0.0f);
-
-        } else if (_display_mode == MAP) {
-            apply_map_offset();
-
-            bool centre = _radar_centre_node->getBoolValue();
-            if (centre) {
-                center_map();
-                _radar_centre_node->setBoolValue(false);
-            }
-
-            //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: displacement "
-            //        << _x_offset <<", "<<_y_offset
-            //        << " user_speed_east_fps * SG_FPS_TO_KT "
-            //        << user_speed_east_fps * SG_FPS_TO_KT
-            //        << " user_speed_north_fps * SG_FPS_TO_KT "
-            //        << user_speed_north_fps * SG_FPS_TO_KT
-            //        << " dt " << delta_time_sec);
-
-            _centerTrans.makeTranslate(_x_offset, _y_offset, 0.0f);
-
-        } else if (_display_mode == PLAN) {
-            if (_radar_rotate_node->getBoolValue()) {
-                _angle_offset = -_view_heading;
-            }
-        } else if (_display_mode == BSCAN) {
-            _angle_offset = -_view_heading;
-        } else {
-            // rose
-        }
-
-        _vertices->clear();
-        _texCoords->clear();
-        _textGeode->removeDrawables(0, _textGeode->getNumDrawables());
-
-#if 0
-        //TODO FIXME Mask below (only used for ARC mode) isn't properly aligned, i.e.
-        // it assumes the a/c position at the center of the display - though it's somewhere at
-        // bottom part for ARC mode.
-        // The mask hadn't worked at all for a while (probably since the OSG port) due to
-        // another bug (which is fixed now). Now, the mask is disabled completely until s.o.
-        // adapted the coordinates below. And the mask is only really useful to limit displayed
-        // weather blobs (not support yet).
-        // Aircraft echos are already limited properly through wxradar's "limit-deg" property.
-        {
-            osg::DrawArrays *maskPSet
-                = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(1));
-            osg::DrawArrays *trimaskPSet
-                = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(2));
-
-            if (_display_mode == ARC) {
-                // erase what is out of sight of antenna
-                /*
-                |\     /|
-                | \   / |
-                |  \ /  |
-                ---------
-                |       |
-                |       |
-                ---------
-                */
-                float xOffset = 256.0f;
-                float yOffset = 200.0f;
-
-                int firstQuadVert = _vertices->size();
-                _texCoords->push_back(osg::Vec2f(0.5f, 0.25f));
-                _vertices->push_back(osg::Vec2f(-xOffset, 0.0 + yOffset));
-                _texCoords->push_back(osg::Vec2f(1.0f, 0.25f));
-                _vertices->push_back(osg::Vec2f(xOffset, 0.0 + yOffset));
-                _texCoords->push_back(osg::Vec2f(1.0f, 0.5f));
-                _vertices->push_back(osg::Vec2f(xOffset, 256.0 + yOffset));
-                _texCoords->push_back(osg::Vec2f(0.5f, 0.5f));
-                _vertices->push_back(osg::Vec2f(-xOffset, 256.0 + yOffset));
-                maskPSet->set(osg::PrimitiveSet::QUADS, firstQuadVert, 4);
-                firstQuadVert += 4;
-
-                // The triangles aren't supposed to be textured, but there's
-                // no need to set up a different Geometry, switch modes,
-                // etc. I happen to know that there's a white pixel in the
-                // texture at 1.0, 0.0 :)
-                float centerY = tan(30 * SG_DEGREES_TO_RADIANS);
-                _vertices->push_back(osg::Vec2f(0.0, 0.0));
-                _vertices->push_back(osg::Vec2f(-256.0, 0.0));
-                _vertices->push_back(osg::Vec2f(-256.0, 256.0 * centerY));
-
-                _vertices->push_back(osg::Vec2f(0.0, 0.0));
-                _vertices->push_back(osg::Vec2f(256.0, 0.0));
-                _vertices->push_back(osg::Vec2f(256.0, 256.0 * centerY));
-
-                _vertices->push_back(osg::Vec2f(-256, 0.0));
-                _vertices->push_back(osg::Vec2f(256.0, 0.0));
-                _vertices->push_back(osg::Vec2f(-256.0, -256.0));
-
-                _vertices->push_back(osg::Vec2f(256, 0.0));
-                _vertices->push_back(osg::Vec2f(256.0, -256.0));
-                _vertices->push_back(osg::Vec2f(-256.0, -256.0));
-
-                const osg::Vec2f whiteSpot(1.0f, 0.0f);
-                for (int i = 0; i < 3 * 4; i++)
-                    _texCoords->push_back(whiteSpot);
-
-                trimaskPSet->set(osg::PrimitiveSet::TRIANGLES, firstQuadVert, 3 * 4);
-
-            } else
-            {
-                maskPSet->set(osg::PrimitiveSet::QUADS, 0, 0);
-                trimaskPSet->set(osg::PrimitiveSet::TRIANGLES, 0, 0);
-            }
-
-            maskPSet->dirty();
-            trimaskPSet->dirty();
-        }
-#endif
-
-        // remember index of next vertex
-        int vIndex = _vertices->size();
-
-        update_weather();
-
-        osg::DrawArrays *quadPSet
-            = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(0));
-
-        update_aircraft();
-        update_tacan();
-        update_heading_marker();
-
-        // draw all new vertices are quads
-        quadPSet->set(osg::PrimitiveSet::QUADS, vIndex, _vertices->size()-vIndex);
-        quadPSet->dirty();
-    }
-}
-
-
-void
-wxRadarBg::update_weather()
-{
-    string modeButton = _Instrument->getStringValue("mode", "WX");
-// FIXME: implementation of radar echoes missing
-//    _radarEchoBuffer = *sgEnviro.get_radar_echo();
-
-    // pretend we have a scan angle bigger then the FOV
-    // TODO:check real fov, enlarge if < nn, and do clipping if > mm
-//    const float fovFactor = 1.45f;
-    _Instrument->setStringValue("status", modeButton.c_str());
-
-// FIXME: implementation of radar echoes missing
-#if 0
-    list_of_SGWxRadarEcho *radarEcho = &_radarEchoBuffer;
-    list_of_SGWxRadarEcho::iterator iradarEcho, end = radarEcho->end();
-    const float LWClevel[] = { 0.1f, 0.5f, 2.1f };
-
-    // draw the cloud radar echo
-    bool drawClouds = _radar_weather_node->getBoolValue();
-    if (drawClouds) {
-
-        // we do that in 3 passes, one for each color level
-        // this is to 'merge' same colors together
-        for (int level = 0; level <= 2; level++) {
-            float col = level * UNIT;
-
-            for (iradarEcho = radarEcho->begin(); iradarEcho != end; ++iradarEcho) {
-                int cloudId = iradarEcho->cloudId;
-                bool upgrade = (cloudId >> 5) & 1;
-                float lwc = iradarEcho->LWC + (upgrade ? 1.0f : 0.0f);
-
-                // skip ns
-                if (iradarEcho->LWC >= 0.5 && iradarEcho->LWC <= 0.6)
-                    continue;
-
-                if (iradarEcho->lightning || lwc < LWClevel[level])
-                    continue;
-
-                float radius = sqrt(iradarEcho->dist) * SG_METER_TO_NM * _scale;
-                float size = iradarEcho->radius * 2.0 * SG_METER_TO_NM * _scale;
-
-                if (radius - size > 180)
-                    continue;
-
-                float angle = (iradarEcho->heading - _angle_offset) //* fovFactor
-                    + 0.5 * SG_PI;
-
-                // Rotate echo into position, and rotate echo to have
-                // a constant orientation towards the
-                // airplane. Compass headings increase in clockwise
-                // direction, while graphics rotations follow
-                // right-hand (counter-clockwise) rule.
-                const osg::Vec2f texBase(col, (UNIT * (float) (4 + (cloudId & 3))));
-
-                osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
-                    * osg::Matrixf::translate(0.0f, radius, 0.0f)
-                    * wxRotate(angle) * _centerTrans);
-                addQuad(_vertices, _texCoords, m, texBase);
-
-                //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: drawing clouds"
-                //        << " ID=" << cloudId
-                //        << " x=" << x
-                //        << " y="<< y
-                //        << " radius=" << radius
-                //        << " view_heading=" << _view_heading * SG_RADIANS_TO_DEGREES
-                //        << " heading=" << iradarEcho->heading * SG_RADIANS_TO_DEGREES
-                //        << " angle=" << angle * SG_RADIANS_TO_DEGREES);
-            }
-        }
-    }
-
-    // draw lightning echos
-    bool drawLightning = _Instrument->getBoolValue("lightning", true);
-    if (drawLightning) {
-        const osg::Vec2f texBase(3 * UNIT, 4 * UNIT);
-
-        for (iradarEcho = radarEcho->begin(); iradarEcho != end; ++iradarEcho) {
-            if (!iradarEcho->lightning)
-                continue;
-
-            float size = UNIT * 0.5f;
-            float radius = iradarEcho->dist * _scale;
-            float angle = iradarEcho->heading * SG_DEGREES_TO_RADIANS
-                - _angle_offset;
-
-            osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
-                * wxRotate(-angle)
-                * osg::Matrixf::translate(0.0f, radius, 0.0f)
-                * wxRotate(angle) * _centerTrans);
-            addQuad(_vertices, _texCoords, m, texBase);
-        }
-    }
-#endif
-}
-
-
-void
-wxRadarBg::update_data(const SGPropertyNode *ac, double altitude, double heading,
-                       double radius, double bearing, bool selected)
-{
-    osgText::Text *callsign = new osgText::Text;
-    callsign->setFont(_font.get());
-    callsign->setFontResolution(12, 12);
-    callsign->setCharacterSize(_font_size);
-    callsign->setColor(selected ? osg::Vec4(1, 1, 1, 1) : _font_color);
-    osg::Matrixf m(wxRotate(-bearing)
-        * osg::Matrixf::translate(0.0f, radius, 0.0f)
-        * wxRotate(bearing) * _centerTrans);
-
-    osg::Vec3 pos = m.preMult(osg::Vec3(16, 16, 0));
-    // cast to int's, otherwise text comes out ugly
-    callsign->setPosition(osg::Vec3((int)pos.x(), (int)pos.y(), 0));
-    callsign->setAlignment(osgText::Text::LEFT_BOTTOM_BASE_LINE);
-    callsign->setLineSpacing(_font_spacing);
-
-    const char *identity = ac->getStringValue("transponder-id");
-    if (!identity[0])
-        identity = ac->getStringValue("callsign");
-
-    stringstream text;
-    text << identity << endl
-        << setprecision(0) << fixed
-        << setw(3) << setfill('0') << heading * SG_RADIANS_TO_DEGREES << "\xB0 "
-        << setw(0) << altitude << "ft" << endl
-        << ac->getDoubleValue("velocities/true-airspeed-kt") << "kts";
-
-    callsign->setText(text.str());
-    _textGeode->addDrawable(callsign);
-}
-
-
-void
-wxRadarBg::update_aircraft()
-{
-    double diff;
-    double age_factor = 1.0;
-    double test_rng;
-    double test_brg;
-    double range;
-    double bearing;
-    float echo_radius;
-    double angle;
-
-    if (!ground_echoes.empty()){
-        ground_echoes_iterator = ground_echoes.begin();
-
-        while(ground_echoes_iterator != ground_echoes.end()) {
-            diff = _elapsed_time - (*ground_echoes_iterator)->elapsed_time;
-
-            if( diff > _persistance) {
-                ground_echoes.erase(ground_echoes_iterator++);
-            } else {
-//                double test_brg = (*ground_echoes_iterator)->bearing;
-//                double bearing = test_brg * SG_DEGREES_TO_RADIANS;
-//                float angle = calcRelBearing(bearing, _view_heading);
-                double bumpinessFactor  = (*ground_echoes_iterator)->bumpiness;
-                float heading = fgGetDouble("/orientation/heading-deg");
-                if ( _display_mode == BSCAN ){
-                    test_rng = (*ground_echoes_iterator)->elevation * 6;
-                    test_brg = (*ground_echoes_iterator)->bearing;
-                    angle = calcRelBearingDeg(test_brg, heading) * 6;
-                    range = sqrt(test_rng * test_rng + angle * angle);
-                    bearing = atan2(angle, test_rng);
-                    //cout << "angle " << angle <<" bearing "
-                    //    << bearing / SG_DEGREES_TO_RADIANS <<  endl;
-                    echo_radius = (0.1 + (1.9 * bumpinessFactor)) * 240 * age_factor;
-                } else {
-                    test_rng = (*ground_echoes_iterator)->range;
-                    range = test_rng * SG_METER_TO_NM;
-                    test_brg = (*ground_echoes_iterator)->bearing;
-                    bearing = test_brg * SG_DEGREES_TO_RADIANS;
-                    echo_radius = (0.1 + (1.9 * bumpinessFactor)) * 120 * age_factor;
-                    bearing += _angle_offset;
-                }
-
-                float radius = range * _scale;
-                //double heading = 90 * SG_DEGREES_TO_RADIANS;
-                //heading += _angle_offset;
-
-                age_factor = 1;
-
-                if (diff != 0)
-                    age_factor = 1 - (0.5 * diff/_persistance);
-
-                float size = echo_radius * UNIT;
-
-                const osg::Vec2f texBase(3 * UNIT, 3 * UNIT);
-                osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
-                    * osg::Matrixf::translate(0.0f, radius, 0.0f)
-                    * wxRotate(bearing) * _centerTrans);
-                addQuad(_vertices, _texCoords, m, texBase);
-
-                ++ground_echoes_iterator;
-
-                //cout << "test bearing " << test_brg 
-                //<< " test_rng " << test_rng * SG_METER_TO_NM
-                //<< " persistance " << _persistance
-                //<< endl;
-            }
-
-        }
-
-    }
-    if (!_ai_enabled_node->getBoolValue())
-        return;
-
-    bool draw_tcas     = _radar_tcas_node->getBoolValue();
-    bool draw_absolute = _radar_absalt_node->getBoolValue();
-    bool draw_echoes   = _radar_position_node->getBoolValue();
-    bool draw_symbols  = _radar_symbol_node->getBoolValue();
-    bool draw_data     = _radar_data_node->getBoolValue();
-    if (!draw_echoes && !draw_symbols && !draw_data)
-        return;
-
-    double user_lat = _user_lat_node->getDoubleValue();
-    double user_lon = _user_lon_node->getDoubleValue();
-    double user_alt = _user_alt_node->getDoubleValue();
-
-    float limit = _radar_coverage_node->getFloatValue();
-    if (limit > 180)
-        limit = 180;
-    else if (limit < 0)
-        limit = 0;
-    limit *= SG_DEGREES_TO_RADIANS;
-
-    int selected_id = fgGetInt("/instrumentation/radar/selected-id", -1);
-
-    const SGPropertyNode *selected_ac = 0;
-    const SGPropertyNode *ai = fgGetNode("/ai/models", true);
-
-    for (int i = ai->nChildren() - 1; i >= -1; i--) {
-        const SGPropertyNode *model;
-
-        if (i < 0) { // last iteration: selected model
-            model = selected_ac;
-        } else {
-            model = ai->getChild(i);
-            if (!model->nChildren())
-                continue;
-            if ((model->getIntValue("id") == selected_id)&&
-                (!draw_tcas)) {
-                selected_ac = model;  // save selected model for last iteration
-                continue;
-            }
-        }
-        if (!model)
-            continue;
-
-        double echo_radius, sigma;
-        const string name = model->getName();
-
-        //cout << "name "<<name << endl;
-        if (name == "aircraft" || name == "tanker")
-            echo_radius = 1, sigma = 1;
-        else if (name == "multiplayer" || name == "wingman" || name == "static")
-            echo_radius = 1.5, sigma = 1;
-        else if (name == "ship" || name == "carrier" || name == "escort" ||name == "storm")
-            echo_radius = 1.5, sigma = 100;
-        else if (name == "thermal")
-            echo_radius = 2, sigma = 100;
-        else if (name == "rocket")
-            echo_radius = 0.1, sigma = 0.1;
-        else if (name == "ballistic")
-            echo_radius = 0.001, sigma = 0.001;
-        else
-            continue;
-
-        double lat = model->getDoubleValue("position/latitude-deg");
-        double lon = model->getDoubleValue("position/longitude-deg");
-        double alt = model->getDoubleValue("position/altitude-ft");
-        double heading = model->getDoubleValue("orientation/true-heading-deg");
-
-        double range, bearing;
-        calcRangeBearing(user_lat, user_lon, lat, lon, range, bearing);
-        //cout << _antenna_ht << _interval<< endl;
-        bool isVisible = withinRadarHorizon(user_alt, alt, range);
-
-        if (!isVisible)
-            continue;
-
-        if (!inRadarRange(sigma, range))
-            continue;
-
-        bearing *= SG_DEGREES_TO_RADIANS;
-        heading *= SG_DEGREES_TO_RADIANS;
-
-        float radius = range * _scale;
-        float angle = calcRelBearing(bearing, _view_heading);
-
-        if (angle > limit || angle < -limit)
-            continue;
-
-        bearing += _angle_offset;
-        heading += _angle_offset;
-
-        bool is_tcas_contact = false;
-        if (draw_tcas)
-        {
-            is_tcas_contact = update_tcas(model,range,user_alt,alt,bearing,radius,draw_absolute);
-        }
-
-        // pos mode
-        if (draw_echoes && (!is_tcas_contact)) {
-            float size = echo_radius * 120 * UNIT;
-
-            const osg::Vec2f texBase(3 * UNIT, 3 * UNIT);
-            osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
-                * osg::Matrixf::translate(0.0f, radius, 0.0f)
-                * wxRotate(bearing) * _centerTrans);
-            addQuad(_vertices, _texCoords, m, texBase);
-        }
-
-        // data mode
-        if (draw_symbols && (!draw_tcas)) {
-            const osg::Vec2f texBase(0, 3 * UNIT);
-            float size = 600 * UNIT;
-            osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
-                * wxRotate(heading - bearing)
-                * osg::Matrixf::translate(0.0f, radius, 0.0f)
-                * wxRotate(bearing) * _centerTrans);
-            addQuad(_vertices, _texCoords, m, texBase);
-        }
-
-        if ((draw_data || i < 0)&&  // selected one (i == -1) is always drawn
-            ((!draw_tcas)||(is_tcas_contact)||(draw_echoes)))
-            update_data(model, alt, heading, radius, bearing, i < 0);
-    }
-}
-
-/** Update TCAS display.
- * Return true when processed as TCAS contact, false otherwise. */
-bool
-wxRadarBg::update_tcas(const SGPropertyNode *model,double range,double user_alt,double alt,
-                       double bearing,double radius,bool absMode)
-{
-    int threatLevel=0;
-    {
-        // update TCAS symbol
-        osg::Vec2f texBase;
-        threatLevel = model->getIntValue("tcas/threat-level",-1);
-        if (threatLevel == -1)
-        {
-            // no TCAS information (i.e. no transponder) => not visible to TCAS
-            return false;
-        }
-        int row = 7 - threatLevel;
-        int col = 4;
-        double vspeed = model->getDoubleValue("velocities/vertical-speed-fps");
-        if (vspeed < -3.0) // descending
-            col+=1;
-        else
-        if (vspeed > 3.0) // climbing
-            col+=2;
-        texBase = osg::Vec2f(col*UNIT,row * UNIT);
-        float size = 200 * UNIT;
-            osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
-                * wxRotate(-bearing)
-                * osg::Matrixf::translate(0.0f, radius, 0.0f)
-                * wxRotate(bearing) * _centerTrans);
-            addQuad(_vertices, _texCoords, m, texBase);
-    }
-
-    {
-        // update TCAS data
-        osgText::Text *altStr = new osgText::Text;
-        altStr->setFont(_font.get());
-        altStr->setFontResolution(12, 12);
-        altStr->setCharacterSize(_font_size);
-        altStr->setColor(_tcas_colors[threatLevel]);
-        osg::Matrixf m(wxRotate(-bearing)
-            * osg::Matrixf::translate(0.0f, radius, 0.0f)
-            * wxRotate(bearing) * _centerTrans);
-    
-        osg::Vec3 pos = m.preMult(osg::Vec3(16, 16, 0));
-        // cast to int's, otherwise text comes out ugly
-        altStr->setLineSpacing(_font_spacing);
-    
-        stringstream text;
-        altStr->setAlignment(osgText::Text::LEFT_CENTER);
-        int altDif = (alt-user_alt+50)/100;
-        char sign = 0;
-        int dy=0;
-        if (altDif>=0)
-        {
-            sign='+';
-            dy=2;
-        }
-        else
-        if (altDif<0)
-        {
-            sign='-';
-            altDif = -altDif;
-            dy=-30;
-        }
-        altStr->setPosition(osg::Vec3((int)pos.x()-30, (int)pos.y()+dy, 0));
-        if (absMode)
-        {
-            // absolute altitude display
-            text << setprecision(0) << fixed
-                 << setw(3) << setfill('0') << alt/100 << endl;
-        }
-        else // relative altitude display
-        if (sign)
-        {
-            text << sign
-                 << setprecision(0) << fixed
-                 << setw(2) << setfill('0') << altDif << endl;
-        }
-    
-        altStr->setText(text.str());
-        _textGeode->addDrawable(altStr);
-    }
-
-    return true;
-}
-
-void
-wxRadarBg::update_tacan()
-{
-    // draw TACAN symbol
-    int mode = _radar_mode_control_node->getIntValue();
-    bool inRange = _tacan_in_range_node->getBoolValue();
-
-    if (mode != 1 || !inRange)
-        return;
-
-    float size = 600 * UNIT;
-    float radius = _tacan_distance_node->getFloatValue() * _scale;
-    float angle = _tacan_bearing_node->getFloatValue() * SG_DEGREES_TO_RADIANS
-        + _angle_offset;
-
-    const osg::Vec2f texBase(1 * UNIT, 3 * UNIT);
-    osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
-        * wxRotate(-angle)
-        * osg::Matrixf::translate(0.0f, radius, 0.0f)
-        * wxRotate(angle) * _centerTrans);
-    addQuad(_vertices, _texCoords, m, texBase);
-
-    //SG_LOG(SG_INSTR, SG_DEBUG, "Radar:     drawing TACAN"
-    //        << " dist=" << radius
-    //        << " view_heading=" << _view_heading * SG_RADIANS_TO_DEGREES
-    //        << " bearing=" << angle * SG_RADIANS_TO_DEGREES
-    //        << " x=" << x << " y="<< y
-    //        << " size=" << size);
-}
-
-
-void
-wxRadarBg::update_heading_marker()
-{
-    if (!_radar_hdg_marker_node->getBoolValue())
-        return;
-
-    const osg::Vec2f texBase(2 * UNIT, 3 * UNIT);
-    float size = 600 * UNIT;
-    osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
-        * wxRotate(_view_heading + _angle_offset));
-
-    m *= _centerTrans;
-    addQuad(_vertices, _texCoords, m, texBase);
-
-    //SG_LOG(SG_INSTR, SG_DEBUG, "Radar:   drawing heading marker"
-    //        << " x,y " << x <<","<< y
-    //        << " dist" << dist
-    //        << " view_heading" << _view_heading * SG_RADIANS_TO_DEGREES
-    //        << " heading " << iradarEcho->heading * SG_RADIANS_TO_DEGREES
-    //        << " angle " << angle * SG_RADIANS_TO_DEGREES);
-}
-
-
-void
-wxRadarBg::center_map()
-{
-    _lat = _user_lat_node->getDoubleValue();
-    _lon = _user_lon_node->getDoubleValue();
-    _x_offset = _y_offset = 0;
-}
-
-
-void
-wxRadarBg::apply_map_offset()
-{
-    double lat = _user_lat_node->getDoubleValue();
-    double lon = _user_lon_node->getDoubleValue();
-    double bearing, distance, az2;
-    geo_inverse_wgs_84(_lat, _lon, lat, lon, &bearing, &az2, &distance);
-    distance *= SG_METER_TO_NM * _scale;
-    bearing *= SG_DEGREES_TO_RADIANS;
-    _x_offset += sin(bearing) * distance;
-    _y_offset += cos(bearing) * distance;
-    _lat = lat;
-    _lon = lon;
-}
-
-
-bool
-wxRadarBg::withinRadarHorizon(double user_alt, double alt, double range_nm)
-{
-    // Radar Horizon  = 1.23(ht^1/2 + hr^1/2),
-    //don't allow negative altitudes (an approximation - yes altitudes can be negative)
-    // Allow antenna ht to be set, but only on ground
-    _antenna_ht = _Instrument->getDoubleValue("antenna-ht-ft");
-
-    if (user_alt <= 0)
-        user_alt = _antenna_ht;
-
-    if (alt <= 0)
-        alt = 0; // to allow some vertical extent of target
-
-    double radarhorizon = 1.23 * (sqrt(alt) + sqrt(user_alt));
-//    SG_LOG(SG_INSTR, SG_ALERT, "Radar: radar horizon " << radarhorizon);
-    return radarhorizon >= range_nm;
-}
-
-
-bool
-wxRadarBg::inRadarRange(double sigma, double range_nm)
-{
-    //The Radar Equation:
-    //
-    // MaxRange^4 = (TxPower * AntGain^2 * lambda^2 * sigma)/((constant) * MDS)
-    //
-    // Where (constant) = (4*pi)3 and MDS is the Minimum Detectable Signal power.
-    //
-    // For a given radar we can assume that the only variable is sigma,
-    // the target radar cross section.
-    //
-    // Here, we will use a normalised rcs (sigma) for a standard taget and assume that this
-    // will provide a maximum range of 35nm;
-    //
-    // TODO - make the maximum range adjustable at runtime
-
-    double constant = _radar_ref_rng;
-
-    if (constant <= 0)
-        constant = 35;
-
-    double maxrange = constant * pow(sigma, 0.25);
-    //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: max range " << maxrange);
-    return maxrange >= range_nm;
-}
-
-
-void
-wxRadarBg::calcRangeBearing(double lat, double lon, double lat2, double lon2,
-                            double &range, double &bearing) const
-{
-    // calculate the bearing and range of the second pos from the first
-    double az2, distance;
-    geo_inverse_wgs_84(lat, lon, lat2, lon2, &bearing, &az2, &distance);
-    range = distance *= SG_METER_TO_NM;
-}
-
-
-float
-wxRadarBg::calcRelBearing(float bearing, float heading)
-{
-    float angle = bearing - heading;
-
-    if (angle >= SG_PI)
-        angle -= 2.0 * SG_PI;
-
-    if (angle < -SG_PI)
-        angle += 2.0 * SG_PI;
-
-    return angle;
-}
-
-float
-wxRadarBg::calcRelBearingDeg(float bearing, float heading)
-{
-    float angle = bearing - heading;
-
-    if (angle >= 180)
-        return angle -= 360;
-
-    if (angle < -180)
-        return angle += 360;
-
-    return angle;
-}
-
-
-void
-wxRadarBg::updateFont()
-{
-    float red = _font_node->getFloatValue("color/red");
-    float green = _font_node->getFloatValue("color/green");
-    float blue = _font_node->getFloatValue("color/blue");
-    float alpha = _font_node->getFloatValue("color/alpha");
-    _font_color.set(red, green, blue, alpha);
-
-    _font_size = _font_node->getFloatValue("size");
-    _font_spacing = _font_size * _font_node->getFloatValue("line-spacing");
-    string path = _font_node->getStringValue("name", DEFAULT_FONT);
-
-    SGPath tpath;
-    if (path[0] != '/') {
-        tpath = globals->get_fg_root();
-        tpath.append("Fonts");
-        tpath.append(path);
-    } else {
-        tpath = path;
-    }
-
-#if (FG_OSG_VERSION >= 21000)
-    osg::ref_ptr<osgDB::ReaderWriter::Options> fontOptions = new osgDB::ReaderWriter::Options("monochrome");
-    osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str(), fontOptions.get());
-#else
-    osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str());
-#endif
-
-    if (font != 0) {
-        _font = font;
-        _font->setMinFilterHint(osg::Texture::NEAREST);
-        _font->setMagFilterHint(osg::Texture::NEAREST);
-        _font->setGlyphImageMargin(0);
-        _font->setGlyphImageMarginRatio(0);
-    }
-
-    for (int i=0;i<4;i++)
-    {
-        const float defaultColors[4][3] = {{0,1,1},{0,1,1},{1,0.5,0},{1,0,0}};
-        SGPropertyNode_ptr color_node = _font_node->getNode("tcas/color",i,true);
-        float red   = color_node->getFloatValue("red",defaultColors[i][0]);
-        float green = color_node->getFloatValue("green",defaultColors[i][1]);
-        float blue  = color_node->getFloatValue("blue",defaultColors[i][2]);
-        float alpha = color_node->getFloatValue("alpha",1);
-        _tcas_colors[i]=osg::Vec4(red, green, blue, alpha);
-    }
-}
-
-void
-wxRadarBg::valueChanged(SGPropertyNode*)
-{
-    updateFont();
-    _time = _interval;
-}
-
diff --git a/src/Instrumentation/wxradar.hxx b/src/Instrumentation/wxradar.hxx
deleted file mode 100644 (file)
index 7aff777..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-// Wx Radar background texture
-//
-// Written by Harald JOHNSEN, started May 2005.
-// With major amendments by Vivian MEAZZA May 2007
-// Ported to OSG by Tim MOORE Jun 2007
-//
-// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-
-#ifndef _INST_WXRADAR_HXX
-#define _INST_WXRADAR_HXX
-
-#include <osg/ref_ptr>
-#include <osg/Geode>
-#include <osg/Texture2D>
-#include <osgText/Text>
-
-#include <simgear/props/props.hxx>
-#include <simgear/structure/subsystem_mgr.hxx>
-
-#include <vector>
-#include <string>
-
-class FGODGauge;
-
-class wxRadarBg : public SGSubsystem, public SGPropertyChangeListener {
-public:
-
-    wxRadarBg(SGPropertyNode *node);
-    wxRadarBg();
-    virtual ~wxRadarBg();
-
-    virtual void init();
-    virtual void update(double dt);
-    virtual void valueChanged(SGPropertyNode *);
-
-protected:
-    std::string _name;
-    int _num;
-    double _time;
-    double _interval;
-    double _elapsed_time;
-    double _persistance;
-
-    SGPropertyNode_ptr _serviceable_node;
-    SGPropertyNode_ptr _sceneryLoaded;
-    SGPropertyNode_ptr _Instrument;
-    SGPropertyNode_ptr _radar_mode_control_node;
-
-    SGPropertyNode_ptr _user_lat_node;
-    SGPropertyNode_ptr _user_lon_node;
-    SGPropertyNode_ptr _user_heading_node;
-    SGPropertyNode_ptr _user_alt_node;
-
-    FGODGauge *_odg;
-
-    typedef struct {
-        double bearing;
-        double range;
-        double elevation;
-        double bumpiness;
-        double elapsed_time;
-    }ground_echo;
-
-    typedef std::vector <ground_echo*> ground_echo_vector_type;
-    typedef ground_echo_vector_type::iterator ground_echo_vector_iterator;
-
-    ground_echo_vector_type       ground_echoes;
-    ground_echo_vector_iterator   ground_echoes_iterator;
-
-    // Convenience function for creating a property node with a
-    // default value
-    template<typename DefaultType>
-    SGPropertyNode *getInstrumentNode(const char *name, DefaultType value);
-
-private:
-    std::string _texture_path;
-
-    typedef enum { ARC, MAP, PLAN, ROSE, BSCAN} DisplayMode;
-    DisplayMode _display_mode;
-
-    float _range_nm;
-    float _scale;   // factor to convert nm to display units
-    float _angle_offset;
-    float _view_heading;
-    float _x_offset, _y_offset;
-
-    double _radar_ref_rng;
-    double _lat, _lon;
-    double _antenna_ht;
-
-    SGPropertyNode_ptr _Tacan;
-    SGPropertyNode_ptr _Radar_controls;
-
-    SGPropertyNode_ptr _user_speed_east_fps_node;
-    SGPropertyNode_ptr _user_speed_north_fps_node;
-
-    SGPropertyNode_ptr _tacan_serviceable_node;
-    SGPropertyNode_ptr _tacan_distance_node;
-    SGPropertyNode_ptr _tacan_name_node;
-    SGPropertyNode_ptr _tacan_bearing_node;
-    SGPropertyNode_ptr _tacan_in_range_node;
-
-    SGPropertyNode_ptr _radar_weather_node;
-    SGPropertyNode_ptr _radar_position_node;
-    SGPropertyNode_ptr _radar_data_node;
-    SGPropertyNode_ptr _radar_symbol_node;
-
-    SGPropertyNode_ptr _radar_centre_node;
-    SGPropertyNode_ptr _radar_coverage_node;
-    SGPropertyNode_ptr _radar_ref_rng_node;
-    SGPropertyNode_ptr _radar_hdg_marker_node;
-    SGPropertyNode_ptr _radar_rotate_node;
-    SGPropertyNode_ptr _radar_tcas_node;
-    SGPropertyNode_ptr _radar_absalt_node;
-
-    SGPropertyNode_ptr _font_node;
-    SGPropertyNode_ptr _ai_enabled_node;
-
-    osg::ref_ptr<osg::Texture2D> _resultTexture;
-    osg::ref_ptr<osg::Texture2D> _wxEcho;
-    osg::ref_ptr<osg::Geode> _radarGeode;
-    osg::ref_ptr<osg::Geode> _textGeode;
-    osg::Geometry *_geom;
-    osg::Vec2Array *_vertices;
-    osg::Vec2Array *_texCoords;
-    osg::Matrixf _centerTrans;
-    osg::ref_ptr<osgText::Font> _font;
-    osg::Vec4 _font_color;
-    osg::Vec4 _tcas_colors[4];
-    float _font_size;
-    float _font_spacing;
-
-// FIXME: implementation of radar echoes missing
-//    list_of_SGWxRadarEcho _radarEchoBuffer;
-
-    void update_weather();
-    void update_aircraft();
-    void update_tacan();
-    void update_heading_marker();
-    void update_data(const SGPropertyNode *ac, double alt, double heading,
-        double radius, double bearing, bool selected);
-    bool update_tcas(const SGPropertyNode *model,double range,double user_alt,double alt,
-                     double bearing,double radius, bool absMode);
-    void center_map();
-    void apply_map_offset();
-    void updateFont();
-    void calcRangeBearing(double lat, double lon, double lat2, double lon2,
-        double &range, double &bearing) const;
-
-    bool withinRadarHorizon(double user_alt, double alt, double range);
-    bool inRadarRange(double sigma, double range);
-
-    float calcRelBearing(float bearing, float heading);
-    float calcRelBearingDeg(float bearing, float heading);
-};
-
-
-template<> inline
-SGPropertyNode *wxRadarBg::getInstrumentNode(const char *name, bool value)
-{
-    SGPropertyNode *result = _Instrument->getNode(name, true);
-    if (!result->hasValue())
-        result->setBoolValue(value);
-    return result;
-}
-
-
-template<> inline
-SGPropertyNode *wxRadarBg::getInstrumentNode(const char *name, double value)
-{
-    SGPropertyNode *result = _Instrument->getNode(name, true);
-    if (!result->hasValue())
-        result->setDoubleValue(value);
-    return result;
-}
-
-
-template<> inline
-SGPropertyNode *wxRadarBg::getInstrumentNode(const char *name, const char *value)
-{
-    SGPropertyNode *result = _Instrument->getNode(name, true);
-    if (result->hasValue())
-        result->setStringValue(value);
-    return result;
-}
-
-
-#endif // _INST_WXRADAR_HXX
index 1def26fe196c996b12330628cc9246912cde7222..9e8c4d83b131ee61f235adb72e34b136c03b7228 100644 (file)
@@ -99,6 +99,7 @@
 #include <Viewer/viewmgr.hxx>
 #include <Navaids/NavDataCache.hxx>
 #include <Instrumentation/HUD/HUD.hxx>
+#include <Cockpit/cockpitDisplayManager.hxx>
 
 #include "fg_init.hxx"
 #include "fg_io.hxx"
@@ -607,6 +608,7 @@ void fgCreateSubsystems() {
     globals->add_subsystem("systems", new FGSystemMgr, SGSubsystemMgr::FDM);
     globals->add_subsystem("instrumentation", new FGInstrumentMgr, SGSubsystemMgr::FDM);
     globals->add_subsystem("hud", new HUD, SGSubsystemMgr::DISPLAY);
+    globals->add_subsystem("cockpit-displays", new flightgear::CockpitDisplayManager, SGSubsystemMgr::DISPLAY);
   
     ////////////////////////////////////////////////////////////////////
     // Initialize the XML Autopilot subsystem.