]> git.mxchange.org Git - flightgear.git/blobdiff - src/Instrumentation/wxradar.cxx
Csaba HALASZ & Syd ADAMS: make radar font configurable
[flightgear.git] / src / Instrumentation / wxradar.cxx
index 2727dd2efbded81f1b7dd2a47cc013110be19b15..35a9c7422f59e5883962ce9d530f5cc1c8b61d03 100644 (file)
@@ -32,6 +32,8 @@
 #include <osg/Matrixf>
 #include <osg/PrimitiveSet>
 #include <osg/StateSet>
+#include <osg/Version>
+#include <osgDB/ReaderWriter>
 #include <osgDB/WriteFile>
 
 #include <simgear/constants.h>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/math/sg_geodesy.hxx>
 
+#include <sstream>
+#include <iomanip>
+SG_USING_STD(stringstream);
+SG_USING_STD(endl);
+SG_USING_STD(setprecision);
+SG_USING_STD(fixed);
+SG_USING_STD(setw);
+SG_USING_STD(setfill);
+
 #include <Main/fg_props.hxx>
 #include <Main/globals.hxx>
 #include <Cockpit/panel.hxx>
 #include <AIModel/AIManager.hxx>
 #include <AIModel/AIBallistic.hxx>
 
+#include <Include/general.hxx>
 #include "instrument_mgr.hxx"
 #include "od_gauge.hxx"
 #include "wxradar.hxx"
 
 
-typedef list <SGSharedPtr<FGAIBase> > radar_list_type;
+typedef list <osg::ref_ptr<FGAIBase> > radar_list_type;
 typedef radar_list_type::iterator radar_list_iterator;
 typedef radar_list_type::const_iterator radar_list_const_iterator;
 
 
 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) :
+wxRadarBg::wxRadarBg(SGPropertyNode *node) :
     _name(node->getStringValue("name", "radar")),
     _num(node->getIntValue("number", 0)),
     _interval(node->getDoubleValue("update-interval-sec", 1.0)),
-    _time( 0.0 ),
-    _last_switchKnob( "off" ),
-    _sim_init_done ( false ),
-    _resultTexture( 0 ),
-    _wxEcho( 0 ),
-    _odg( 0 )
+    _time(0.0),
+    _last_switchKnob("off"),
+    _sim_init_done(false),
+    _resultTexture(0),
+    _wxEcho(0),
+    _odg(0)
 {
-    const char *tacan_source = node->getStringValue("tacan-source",
-            "/instrumentation/tacan");
+    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);
+    _font_node->addChangeListener(this, true);
 }
 
 
 wxRadarBg::~wxRadarBg ()
 {
+    _font_node->removeChangeListener(this);
 }
 
 
 void
 wxRadarBg::init ()
 {
-    string branch;
-    branch = "/instrumentation/" + _name;
-
-    _Instrument = fgGetNode(branch.c_str(), _num, true );
     _serviceable_node = _Instrument->getNode("serviceable", 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());
+    _resultTexture = FGTextureManager::createTexture(_texture_path.c_str(), false);
 
     SGPath tpath(globals->get_fg_root());
     string path = _Instrument->getStringValue("echo-texture-path",
@@ -105,7 +120,8 @@ wxRadarBg::init ()
     tpath.append(path);
 
     // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect
-    _wxEcho = SGLoadTexture2D(tpath.c_str(), false, false);
+    _wxEcho = SGLoadTexture2D(tpath, false, false);
+
 
     _Instrument->setFloatValue("trk", 0.0);
     _Instrument->setFloatValue("tilt", 0.0);
@@ -121,9 +137,9 @@ wxRadarBg::init ()
     // input range = n nm (20/40/80)
     // input display-mode = arc | rose | map | plan
 
-    FGInstrumentMgr *imgr = (FGInstrumentMgr *) globals->get_subsystem("instrumentation");
-    _odg = (FGODGauge *) imgr->get_subsystem("od_gauge");
-    _odg->setSize(256);
+    FGInstrumentMgr *imgr = (FGInstrumentMgr *)globals->get_subsystem("instrumentation");
+    _odg = (FGODGauge *)imgr->get_subsystem("od_gauge");
+    _odg->setSize(512);
 
     _ai = (FGAIManager*)globals->get_subsystem("ai_model");
     _ai_enabled_node = fgGetNode("/sim/ai/enabled", true);
@@ -150,7 +166,9 @@ wxRadarBg::init ()
     _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_centre_node->setBoolValue(false);
     if (_radar_coverage_node->getType() == SGPropertyNode::NONE)
@@ -203,8 +221,11 @@ wxRadarBg::init ()
     // 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());
 }
 
 
@@ -330,13 +351,16 @@ wxRadarBg::update (double delta_time_sec)
             _centerTrans.makeTranslate(_x_offset, _y_offset, 0.0f);
 
         } else if ( _display_mode == PLAN ) {
-            // no sense I presume
+            if (_radar_rotate_node->getBoolValue()) {
+                _angle_offset = -_view_heading;
+            }
         } else {
             // rose
         }
 
         _vertices->clear();
         _texCoords->clear();
