]> git.mxchange.org Git - flightgear.git/blobdiff - src/Instrumentation/NavDisplay.cxx
NavDisplay: fix update lag when switching range or centre.
[flightgear.git] / src / Instrumentation / NavDisplay.cxx
index 4cc9d5b3b1ce28dfba986a4a0b8a750c5afbaad1..45fdc3ba7e4e32ee632e26755ee4ee30c6025ecd 100644 (file)
@@ -42,6 +42,7 @@
 #include <simgear/scene/model/model.hxx>
 #include <simgear/structure/exception.hxx>
 #include <simgear/misc/sg_path.hxx>
+#include <simgear/misc/strutils.hxx>
 #include <simgear/math/sg_geodesy.hxx>
 
 #include <sstream>
@@ -161,6 +162,22 @@ private:
     NavDisplay* _nd;
 };
 
+class NavDisplay::ForceUpdateListener : public SGPropertyChangeListener
+{
+public:
+  ForceUpdateListener(NavDisplay *nd) : 
+    _nd(nd)
+  {}
+  
+  virtual void valueChanged (SGPropertyNode * prop)
+  {
+    SG_LOG(SG_INSTR, SG_INFO, "forcing NavDisplay update");
+    _nd->forceUpdate();
+  }
+private:
+  NavDisplay* _nd;
+};
+
 ///////////////////////////////////////////////////////////////////
 
 class SymbolDef
@@ -170,7 +187,7 @@ public:
       enable(NULL)
     {}
   
-    bool initFromNode(SGPropertyNode* node)
+    bool initFromNode(SGPropertyNode* node, NavDisplay* owner)
     {
         type = node->getStringValue("type");
         SGPropertyNode* enableNode = node->getChild("enable");
@@ -202,7 +219,7 @@ public:
         xy1.y()  = node->getFloatValue("y1", 5);
       }
       
-        double texSize = node->getFloatValue("texture-size", 1.0);
+        double texSize = node->getFloatValue("texture-size", owner->textureSize());
         
         uv0.x()  = node->getFloatValue("u0", 0) / texSize;
         uv0.y()  = node->getFloatValue("v0", 0) / texSize;
@@ -232,8 +249,8 @@ public:
         if (stretchSymbol) {
             stretchY2 = node->getFloatValue("y2");
             stretchY3 = node->getFloatValue("y3");
-            stretchV2 = node->getFloatValue("v2");
-            stretchV3 = node->getFloatValue("v3");
+            stretchV2 = node->getFloatValue("v2") / texSize;
+            stretchV3 = node->getFloatValue("v3") / texSize;
         }
       
         return true;
@@ -353,6 +370,7 @@ NavDisplay::NavDisplay(SGPropertyNode *node) :
     _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),
@@ -374,12 +392,13 @@ NavDisplay::NavDisplay(SGPropertyNode *node) :
     INITFONT("color/alpha", 1, Float);
 #undef INITFONT
 
+    _textureSize = _Instrument->getNode("symbol-teture-size", true)->getIntValue();
     SGPropertyNode* symbolsNode = node->getNode("symbols");
     SGPropertyNode* symbol;
-  
+
     for (int i = 0; (symbol = symbolsNode->getChild("symbol", i)) != NULL; ++i) {
         SymbolDef* def = new SymbolDef;
-        if (!def->initFromNode(symbol)) {
+        if (!def->initFromNode(symbol, this)) {
           delete def;
           continue;
         }
@@ -399,12 +418,21 @@ 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);
     _rangeNode->setDoubleValue(40.0);
     _rangeNode->addChangeListener(_cacheListener.get());
-    
+    _rangeNode->addChangeListener(_forceUpdateListener.get());
+  
+    _xCenterNode = _Instrument->getNode("x-center");
+    _xCenterNode->setDoubleValue(0.5);
+    _xCenterNode->addChangeListener(_forceUpdateListener.get());
+    _yCenterNode = _Instrument->getNode("y-center");
+    _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");
@@ -431,6 +459,8 @@ NavDisplay::init ()
     
     _excessDataNode = _Instrument->getChild("excess-data", 0, true);
     _excessDataNode->setBoolValue(false);
+    _testModeNode = _Instrument->getChild("test-mode", 0, true);
+    _testModeNode->setBoolValue(false);
   
 // OSG geometry setup
     _radarGeode = new osg::Geode;
@@ -440,7 +470,8 @@ NavDisplay::init ()
     
     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);
@@ -519,25 +550,44 @@ NavDisplay::update (double delta_time_sec)
     return;
   }
   
-  _time += delta_time_sec;
-  if (_time < _updateInterval){
-    return;
+  if (_forceUpdate) {
+    _forceUpdate = false;
+    _time = 0.0;
+  } else {
+    _time += delta_time_sec;
+    if (_time < _updateInterval){
+      return;
+    }
+    _time -= _updateInterval;
   }
-  _time -= _updateInterval;
 
   _rangeNm = _rangeNode->getFloatValue();
-  if (_Instrument->getBoolValue("aircraft-heading-up", true)) {
+  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);
   }
   
-  _scale = _odg->size() / _rangeNm;
+  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);
+  
   
-  double xCenterFrac = _Instrument->getDoubleValue("x-center", 0.5);
-  double yCenterFrac = _Instrument->getDoubleValue("y-center", 0.5);
-  _centerTrans = osg::Matrixf::translate(xCenterFrac * _odg->size(), 
-      yCenterFrac * _odg->size(), 0.0);
+  _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
@@ -576,11 +626,16 @@ NavDisplay::update (double delta_time_sec)
     }
   }
   
