]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/NavDisplay.cxx
Expose departure+arrival airport + runway on route-manager.
[flightgear.git] / src / Instrumentation / NavDisplay.cxx
1 // navigation display texture
2 //
3 // Written by James Turner, forked from wxradar code
4 //
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License as
8 // published by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 //
20 //
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include "NavDisplay.hxx"
27
28 #include <osg/Array>
29 #include <osg/Geometry>
30 #include <osg/Matrixf>
31 #include <osg/PrimitiveSet>
32 #include <osg/StateSet>
33 #include <osg/LineWidth>
34 #include <osg/Version>
35
36 #include <simgear/constants.h>
37 #include <simgear/misc/sg_path.hxx>
38 #include <simgear/scene/model/model.hxx>
39 #include <simgear/structure/exception.hxx>
40 #include <simgear/misc/sg_path.hxx>
41 #include <simgear/math/sg_geodesy.hxx>
42
43 #include <sstream>
44 #include <iomanip>
45 #include <iostream>             // for cout, endl
46
47 using std::stringstream;
48 using std::endl;
49 using std::setprecision;
50 using std::fixed;
51 using std::setw;
52 using std::setfill;
53 using std::cout;
54 using std::endl;
55
56 #include <Main/fg_props.hxx>
57 #include <Main/globals.hxx>
58 #include <Cockpit/panel.hxx>
59 #include <Navaids/routePath.hxx>
60 #include <Autopilot/route_mgr.hxx>
61 #include <Navaids/navrecord.hxx>
62 #include <Navaids/navlist.hxx>
63 #include <Navaids/fix.hxx>
64 #include <Airports/simple.hxx>
65 #include <Airports/runways.hxx>
66
67 #include "instrument_mgr.hxx"
68 #include "od_gauge.hxx"
69
70 static const int SYMBOL_TEX_DIM = 8;
71 static const float UNIT = 1.0 / SYMBOL_TEX_DIM;
72 static const char *DEFAULT_FONT = "typewriter.txf";
73
74 NavDisplay::NavDisplay(SGPropertyNode *node) :
75     _name(node->getStringValue("name", "nd")),
76     _num(node->getIntValue("number", 0)),
77     _time(0.0),
78     _updateInterval(node->getDoubleValue("update-interval-sec", 0.1)),
79     _odg(0),
80     _scale(0),
81     _view_heading(0),
82     _resultTexture(0),
83     _font_size(0),
84     _font_spacing(0),
85     _rangeNm(0)
86 {
87     _Instrument = fgGetNode(string("/instrumentation/" + _name).c_str(), _num, true);
88     _font_node = _Instrument->getNode("font", true);
89
90 #define INITFONT(p, val, type) if (!_font_node->hasValue(p)) _font_node->set##type##Value(p, val)
91     INITFONT("name", DEFAULT_FONT, String);
92     INITFONT("size", 8, Float);
93     INITFONT("line-spacing", 0.25, Float);
94     INITFONT("color/red", 0, Float);
95     INITFONT("color/green", 0.8, Float);
96     INITFONT("color/blue", 0, Float);
97     INITFONT("color/alpha", 1, Float);
98 #undef INITFONT
99
100 }
101
102
103 NavDisplay::~NavDisplay()
104 {
105 }
106
107
108 void
109 NavDisplay::init ()
110 {
111     _serviceable_node = _Instrument->getNode("serviceable", true);
112
113     // texture name to use in 2D and 3D instruments
114     _texture_path = _Instrument->getStringValue("radar-texture-path",
115         "Aircraft/Instruments/Textures/od_wxradar.rgb");
116     _resultTexture = FGTextureManager::createTexture(_texture_path.c_str(), false);
117
118     string path = _Instrument->getStringValue("symbol-texture-path",
119         "Aircraft/Instruments/Textures/nd-symbols.png");
120     SGPath tpath = globals->resolve_aircraft_path(path);
121
122     // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect
123     _symbols = SGLoadTexture2D(tpath, NULL, false, false);
124
125     FGInstrumentMgr *imgr = (FGInstrumentMgr *)globals->get_subsystem("instrumentation");
126     _odg = (FGODGauge *)imgr->get_subsystem("od_gauge");
127     _odg->setSize(_Instrument->getIntValue("texture-size", 512));
128
129
130     _user_lat_node = fgGetNode("/position/latitude-deg", true);
131     _user_lon_node = fgGetNode("/position/longitude-deg", true);
132     _user_alt_node = fgGetNode("/position/altitude-ft", true);
133
134     SGPropertyNode *n = _Instrument->getNode("display-controls", true);
135     _radar_weather_node     = n->getNode("WX", true);
136     _radar_position_node    = n->getNode("pos", true);
137     _radar_data_node        = n->getNode("data", true);
138     _radar_symbol_node      = n->getNode("symbol", true);
139     _radar_centre_node      = n->getNode("centre", true);
140     _radar_tcas_node        = n->getNode("tcas", true);
141     _radar_absalt_node      = n->getNode("abs-altitude", true);
142     _radar_arpt_node        = n->getNode("airport", true);
143     _radar_station_node     = n->getNode("station", true);
144     _draw_track_node        = n->getNode("ground-track", true);
145     _draw_heading_node      = n->getNode("heading", true);
146     _draw_north_node        = n->getNode("north", true);
147     _draw_fix_node          = n->getNode("fixes", true);
148     
149     _ai_enabled_node = fgGetNode("/sim/ai/enabled", true);
150     _route = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
151     
152     _navRadio1Node = fgGetNode("/instrumentation/nav[0]", true);
153     _navRadio2Node = fgGetNode("/instrumentation/nav[1]", true);
154     
155 // OSG geometry setup
156     _radarGeode = new osg::Geode;
157
158     _geom = new osg::Geometry;
159     _geom->setUseDisplayList(false);
160     
161     osg::StateSet *stateSet = _geom->getOrCreateStateSet();
162     stateSet->setTextureAttributeAndModes(0, _symbols.get());
163     
164     // Initially allocate space for 128 quads
165     _vertices = new osg::Vec2Array;
166     _vertices->setDataVariance(osg::Object::DYNAMIC);
167     _vertices->reserve(128 * 4);
168     _geom->setVertexArray(_vertices);
169     _texCoords = new osg::Vec2Array;
170     _texCoords->setDataVariance(osg::Object::DYNAMIC);
171     _texCoords->reserve(128 * 4);
172     _geom->setTexCoordArray(0, _texCoords);
173     
174     _quadColors = new osg::Vec3Array;
175     _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
176     _geom->setColorArray(_quadColors);
177     
178     _symbolPrimSet = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
179     _symbolPrimSet->setDataVariance(osg::Object::DYNAMIC);
180     _geom->addPrimitiveSet(_symbolPrimSet);
181     
182     _geom->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f),
183         osg::Vec3f(256.0f, 256.0f, 0.0f)));
184     _radarGeode->addDrawable(_geom);
185     _odg->allocRT();
186     // Texture in the 2D panel system
187     FGTextureManager::addTexture(_texture_path.c_str(), _odg->getTexture());
188
189     _lineGeometry = new osg::Geometry;
190     _lineGeometry->setUseDisplayList(false);
191     stateSet = _lineGeometry->getOrCreateStateSet();    
192     osg::LineWidth *lw = new osg::LineWidth();
193     lw->setWidth(2.0);
194     stateSet->setAttribute(lw);
195     
196     _lineVertices = new osg::Vec2Array;
197     _lineVertices->setDataVariance(osg::Object::DYNAMIC);
198     _lineVertices->reserve(128 * 4);
199     _lineGeometry->setVertexArray(_lineVertices);
200     
201                   
202     _lineColors = new osg::Vec3Array;
203     _lineColors->setDataVariance(osg::Object::DYNAMIC);
204     _lineGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
205     _lineGeometry->setColorArray(_lineColors);
206     
207     _linePrimSet = new osg::DrawArrays(osg::PrimitiveSet::LINES);
208     _linePrimSet->setDataVariance(osg::Object::DYNAMIC);
209     _lineGeometry->addPrimitiveSet(_linePrimSet);
210     
211     _lineGeometry->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f),
212                                             osg::Vec3f(256.0f, 256.0f, 0.0f)));
213
214     _radarGeode->addDrawable(_lineGeometry);              
215                   
216     _textGeode = new osg::Geode;
217
218     osg::Camera *camera = _odg->getCamera();
219     camera->addChild(_radarGeode.get());
220     camera->addChild(_textGeode.get());
221     osg::Texture2D* tex = _odg->getTexture();
222     camera->setProjectionMatrixAsOrtho2D(0, tex->getTextureWidth(), 
223         0, tex->getTextureHeight());
224     
225     updateFont();
226 }
227
228
229 // Local coordinates for each echo
230 const osg::Vec3f symbolCoords[4] = {
231     osg::Vec3f(-.7f, -.7f, 0.0f), osg::Vec3f(.7f, -.7f, 0.0f),
232     osg::Vec3f(.7f, .7f, 0.0f), osg::Vec3f(-.7f, .7f, 0.0f)
233 };
234
235
236 const osg::Vec2f symbolTexCoords[4] = {
237     osg::Vec2f(0.0f, 0.0f), osg::Vec2f(UNIT, 0.0f),
238     osg::Vec2f(UNIT, UNIT), osg::Vec2f(0.0f, UNIT)
239 };
240
241
242 // Rotate by a heading value
243 static inline
244 osg::Matrixf wxRotate(float angle)
245 {
246     return osg::Matrixf::rotate(angle, 0.0f, 0.0f, -1.0f);
247 }
248
249
250 void
251 NavDisplay::update (double delta_time_sec)
252 {
253   if (!fgGetBool("sim/sceneryloaded", false)) {
254     return;
255   }
256
257   if (!_odg || !_serviceable_node->getBoolValue()) {
258     _Instrument->setStringValue("status", "");
259     return;
260   }
261   
262   _time += delta_time_sec;
263   if (_time < _updateInterval){
264     return;
265   }
266   _time -= _updateInterval;
267
268   string mode = _Instrument->getStringValue("display-mode", "arc");
269   _rangeNm = _Instrument->getFloatValue("range", 40.0);
270   if (_Instrument->getBoolValue("aircraft-heading-up", true)) {
271     _view_heading = fgGetDouble("/orientation/heading-deg");
272   } else {
273     _view_heading = _Instrument->getFloatValue("heading-up-deg", 0.0);
274   }
275   _view_heading *= SG_DEGREES_TO_RADIANS;
276   
277   _scale = _odg->size() / _rangeNm;
278   _drawData = _radar_data_node->getBoolValue();
279   
280   double xCenterFrac = _Instrument->getDoubleValue("x-center", 0.5);
281   double yCenterFrac = _Instrument->getDoubleValue("y-center", 0.5);
282   _centerTrans = osg::Matrixf::translate(xCenterFrac * _odg->size(), 
283       yCenterFrac * _odg->size(), 0.0);
284
285 // scale from nm to display units, rotate so aircraft heading is up
286 // (as opposed to north), and compensate for centering
287   _projectMat = osg::Matrixf::scale(_scale, _scale, 1.0) * 
288       wxRotate(-_view_heading) * _centerTrans;
289   
290     _pos = SGGeod::fromDegFt(_user_lon_node->getDoubleValue(),
291                                       _user_lat_node->getDoubleValue(),
292                                       _user_alt_node->getDoubleValue());
293     
294   _vertices->clear();
295   _lineVertices->clear();
296   _lineColors->clear();
297   _texCoords->clear();
298   _textGeode->removeDrawables(0, _textGeode->getNumDrawables());
299   
300   update_aircraft();
301   update_stations();
302   update_airports();
303   update_waypoints();
304   update_route();
305     
306   osg::Vec2 origin = projectGeod(_pos);
307   if (_draw_heading_node->getBoolValue()) {
308     addLine(origin, projectBearingRange(fgGetDouble("/orientation/heading-deg"), _rangeNm), osg::Vec3(1, 1, 1));
309   }
310   
311   if (_draw_track_node->getBoolValue()) {
312     double groundTrackDeg = fgGetDouble("/instrumentation/gps/indicated-track-true-deg");
313     addLine(origin, projectBearingRange(groundTrackDeg, _rangeNm), osg::Vec3(1, 1, 1));
314   }
315   
316   if (_draw_north_node->getBoolValue()) {
317     addLine(origin, projectBearingRange(0, _rangeNm), osg::Vec3(0, 1, 1));
318   }
319   
320   _symbolPrimSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size());
321   _symbolPrimSet->dirty();
322   _linePrimSet->set(osg::PrimitiveSet::LINES, 0, _lineVertices->size());
323   _linePrimSet->dirty();
324 }
325
326 void NavDisplay::addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec3& color)
327 {    
328     _lineVertices->push_back(a);
329     _lineVertices->push_back(b);
330     _lineColors->push_back(color);
331     _lineColors->push_back(color);
332 }
333
334 osg::Vec2 NavDisplay::projectBearingRange(double bearingDeg, double rangeNm) const
335 {
336     osg::Vec3 p(0, rangeNm, 0.0);
337     p = wxRotate(bearingDeg * SG_DEGREES_TO_RADIANS).preMult(p);
338     p = _projectMat.preMult(p);
339     return osg::Vec2(p.x(), p.y());
340 }
341
342 osg::Matrixf NavDisplay::project(const SGGeod& geod) const
343 {
344     double rangeM, bearing, az2;
345     SGGeodesy::inverse(_pos, geod, bearing, az2, rangeM);
346     
347     double radius = ((rangeM * SG_METER_TO_NM) / _rangeNm) * _scale;
348     bearing *= SG_DEGREES_TO_RADIANS;
349     
350     return osg::Matrixf(wxRotate(_view_heading - bearing)
351                           * osg::Matrixf::translate(0.0f, radius, 0.0f)
352                           * wxRotate(bearing) * _centerTrans);
353
354 }
355
356 osg::Vec2 NavDisplay::projectGeod(const SGGeod& geod) const
357 {
358     double rangeM, bearing, az2;
359     SGGeodesy::inverse(_pos, geod, bearing, az2, rangeM);
360     return projectBearingRange(bearing, rangeM * SG_METER_TO_NM);
361 }
362
363 void
364 NavDisplay::addSymbol(const SGGeod& pos, int symbolIndex, const std::string& data, const osg::Vec3& color)
365 {
366     osg::Vec2 xy = projectGeod(pos);
367     double scale = 20.0;    
368     int symbolRow = (SYMBOL_TEX_DIM - 1) - (symbolIndex >> 4);
369     int symbolColumn = symbolIndex & 0x0f;
370     const osg::Vec2f texBase(UNIT * symbolColumn, UNIT * symbolRow);
371     
372     for (int i = 0; i < 4; i++) {
373         _texCoords->push_back(texBase + symbolTexCoords[i]);
374         osg::Vec2 coord(symbolCoords[i].x() * scale, symbolCoords[i].y() * scale);
375         _vertices->push_back(xy + coord);
376         _quadColors->push_back(color);
377     }
378     
379 // add data drawable
380     osgText::Text* text = new osgText::Text;
381     text->setFont(_font.get());
382     text->setFontResolution(12, 12);
383     text->setCharacterSize(_font_size);
384     text->setLineSpacing(_font_spacing);
385
386     text->setAlignment(osgText::Text::LEFT_CENTER);
387     text->setText(data);
388     
389     osg::Vec4 textColor(color.x(), color.y(), color.z(), 1);
390     text->setColor(textColor);
391     text->setPosition( osg::Vec3(xy.x() + 16, xy.y(), 0));
392     _textGeode->addDrawable(text);
393 }
394
395 void
396 NavDisplay::update_route()
397 {
398     if (_route->numWaypts() < 2) {
399         return;
400     }
401     
402     RoutePath path(_route->waypts());
403     for (int w=0; w<_route->numWaypts(); ++w) {
404         osg::Vec3 color(1.0, 0.0, 1.0);
405         SGGeodVec gv(path.pathForIndex(w));
406         if (!gv.empty()) {
407             osg::Vec2 pr = projectGeod(gv[0]);
408             for (unsigned int i=1; i<gv.size(); ++i) {
409                 osg::Vec2 p = projectGeod(gv[i]);
410                 addLine(pr, p, color);
411                 pr = p;
412             }
413         } // of line drawing
414         
415         flightgear::WayptRef wpt(_route->wayptAtIndex(w));
416         SGGeod g = path.positionForIndex(w);
417         if (!(g == SGGeod())) {
418             int symbolIndex = (6 << 4);
419             osg::Vec3 color(1.0, 1.0, 1.0);
420         // active waypoint is magenta, not white
421             if (w ==  _route->currentIndex()) {
422                 color = osg::Vec3(1.0, 0.0, 1.0);
423             }
424             
425             addSymbol(g, symbolIndex, wpt->ident(), color);
426         }
427     } // of waypoints iteration
428 }
429
430 void
431 NavDisplay::update_stations()
432 {
433     FGNavRecord* nav1 = drawTunedNavaid(_navRadio1Node);
434     FGNavRecord* nav2 = drawTunedNavaid(_navRadio2Node);
435     
436     if (_radar_station_node->getBoolValue()) {
437         osg::Vec3 cyanColor(0, 1, 1);
438         FGPositioned::TypeFilter filt(FGPositioned::VOR);
439         FGPositioned::List stations = 
440             FGPositioned::findWithinRange(_pos, _rangeNm, &filt);
441
442         FGPositioned::List::const_iterator it;
443         for (it = stations.begin(); it != stations.end(); ++it) {
444             FGPositioned* sta = *it;
445             if ((sta == nav1) || (sta == nav2)) {
446                 continue;
447             }
448             
449             int symbolIndex = (6 << 4) + 2;
450             addSymbol(sta->geod(), symbolIndex, sta->ident(), cyanColor);
451         }
452     } // of stations beiong drawn
453 }
454
455 class FixFilter : public FGPositioned::Filter
456 {
457 public:
458   virtual bool pass(FGPositioned* aPos) const
459   {
460     // ignore fixes which end in digits
461       if (isdigit(aPos->ident()[3]) && isdigit(aPos->ident()[4])) {
462         return false;
463       }
464
465     return true;
466   }
467
468   virtual FGPositioned::Type minType() const {
469     return FGPositioned::FIX;
470   }
471
472   virtual FGPositioned::Type maxType() const {
473     return FGPositioned::FIX;
474   }
475
476 private:
477   bool _fixes, _navaids;
478 };
479
480 void
481 NavDisplay::update_waypoints()
482 {
483     if (!_draw_fix_node->getBoolValue()) {
484         return;
485     }
486     
487     std::set<FGPositioned*> routeWaypts;
488     for (int w=0; w<_route->numWaypts(); ++w) {
489         flightgear::WayptRef wpt(_route->wayptAtIndex(w));
490         routeWaypts.insert(wpt->source());
491     }
492     
493     osg::Vec3 cyanColor(0, 1, 1);
494     FixFilter filt;
495     FGPositioned::List fixes = 
496         FGPositioned::findWithinRange(_pos, _rangeNm, &filt);
497
498     FGPositioned::List::const_iterator it;
499     for (it = fixes.begin(); it != fixes.end(); ++it) {
500         FGPositioned* fix = *it;
501         if (routeWaypts.count(fix)) {
502             continue; // part of active route, don't draw here
503         }
504         
505         int symbolIndex = (6 << 4) + 0;
506         addSymbol(fix->geod(), symbolIndex, fix->ident(), cyanColor);
507     } // of draw fixes iteration
508 }
509
510 FGNavRecord*
511 NavDisplay::drawTunedNavaid(const SGPropertyNode_ptr& radio )
512 {
513     double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0);
514     FGNavRecord* nav = globals->get_navlist()->findByFreq(mhz, _pos);
515     if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) {
516         // station was not found
517         return NULL;
518     }
519
520     osg::Vec3 greenColor(0, 1, 0);
521     int symbolIndex = (6 << 4) + 2;
522     addSymbol(nav->geod(), symbolIndex, nav->ident(), greenColor);
523     
524 // Boeing: only show radial + reciprocal if manually tuned ... hmmmm
525     osg::Vec2 stationXY = projectGeod(nav->geod());
526     SGGeod radialEnd;
527     double az2;
528     double trueRadial = radio->getDoubleValue("radials/target-radial-deg");
529     SGGeodesy::direct(nav->geod(), trueRadial, 
530         nav->get_range() * SG_NM_TO_METER, radialEnd, az2);
531     osg::Vec2 radialXY = projectGeod(radialEnd);
532     
533     osg::Vec2 d = radialXY - stationXY;
534     addLine(stationXY - d, radialXY, greenColor);
535     
536 // Boeing: if POS is selected, show radial to station
537     osg::Vec2 posXY = projectGeod(_pos);
538     addLine(posXY, stationXY, greenColor);
539     
540     osgText::Text* text = new osgText::Text;
541     text->setFont(_font.get());
542     text->setFontResolution(12, 12);
543     text->setCharacterSize(_font_size);
544     text->setLineSpacing(_font_spacing);
545     text->setAlignment(osgText::Text::CENTER_BOTTOM);
546     text->setColor(osg::Vec4(0, 1, 0, 1));
547     
548     stringstream s;
549     s << "R-" << setw(3) << setfill('0') << static_cast<int>(trueRadial);
550     text->setText(s.str());
551     osg::Vec2 mid = (posXY + stationXY) * 0.5; // radial mid-point
552     text->setPosition( osg::Vec3(mid.x(), mid.y(), 0));
553     _textGeode->addDrawable(text);
554     
555     return nav;
556 }
557
558 void
559 NavDisplay::update_airports()
560 {
561     FGAirport* dep = _route->departureAirport(), 
562         *arr = _route->destinationAirport();
563     
564     if (_radar_arpt_node->getBoolValue()) {
565         osg::Vec3 cyanColor(0, 1, 1);
566         int symbolIndex = (6 << 4) + 3;
567         double minRunway = _Instrument->getDoubleValue("display-controls/min-runway-len-ft", 0.0);
568         FGAirport::HardSurfaceFilter filt(minRunway);
569         FGPositioned::List apts = 
570             FGPositioned::findWithinRange(_pos, _rangeNm, &filt);
571             
572         FGPositioned::List::const_iterator it;
573         for (it = apts.begin(); it != apts.end(); ++it) {
574             FGPositioned* apt = *it;
575             if ((apt == dep) || (apt == arr)) {
576             // added seperately
577                 continue;
578             }
579         
580             addSymbol(apt->geod(), symbolIndex, apt->ident(), cyanColor);
581         } // of airports iteration
582     }
583     
584     int symbolIndex = (7 << 4) + 1;
585     osg::Vec3 whiteColor(1, 1, 1);
586     FGRunway* rwy = _route->departureRunway();
587     if (rwy) {
588         addSymbol(dep->geod(), symbolIndex, dep->ident() + "\n" + rwy->ident(), whiteColor);
589     }
590     
591     rwy = _route->destinationRunway();
592     if (rwy) {
593         addSymbol(arr->geod(), symbolIndex, arr->ident() + "\n" + rwy->ident(), whiteColor);
594     }
595 }
596
597 void
598 NavDisplay::update_aircraft()
599 {
600     if (!_ai_enabled_node->getBoolValue()) {
601         return;
602     }
603
604     bool draw_tcas     = _radar_tcas_node->getBoolValue();
605     if (!draw_tcas) {
606         return;
607     }
608     
609     bool draw_absolute = _radar_absalt_node->getBoolValue();
610     
611     const SGPropertyNode *ai = fgGetNode("/ai/models", true);
612     for (int i = ai->nChildren() - 1; i >= 0; i--) {
613         const SGPropertyNode *model = ai->getChild(i);
614         if (!model->nChildren()) {
615             continue;
616         }
617
618         double echo_radius, sigma;
619         const string name = model->getName();
620
621         if (name == "aircraft" || name == "tanker")
622             echo_radius = 1, sigma = 1;
623         else if (name == "multiplayer" || name == "wingman" || name == "static")
624             echo_radius = 1.5, sigma = 1;
625         else
626             continue;
627       
628         SGGeod aiModelPos = SGGeod::fromDegFt(model->getDoubleValue("position/longitude-deg"), 
629                                             model->getDoubleValue("position/latitude-deg"), 
630                                             model->getDoubleValue("position/altitude-ft"));
631       
632         double heading = model->getDoubleValue("orientation/true-heading-deg");
633         double rangeM, bearing, az2;
634         SGGeodesy::inverse(_pos, aiModelPos, bearing, az2, rangeM);
635
636         bearing *= SG_DEGREES_TO_RADIANS;
637         heading *= SG_DEGREES_TO_RADIANS;
638
639         float radius = rangeM * _scale;
640
641         bool is_tcas_contact = update_tcas(model,aiModelPos, 
642                                           _pos.getElevationFt(),
643                                           aiModelPos.getElevationFt(),
644                                           draw_absolute);
645
646         update_data(model, aiModelPos.getElevationFt(), heading, radius, bearing, false);
647     } // of ai models iteration
648 }
649
650 /** Update TCAS display.
651  * Return true when processed as TCAS contact, false otherwise. */
652 bool
653 NavDisplay::update_tcas(const SGPropertyNode *model, const SGGeod& modelPos, 
654                         double user_alt,double alt, bool absMode)
655 {
656     int threatLevel = model->getIntValue("tcas/threat-level",-1);
657     if (threatLevel == -1) {
658         // no TCAS information (i.e. no transponder) => not visible to TCAS
659         return false;
660     }
661     
662     int row = 4;
663     int col = threatLevel;
664     double vspeed = model->getDoubleValue("velocities/vertical-speed-fps");
665     if (vspeed < -3.0) // descending
666         row+=1;
667   //  else
668 //    if (vspeed > 3.0) // climbing
669   //      col+=2;
670         
671     osg::Vec4 color = _tcas_colors[threatLevel];
672     
673         stringstream text;
674        // altStr->setAlignment(osgText::Text::LEFT_CENTER);
675         int altDif = (alt-user_alt+50)/100;
676         char sign = 0;
677         int dy=0;
678         if (altDif>=0)
679         {
680             sign='+';
681             dy=2;
682         }
683         else
684         if (altDif<0)
685         {
686             sign='-';
687             altDif = -altDif;
688             dy=-30;
689         }
690       //  altStr->setPosition(osg::Vec3((int)pos.x()-30, (int)pos.y()+dy, 0));
691         if (absMode)
692         {
693             // absolute altitude display
694             text << setprecision(0) << fixed
695                  << setw(3) << setfill('0') << alt/100 << endl;
696         }
697         else // relative altitude display
698         if (sign)
699         {
700             text << sign
701                  << setprecision(0) << fixed
702                  << setw(2) << setfill('0') << altDif << endl;
703         }
704
705     addSymbol(modelPos, (col << 4) | row, text.str(), 
706         osg::Vec3(color.r(), color.g(), color.b()));
707             
708     return true;
709 }
710
711 void NavDisplay::update_data(const SGPropertyNode *ac, double altitude, double heading,
712                        double radius, double bearing, bool selected)
713 {
714   osgText::Text *callsign = new osgText::Text;
715   callsign->setFont(_font.get());
716   callsign->setFontResolution(12, 12);
717   callsign->setCharacterSize(_font_size);
718   callsign->setColor(selected ? osg::Vec4(1, 1, 1, 1) : _font_color);
719   osg::Matrixf m(wxRotate(-bearing)
720                  * osg::Matrixf::translate(0.0f, radius, 0.0f)
721                  * wxRotate(bearing) * _centerTrans);
722   
723   osg::Vec3 pos = m.preMult(osg::Vec3(16, 16, 0));
724   // cast to int's, otherwise text comes out ugly
725   callsign->setPosition(osg::Vec3((int)pos.x(), (int)pos.y(), 0));
726   callsign->setAlignment(osgText::Text::LEFT_BOTTOM_BASE_LINE);
727   callsign->setLineSpacing(_font_spacing);
728   
729   const char *identity = ac->getStringValue("transponder-id");
730   if (!identity[0])
731     identity = ac->getStringValue("callsign");
732   
733   stringstream text;
734   text << identity << endl
735   << setprecision(0) << fixed
736   << setw(3) << setfill('0') << heading * SG_RADIANS_TO_DEGREES << "\xB0 "
737   << setw(0) << altitude << "ft" << endl
738   << ac->getDoubleValue("velocities/true-airspeed-kt") << "kts";
739   
740   callsign->setText(text.str());
741   _textGeode->addDrawable(callsign);
742 }
743
744 void
745 NavDisplay::updateFont()
746 {
747     float red = _font_node->getFloatValue("color/red");
748     float green = _font_node->getFloatValue("color/green");
749     float blue = _font_node->getFloatValue("color/blue");
750     float alpha = _font_node->getFloatValue("color/alpha");
751     _font_color.set(red, green, blue, alpha);
752
753     _font_size = _font_node->getFloatValue("size");
754     _font_spacing = _font_size * _font_node->getFloatValue("line-spacing");
755     string path = _font_node->getStringValue("name", DEFAULT_FONT);
756
757     SGPath tpath;
758     if (path[0] != '/') {
759         tpath = globals->get_fg_root();
760         tpath.append("Fonts");
761         tpath.append(path);
762     } else {
763         tpath = path;
764     }
765
766 #if (FG_OSG_VERSION >= 21000)
767     osg::ref_ptr<osgDB::ReaderWriter::Options> fontOptions = new osgDB::ReaderWriter::Options("monochrome");
768     osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str(), fontOptions.get());
769 #else
770     osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str());
771 #endif
772
773     if (font != 0) {
774         _font = font;
775         _font->setMinFilterHint(osg::Texture::NEAREST);
776         _font->setMagFilterHint(osg::Texture::NEAREST);
777         _font->setGlyphImageMargin(0);
778         _font->setGlyphImageMarginRatio(0);
779     }
780
781     for (int i=0;i<4;i++)
782     {
783         const float defaultColors[4][3] = {{0,1,1},{0,1,1},{1,0.5,0},{1,0,0}};
784         SGPropertyNode_ptr color_node = _font_node->getNode("tcas/color",i,true);
785         float red   = color_node->getFloatValue("red",defaultColors[i][0]);
786         float green = color_node->getFloatValue("green",defaultColors[i][1]);
787         float blue  = color_node->getFloatValue("blue",defaultColors[i][2]);
788         float alpha = color_node->getFloatValue("alpha",1);
789         _tcas_colors[i]=osg::Vec4(red, green, blue, alpha);
790     }
791 }
792
793