1 // navigation display texture
3 // Written by James Turner, forked from wxradar code
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.
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.
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.
26 #include "NavDisplay.hxx"
29 #include <osg/Geometry>
30 #include <osg/Matrixf>
31 #include <osg/PrimitiveSet>
32 #include <osg/StateSet>
33 #include <osg/LineWidth>
34 #include <osg/Version>
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>
45 #include <iostream> // for cout, endl
47 using std::stringstream;
49 using std::setprecision;
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>
67 #include "instrument_mgr.hxx"
68 #include "od_gauge.hxx"
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";
74 NavDisplay::NavDisplay(SGPropertyNode *node) :
75 _name(node->getStringValue("name", "nd")),
76 _num(node->getIntValue("number", 0)),
78 _updateInterval(node->getDoubleValue("update-interval-sec", 0.1)),
87 _Instrument = fgGetNode(string("/instrumentation/" + _name).c_str(), _num, true);
88 _font_node = _Instrument->getNode("font", true);
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);
103 NavDisplay::~NavDisplay()
111 _serviceable_node = _Instrument->getNode("serviceable", true);
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);
118 string path = _Instrument->getStringValue("symbol-texture-path",
119 "Aircraft/Instruments/Textures/nd-symbols.png");
120 SGPath tpath = globals->resolve_aircraft_path(path);
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);
125 FGInstrumentMgr *imgr = (FGInstrumentMgr *)globals->get_subsystem("instrumentation");
126 _odg = (FGODGauge *)imgr->get_subsystem("od_gauge");
127 _odg->setSize(_Instrument->getIntValue("texture-size", 512));
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);
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);
149 _ai_enabled_node = fgGetNode("/sim/ai/enabled", true);
150 _route = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
152 _navRadio1Node = fgGetNode("/instrumentation/nav[0]", true);
153 _navRadio2Node = fgGetNode("/instrumentation/nav[1]", true);
155 // OSG geometry setup
156 _radarGeode = new osg::Geode;
158 _geom = new osg::Geometry;
159 _geom->setUseDisplayList(false);
161 osg::StateSet *stateSet = _geom->getOrCreateStateSet();
162 stateSet->setTextureAttributeAndModes(0, _symbols.get());
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);
174 _quadColors = new osg::Vec3Array;
175 _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
176 _geom->setColorArray(_quadColors);
178 _symbolPrimSet = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
179 _symbolPrimSet->setDataVariance(osg::Object::DYNAMIC);
180 _geom->addPrimitiveSet(_symbolPrimSet);
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);
186 // Texture in the 2D panel system
187 FGTextureManager::addTexture(_texture_path.c_str(), _odg->getTexture());
189 _lineGeometry = new osg::Geometry;
190 _lineGeometry->setUseDisplayList(false);
191 stateSet = _lineGeometry->getOrCreateStateSet();
192 osg::LineWidth *lw = new osg::LineWidth();
194 stateSet->setAttribute(lw);
196 _lineVertices = new osg::Vec2Array;
197 _lineVertices->setDataVariance(osg::Object::DYNAMIC);
198 _lineVertices->reserve(128 * 4);
199 _lineGeometry->setVertexArray(_lineVertices);
202 _lineColors = new osg::Vec3Array;
203 _lineColors->setDataVariance(osg::Object::DYNAMIC);
204 _lineGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
205 _lineGeometry->setColorArray(_lineColors);
207 _linePrimSet = new osg::DrawArrays(osg::PrimitiveSet::LINES);
208 _linePrimSet->setDataVariance(osg::Object::DYNAMIC);
209 _lineGeometry->addPrimitiveSet(_linePrimSet);
211 _lineGeometry->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f),
212 osg::Vec3f(256.0f, 256.0f, 0.0f)));
214 _radarGeode->addDrawable(_lineGeometry);
216 _textGeode = new osg::Geode;
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());
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)
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)
242 // Rotate by a heading value
244 osg::Matrixf wxRotate(float angle)
246 return osg::Matrixf::rotate(angle, 0.0f, 0.0f, -1.0f);
251 NavDisplay::update (double delta_time_sec)
253 if (!fgGetBool("sim/sceneryloaded", false)) {
257 if (!_odg || !_serviceable_node->getBoolValue()) {
258 _Instrument->setStringValue("status", "");
262 _time += delta_time_sec;
263 if (_time < _updateInterval){
266 _time -= _updateInterval;
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");
273 _view_heading = _Instrument->getFloatValue("heading-up-deg", 0.0);
275 _view_heading *= SG_DEGREES_TO_RADIANS;
277 _scale = _odg->size() / _rangeNm;
278 _drawData = _radar_data_node->getBoolValue();
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);
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;
290 _pos = SGGeod::fromDegFt(_user_lon_node->getDoubleValue(),
291 _user_lat_node->getDoubleValue(),
292 _user_alt_node->getDoubleValue());
295 _lineVertices->clear();
296 _lineColors->clear();
298 _textGeode->removeDrawables(0, _textGeode->getNumDrawables());
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));
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));
316 if (_draw_north_node->getBoolValue()) {
317 addLine(origin, projectBearingRange(0, _rangeNm), osg::Vec3(0, 1, 1));
320 _symbolPrimSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size());
321 _symbolPrimSet->dirty();
322 _linePrimSet->set(osg::PrimitiveSet::LINES, 0, _lineVertices->size());
323 _linePrimSet->dirty();
326 void NavDisplay::addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec3& color)
328 _lineVertices->push_back(a);
329 _lineVertices->push_back(b);
330 _lineColors->push_back(color);
331 _lineColors->push_back(color);
334 osg::Vec2 NavDisplay::projectBearingRange(double bearingDeg, double rangeNm) const
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());
342 osg::Matrixf NavDisplay::project(const SGGeod& geod) const
344 double rangeM, bearing, az2;
345 SGGeodesy::inverse(_pos, geod, bearing, az2, rangeM);
347 double radius = ((rangeM * SG_METER_TO_NM) / _rangeNm) * _scale;
348 bearing *= SG_DEGREES_TO_RADIANS;
350 return osg::Matrixf(wxRotate(_view_heading - bearing)
351 * osg::Matrixf::translate(0.0f, radius, 0.0f)
352 * wxRotate(bearing) * _centerTrans);
356 osg::Vec2 NavDisplay::projectGeod(const SGGeod& geod) const
358 double rangeM, bearing, az2;
359 SGGeodesy::inverse(_pos, geod, bearing, az2, rangeM);
360 return projectBearingRange(bearing, rangeM * SG_METER_TO_NM);
364 NavDisplay::addSymbol(const SGGeod& pos, int symbolIndex, const std::string& data, const osg::Vec3& color)
366 osg::Vec2 xy = projectGeod(pos);
368 int symbolRow = (SYMBOL_TEX_DIM - 1) - (symbolIndex >> 4);
369 int symbolColumn = symbolIndex & 0x0f;
370 const osg::Vec2f texBase(UNIT * symbolColumn, UNIT * symbolRow);
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);
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);
386 text->setAlignment(osgText::Text::LEFT_CENTER);
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);
396 NavDisplay::update_route()
398 if (_route->numWaypts() < 2) {
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));
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);
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);
425 addSymbol(g, symbolIndex, wpt->ident(), color);
427 } // of waypoints iteration
431 NavDisplay::update_stations()
433 FGNavRecord* nav1 = drawTunedNavaid(_navRadio1Node);
434 FGNavRecord* nav2 = drawTunedNavaid(_navRadio2Node);
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);
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)) {
449 int symbolIndex = (6 << 4) + 2;
450 addSymbol(sta->geod(), symbolIndex, sta->ident(), cyanColor);
452 } // of stations beiong drawn
455 class FixFilter : public FGPositioned::Filter
458 virtual bool pass(FGPositioned* aPos) const
460 // ignore fixes which end in digits
461 if (isdigit(aPos->ident()[3]) && isdigit(aPos->ident()[4])) {
468 virtual FGPositioned::Type minType() const {
469 return FGPositioned::FIX;
472 virtual FGPositioned::Type maxType() const {
473 return FGPositioned::FIX;
477 bool _fixes, _navaids;
481 NavDisplay::update_waypoints()
483 if (!_draw_fix_node->getBoolValue()) {
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());
493 osg::Vec3 cyanColor(0, 1, 1);
495 FGPositioned::List fixes =
496 FGPositioned::findWithinRange(_pos, _rangeNm, &filt);
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
505 int symbolIndex = (6 << 4) + 0;
506 addSymbol(fix->geod(), symbolIndex, fix->ident(), cyanColor);
507 } // of draw fixes iteration
511 NavDisplay::drawTunedNavaid(const SGPropertyNode_ptr& radio )
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
520 osg::Vec3 greenColor(0, 1, 0);
521 int symbolIndex = (6 << 4) + 2;
522 addSymbol(nav->geod(), symbolIndex, nav->ident(), greenColor);
524 // Boeing: only show radial + reciprocal if manually tuned ... hmmmm
525 osg::Vec2 stationXY = projectGeod(nav->geod());
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);
533 osg::Vec2 d = radialXY - stationXY;
534 addLine(stationXY - d, radialXY, greenColor);
536 // Boeing: if POS is selected, show radial to station
537 osg::Vec2 posXY = projectGeod(_pos);
538 addLine(posXY, stationXY, greenColor);
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));
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);
559 NavDisplay::update_airports()
561 FGAirport* dep = _route->departureAirport(),
562 *arr = _route->destinationAirport();
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);
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)) {
580 addSymbol(apt->geod(), symbolIndex, apt->ident(), cyanColor);
581 } // of airports iteration
584 int symbolIndex = (7 << 4) + 1;
585 osg::Vec3 whiteColor(1, 1, 1);
586 FGRunway* rwy = _route->departureRunway();
588 addSymbol(dep->geod(), symbolIndex, dep->ident() + "\n" + rwy->ident(), whiteColor);
591 rwy = _route->destinationRunway();
593 addSymbol(arr->geod(), symbolIndex, arr->ident() + "\n" + rwy->ident(), whiteColor);
598 NavDisplay::update_aircraft()
600 if (!_ai_enabled_node->getBoolValue()) {
604 bool draw_tcas = _radar_tcas_node->getBoolValue();
609 bool draw_absolute = _radar_absalt_node->getBoolValue();
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()) {
618 double echo_radius, sigma;
619 const string name = model->getName();
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;
628 SGGeod aiModelPos = SGGeod::fromDegFt(model->getDoubleValue("position/longitude-deg"),
629 model->getDoubleValue("position/latitude-deg"),
630 model->getDoubleValue("position/altitude-ft"));
632 double heading = model->getDoubleValue("orientation/true-heading-deg");
633 double rangeM, bearing, az2;
634 SGGeodesy::inverse(_pos, aiModelPos, bearing, az2, rangeM);
636 bearing *= SG_DEGREES_TO_RADIANS;
637 heading *= SG_DEGREES_TO_RADIANS;
639 float radius = rangeM * _scale;
641 bool is_tcas_contact = update_tcas(model,aiModelPos,
642 _pos.getElevationFt(),
643 aiModelPos.getElevationFt(),
646 update_data(model, aiModelPos.getElevationFt(), heading, radius, bearing, false);
647 } // of ai models iteration
650 /** Update TCAS display.
651 * Return true when processed as TCAS contact, false otherwise. */
653 NavDisplay::update_tcas(const SGPropertyNode *model, const SGGeod& modelPos,
654 double user_alt,double alt, bool absMode)
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
663 int col = threatLevel;
664 double vspeed = model->getDoubleValue("velocities/vertical-speed-fps");
665 if (vspeed < -3.0) // descending
668 // if (vspeed > 3.0) // climbing
671 osg::Vec4 color = _tcas_colors[threatLevel];
674 // altStr->setAlignment(osgText::Text::LEFT_CENTER);
675 int altDif = (alt-user_alt+50)/100;
690 // altStr->setPosition(osg::Vec3((int)pos.x()-30, (int)pos.y()+dy, 0));
693 // absolute altitude display
694 text << setprecision(0) << fixed
695 << setw(3) << setfill('0') << alt/100 << endl;
697 else // relative altitude display
701 << setprecision(0) << fixed
702 << setw(2) << setfill('0') << altDif << endl;
705 addSymbol(modelPos, (col << 4) | row, text.str(),
706 osg::Vec3(color.r(), color.g(), color.b()));
711 void NavDisplay::update_data(const SGPropertyNode *ac, double altitude, double heading,
712 double radius, double bearing, bool selected)
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);
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);
729 const char *identity = ac->getStringValue("transponder-id");
731 identity = ac->getStringValue("callsign");
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";
740 callsign->setText(text.str());
741 _textGeode->addDrawable(callsign);
745 NavDisplay::updateFont()
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);
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);
758 if (path[0] != '/') {
759 tpath = globals->get_fg_root();
760 tpath.append("Fonts");
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());
770 osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str());
775 _font->setMinFilterHint(osg::Texture::NEAREST);
776 _font->setMagFilterHint(osg::Texture::NEAREST);
777 _font->setGlyphImageMargin(0);
778 _font->setGlyphImageMarginRatio(0);
781 for (int i=0;i<4;i++)
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);