#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>
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
)
--- /dev/null
+// 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);
+}
+
--- /dev/null
+// 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
--- /dev/null
+// 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;
+
+ }
+ }
+}
+
+
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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
--- /dev/null
+// 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;
+}
+
--- /dev/null
+// 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
set(SOURCES
adf.cxx
- agradar.cxx
airspeed_indicator.cxx
altimeter.cxx
attitude_indicator.cxx
dclgps.cxx
dme.cxx
gps.cxx
- groundradar.cxx
gsdi.cxx
gyro.cxx
heading_indicator.cxx
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
transponder.cxx
turn_indicator.cxx
vertical_speed_indicator.cxx
- wxradar.cxx
- NavDisplay.cxx
HUD/HUD.cxx
HUD/HUD_dial.cxx
HUD/HUD_gauge.cxx
set(HEADERS
adf.hxx
- agradar.hxx
airspeed_indicator.hxx
altimeter.hxx
attitude_indicator.hxx
dclgps.hxx
dme.hxx
gps.hxx
- groundradar.hxx
gsdi.hxx
gyro.hxx
heading_indicator.hxx
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
transponder.hxx
turn_indicator.hxx
vertical_speed_indicator.hxx
- wxradar.hxx
- NavDisplay.hxx
HUD/HUD.hxx
HUD/HUD_private.hxx
KLN89/kln89.hxx
+++ /dev/null
-// 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);
-}
-
+++ /dev/null
-// 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
+++ /dev/null
-// 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;
-
- }
- }
-}
-
-
+++ /dev/null
-// 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
#ifndef _DCLGPS_HXX
#define _DCLGPS_HXX
-#include "render_area_2d.hxx"
+#include <Cockpit/render_area_2d.hxx>
+
#include <string>
#include <list>
#include <vector>
+++ /dev/null
-// 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
+++ /dev/null
-// 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
#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)
if (index > 0)
subsystemname << '['<< index << ']';
string id = subsystemname.str();
- _instruments.push_back(id);
if ( name == "adf" ) {
set_subsystem( id, new ADF( node ), 0.15 );
} 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 ) );
} 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;
}
+++ /dev/null
-// 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();
-}
+++ /dev/null
-// 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
#include <Scenery/scenery.hxx>
#include <simgear/scene/material/mat.hxx>
-#include "agradar.hxx"
+#include <Cockpit/agradar.hxx>
class radAlt : public agRadar{
public:
+++ /dev/null
-// 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();
-}
+++ /dev/null
-// 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
+++ /dev/null
-// 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;
-}
-
+++ /dev/null
-// 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
#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"
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.