+        _textGeode->removeDrawables(0, _textGeode->getNumDrawables());
 
 
         update_weather();
@@ -521,6 +545,36 @@ wxRadarBg::update_weather()
 }
 
 
+void
+wxRadarBg::update_data(FGAIBase* ac, 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);
+
+    stringstream text;
+    text << ac->_getCallsign() << endl
+        << setprecision(0) << fixed
+        << setw(3) << setfill('0') << ac->_getHeading() << "\xB0 "
+        << setw(0) << ac->_getAltitude() << "ft" << endl
+        << ac->_getSpeed() << "kts";
+
+    callsign->setText(text.str());
+    _textGeode->addDrawable(callsign);
+}
+
+
 void
 wxRadarBg::update_aircraft()
 {
@@ -528,27 +582,39 @@ wxRadarBg::update_aircraft()
         return;
 
     bool draw_echoes = _radar_position_node->getBoolValue();
-    bool draw_symbols = _radar_data_node->getBoolValue();
-    if (!draw_echoes && !draw_symbols)
+    bool draw_symbols = _radar_symbol_node->getBoolValue();
+    bool draw_data = _radar_data_node->getBoolValue();
+    if (!draw_echoes && !draw_symbols && !draw_data)
         return;
 
     radar_list_type radar_list = _ai->get_ai_list();
-    SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: AI submodel list size" << radar_list.size());
+    //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: AI submodel list size" << radar_list.size());
     if (radar_list.empty())
         return;
 
-    SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: Loading AI submodels ");
-    const double echo_radii[] = {0, 1, 1.5, 1.5, 0.001, 0.1, 1.5, 2, 1.5, 1.5};
+    //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: Loading AI submodels ");
+    const double echo_radii[] = {0, 1, 1.5, 1.5, 0.001, 0.1, 1.5, 2, 1.5, 1.5, 1.5};
 
     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;
+
     radar_list_iterator it = radar_list.begin();
     radar_list_iterator end = radar_list.end();
+    FGAIBase *selected_ac = 0;
+    double selected_radius = 0;
+    double selected_bearing = 0;
+    int selected_id = fgGetInt("/instrumentation/radar/selected-id", -1);
 
     for (; it != end; ++it) {
-        FGAIBase *ac = *it;
+        FGAIBase *ac = (*it).get();
         int type       = ac->getType();
         double lat     = ac->_getLatitude();
         double lon     = ac->_getLongitude();
@@ -559,16 +625,16 @@ wxRadarBg::update_aircraft()
         calcRangeBearing(user_lat, user_lon, lat, lon, range, bearing);
 
         //SG_LOG(SG_GENERAL, SG_DEBUG,
-        //        "Radar: ID=" << ac->getID() << "(" << radar_list.size() << ")"
-        //        << " type=" << type
-        //        << " view_heading=" << _view_heading * SG_RADIANS_TO_DEGREES
-        //        << " alt=" << alt
-        //        << " heading=" << heading
-        //        << " range=" << range
-        //        << " bearing=" << bearing);
+        /*        "Radar: ID=" << ac->getID() << "(" << radar_list.size() << ")"
+                << " type=" << type
+                << " view_heading=" << _view_heading * SG_RADIANS_TO_DEGREES
+                << " alt=" << alt
+                << " heading=" << heading
+                << " range=" << range
+                << " bearing=" << bearing);*/
 
         bool isVisible = withinRadarHorizon(user_alt, alt, range);
-        SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: visible " << isVisible);
+        //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: visible " << isVisible);
         if (!isVisible)
             continue;
 
@@ -581,12 +647,6 @@ wxRadarBg::update_aircraft()
         float radius = range * _scale;
         float angle = calcRelBearing(bearing, _view_heading);
 
-        float limit = _radar_coverage_node->getFloatValue();
-        if (limit > 180)
-            limit = 180;
-        else if (limit < 0)
-            limit = 0;
-        limit *= SG_DEGREES_TO_RADIANS;
         if (angle > limit || angle < -limit)
             continue;
 
@@ -605,7 +665,8 @@ wxRadarBg::update_aircraft()
             addQuad(_vertices, _texCoords, m, texBase);
 
             //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar:    drawing AI"
-            //        << " x=" << x << " y=" << y
+                //<< " ID=" << ac->getID()
+                //<< " type=" << type
             //        << " radius=" << radius
             //        << " angle=" << angle * SG_RADIANS_TO_DEGREES);
         }
@@ -625,6 +686,19 @@ wxRadarBg::update_aircraft()
             //        << " bearing=" << angle * SG_RADIANS_TO_DEGREES
             //        << " radius=" << radius);
         }
