From: James Turner Date: Wed, 26 Sep 2012 19:30:24 +0000 (+0100) Subject: Separate instruments from cockpit displays. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=4994973ef4d5a079acf76267b525313c15376478;p=flightgear.git Separate instruments from cockpit displays. --- diff --git a/src/Canvas/canvas.hxx b/src/Canvas/canvas.hxx index 25cd8e816..207145299 100644 --- a/src/Canvas/canvas.hxx +++ b/src/Canvas/canvas.hxx @@ -23,7 +23,7 @@ #include "property_based_element.hxx" #include -#include +#include #include #include diff --git a/src/Cockpit/CMakeLists.txt b/src/Cockpit/CMakeLists.txt index d68022e77..42b03bac2 100644 --- a/src/Cockpit/CMakeLists.txt +++ b/src/Cockpit/CMakeLists.txt @@ -1,15 +1,29 @@ include(FlightGearComponent) set(SOURCES + cockpitDisplayManager.cxx panel.cxx panel_io.cxx built_in/FGMagRibbon.cxx + agradar.cxx + groundradar.cxx + od_gauge.cxx + render_area_2d.cxx + wxradar.cxx + NavDisplay.cxx ) set(HEADERS + cockpitDisplayManager.hxx panel.hxx panel_io.hxx built_in/FGMagRibbon.hxx + agradar.hxx + groundradar.hxx + od_gauge.hxx + render_area_2d.hxx + wxradar.hxx + NavDisplay.hxx ) diff --git a/src/Cockpit/NavDisplay.cxx b/src/Cockpit/NavDisplay.cxx new file mode 100644 index 000000000..15452a2e9 --- /dev/null +++ b/src/Cockpit/NavDisplay.cxx @@ -0,0 +1,1433 @@ +// navigation display texture +// +// Written by James Turner, forked from wxradar code +// +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "NavDisplay.hxx" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include // 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
+#include
+#include "panel.hxx" +#include +#include +#include +#include +#include +#include +#include +#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 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 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 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(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 fontOptions = new osgDB::ReaderWriter::Options("monochrome"); + osg::ref_ptr 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; lnumLegs(); ++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; igetDefinition()->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(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(pos); + heading = rwy->headingDeg(); + nd->setDoubleValue("heading-deg", heading); + nd->setIntValue("length-ft", rwy->lengthFt()); + nd->setStringValue("airport", rwy->airport()->ident()); + break; + } + + default: + break; + } +} + +void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states) +{ + if (_routeSources.count(pos) != 0) { + states.insert("on-active-route"); + } + + flightgear::FlightPlan* fp = _route->flightPlan(); + switch (pos->type()) { + case FGPositioned::VOR: + case FGPositioned::LOC: + if (pos == _nav1Station) { + states.insert("tuned"); + states.insert("nav1"); + } + + if (pos == _nav2Station) { + states.insert("tuned"); + states.insert("nav2"); + } + break; + + case FGPositioned::AIRPORT: + case FGPositioned::SEAPORT: + case FGPositioned::HELIPORT: + // mark alternates! + // once the FMS system has some way to tell us about them, of course + + if (pos == fp->departureAirport()) { + states.insert("departure"); + } + + if (pos == fp->destinationAirport()) { + states.insert("destination"); + } + break; + + case FGPositioned::RUNWAY: + if (pos == fp->departureRunway()) { + states.insert("departure"); + } + + if (pos == fp->destinationRunway()) { + states.insert("destination"); + } + break; + + case FGPositioned::OBSTACLE: + #if 0 + FGObstacle* obs = (FGObstacle*) pos; + if (obj->isLit()) { + states.insert("lit"); + } + + if (obj->getHeightAGLFt() >= 1000) { + states.insert("greater-1000-ft"); + } + #endif + break; + + default: + break; + } // FGPositioned::Type switch +} + +static string mapAINodeToType(SGPropertyNode* model) +{ + // assume all multiplayer items are aircraft for the moment. Not ideal. + if (!strcmp(model->getName(), "multiplayer")) { + return "ai-aircraft"; + } + + return string("ai-") + model->getName(); +} + +void NavDisplay::processAI() +{ + SGPropertyNode *ai = fgGetNode("/ai/models", true); + for (int i = ai->nChildren() - 1; i >= 0; i--) { + SGPropertyNode *model = ai->getChild(i); + if (!model->nChildren()) { + continue; + } + + // prefix types with 'ai-', to avoid any chance of namespace collisions + // with fg-positioned. + string_set ss; + computeAIStates(model, ss); + SymbolRuleVector rules; + findRules(mapAINodeToType(model), ss, rules); + if (rules.empty()) { + return; // no rules matched, we can skip this item + } + + double heading = model->getDoubleValue("orientation/true-heading-deg"); + SGGeod aiModelPos = SGGeod::fromDegFt(model->getDoubleValue("position/longitude-deg"), + model->getDoubleValue("position/latitude-deg"), + model->getDoubleValue("position/altitude-ft")); + // compute some additional props + int fl = (aiModelPos.getElevationFt() / 1000); + model->setIntValue("flight-level", fl * 10); + + osg::Vec2 projected = projectGeod(aiModelPos); + BOOST_FOREACH(SymbolRule* r, rules) { + addSymbolInstance(projected, heading, r->getDefinition(), (SGPropertyNode*) model); + } + } // of ai models iteration +} + +void NavDisplay::computeAIStates(const SGPropertyNode* ai, string_set& states) +{ + int threatLevel = ai->getIntValue("tcas/threat-level",-1); + if (threatLevel < 1) + threatLevel = 0; + + states.insert("tcas"); + + std::ostringstream os; + os << "tcas-threat-level-" << threatLevel; + states.insert(os.str()); + + double vspeed = ai->getDoubleValue("velocities/vertical-speed-fps"); + if (vspeed < -3.0) { + states.insert("descending"); + } else if (vspeed > 3.0) { + states.insert("climbing"); + } +} + +SymbolInstance* NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars) +{ + if (isProjectedClipped(proj)) { + return NULL; + } + + if ((def->limitCount > 0) && (def->instanceCount >= def->limitCount)) { + return NULL; + } + + ++def->instanceCount; + SymbolInstance* sym = new SymbolInstance(proj, heading, def, vars); + _symbols.push_back(sym); + return sym; +} + +bool NavDisplay::isProjectedClipped(const osg::Vec2& projected) const +{ + double size = _odg->size(); + return (projected.x() < 0.0) || + (projected.y() < 0.0) || + (projected.x() >= size) || + (projected.y() >= size); +} + +void NavDisplay::addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars) +{ + string_set stateSet; + BOOST_FOREACH(std::string s, simgear::strutils::split(states, ",")) { + stateSet.insert(s); + } + + SymbolRuleVector rules; + findRules(type, stateSet, rules); + if (rules.empty()) { + return; // no rules matched, we can skip this item + } + + osg::Vec2 projected = projectGeod(pos); + BOOST_FOREACH(SymbolRule* r, rules) { + addSymbolInstance(projected, heading, r->getDefinition(), vars); + } +} + +void NavDisplay::addTestSymbols() +{ + _pos = SGGeod::fromDeg(-122.3748889, 37.6189722); // KSFO + + SGGeod a1; + double dummy; + SGGeodesy::direct(_pos, 45.0, 20.0 * SG_NM_TO_METER, a1, dummy); + + addTestSymbol("airport", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 95.0, 40.0 * SG_NM_TO_METER, a1, dummy); + + addTestSymbol("vor", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 120, 80.0 * SG_NM_TO_METER, a1, dummy); + + addTestSymbol("airport", "destination", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 80.0, 20.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); + + + SGGeodesy::direct(_pos, 140.0, 20.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 110.0, 10.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 110.0, 5.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); +} + +void NavDisplay::addRule(SymbolRule* r) +{ + _rules.push_back(r); +} + diff --git a/src/Cockpit/NavDisplay.hxx b/src/Cockpit/NavDisplay.hxx new file mode 100644 index 000000000..870c3cc30 --- /dev/null +++ b/src/Cockpit/NavDisplay.hxx @@ -0,0 +1,202 @@ +// Wx Radar background texture +// +// Written by Harald JOHNSEN, started May 2005. +// With major amendments by Vivian MEAZZA May 2007 +// Ported to OSG by Tim MOORE Jun 2007 +// +// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef _INST_ND_HXX +#define _INST_ND_HXX + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +class FGODGauge; +class FGRouteMgr; +class FGNavRecord; + +class SymbolInstance; +class SymbolDef; +class SymbolRule; + +namespace flightgear +{ + class Waypt; +} + +typedef std::set string_set; +typedef std::vector SymbolRuleVector; +typedef std::vector 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 + 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 _symbolTexture; + osg::ref_ptr _radarGeode; + osg::ref_ptr _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 _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 _symbols; + std::set _routeSources; + + bool _cachedItemsValid; + SGVec3d _cachedPos; + FGPositioned::List _itemsInRange; + SGPropertyNode_ptr _excessDataNode; + int _maxSymbols; + + class CacheListener; + std::auto_ptr _cacheListener; + + class ForceUpdateListener; + std::auto_ptr _forceUpdateListener; +}; + +#endif // _INST_ND_HXX diff --git a/src/Cockpit/agradar.cxx b/src/Cockpit/agradar.cxx new file mode 100644 index 000000000..768fdea41 --- /dev/null +++ b/src/Cockpit/agradar.cxx @@ -0,0 +1,325 @@ +// Air Ground Radar +// +// Written by Vivian MEAZZA, started Feb 2008. +// +// +// Copyright (C) 2008 Vivian Meazza +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include
+#include
+#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(mat); + if (material) { + const std::vector& names = material->get_names(); + + _solid = material->get_solid(); + _load_resistance = material->get_load_resistance(); + _frictionFactor = material->get_friction_factor(); + _bumpinessFactor = material->get_bumpiness(); + + if (!names.empty()) + _mat_name = names[0]; + else + _mat_name = ""; + + } + /*cout << "material " << mat_name + << " solid " << _solid + << " load " << _load_resistance + << " frictionFactor " << frictionFactor + << " _bumpinessFactor " << _bumpinessFactor + << endl;*/ + return true; + } else { + return false; + } + +} + +void +agRadar::update_terrain() +{ + int mode = _radar_mode_control_node->getIntValue(); + + double el_limit = 1; + double el_step = 1; + double az_limit = 50; + double az_step = 10; + double max_range = 40000; + double min_range = 250; + double tilt = -2.5; + bool roll_stab = _rollStabNode->getBoolValue(); + bool pitch_stab = _pitchStabNode->getBoolValue(); + const char* status = ""; + bool hdg_mkr = true; + + if (mode == 5){ + status = "TW"; + hdg_mkr = false; + tilt = _tiltNode->getDoubleValue(); + el_limit = _elevLimitDegNode->getDoubleValue(); + el_step = _elevStepDegNode->getDoubleValue(); + az_limit = _azLimitDegNode->getDoubleValue(); + az_step = _azStepDegNode->getDoubleValue(); + max_range = _maxRangeMNode->getDoubleValue(); + min_range = _minRangeMNode->getDoubleValue(); + } + + _Instrument->setDoubleValue("tilt", tilt); + _Instrument->setBoolValue("stabilisation/roll", roll_stab); + _Instrument->setBoolValue("stabilisation/pitch", pitch_stab); + _Instrument->setStringValue("status", status); + _Instrument->setDoubleValue("limit-deg", az_limit); + _Instrument->setBoolValue("heading-marker", hdg_mkr); + setUserPos(); + setAntennaPos(); + SGVec3d cartantennapos = getCartAntennaPos(); + + for(double brg = -az_limit; brg <= az_limit; brg += az_step){ + for(double elev = el_limit; elev >= - el_limit; elev -= el_step){ + setUserVec(brg, elev); + SGVec3d nearestHit; + globals->get_scenery()->get_cart_ground_intersection(cartantennapos, uservec, nearestHit); + SGGeodesy::SGCartToGeod(nearestHit, hitpos); + + double course1, course2, distance; + + SGGeodesy::inverse(hitpos, antennapos, course1, course2, distance); + + if (distance >= min_range && distance <= max_range) { + _terrain_warning_node->setBoolValue(true); + getMaterial(); + _brgDegNode->setDoubleValue(course2); + _rangeMNode->setDoubleValue(distance); + _materialNode->setStringValue(_mat_name.c_str()); + _bumpinessNode->setDoubleValue(_bumpinessFactor); + _elevationMNode->setDoubleValue(_elevation_m); + } else { + _terrain_warning_node->setBoolValue(false); + _brgDegNode->setDoubleValue(0); + _rangeMNode->setDoubleValue(0); + _materialNode->setStringValue(""); + _bumpinessNode->setDoubleValue(0); + _elevationMNode->setDoubleValue(0); + } + + //cout << "usr hdg " << _user_hdg_deg_node->getDoubleValue() + // << " ant brg " << course2 + // << " elev " << _Instrument->getDoubleValue("tilt") + // << " gnd rng nm " << distance * SG_METER_TO_NM + // << " ht " << hitpos.getElevationFt() + // << " mat " << _mat_name + // << " solid " << _solid + // << " bumpiness " << _bumpinessFactor + // << endl; + + } + } +} + + diff --git a/src/Cockpit/agradar.hxx b/src/Cockpit/agradar.hxx new file mode 100644 index 000000000..dda152e0b --- /dev/null +++ b/src/Cockpit/agradar.hxx @@ -0,0 +1,94 @@ +// Air Ground Radar +// +// Written by Vivian MEAZZA, started Feb 2008. +// +// +// Copyright (C) 2008 Vivain MEAZZA - vivian.meazza@lineone.net +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifndef _INST_AGRADAR_HXX +#define _INST_AGRADAR_HXX + +#include +#include +#include + +#include "wxradar.hxx" + +class agRadar : public wxRadarBg{ +public: + + agRadar ( SGPropertyNode *node ); + agRadar (); + virtual ~agRadar (); + + virtual void init (); + virtual void update (double dt); + + void setUserPos(); + void setUserVec(double az, double el); + void update_terrain(); + void setAntennaPos(); + + bool getMaterial(); + + double _load_resistance; // ground load resistanc N/m^2 + double _frictionFactor; // dimensionless modifier for Coefficient of Friction + double _bumpinessFactor; // dimensionless modifier for Bumpiness + double _elevation_m; // ground elevation in meters + bool _solid; // if true ground is solid for FDMs + + std::string _mat_name; // ground material + + SGVec3d getCartUserPos() const; + SGVec3d getCartAntennaPos()const; + + SGVec3d uservec; + + SGPropertyNode_ptr _user_hdg_deg_node; + SGPropertyNode_ptr _user_roll_deg_node; + SGPropertyNode_ptr _user_pitch_deg_node; + SGPropertyNode_ptr _terrain_warning_node; + + SGPropertyNode_ptr _xOffsetMNode; + SGPropertyNode_ptr _yOffsetMNode; + SGPropertyNode_ptr _zOffsetMNode; + + SGPropertyNode_ptr _elevLimitDegNode; + SGPropertyNode_ptr _elevStepDegNode; + SGPropertyNode_ptr _azLimitDegNode; + SGPropertyNode_ptr _azStepDegNode; + SGPropertyNode_ptr _maxRangeMNode; + SGPropertyNode_ptr _minRangeMNode; + SGPropertyNode_ptr _tiltNode; + + SGPropertyNode_ptr _brgDegNode; + SGPropertyNode_ptr _rangeMNode; + SGPropertyNode_ptr _elevationMNode; + SGPropertyNode_ptr _materialNode; + SGPropertyNode_ptr _bumpinessNode; + + SGPropertyNode_ptr _rollStabNode; + SGPropertyNode_ptr _pitchStabNode; + + SGGeod userpos; + SGGeod hitpos; + SGGeod antennapos; +}; + +#endif // _INST_AGRADAR_HXX diff --git a/src/Cockpit/cockpitDisplayManager.cxx b/src/Cockpit/cockpitDisplayManager.cxx new file mode 100644 index 000000000..18e6e7d8a --- /dev/null +++ b/src/Cockpit/cockpitDisplayManager.cxx @@ -0,0 +1,126 @@ +// cockpitDisplayManager.cxx -- manage cockpit displays, typically +// rendered using a sub-camera or render-texture +// +// Copyright (C) 2012 James Turner zakalawe@mac.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "cockpitDisplayManager.hxx" + +#include +#include +#include +#include + +#include + +#include
+#include
+ +#include "agradar.hxx" +#include "NavDisplay.hxx" +#include "groundradar.hxx" +#include "wxradar.hxx" + +namespace flightgear +{ + +CockpitDisplayManager::CockpitDisplayManager () +{ +} + +CockpitDisplayManager::~CockpitDisplayManager () +{ +} + +SGSubsystem::InitStatus CockpitDisplayManager::incrementalInit() +{ + init(); + return INIT_DONE; +} + +void CockpitDisplayManager::init() +{ + SGPropertyNode_ptr config_props = new SGPropertyNode; + SGPropertyNode* path_n = fgGetNode("/sim/instrumentation/path"); + if (!path_n) { + SG_LOG(SG_COCKPIT, SG_WARN, "No instrumentation model specified for this model!"); + return; + } + + SGPath config = globals->resolve_aircraft_path(path_n->getStringValue()); + SG_LOG( SG_COCKPIT, SG_INFO, "Reading cockpit displays from " << config.str() ); + + try { + readProperties( config.str(), config_props ); + if (!build(config_props)) { + throw sg_exception( + "Detected an internal inconsistency in the instrumentation\n" + "system specification file. See earlier errors for details."); + } + } catch (const sg_exception& e) { + SG_LOG(SG_COCKPIT, SG_ALERT, "Failed to load instrumentation system model: " + << config.str() << ":" << e.getFormattedMessage() ); + } + + // bind() created instruments before init. + BOOST_FOREACH(std::string s, _displays) { + get_subsystem(s)->bind(); + } + + SGSubsystemGroup::init(); +} + +bool CockpitDisplayManager::build (SGPropertyNode* config_props) +{ + for ( int i = 0; i < config_props->nChildren(); ++i ) { + SGPropertyNode *node = config_props->getChild(i); + std::string name = node->getName(); + + std::ostringstream subsystemname; + subsystemname << "instrument-" << i << '-' + << node->getStringValue("name", name.c_str()); + int index = node->getIntValue("number", 0); + if (index > 0) + subsystemname << '['<< index << ']'; + std::string id = subsystemname.str(); + + if ( name == "radar" ) { + set_subsystem( id, new wxRadarBg ( node ) ); + + } else if ( name == "groundradar" ) { + set_subsystem( id, new GroundRadar( node ) ); + + } else if ( name == "air-ground-radar" ) { + set_subsystem( id, new agRadar( node ) ); + + } else if ( name == "navigation-display" ) { + set_subsystem( id, new NavDisplay( node ) ); + + } else { + // probably a regular instrument + continue; + } + // only add to our list if we build a display + _displays.push_back(id); + } + return true; +} + +} // of namespace flightgear diff --git a/src/Cockpit/cockpitDisplayManager.hxx b/src/Cockpit/cockpitDisplayManager.hxx new file mode 100644 index 000000000..53febaff9 --- /dev/null +++ b/src/Cockpit/cockpitDisplayManager.hxx @@ -0,0 +1,54 @@ +// cockpitDisplayManager.hxx -- manage cockpit displays, typically +// rendered using a sub-camera or render-texture +// +// Copyright (C) 2012 James Turner zakalawe@mac.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef COCKPIT_DISPLAY_MGR_HXX +#define COCKPIT_DISPLAY_MGR_HXX 1 + +#include + +#include +#include + +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 _displays; +}; + +} // of namespace lfightgear + +#endif // COCKPIT_DISPLAY_MGR_HXX diff --git a/src/Cockpit/groundradar.cxx b/src/Cockpit/groundradar.cxx new file mode 100644 index 000000000..b7d283413 --- /dev/null +++ b/src/Cockpit/groundradar.cxx @@ -0,0 +1,354 @@ +// groundradar.cxx - Background layer for the ATC radar. +// +// Copyright (C) 2007 Csaba Halasz. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include
+#include
+#include +#include +#include +#include +#include +#include +#include + +#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(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 tess = new osgUtil::Tessellator; + osg::ref_ptr polygon = new osg::Geometry; + osg::ref_ptr 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( 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 bCurv( p1, p2, p3 ); + simgear::BezierCurve::PointList &ptList = bCurv.pointList(); + for ( simgear::BezierCurve::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 rwy_vertices = new osg::Vec3Array; + osg::ref_ptr taxi_vertices = new osg::Vec3Array; + osg::ref_ptr 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; inumTaxiways(); ++i) + { + FGTaxiway* txwy(apt->getTaxiwayByIndex(i)); + addRunwayVertices(txwy, tower_lat, tower_lon, scale, taxi_vertices.get()); + } + osg::Geometry *taxi_geom = dynamic_cast(_geode->getDrawable(0)); + taxi_geom->setVertexArray(taxi_vertices.get()); + osg::DrawArrays* taxi = dynamic_cast(taxi_geom->getPrimitiveSet(0)); + taxi->setCount(taxi_vertices->size()); + + osg::Geometry *pvt_geom = dynamic_cast(_geode->getDrawable(1)); + osg::Geometry::PrimitiveSetList &pvt_prim_list = pvt_geom->getPrimitiveSetList(); + pvt_prim_list.clear(); + for (unsigned int i=0; inumPavements(); ++i) + { + FGPavement* pvt(apt->getPavementByIndex(i)); + osg::ref_ptr geom = addPavementGeometry(pvt, tower_lat, tower_lon, scale); + osg::Geometry::PrimitiveSetList &prim_list = geom->getPrimitiveSetList(); + osg::Vec3Array *vertices = dynamic_cast(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(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(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(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(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; inumRunways(); ++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(_geode->getDrawable(2)); + rwy_geom->setVertexArray(rwy_vertices.get()); + osg::DrawArrays* rwy = dynamic_cast(rwy_geom->getPrimitiveSet(0)); + rwy->setCount(rwy_vertices->size()); + + getCamera()->setNodeMask(0xffffffff); +} + +// end of GroundRadar.cxx diff --git a/src/Cockpit/groundradar.hxx b/src/Cockpit/groundradar.hxx new file mode 100644 index 000000000..ec439c273 --- /dev/null +++ b/src/Cockpit/groundradar.hxx @@ -0,0 +1,59 @@ +// groundradar.hxx - Background layer for the ATC radar. +// +// Copyright (C) 2007 Csaba Halasz. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +#ifndef __INST_GROUNDRADAR_HXX +#define __INST_GROUNDRADAR_HXX + +#include +#include + +#include +#include + +#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 _geode; + SGPropertyNode_ptr _airport_node; + SGPropertyNode_ptr _range_node; +}; + +#endif // __INST_GROUNDRADAR_HXX diff --git a/src/Cockpit/od_gauge.cxx b/src/Cockpit/od_gauge.cxx new file mode 100644 index 000000000..c6be269e4 --- /dev/null +++ b/src/Cockpit/od_gauge.cxx @@ -0,0 +1,479 @@ +// Owner Drawn Gauge helper class +// +// Written by Harald JOHNSEN, started May 2005. +// +// Copyright (C) 2005 Harald JOHNSEN +// +// Ported to OSG by Tim Moore - Jun 2007 +// +// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012 +// Supports now multisampling/mipmapping, usage of the stencil buffer and placing +// the texture in the scene by certain filter criteria +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for GL_DEPTH_STENCIL_EXT on Windows + +#include + +#include +#include + +#include
+#include +#include +#include "od_gauge.hxx" + +#include + +//------------------------------------------------------------------------------ +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(&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(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 + ( + 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 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 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 _group; + }; + + std::string _tex_name, ///get_scenery()->get_aircraft_branch(); + ReplaceStaticTextureVisitor visitor(name, new_texture); + root->accept(visitor); + return visitor.getPlacements(); +} + +//------------------------------------------------------------------------------ +canvas::Placements FGODGauge::set_texture( const SGPropertyNode* placement, + osg::Texture2D* new_texture, + osg::NodeCallback* cull_callback ) +{ + osg::Group* root = globals->get_scenery()->get_aircraft_branch(); + ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback); + root->accept(visitor); + return visitor.getPlacements(); +} diff --git a/src/Cockpit/od_gauge.hxx b/src/Cockpit/od_gauge.hxx new file mode 100644 index 000000000..7c4ee00ed --- /dev/null +++ b/src/Cockpit/od_gauge.hxx @@ -0,0 +1,177 @@ +// Owner Drawn Gauge helper class +// +// Written by Harald JOHNSEN, started May 2005. +// +// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net +// +// Ported to OSG by Tim Moore - Jun 2007 +// +// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012 +// Supports now multisampling/mipmapping, usage of the stencil buffer and placing +// the texture in the scene by certain filter criteria +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifndef _OD_GAUGE_HXX +#define _OD_GAUGE_HXX + +#include +#include + +#include +#include + +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 camera; + osg::ref_ptr texture; + + void updateCoordinateFrame(); + void updateStencil(); + void updateSampling(); + +}; + +#endif // _OD_GAUGE_HXX diff --git a/src/Cockpit/render_area_2d.cxx b/src/Cockpit/render_area_2d.cxx new file mode 100644 index 000000000..8ad68a74b --- /dev/null +++ b/src/Cockpit/render_area_2d.cxx @@ -0,0 +1,426 @@ +// RenderArea2D.cxx - a class to manage 2D polygon-based drawing +// for a complex instrument (eg. GPS). +// +// Written by David Luff, started 2005. +// +// Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#ifdef HAVE_CONFIG_H +# include +#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 renderArea2DStateSet; + if(!renderArea2DStateSet.valid()) { + renderArea2DStateSet = new osg::StateSet; + renderArea2DStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF); + renderArea2DStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + } + + state.pushStateSet(renderArea2DStateSet.get()); + state.apply(); + state.setActiveTextureUnit(0); + state.setClientActiveTextureUnit(0); + + // DCL - the 2 lines below are copied verbatim from the hotspot drawing code. + // I am not sure if they are needed here or not. + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_COLOR_MATERIAL); + + // FIXME - disabling all clip planes causes bleed-through through the splash screen. + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + + DoDrawBackground(); + + for(unsigned int i = 0; i < drawing_list.size(); ++i) { + RA2DPrimitive prim = drawing_list[i]; + switch(prim.type) { + case RA2D_LINE: + DoDrawLine(prim.x1, prim.y1, prim.x2, prim.y2); + break; + case RA2D_QUAD: + if(prim.debug) { + //cout << "Clipping = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n'; + //cout << "Drawing quad " << prim.x1 << ", " << prim.y1 << " to " << prim.x2 << ", " << prim.y2 << '\n'; + } + DoDrawQuad(prim.x1, prim.y1, prim.x2, prim.y2, prim.invert); + break; + case RA2D_PIXEL: + DoDrawPixel(prim.x1, prim.y1, prim.invert); + break; + } + } + + drawing_list.clear(); + + glPopAttrib(); + + state.popStateSet(); + state.apply(); + state.setActiveTextureUnit(0); + state.setClientActiveTextureUnit(0); +} + +void RenderArea2D::Flush() { + drawing_list.clear(); +} + +void RenderArea2D::SetPixelColor(const float* rgba) { + _pixelColor[0] = rgba[0]; + _pixelColor[1] = rgba[1]; + _pixelColor[2] = rgba[2]; + _pixelColor[3] = rgba[3]; +} + +// Set clipping region in logical units +void RenderArea2D::SetClipRegion(int x1, int y1, int x2, int y2) { + _clipx1 = x1; + _clipx2 = x2; + _clipy1 = y1; + _clipy2 = y2; + //cout << "Set clip region, clip region = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n'; +} + +// Set clip region to be the same as the rendered area (default) +void RenderArea2D::ResetClipRegion() { + _clipx1 = 0; + _clipx2 = _logx - 1; + _clipy1 = 0; + _clipy2 = _logy - 1; + //cout << "Reset clip region, clip region = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n'; +} + +void RenderArea2D::SetPosition(int posx, int posy) { + _posx = posx; + _posy = posy; +} + +void RenderArea2D::SetLogicalSize(int logx, int logy) { + _logx = logx; + _logy = logy; +} + +void RenderArea2D::SetActualSize(int sizex, int sizey) { + _sizex = sizex; + _sizey = sizey; +} + +void RenderArea2D::DrawPixel(int x, int y, bool invert) { + // Clip + if(x < _clipx1 || x > _clipx2 || y < _clipy1 || y > _clipy2) return; + + RA2DPrimitive prim; + prim.x1 = x; + prim.y1 = y; + prim.x2 = 0; + prim.y2 = 0; + prim.type = RA2D_PIXEL; + prim.invert = invert; + drawing_list.push_back(prim); +} + +void RenderArea2D::DoDrawPixel(int x, int y, bool invert) { + // Clip. In theory this shouldn't be necessary, since all input is clipped before adding + // to the drawing list, but it ensures that any errors in clipping lines etc will only + // spill over the clip area within the instrument, and still be clipped from straying + // outside the instrument. + if(x < _clipx1 || x > _clipx2 || y < _clipy1 || y > _clipy2) return; + + // Scale to position within background + float fx1 = (float)x, fy1 = (float)y; + float rx = (float)_sizex / (float)_logx; + float ry = (float)_sizey / (float)_logy; + fx1 *= rx; + fy1 *= ry; + float fx2 = fx1 + rx; + float fy2 = fy1 + ry; + + // Translate to final position + fx1 += (float)_posx; + fx2 += (float)_posx; + fy1 += (float)_posy; + fy2 += (float)_posy; + + //cout << "DP: " << fx1 << ", " << fy1 << " ... " << fx2 << ", " << fy2 << '\n'; + + SetRenderColor(invert ? _backgroundColor : _pixelColor); + SGVec2f corners[4] = { + SGVec2f(fx1, fy1), + SGVec2f(fx2, fy1), + SGVec2f(fx2, fy2), + SGVec2f(fx1, fy2) + }; + RenderQuad(corners); +} + +void RenderArea2D::DrawLine(int x1, int y1, int x2, int y2) { + // We need to clip the line to the current region before storing it in the drawing + // list, since when we come to actually draw it the clip region may have changed. + + // Liang-Barsky clipping algorithm + int p[4], q[4]; + float u1 = 0.0f, u2 = 1.0f; + p[0] = -(x2 - x1); q[0] = (x1 - _clipx1); + p[1] = (x2 - x1); q[1] = (_clipx2 - x1); + p[2] = -(y2 - y1); q[2] = (y1 - _clipy1); + p[3] = (y2 - y1); q[3] = (_clipy2 - y1); + + for(int i=0; i<4; ++i) { + if(p[i] == 0) { + if(q[i] < 0) { + // Then we have a trivial rejection of a line parallel to a clip plane + // completely outside the clip region. + return; + } + } else if(p[i] < 0) { + float r = (float)q[i]/(float)p[i]; + u1 = (u1 > r ? u1 : r); + } else { // p[i] > 0 + float r = (float)q[i]/(float)p[i]; + u2 = (u2 < r ? u2 : r); + } + if(u1 > u2) { + // Then the line is completely outside the clip area. + return; + } + } + + float fx1 = x1 + u1 * (float)(x2 - x1); + float fy1 = y1 + u1 * (float)(y2 - y1); + float fx2 = x1 + u2 * (float)(x2 - x1); + float fy2 = y1 + u2 * (float)(y2 - y1); + x1 = (int)(fx1 + 0.5); + y1 = (int)(fy1 + 0.5); + x2 = (int)(fx2 + 0.5); + y2 = (int)(fy2 + 0.5); + + RA2DPrimitive prim; + prim.x1 = x1; + prim.y1 = y1; + prim.x2 = x2; + prim.y2 = y2; + prim.type = RA2D_LINE; + prim.invert = false; + drawing_list.push_back(prim); +} + +void RenderArea2D::DoDrawLine(int x1, int y1, int x2, int y2) { + // Crude implementation of Bresenham line drawing algorithm. + + // Our lines are non directional, so first order the points x-direction-wise to leave only 4 octants to consider. + if(x2 < x1) { + int tmp_x = x1; + int tmp_y = y1; + x1 = x2; + y1 = y2; + x2 = tmp_x; + y2 = tmp_y; + } + + bool flip_y = (y1 > y2 ? true : false); + int dx = x2 - x1; + int dy = (flip_y ? y1 - y2 : y2 - y1); + if(dx > dy) { + // push the x dir + int y = y1; + int yn = dx/2; + for(int x=x1; x<=x2; ++x) { + DoDrawPixel(x, y); + yn += dy; + if(yn >= dx) { + yn -= dx; + y = (flip_y ? y - 1 : y + 1); + } + } + } else { + // push the y dir + int x = x1; + int xn = dy/2; + // Must be a more elegant way to roll the next two cases into one! + if(flip_y) { + for(int y=y1; y>=y2; --y) { + DoDrawPixel(x, y); + xn += dx; + if(xn >= dy) { + xn -= dy; + x++; + } + } + } else { + for(int y=y1; y<=y2; ++y) { + DoDrawPixel(x, y); + xn += dx; + if(xn >= dy) { + xn -= dy; + x++; + } + } + } + } +} + +void RenderArea2D::DrawQuad(int x1, int y1, int x2, int y2, bool invert) { + // Force the input to be ordered with x1 < x2 and y1 < y2. + if(x1 > x2) { + int x = x2; + x2 = x1; + x1 = x; + } + if(y1 > y2) { + int y = y2; + y2 = y1; + y1 = y; + } + + // Clip the input to the current drawing region. + x1 = x1 < _clipx1 ? _clipx1 : x1; + if(x1 > _clipx2) { return; } + x2 = x2 > _clipx2 ? _clipx2 : x2; + if(x2 < _clipx1) { return; } + y1 = y1 < _clipy1 ? _clipy1 : y1; + if(y1 > _clipy2) { return; } + y2 = y2 > _clipy2 ? _clipy2 : y2; + if(y2 < _clipy1) { return; } + + RA2DPrimitive prim; + prim.x1 = x1; + prim.y1 = y1; + prim.x2 = x2; + prim.y2 = y2; + prim.type = RA2D_QUAD; + prim.invert = invert; + if(_ra2d_debug) prim.debug = true; + drawing_list.push_back(prim); +} + +void RenderArea2D::DoDrawQuad(int x1, int y1, int x2, int y2, bool invert) { + // Scale to position within background + float fx1 = (float)x1, fy1 = (float)y1; + float fx2 = (float)x2, fy2 = (float)y2; + float rx = (float)_sizex / (float)_logx; + float ry = (float)_sizey / (float)_logy; + fx1 *= rx; + fy1 *= ry; + fx2 *= rx; + fy2 *= ry; + + fx2 += rx; + fy2 += ry; + + // Translate to final position + fx1 += (float)_posx; + fx2 += (float)_posx; + fy1 += (float)_posy; + fy2 += (float)_posy; + + //cout << "DP: " << fx1 << ", " << fy1 << " ... " << fx2 << ", " << fy2 << '\n'; + + SetRenderColor(invert ? _backgroundColor : _pixelColor); + SGVec2f corners[4] = { + SGVec2f(fx1, fy1), + SGVec2f(fx2, fy1), + SGVec2f(fx2, fy2), + SGVec2f(fx1, fy2) + }; + RenderQuad(corners); +} + +void RenderArea2D::DrawBackground() { + // Currently a NO-OP +} + +void RenderArea2D::DoDrawBackground() { + SetRenderColor(_backgroundColor); + SGVec2f corners[4] = { + SGVec2f(_posx, _posy), + SGVec2f(_posx + _sizex, _posy), + SGVec2f(_posx + _sizex, _posy + _sizey), + SGVec2f(_posx, _posy + _sizey) + }; + + RenderQuad(corners); +} + +// ----------------------------------------- +// +// Actual drawing routines copied from Atlas +// +// ----------------------------------------- + +void RenderArea2D::SetRenderColor( const float *rgba ) { + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, rgba); + glColor4fv( rgba ); +} + +void RenderArea2D::RenderQuad( const SGVec2f *p) { + glBegin(GL_QUADS); + glNormal3f(0.0f, 0.0f, 0.0f); + glVertex2fv( p[0].data() ); + glVertex2fv( p[1].data() ); + glVertex2fv( p[2].data() ); + glVertex2fv( p[3].data() ); + glEnd(); +} + +void RenderArea2D::RenderQuad( const SGVec2f *p, const SGVec4f *color ) { + glBegin(GL_QUADS); + glNormal3f(0.0f, 0.0f, 0.0f); + glColor4fv( color[0].data() ); glVertex2fv( p[0].data() ); + glColor4fv( color[1].data() ); glVertex2fv( p[1].data() ); + glColor4fv( color[2].data() ); glVertex2fv( p[2].data() ); + glColor4fv( color[3].data() ); glVertex2fv( p[3].data() ); + glEnd(); +} diff --git a/src/Cockpit/render_area_2d.hxx b/src/Cockpit/render_area_2d.hxx new file mode 100644 index 000000000..83d74efef --- /dev/null +++ b/src/Cockpit/render_area_2d.hxx @@ -0,0 +1,116 @@ +// RenderArea2D.hxx - a class to manage 2D polygon-based drawing +// for a complex instrument (eg. GPS). +// +// Written by David Luff, started 2005. +// +// Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifndef _RENDER_AREA_2D +#define _RENDER_AREA_2D + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include +#include + +#include + +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 drawing_list; + + // Control whether to output debugging output + bool _ra2d_debug; +}; + +#endif diff --git a/src/Cockpit/wxradar.cxx b/src/Cockpit/wxradar.cxx new file mode 100644 index 000000000..2d3ce04c5 --- /dev/null +++ b/src/Cockpit/wxradar.cxx @@ -0,0 +1,1115 @@ +// Wx Radar background texture +// +// Written by Harald JOHNSEN, started May 2005. +// With major amendments by Vivian MEAZZA May 2007 +// Ported to OSG by Tim Moore Jun 2007 +// +// +// Copyright (C) 2005 Harald JOHNSEN +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using std::stringstream; +using std::endl; +using std::setprecision; +using std::fixed; +using std::setw; +using std::setfill; +using std::string; + +#include
+#include
+ +#include "panel.hxx" // for FGTextureManager +#include "od_gauge.hxx" +#include "wxradar.hxx" + +#include // 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(_geom->getPrimitiveSet(1)); + osg::DrawArrays *trimaskPSet + = static_cast(_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(_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 "<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 fontOptions = new osgDB::ReaderWriter::Options("monochrome"); + osg::ref_ptr font = osgText::readFontFile(tpath.c_str(), fontOptions.get()); +#else + osg::ref_ptr font = osgText::readFontFile(tpath.c_str()); +#endif + + if (font != 0) { + _font = font; + _font->setMinFilterHint(osg::Texture::NEAREST); + _font->setMagFilterHint(osg::Texture::NEAREST); + _font->setGlyphImageMargin(0); + _font->setGlyphImageMarginRatio(0); + } + + for (int i=0;i<4;i++) + { + const float defaultColors[4][3] = {{0,1,1},{0,1,1},{1,0.5,0},{1,0,0}}; + SGPropertyNode_ptr color_node = _font_node->getNode("tcas/color",i,true); + float red = color_node->getFloatValue("red",defaultColors[i][0]); + float green = color_node->getFloatValue("green",defaultColors[i][1]); + float blue = color_node->getFloatValue("blue",defaultColors[i][2]); + float alpha = color_node->getFloatValue("alpha",1); + _tcas_colors[i]=osg::Vec4(red, green, blue, alpha); + } +} + +void +wxRadarBg::valueChanged(SGPropertyNode*) +{ + updateFont(); + _time = _interval; +} + diff --git a/src/Cockpit/wxradar.hxx b/src/Cockpit/wxradar.hxx new file mode 100644 index 000000000..7aff77717 --- /dev/null +++ b/src/Cockpit/wxradar.hxx @@ -0,0 +1,202 @@ +// Wx Radar background texture +// +// Written by Harald JOHNSEN, started May 2005. +// With major amendments by Vivian MEAZZA May 2007 +// Ported to OSG by Tim MOORE Jun 2007 +// +// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef _INST_WXRADAR_HXX +#define _INST_WXRADAR_HXX + +#include +#include +#include +#include + +#include +#include + +#include +#include + +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_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 + 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 _resultTexture; + osg::ref_ptr _wxEcho; + osg::ref_ptr _radarGeode; + osg::ref_ptr _textGeode; + osg::Geometry *_geom; + osg::Vec2Array *_vertices; + osg::Vec2Array *_texCoords; + osg::Matrixf _centerTrans; + osg::ref_ptr _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 diff --git a/src/Instrumentation/CMakeLists.txt b/src/Instrumentation/CMakeLists.txt index 4060b83b9..ba0ba8b7b 100644 --- a/src/Instrumentation/CMakeLists.txt +++ b/src/Instrumentation/CMakeLists.txt @@ -2,7 +2,6 @@ include(FlightGearComponent) set(SOURCES adf.cxx - agradar.cxx airspeed_indicator.cxx altimeter.cxx attitude_indicator.cxx @@ -10,7 +9,6 @@ set(SOURCES dclgps.cxx dme.cxx gps.cxx - groundradar.cxx gsdi.cxx gyro.cxx heading_indicator.cxx @@ -26,9 +24,7 @@ set(SOURCES mrg.cxx navradio.cxx newnavradio.cxx - od_gauge.cxx rad_alt.cxx - render_area_2d.cxx rnav_waypt_controller.cxx slip_skid_ball.cxx tacan.cxx @@ -36,8 +32,6 @@ set(SOURCES transponder.cxx turn_indicator.cxx vertical_speed_indicator.cxx - wxradar.cxx - NavDisplay.cxx HUD/HUD.cxx HUD/HUD_dial.cxx HUD/HUD_gauge.cxx @@ -69,7 +63,6 @@ set(SOURCES set(HEADERS adf.hxx - agradar.hxx airspeed_indicator.hxx altimeter.hxx attitude_indicator.hxx @@ -77,7 +70,6 @@ set(HEADERS dclgps.hxx dme.hxx gps.hxx - groundradar.hxx gsdi.hxx gyro.hxx heading_indicator.hxx @@ -93,9 +85,7 @@ set(HEADERS mrg.hxx navradio.hxx newnavradio.hxx - od_gauge.hxx rad_alt.hxx - render_area_2d.hxx rnav_waypt_controller.hxx slip_skid_ball.hxx tacan.hxx @@ -103,8 +93,6 @@ set(HEADERS transponder.hxx turn_indicator.hxx vertical_speed_indicator.hxx - wxradar.hxx - NavDisplay.hxx HUD/HUD.hxx HUD/HUD_private.hxx KLN89/kln89.hxx diff --git a/src/Instrumentation/NavDisplay.cxx b/src/Instrumentation/NavDisplay.cxx deleted file mode 100644 index ecf9bdfc5..000000000 --- a/src/Instrumentation/NavDisplay.cxx +++ /dev/null @@ -1,1435 +0,0 @@ -// navigation display texture -// -// Written by James Turner, forked from wxradar code -// -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "NavDisplay.hxx" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include // 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
-#include
-#include -#include -#include -#include -#include -#include -#include -#include - -#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 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 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 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(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 fontOptions = new osgDB::ReaderWriter::Options("monochrome"); - osg::ref_ptr 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; lnumLegs(); ++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; igetDefinition()->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(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(pos); - heading = rwy->headingDeg(); - nd->setDoubleValue("heading-deg", heading); - nd->setIntValue("length-ft", rwy->lengthFt()); - nd->setStringValue("airport", rwy->airport()->ident()); - break; - } - - default: - break; - } -} - -void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states) -{ - if (_routeSources.count(pos) != 0) { - states.insert("on-active-route"); - } - - flightgear::FlightPlan* fp = _route->flightPlan(); - switch (pos->type()) { - case FGPositioned::VOR: - case FGPositioned::LOC: - if (pos == _nav1Station) { - states.insert("tuned"); - states.insert("nav1"); - } - - if (pos == _nav2Station) { - states.insert("tuned"); - states.insert("nav2"); - } - break; - - case FGPositioned::AIRPORT: - case FGPositioned::SEAPORT: - case FGPositioned::HELIPORT: - // mark alternates! - // once the FMS system has some way to tell us about them, of course - - if (pos == fp->departureAirport()) { - states.insert("departure"); - } - - if (pos == fp->destinationAirport()) { - states.insert("destination"); - } - break; - - case FGPositioned::RUNWAY: - if (pos == fp->departureRunway()) { - states.insert("departure"); - } - - if (pos == fp->destinationRunway()) { - states.insert("destination"); - } - break; - - case FGPositioned::OBSTACLE: - #if 0 - FGObstacle* obs = (FGObstacle*) pos; - if (obj->isLit()) { - states.insert("lit"); - } - - if (obj->getHeightAGLFt() >= 1000) { - states.insert("greater-1000-ft"); - } - #endif - break; - - default: - break; - } // FGPositioned::Type switch -} - -static string mapAINodeToType(SGPropertyNode* model) -{ - // assume all multiplayer items are aircraft for the moment. Not ideal. - if (!strcmp(model->getName(), "multiplayer")) { - return "ai-aircraft"; - } - - return string("ai-") + model->getName(); -} - -void NavDisplay::processAI() -{ - SGPropertyNode *ai = fgGetNode("/ai/models", true); - for (int i = ai->nChildren() - 1; i >= 0; i--) { - SGPropertyNode *model = ai->getChild(i); - if (!model->nChildren()) { - continue; - } - - // prefix types with 'ai-', to avoid any chance of namespace collisions - // with fg-positioned. - string_set ss; - computeAIStates(model, ss); - SymbolRuleVector rules; - findRules(mapAINodeToType(model), ss, rules); - if (rules.empty()) { - return; // no rules matched, we can skip this item - } - - double heading = model->getDoubleValue("orientation/true-heading-deg"); - SGGeod aiModelPos = SGGeod::fromDegFt(model->getDoubleValue("position/longitude-deg"), - model->getDoubleValue("position/latitude-deg"), - model->getDoubleValue("position/altitude-ft")); - // compute some additional props - int fl = (aiModelPos.getElevationFt() / 1000); - model->setIntValue("flight-level", fl * 10); - - osg::Vec2 projected = projectGeod(aiModelPos); - BOOST_FOREACH(SymbolRule* r, rules) { - addSymbolInstance(projected, heading, r->getDefinition(), (SGPropertyNode*) model); - } - } // of ai models iteration -} - -void NavDisplay::computeAIStates(const SGPropertyNode* ai, string_set& states) -{ - int threatLevel = ai->getIntValue("tcas/threat-level",-1); - if (threatLevel < 1) - threatLevel = 0; - - states.insert("tcas"); - - std::ostringstream os; - os << "tcas-threat-level-" << threatLevel; - states.insert(os.str()); - - double vspeed = ai->getDoubleValue("velocities/vertical-speed-fps"); - if (vspeed < -3.0) { - states.insert("descending"); - } else if (vspeed > 3.0) { - states.insert("climbing"); - } -} - -SymbolInstance* NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars) -{ - if (isProjectedClipped(proj)) { - return NULL; - } - - if ((def->limitCount > 0) && (def->instanceCount >= def->limitCount)) { - return NULL; - } - - ++def->instanceCount; - SymbolInstance* sym = new SymbolInstance(proj, heading, def, vars); - _symbols.push_back(sym); - return sym; -} - -bool NavDisplay::isProjectedClipped(const osg::Vec2& projected) const -{ - double size = _odg->size(); - return (projected.x() < 0.0) || - (projected.y() < 0.0) || - (projected.x() >= size) || - (projected.y() >= size); -} - -void NavDisplay::addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars) -{ - string_set stateSet; - BOOST_FOREACH(std::string s, simgear::strutils::split(states, ",")) { - stateSet.insert(s); - } - - SymbolRuleVector rules; - findRules(type, stateSet, rules); - if (rules.empty()) { - return; // no rules matched, we can skip this item - } - - osg::Vec2 projected = projectGeod(pos); - BOOST_FOREACH(SymbolRule* r, rules) { - addSymbolInstance(projected, heading, r->getDefinition(), vars); - } -} - -void NavDisplay::addTestSymbols() -{ - _pos = SGGeod::fromDeg(-122.3748889, 37.6189722); // KSFO - - SGGeod a1; - double dummy; - SGGeodesy::direct(_pos, 45.0, 20.0 * SG_NM_TO_METER, a1, dummy); - - addTestSymbol("airport", "", a1, 0.0, NULL); - - SGGeodesy::direct(_pos, 95.0, 40.0 * SG_NM_TO_METER, a1, dummy); - - addTestSymbol("vor", "", a1, 0.0, NULL); - - SGGeodesy::direct(_pos, 120, 80.0 * SG_NM_TO_METER, a1, dummy); - - addTestSymbol("airport", "destination", a1, 0.0, NULL); - - SGGeodesy::direct(_pos, 80.0, 20.0 * SG_NM_TO_METER, a1, dummy); - addTestSymbol("fix", "", a1, 0.0, NULL); - - - SGGeodesy::direct(_pos, 140.0, 20.0 * SG_NM_TO_METER, a1, dummy); - addTestSymbol("fix", "", a1, 0.0, NULL); - - SGGeodesy::direct(_pos, 110.0, 10.0 * SG_NM_TO_METER, a1, dummy); - addTestSymbol("fix", "", a1, 0.0, NULL); - - SGGeodesy::direct(_pos, 110.0, 5.0 * SG_NM_TO_METER, a1, dummy); - addTestSymbol("fix", "", a1, 0.0, NULL); -} - -void NavDisplay::addRule(SymbolRule* r) -{ - _rules.push_back(r); -} - diff --git a/src/Instrumentation/NavDisplay.hxx b/src/Instrumentation/NavDisplay.hxx deleted file mode 100644 index 870c3cc30..000000000 --- a/src/Instrumentation/NavDisplay.hxx +++ /dev/null @@ -1,202 +0,0 @@ -// Wx Radar background texture -// -// Written by Harald JOHNSEN, started May 2005. -// With major amendments by Vivian MEAZZA May 2007 -// Ported to OSG by Tim MOORE Jun 2007 -// -// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -#ifndef _INST_ND_HXX -#define _INST_ND_HXX - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include - -class FGODGauge; -class FGRouteMgr; -class FGNavRecord; - -class SymbolInstance; -class SymbolDef; -class SymbolRule; - -namespace flightgear -{ - class Waypt; -} - -typedef std::set string_set; -typedef std::vector SymbolRuleVector; -typedef std::vector 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 - 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 _symbolTexture; - osg::ref_ptr _radarGeode; - osg::ref_ptr _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 _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 _symbols; - std::set _routeSources; - - bool _cachedItemsValid; - SGVec3d _cachedPos; - FGPositioned::List _itemsInRange; - SGPropertyNode_ptr _excessDataNode; - int _maxSymbols; - - class CacheListener; - std::auto_ptr _cacheListener; - - class ForceUpdateListener; - std::auto_ptr _forceUpdateListener; -}; - -#endif // _INST_ND_HXX diff --git a/src/Instrumentation/agradar.cxx b/src/Instrumentation/agradar.cxx deleted file mode 100644 index 768fdea41..000000000 --- a/src/Instrumentation/agradar.cxx +++ /dev/null @@ -1,325 +0,0 @@ -// Air Ground Radar -// -// Written by Vivian MEAZZA, started Feb 2008. -// -// -// Copyright (C) 2008 Vivian Meazza -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include
-#include
-#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(mat); - if (material) { - const std::vector& names = material->get_names(); - - _solid = material->get_solid(); - _load_resistance = material->get_load_resistance(); - _frictionFactor = material->get_friction_factor(); - _bumpinessFactor = material->get_bumpiness(); - - if (!names.empty()) - _mat_name = names[0]; - else - _mat_name = ""; - - } - /*cout << "material " << mat_name - << " solid " << _solid - << " load " << _load_resistance - << " frictionFactor " << frictionFactor - << " _bumpinessFactor " << _bumpinessFactor - << endl;*/ - return true; - } else { - return false; - } - -} - -void -agRadar::update_terrain() -{ - int mode = _radar_mode_control_node->getIntValue(); - - double el_limit = 1; - double el_step = 1; - double az_limit = 50; - double az_step = 10; - double max_range = 40000; - double min_range = 250; - double tilt = -2.5; - bool roll_stab = _rollStabNode->getBoolValue(); - bool pitch_stab = _pitchStabNode->getBoolValue(); - const char* status = ""; - bool hdg_mkr = true; - - if (mode == 5){ - status = "TW"; - hdg_mkr = false; - tilt = _tiltNode->getDoubleValue(); - el_limit = _elevLimitDegNode->getDoubleValue(); - el_step = _elevStepDegNode->getDoubleValue(); - az_limit = _azLimitDegNode->getDoubleValue(); - az_step = _azStepDegNode->getDoubleValue(); - max_range = _maxRangeMNode->getDoubleValue(); - min_range = _minRangeMNode->getDoubleValue(); - } - - _Instrument->setDoubleValue("tilt", tilt); - _Instrument->setBoolValue("stabilisation/roll", roll_stab); - _Instrument->setBoolValue("stabilisation/pitch", pitch_stab); - _Instrument->setStringValue("status", status); - _Instrument->setDoubleValue("limit-deg", az_limit); - _Instrument->setBoolValue("heading-marker", hdg_mkr); - setUserPos(); - setAntennaPos(); - SGVec3d cartantennapos = getCartAntennaPos(); - - for(double brg = -az_limit; brg <= az_limit; brg += az_step){ - for(double elev = el_limit; elev >= - el_limit; elev -= el_step){ - setUserVec(brg, elev); - SGVec3d nearestHit; - globals->get_scenery()->get_cart_ground_intersection(cartantennapos, uservec, nearestHit); - SGGeodesy::SGCartToGeod(nearestHit, hitpos); - - double course1, course2, distance; - - SGGeodesy::inverse(hitpos, antennapos, course1, course2, distance); - - if (distance >= min_range && distance <= max_range) { - _terrain_warning_node->setBoolValue(true); - getMaterial(); - _brgDegNode->setDoubleValue(course2); - _rangeMNode->setDoubleValue(distance); - _materialNode->setStringValue(_mat_name.c_str()); - _bumpinessNode->setDoubleValue(_bumpinessFactor); - _elevationMNode->setDoubleValue(_elevation_m); - } else { - _terrain_warning_node->setBoolValue(false); - _brgDegNode->setDoubleValue(0); - _rangeMNode->setDoubleValue(0); - _materialNode->setStringValue(""); - _bumpinessNode->setDoubleValue(0); - _elevationMNode->setDoubleValue(0); - } - - //cout << "usr hdg " << _user_hdg_deg_node->getDoubleValue() - // << " ant brg " << course2 - // << " elev " << _Instrument->getDoubleValue("tilt") - // << " gnd rng nm " << distance * SG_METER_TO_NM - // << " ht " << hitpos.getElevationFt() - // << " mat " << _mat_name - // << " solid " << _solid - // << " bumpiness " << _bumpinessFactor - // << endl; - - } - } -} - - diff --git a/src/Instrumentation/agradar.hxx b/src/Instrumentation/agradar.hxx deleted file mode 100644 index dda152e0b..000000000 --- a/src/Instrumentation/agradar.hxx +++ /dev/null @@ -1,94 +0,0 @@ -// Air Ground Radar -// -// Written by Vivian MEAZZA, started Feb 2008. -// -// -// Copyright (C) 2008 Vivain MEAZZA - vivian.meazza@lineone.net -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// - -#ifndef _INST_AGRADAR_HXX -#define _INST_AGRADAR_HXX - -#include -#include -#include - -#include "wxradar.hxx" - -class agRadar : public wxRadarBg{ -public: - - agRadar ( SGPropertyNode *node ); - agRadar (); - virtual ~agRadar (); - - virtual void init (); - virtual void update (double dt); - - void setUserPos(); - void setUserVec(double az, double el); - void update_terrain(); - void setAntennaPos(); - - bool getMaterial(); - - double _load_resistance; // ground load resistanc N/m^2 - double _frictionFactor; // dimensionless modifier for Coefficient of Friction - double _bumpinessFactor; // dimensionless modifier for Bumpiness - double _elevation_m; // ground elevation in meters - bool _solid; // if true ground is solid for FDMs - - std::string _mat_name; // ground material - - SGVec3d getCartUserPos() const; - SGVec3d getCartAntennaPos()const; - - SGVec3d uservec; - - SGPropertyNode_ptr _user_hdg_deg_node; - SGPropertyNode_ptr _user_roll_deg_node; - SGPropertyNode_ptr _user_pitch_deg_node; - SGPropertyNode_ptr _terrain_warning_node; - - SGPropertyNode_ptr _xOffsetMNode; - SGPropertyNode_ptr _yOffsetMNode; - SGPropertyNode_ptr _zOffsetMNode; - - SGPropertyNode_ptr _elevLimitDegNode; - SGPropertyNode_ptr _elevStepDegNode; - SGPropertyNode_ptr _azLimitDegNode; - SGPropertyNode_ptr _azStepDegNode; - SGPropertyNode_ptr _maxRangeMNode; - SGPropertyNode_ptr _minRangeMNode; - SGPropertyNode_ptr _tiltNode; - - SGPropertyNode_ptr _brgDegNode; - SGPropertyNode_ptr _rangeMNode; - SGPropertyNode_ptr _elevationMNode; - SGPropertyNode_ptr _materialNode; - SGPropertyNode_ptr _bumpinessNode; - - SGPropertyNode_ptr _rollStabNode; - SGPropertyNode_ptr _pitchStabNode; - - SGGeod userpos; - SGGeod hitpos; - SGGeod antennapos; -}; - -#endif // _INST_AGRADAR_HXX diff --git a/src/Instrumentation/dclgps.hxx b/src/Instrumentation/dclgps.hxx index 1eeadfd67..405c5a70b 100644 --- a/src/Instrumentation/dclgps.hxx +++ b/src/Instrumentation/dclgps.hxx @@ -26,7 +26,8 @@ #ifndef _DCLGPS_HXX #define _DCLGPS_HXX -#include "render_area_2d.hxx" +#include + #include #include #include diff --git a/src/Instrumentation/groundradar.cxx b/src/Instrumentation/groundradar.cxx deleted file mode 100644 index b7d283413..000000000 --- a/src/Instrumentation/groundradar.cxx +++ /dev/null @@ -1,354 +0,0 @@ -// groundradar.cxx - Background layer for the ATC radar. -// -// Copyright (C) 2007 Csaba Halasz. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include
-#include
-#include -#include -#include -#include -#include -#include -#include - -#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(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 tess = new osgUtil::Tessellator; - osg::ref_ptr polygon = new osg::Geometry; - osg::ref_ptr 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( 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 bCurv( p1, p2, p3 ); - simgear::BezierCurve::PointList &ptList = bCurv.pointList(); - for ( simgear::BezierCurve::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 rwy_vertices = new osg::Vec3Array; - osg::ref_ptr taxi_vertices = new osg::Vec3Array; - osg::ref_ptr 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; inumTaxiways(); ++i) - { - FGTaxiway* txwy(apt->getTaxiwayByIndex(i)); - addRunwayVertices(txwy, tower_lat, tower_lon, scale, taxi_vertices.get()); - } - osg::Geometry *taxi_geom = dynamic_cast(_geode->getDrawable(0)); - taxi_geom->setVertexArray(taxi_vertices.get()); - osg::DrawArrays* taxi = dynamic_cast(taxi_geom->getPrimitiveSet(0)); - taxi->setCount(taxi_vertices->size()); - - osg::Geometry *pvt_geom = dynamic_cast(_geode->getDrawable(1)); - osg::Geometry::PrimitiveSetList &pvt_prim_list = pvt_geom->getPrimitiveSetList(); - pvt_prim_list.clear(); - for (unsigned int i=0; inumPavements(); ++i) - { - FGPavement* pvt(apt->getPavementByIndex(i)); - osg::ref_ptr geom = addPavementGeometry(pvt, tower_lat, tower_lon, scale); - osg::Geometry::PrimitiveSetList &prim_list = geom->getPrimitiveSetList(); - osg::Vec3Array *vertices = dynamic_cast(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(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(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(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(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; inumRunways(); ++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(_geode->getDrawable(2)); - rwy_geom->setVertexArray(rwy_vertices.get()); - osg::DrawArrays* rwy = dynamic_cast(rwy_geom->getPrimitiveSet(0)); - rwy->setCount(rwy_vertices->size()); - - getCamera()->setNodeMask(0xffffffff); -} - -// end of GroundRadar.cxx diff --git a/src/Instrumentation/groundradar.hxx b/src/Instrumentation/groundradar.hxx deleted file mode 100644 index fb603be19..000000000 --- a/src/Instrumentation/groundradar.hxx +++ /dev/null @@ -1,56 +0,0 @@ -// groundradar.hxx - Background layer for the ATC radar. -// -// Copyright (C) 2007 Csaba Halasz. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - -#ifndef __INST_GROUNDRADAR_HXX -#define __INST_GROUNDRADAR_HXX - -#include -#include -#include -#include "od_gauge.hxx" -#include - -// 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 _geode; - SGPropertyNode_ptr _airport_node; - SGPropertyNode_ptr _range_node; -}; - -#endif // __INST_GROUNDRADAR_HXX diff --git a/src/Instrumentation/instrument_mgr.cxx b/src/Instrumentation/instrument_mgr.cxx index 86b09e13e..b9e30154d 100644 --- a/src/Instrumentation/instrument_mgr.cxx +++ b/src/Instrumentation/instrument_mgr.cxx @@ -42,15 +42,11 @@ #include "turn_indicator.hxx" #include "vertical_speed_indicator.hxx" #include "inst_vertical_speed_indicator.hxx" -#include "wxradar.hxx" #include "tacan.hxx" #include "mk_viii.hxx" #include "mrg.hxx" -#include "groundradar.hxx" -#include "agradar.hxx" #include "rad_alt.hxx" #include "tcas.hxx" -#include "NavDisplay.hxx" FGInstrumentMgr::FGInstrumentMgr () : _explicitGps(false) @@ -124,7 +120,6 @@ bool FGInstrumentMgr::build (SGPropertyNode* config_props) if (index > 0) subsystemname << '['<< index << ']'; string id = subsystemname.str(); - _instruments.push_back(id); if ( name == "adf" ) { set_subsystem( id, new ADF( node ), 0.15 ); @@ -189,9 +184,6 @@ bool FGInstrumentMgr::build (SGPropertyNode* config_props) } else if ( name == "vertical-speed-indicator" ) { set_subsystem( id, new VerticalSpeedIndicator( node ) ); - } else if ( name == "radar" ) { - set_subsystem( id, new wxRadarBg ( node ) ); - } else if ( name == "inst-vertical-speed-indicator" ) { set_subsystem( id, new InstVerticalSpeedIndicator( node ) ); @@ -204,27 +196,30 @@ bool FGInstrumentMgr::build (SGPropertyNode* config_props) } else if ( name == "master-reference-gyro" ) { set_subsystem( id, new MasterReferenceGyro( node ) ); - } else if ( name == "groundradar" ) { - set_subsystem( id, new GroundRadar( node ) ); - - } else if ( name == "air-ground-radar" ) { - set_subsystem( id, new agRadar( node ) ); - + } else if (( name == "groundradar" ) || + ( name == "radar" ) || + ( name == "air-ground-radar" ) || + ( name == "navigation-display" )) + { + // these instruments are handled by the CockpitDisplayManager + // catch them here so we can still warn about bogus names in + // the instruments file + continue; } else if ( name == "radar-altimeter" ) { set_subsystem( id, new radAlt( node ) ); } else if ( name == "tcas" ) { set_subsystem( id, new TCAS( node ), 0.2); - - } else if ( name == "navigation-display" ) { - set_subsystem( id, new NavDisplay( node ) ); } else { SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: " << name ); return false; } - } + + // only push to our array if we actually built an insturment + _instruments.push_back(id); + } // of instruments iteration return true; } diff --git a/src/Instrumentation/od_gauge.cxx b/src/Instrumentation/od_gauge.cxx deleted file mode 100644 index c6be269e4..000000000 --- a/src/Instrumentation/od_gauge.cxx +++ /dev/null @@ -1,479 +0,0 @@ -// Owner Drawn Gauge helper class -// -// Written by Harald JOHNSEN, started May 2005. -// -// Copyright (C) 2005 Harald JOHNSEN -// -// Ported to OSG by Tim Moore - Jun 2007 -// -// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012 -// Supports now multisampling/mipmapping, usage of the stencil buffer and placing -// the texture in the scene by certain filter criteria -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // for GL_DEPTH_STENCIL_EXT on Windows - -#include - -#include -#include - -#include
-#include -#include -#include "od_gauge.hxx" - -#include - -//------------------------------------------------------------------------------ -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(&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(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 - ( - 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 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 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 _group; - }; - - std::string _tex_name, ///get_scenery()->get_aircraft_branch(); - ReplaceStaticTextureVisitor visitor(name, new_texture); - root->accept(visitor); - return visitor.getPlacements(); -} - -//------------------------------------------------------------------------------ -canvas::Placements FGODGauge::set_texture( const SGPropertyNode* placement, - osg::Texture2D* new_texture, - osg::NodeCallback* cull_callback ) -{ - osg::Group* root = globals->get_scenery()->get_aircraft_branch(); - ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback); - root->accept(visitor); - return visitor.getPlacements(); -} diff --git a/src/Instrumentation/od_gauge.hxx b/src/Instrumentation/od_gauge.hxx deleted file mode 100644 index 7c4ee00ed..000000000 --- a/src/Instrumentation/od_gauge.hxx +++ /dev/null @@ -1,177 +0,0 @@ -// Owner Drawn Gauge helper class -// -// Written by Harald JOHNSEN, started May 2005. -// -// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net -// -// Ported to OSG by Tim Moore - Jun 2007 -// -// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012 -// Supports now multisampling/mipmapping, usage of the stencil buffer and placing -// the texture in the scene by certain filter criteria -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// - -#ifndef _OD_GAUGE_HXX -#define _OD_GAUGE_HXX - -#include -#include - -#include -#include - -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 camera; - osg::ref_ptr texture; - - void updateCoordinateFrame(); - void updateStencil(); - void updateSampling(); - -}; - -#endif // _OD_GAUGE_HXX diff --git a/src/Instrumentation/rad_alt.hxx b/src/Instrumentation/rad_alt.hxx index 7ab1e657f..5babe3160 100644 --- a/src/Instrumentation/rad_alt.hxx +++ b/src/Instrumentation/rad_alt.hxx @@ -28,7 +28,7 @@ #include #include -#include "agradar.hxx" +#include class radAlt : public agRadar{ public: diff --git a/src/Instrumentation/render_area_2d.cxx b/src/Instrumentation/render_area_2d.cxx deleted file mode 100644 index 8ad68a74b..000000000 --- a/src/Instrumentation/render_area_2d.cxx +++ /dev/null @@ -1,426 +0,0 @@ -// RenderArea2D.cxx - a class to manage 2D polygon-based drawing -// for a complex instrument (eg. GPS). -// -// Written by David Luff, started 2005. -// -// Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// $Id$ - - -#ifdef HAVE_CONFIG_H -# include -#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 renderArea2DStateSet; - if(!renderArea2DStateSet.valid()) { - renderArea2DStateSet = new osg::StateSet; - renderArea2DStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF); - renderArea2DStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - } - - state.pushStateSet(renderArea2DStateSet.get()); - state.apply(); - state.setActiveTextureUnit(0); - state.setClientActiveTextureUnit(0); - - // DCL - the 2 lines below are copied verbatim from the hotspot drawing code. - // I am not sure if they are needed here or not. - glPushAttrib(GL_ENABLE_BIT); - glDisable(GL_COLOR_MATERIAL); - - // FIXME - disabling all clip planes causes bleed-through through the splash screen. - glDisable(GL_CLIP_PLANE0); - glDisable(GL_CLIP_PLANE1); - glDisable(GL_CLIP_PLANE2); - glDisable(GL_CLIP_PLANE3); - - DoDrawBackground(); - - for(unsigned int i = 0; i < drawing_list.size(); ++i) { - RA2DPrimitive prim = drawing_list[i]; - switch(prim.type) { - case RA2D_LINE: - DoDrawLine(prim.x1, prim.y1, prim.x2, prim.y2); - break; - case RA2D_QUAD: - if(prim.debug) { - //cout << "Clipping = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n'; - //cout << "Drawing quad " << prim.x1 << ", " << prim.y1 << " to " << prim.x2 << ", " << prim.y2 << '\n'; - } - DoDrawQuad(prim.x1, prim.y1, prim.x2, prim.y2, prim.invert); - break; - case RA2D_PIXEL: - DoDrawPixel(prim.x1, prim.y1, prim.invert); - break; - } - } - - drawing_list.clear(); - - glPopAttrib(); - - state.popStateSet(); - state.apply(); - state.setActiveTextureUnit(0); - state.setClientActiveTextureUnit(0); -} - -void RenderArea2D::Flush() { - drawing_list.clear(); -} - -void RenderArea2D::SetPixelColor(const float* rgba) { - _pixelColor[0] = rgba[0]; - _pixelColor[1] = rgba[1]; - _pixelColor[2] = rgba[2]; - _pixelColor[3] = rgba[3]; -} - -// Set clipping region in logical units -void RenderArea2D::SetClipRegion(int x1, int y1, int x2, int y2) { - _clipx1 = x1; - _clipx2 = x2; - _clipy1 = y1; - _clipy2 = y2; - //cout << "Set clip region, clip region = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n'; -} - -// Set clip region to be the same as the rendered area (default) -void RenderArea2D::ResetClipRegion() { - _clipx1 = 0; - _clipx2 = _logx - 1; - _clipy1 = 0; - _clipy2 = _logy - 1; - //cout << "Reset clip region, clip region = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n'; -} - -void RenderArea2D::SetPosition(int posx, int posy) { - _posx = posx; - _posy = posy; -} - -void RenderArea2D::SetLogicalSize(int logx, int logy) { - _logx = logx; - _logy = logy; -} - -void RenderArea2D::SetActualSize(int sizex, int sizey) { - _sizex = sizex; - _sizey = sizey; -} - -void RenderArea2D::DrawPixel(int x, int y, bool invert) { - // Clip - if(x < _clipx1 || x > _clipx2 || y < _clipy1 || y > _clipy2) return; - - RA2DPrimitive prim; - prim.x1 = x; - prim.y1 = y; - prim.x2 = 0; - prim.y2 = 0; - prim.type = RA2D_PIXEL; - prim.invert = invert; - drawing_list.push_back(prim); -} - -void RenderArea2D::DoDrawPixel(int x, int y, bool invert) { - // Clip. In theory this shouldn't be necessary, since all input is clipped before adding - // to the drawing list, but it ensures that any errors in clipping lines etc will only - // spill over the clip area within the instrument, and still be clipped from straying - // outside the instrument. - if(x < _clipx1 || x > _clipx2 || y < _clipy1 || y > _clipy2) return; - - // Scale to position within background - float fx1 = (float)x, fy1 = (float)y; - float rx = (float)_sizex / (float)_logx; - float ry = (float)_sizey / (float)_logy; - fx1 *= rx; - fy1 *= ry; - float fx2 = fx1 + rx; - float fy2 = fy1 + ry; - - // Translate to final position - fx1 += (float)_posx; - fx2 += (float)_posx; - fy1 += (float)_posy; - fy2 += (float)_posy; - - //cout << "DP: " << fx1 << ", " << fy1 << " ... " << fx2 << ", " << fy2 << '\n'; - - SetRenderColor(invert ? _backgroundColor : _pixelColor); - SGVec2f corners[4] = { - SGVec2f(fx1, fy1), - SGVec2f(fx2, fy1), - SGVec2f(fx2, fy2), - SGVec2f(fx1, fy2) - }; - RenderQuad(corners); -} - -void RenderArea2D::DrawLine(int x1, int y1, int x2, int y2) { - // We need to clip the line to the current region before storing it in the drawing - // list, since when we come to actually draw it the clip region may have changed. - - // Liang-Barsky clipping algorithm - int p[4], q[4]; - float u1 = 0.0f, u2 = 1.0f; - p[0] = -(x2 - x1); q[0] = (x1 - _clipx1); - p[1] = (x2 - x1); q[1] = (_clipx2 - x1); - p[2] = -(y2 - y1); q[2] = (y1 - _clipy1); - p[3] = (y2 - y1); q[3] = (_clipy2 - y1); - - for(int i=0; i<4; ++i) { - if(p[i] == 0) { - if(q[i] < 0) { - // Then we have a trivial rejection of a line parallel to a clip plane - // completely outside the clip region. - return; - } - } else if(p[i] < 0) { - float r = (float)q[i]/(float)p[i]; - u1 = (u1 > r ? u1 : r); - } else { // p[i] > 0 - float r = (float)q[i]/(float)p[i]; - u2 = (u2 < r ? u2 : r); - } - if(u1 > u2) { - // Then the line is completely outside the clip area. - return; - } - } - - float fx1 = x1 + u1 * (float)(x2 - x1); - float fy1 = y1 + u1 * (float)(y2 - y1); - float fx2 = x1 + u2 * (float)(x2 - x1); - float fy2 = y1 + u2 * (float)(y2 - y1); - x1 = (int)(fx1 + 0.5); - y1 = (int)(fy1 + 0.5); - x2 = (int)(fx2 + 0.5); - y2 = (int)(fy2 + 0.5); - - RA2DPrimitive prim; - prim.x1 = x1; - prim.y1 = y1; - prim.x2 = x2; - prim.y2 = y2; - prim.type = RA2D_LINE; - prim.invert = false; - drawing_list.push_back(prim); -} - -void RenderArea2D::DoDrawLine(int x1, int y1, int x2, int y2) { - // Crude implementation of Bresenham line drawing algorithm. - - // Our lines are non directional, so first order the points x-direction-wise to leave only 4 octants to consider. - if(x2 < x1) { - int tmp_x = x1; - int tmp_y = y1; - x1 = x2; - y1 = y2; - x2 = tmp_x; - y2 = tmp_y; - } - - bool flip_y = (y1 > y2 ? true : false); - int dx = x2 - x1; - int dy = (flip_y ? y1 - y2 : y2 - y1); - if(dx > dy) { - // push the x dir - int y = y1; - int yn = dx/2; - for(int x=x1; x<=x2; ++x) { - DoDrawPixel(x, y); - yn += dy; - if(yn >= dx) { - yn -= dx; - y = (flip_y ? y - 1 : y + 1); - } - } - } else { - // push the y dir - int x = x1; - int xn = dy/2; - // Must be a more elegant way to roll the next two cases into one! - if(flip_y) { - for(int y=y1; y>=y2; --y) { - DoDrawPixel(x, y); - xn += dx; - if(xn >= dy) { - xn -= dy; - x++; - } - } - } else { - for(int y=y1; y<=y2; ++y) { - DoDrawPixel(x, y); - xn += dx; - if(xn >= dy) { - xn -= dy; - x++; - } - } - } - } -} - -void RenderArea2D::DrawQuad(int x1, int y1, int x2, int y2, bool invert) { - // Force the input to be ordered with x1 < x2 and y1 < y2. - if(x1 > x2) { - int x = x2; - x2 = x1; - x1 = x; - } - if(y1 > y2) { - int y = y2; - y2 = y1; - y1 = y; - } - - // Clip the input to the current drawing region. - x1 = x1 < _clipx1 ? _clipx1 : x1; - if(x1 > _clipx2) { return; } - x2 = x2 > _clipx2 ? _clipx2 : x2; - if(x2 < _clipx1) { return; } - y1 = y1 < _clipy1 ? _clipy1 : y1; - if(y1 > _clipy2) { return; } - y2 = y2 > _clipy2 ? _clipy2 : y2; - if(y2 < _clipy1) { return; } - - RA2DPrimitive prim; - prim.x1 = x1; - prim.y1 = y1; - prim.x2 = x2; - prim.y2 = y2; - prim.type = RA2D_QUAD; - prim.invert = invert; - if(_ra2d_debug) prim.debug = true; - drawing_list.push_back(prim); -} - -void RenderArea2D::DoDrawQuad(int x1, int y1, int x2, int y2, bool invert) { - // Scale to position within background - float fx1 = (float)x1, fy1 = (float)y1; - float fx2 = (float)x2, fy2 = (float)y2; - float rx = (float)_sizex / (float)_logx; - float ry = (float)_sizey / (float)_logy; - fx1 *= rx; - fy1 *= ry; - fx2 *= rx; - fy2 *= ry; - - fx2 += rx; - fy2 += ry; - - // Translate to final position - fx1 += (float)_posx; - fx2 += (float)_posx; - fy1 += (float)_posy; - fy2 += (float)_posy; - - //cout << "DP: " << fx1 << ", " << fy1 << " ... " << fx2 << ", " << fy2 << '\n'; - - SetRenderColor(invert ? _backgroundColor : _pixelColor); - SGVec2f corners[4] = { - SGVec2f(fx1, fy1), - SGVec2f(fx2, fy1), - SGVec2f(fx2, fy2), - SGVec2f(fx1, fy2) - }; - RenderQuad(corners); -} - -void RenderArea2D::DrawBackground() { - // Currently a NO-OP -} - -void RenderArea2D::DoDrawBackground() { - SetRenderColor(_backgroundColor); - SGVec2f corners[4] = { - SGVec2f(_posx, _posy), - SGVec2f(_posx + _sizex, _posy), - SGVec2f(_posx + _sizex, _posy + _sizey), - SGVec2f(_posx, _posy + _sizey) - }; - - RenderQuad(corners); -} - -// ----------------------------------------- -// -// Actual drawing routines copied from Atlas -// -// ----------------------------------------- - -void RenderArea2D::SetRenderColor( const float *rgba ) { - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, rgba); - glColor4fv( rgba ); -} - -void RenderArea2D::RenderQuad( const SGVec2f *p) { - glBegin(GL_QUADS); - glNormal3f(0.0f, 0.0f, 0.0f); - glVertex2fv( p[0].data() ); - glVertex2fv( p[1].data() ); - glVertex2fv( p[2].data() ); - glVertex2fv( p[3].data() ); - glEnd(); -} - -void RenderArea2D::RenderQuad( const SGVec2f *p, const SGVec4f *color ) { - glBegin(GL_QUADS); - glNormal3f(0.0f, 0.0f, 0.0f); - glColor4fv( color[0].data() ); glVertex2fv( p[0].data() ); - glColor4fv( color[1].data() ); glVertex2fv( p[1].data() ); - glColor4fv( color[2].data() ); glVertex2fv( p[2].data() ); - glColor4fv( color[3].data() ); glVertex2fv( p[3].data() ); - glEnd(); -} diff --git a/src/Instrumentation/render_area_2d.hxx b/src/Instrumentation/render_area_2d.hxx deleted file mode 100644 index 83d74efef..000000000 --- a/src/Instrumentation/render_area_2d.hxx +++ /dev/null @@ -1,116 +0,0 @@ -// RenderArea2D.hxx - a class to manage 2D polygon-based drawing -// for a complex instrument (eg. GPS). -// -// Written by David Luff, started 2005. -// -// Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// $Id$ - -#ifndef _RENDER_AREA_2D -#define _RENDER_AREA_2D - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include - -#include -#include - -#include - -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 drawing_list; - - // Control whether to output debugging output - bool _ra2d_debug; -}; - -#endif diff --git a/src/Instrumentation/wxradar.cxx b/src/Instrumentation/wxradar.cxx deleted file mode 100644 index c773c6c2c..000000000 --- a/src/Instrumentation/wxradar.cxx +++ /dev/null @@ -1,1116 +0,0 @@ -// Wx Radar background texture -// -// Written by Harald JOHNSEN, started May 2005. -// With major amendments by Vivian MEAZZA May 2007 -// Ported to OSG by Tim Moore Jun 2007 -// -// -// Copyright (C) 2005 Harald JOHNSEN -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -using std::stringstream; -using std::endl; -using std::setprecision; -using std::fixed; -using std::setw; -using std::setfill; -using std::string; - -#include
-#include
-#include - -#include "instrument_mgr.hxx" -#include "od_gauge.hxx" -#include "wxradar.hxx" - -#include // 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(_geom->getPrimitiveSet(1)); - osg::DrawArrays *trimaskPSet - = static_cast(_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(_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 "<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 fontOptions = new osgDB::ReaderWriter::Options("monochrome"); - osg::ref_ptr font = osgText::readFontFile(tpath.c_str(), fontOptions.get()); -#else - osg::ref_ptr font = osgText::readFontFile(tpath.c_str()); -#endif - - if (font != 0) { - _font = font; - _font->setMinFilterHint(osg::Texture::NEAREST); - _font->setMagFilterHint(osg::Texture::NEAREST); - _font->setGlyphImageMargin(0); - _font->setGlyphImageMarginRatio(0); - } - - for (int i=0;i<4;i++) - { - const float defaultColors[4][3] = {{0,1,1},{0,1,1},{1,0.5,0},{1,0,0}}; - SGPropertyNode_ptr color_node = _font_node->getNode("tcas/color",i,true); - float red = color_node->getFloatValue("red",defaultColors[i][0]); - float green = color_node->getFloatValue("green",defaultColors[i][1]); - float blue = color_node->getFloatValue("blue",defaultColors[i][2]); - float alpha = color_node->getFloatValue("alpha",1); - _tcas_colors[i]=osg::Vec4(red, green, blue, alpha); - } -} - -void -wxRadarBg::valueChanged(SGPropertyNode*) -{ - updateFont(); - _time = _interval; -} - diff --git a/src/Instrumentation/wxradar.hxx b/src/Instrumentation/wxradar.hxx deleted file mode 100644 index 7aff77717..000000000 --- a/src/Instrumentation/wxradar.hxx +++ /dev/null @@ -1,202 +0,0 @@ -// Wx Radar background texture -// -// Written by Harald JOHNSEN, started May 2005. -// With major amendments by Vivian MEAZZA May 2007 -// Ported to OSG by Tim MOORE Jun 2007 -// -// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -#ifndef _INST_WXRADAR_HXX -#define _INST_WXRADAR_HXX - -#include -#include -#include -#include - -#include -#include - -#include -#include - -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_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 - 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 _resultTexture; - osg::ref_ptr _wxEcho; - osg::ref_ptr _radarGeode; - osg::ref_ptr _textGeode; - osg::Geometry *_geom; - osg::Vec2Array *_vertices; - osg::Vec2Array *_texCoords; - osg::Matrixf _centerTrans; - osg::ref_ptr _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 diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 1def26fe1..9e8c4d83b 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -99,6 +99,7 @@ #include #include #include +#include #include "fg_init.hxx" #include "fg_io.hxx" @@ -607,6 +608,7 @@ void fgCreateSubsystems() { globals->add_subsystem("systems", new FGSystemMgr, SGSubsystemMgr::FDM); globals->add_subsystem("instrumentation", new FGInstrumentMgr, SGSubsystemMgr::FDM); globals->add_subsystem("hud", new HUD, SGSubsystemMgr::DISPLAY); + globals->add_subsystem("cockpit-displays", new flightgear::CockpitDisplayManager, SGSubsystemMgr::DISPLAY); //////////////////////////////////////////////////////////////////// // Initialize the XML Autopilot subsystem.