-  processRoute();
-  processNavRadios();
-  processAI();
-  findItems();
-  limitDisplayedSymbols();
+  if (_testModeNode->getBoolValue()) {
+    addTestSymbols();
+  } else {
+    processRoute();
+    processNavRadios();
+    processAI();
+    findItems();
+    limitDisplayedSymbols();
+  }
+
   addSymbolsToScene();
   
   _symbolPrimSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size());
@@ -635,7 +690,7 @@ void NavDisplay::addSymbolToScene(SymbolInstance* sym)
     verts[3] = osg::Vec2(def->xy0.x(), def->xy1.y());
     
     if (def->rotateToHeading) {
-        osg::Matrixf m(degRotation(sym->headingDeg));
+        osg::Matrixf m(degRotation(sym->headingDeg - _view_heading));
         for (int i=0; i<4; ++i) {
             verts[i] = mult(verts[i], m);
         }
@@ -663,7 +718,7 @@ void NavDisplay::addSymbolToScene(SymbolInstance* sym)
         stretchVerts[2] = osg::Vec2(def->xy1.x(), def->stretchY3);
         stretchVerts[3] = osg::Vec2(def->xy0.x(), def->stretchY3);
         
-        osg::Matrixf m(degRotation(sym->headingDeg));
+        osg::Matrixf m(degRotation(sym->headingDeg - _view_heading));
         for (int i=0; i<4; ++i) {
             stretchVerts[i] = mult(stretchVerts[i], m);
         }
@@ -990,8 +1045,17 @@ void NavDisplay::foundPositionedItem(FGPositioned* pos)
     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(SymbolDef* r, rules) {
-        addSymbolInstance(projected, heading, r, vars);
+        SymbolInstance* ins = addSymbolInstance(projected, heading, r, vars);
+        if (pos->type() == FGPositioned::RUNWAY) {
+            FGRunway* rwy = (FGRunway*) pos;
+            ins->endPos = projectGeod(rwy->end());
+        }
     }
 }
 
@@ -1001,6 +1065,7 @@ void NavDisplay::computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyN
     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:
@@ -1010,11 +1075,12 @@ void NavDisplay::computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyN
         nd->setDoubleValue("frequency-mhz", nav->get_freq());
         
         if (pos == _nav1Station) {
-            nd->setIntValue("heading-deg", _navRadio1Node->getDoubleValue("radials/target-radial-deg"));
+            heading = _navRadio1Node->getDoubleValue("radials/target-radial-deg");
         } else if (pos == _nav2Station) {
-            nd->setIntValue("heading-deg", _navRadio2Node->getDoubleValue("radials/target-radial-deg"));
+            heading = _navRadio2Node->getDoubleValue("radials/target-radial-deg");
         }
         
+        nd->setIntValue("heading-deg", heading);
         break;
     }
 
@@ -1026,7 +1092,8 @@ void NavDisplay::computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyN
         
     case FGPositioned::RUNWAY: {
         FGRunway* rwy = static_cast<FGRunway*>(pos);
-        nd->setDoubleValue("heading-deg", rwy->headingDeg());
+        heading = rwy->headingDeg();
+        nd->setDoubleValue("heading-deg", heading);
         nd->setIntValue("length-ft", rwy->lengthFt());
         nd->setStringValue("airport", rwy->airport()->ident());
         break;
@@ -1100,6 +1167,16 @@ void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states)
     } // 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);
@@ -1114,7 +1191,7 @@ void NavDisplay::processAI()
         string_set ss;
         computeAIStates(model, ss);        
         SymbolDefVector rules;
-        findRules(string("ai-") + model->getName(), ss, rules);
+        findRules(mapAINodeToType(model), ss, rules);
         if (rules.empty()) {
             return; // no rules matched, we can skip this item
         }
@@ -1137,11 +1214,15 @@ void NavDisplay::processAI()
 void NavDisplay::computeAIStates(const SGPropertyNode* ai, string_set& states)
 {
     int threatLevel = ai->getIntValue("tcas/threat-level",-1);
-    if (threatLevel >= 0) {
-        states.insert("tcas");
-    //    states.insert("tcas-threat-level-" + itoa(threatLevel));
-    }
-    
+    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");
@@ -1150,15 +1231,15 @@ void NavDisplay::computeAIStates(const SGPropertyNode* ai, string_set& states)
     }
 }
 
-bool NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars)
+SymbolInstance* NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars)
 {
     if (isProjectedClipped(proj)) {
-        return false;
+        return NULL;
     }
     
     SymbolInstance* sym = new SymbolInstance(proj, heading, def, vars);
     _symbols.push_back(sym);
-    return true;
+    return sym;
 }
 
 bool NavDisplay::isProjectedClipped(const osg::Vec2& projected) const
@@ -1170,6 +1251,55 @@ bool NavDisplay::isProjectedClipped(const osg::Vec2& projected) const
             (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);
+  }
+  
+  SymbolDefVector rules;
+  findRules(type, stateSet, rules);
+  if (rules.empty()) {
+    return; // no rules matched, we can skip this item
+  }
+    
+  osg::Vec2 projected = projectGeod(pos);
+  BOOST_FOREACH(SymbolDef* r, rules) {
+    addSymbolInstance(projected, heading, r, 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);
+}