+
+        if (draw_data) {
+            if (ac->getID() == selected_id) {
+                selected_ac = ac;
+                selected_radius = radius;
+                selected_bearing = bearing;
+            } else {
+                update_data(ac, radius, bearing, false);
+            }
+        }
+    }
+    if (selected_ac) {
+        update_data(selected_ac, selected_radius, selected_bearing, true);
     }
 }
 
@@ -721,7 +795,7 @@ wxRadarBg::withinRadarHorizon(double user_alt, double alt, double range_nm)
         alt = 0;
 
     double radarhorizon = 1.23 * (sqrt(alt) + sqrt(user_alt));
-    SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: horizon " << radarhorizon);
+    //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: horizon " << radarhorizon);
     return radarhorizon >= range_nm;
 }
 
@@ -743,14 +817,14 @@ wxRadarBg::inRadarRange(int type, double range_nm)
     //
     // TODO - make the maximum range adjustable at runtime
 
-    const double sigma[] = {0, 1, 100, 100, 0.001, 0.1, 100, 100, 1, 1};
+    const double sigma[] = {0, 1, 100, 100, 0.001, 0.1, 100, 100, 1, 1, 1};
     double constant = _radar_ref_rng;
 
     if (constant <= 0)
         constant = 35;
 
     double maxrange = constant * pow(sigma[type], 0.25);
-    SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: max range " << maxrange);
+    //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: max range " << maxrange);
     return maxrange >= range_nm;
 }
 
@@ -780,3 +854,48 @@ wxRadarBg::calcRelBearing(float bearing, float heading)
     return angle;
 }
 
+
+void
+wxRadarBg::updateFont()
+{
+    float red = _font_node->getFloatValue("color/red", 0);
+    float green = _font_node->getFloatValue("color/green", 0.8);
+    float blue = _font_node->getFloatValue("color/blue", 0);
+    float alpha = _font_node->getFloatValue("color/alpha", 1);
+    _font_color.set(red, green, blue, alpha);
+
+    _font_size = _font_node->getFloatValue("size", 12);
+    _font_spacing = _font_size * _font_node->getFloatValue("line-spacing", 0.25);
+    string path = _font_node->getStringValue("name", DEFAULT_FONT);
+
+    SGPath tpath;
+    if (path[0] != '/') {
+        tpath = globals->get_fg_root();
+        tpath.append("Fonts");
+        tpath.append(path);
+    } else {
+        tpath = path;
+    }
+
+#if (FG_OSG_VERSION >= 21000)
+    osg::ref_ptr<osgDB::ReaderWriter::Options> fontOptions = new osgDB::ReaderWriter::Options("monochrome");
+    osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str(), fontOptions.get());
+#else
+    osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.c_str());
+#endif
+
+    if (font != 0) {
+        _font = font;
+        _font->setMinFilterHint(osg::Texture::NEAREST);
+        _font->setMagFilterHint(osg::Texture::NEAREST);
+        _font->setGlyphImageMargin(0);
+        _font->setGlyphImageMarginRatio(0);
+    }
+}
+
+void
+wxRadarBg::valueChanged(SGPropertyNode*)
+{
+    updateFont();
+}
+