From 800dd73551b53dc2f048902845276657cc0fcbbb Mon Sep 17 00:00:00 2001 From: mfranz Date: Sun, 24 Jun 2007 07:57:45 +0000 Subject: [PATCH] Vivian MEAZZA & Tim MOORE: - re-enable od_gauge ("owner drawn" render-to-texture instruments) - implement radar in c++ (unlimited number of clouds/ai/mp/... objects, better performance) --- src/AIModel/AIBase.hxx | 3 +- src/AIModel/AIShip.cxx | 2 + src/AIModel/submodel.cxx | 78 ++- src/Cockpit/panel.cxx | 8 +- src/Cockpit/panel.hxx | 1 + src/Instrumentation/od_gauge.cxx | 235 ++++----- src/Instrumentation/od_gauge.hxx | 43 +- src/Instrumentation/wxradar.cxx | 821 ++++++++++++++++++++++++------- src/Instrumentation/wxradar.hxx | 80 ++- 9 files changed, 878 insertions(+), 393 deletions(-) diff --git a/src/AIModel/AIBase.hxx b/src/AIModel/AIBase.hxx index 01102b4e6..f802c9937 100644 --- a/src/AIModel/AIBase.hxx +++ b/src/AIModel/AIBase.hxx @@ -193,7 +193,6 @@ public: double _getAltitude() const; double _getLongitude() const; double _getLatitude() const; - double _getBearing() const; double _getElevationFt() const; double _getRdot() const; double _getH_offset() const; @@ -245,6 +244,8 @@ public: static const double lbs_to_slugs; inline double _getRange() { return range; }; + inline double _getBearing() { return bearing; }; + osg::Node* load3DModel(const string& fg_root, const string &path, SGPropertyNode *prop_root, diff --git a/src/AIModel/AIShip.cxx b/src/AIModel/AIShip.cxx index 30e889aba..bd7844440 100644 --- a/src/AIModel/AIShip.cxx +++ b/src/AIModel/AIShip.cxx @@ -334,6 +334,8 @@ void FGAIShip::Run(double dt) { } + // do calculations for radar + UpdateRadar(manager); } }//end function diff --git a/src/AIModel/submodel.cxx b/src/AIModel/submodel.cxx index d08ceeafb..c158c05f8 100644 --- a/src/AIModel/submodel.cxx +++ b/src/AIModel/submodel.cxx @@ -1,4 +1,4 @@ -// submodel.cxx - models a releasable submodel. +//// submodel.cxx - models a releasable submodel. // Written by Dave Culp, started Aug 2004 // With major additions by Vivian Meaaza 2004 - 2007 // @@ -116,18 +116,19 @@ void FGSubmodelMgr::update(double dt) _impact = (*sm_list_itr)->_getImpactData(); _hit = (*sm_list_itr)->_getCollisionData(); int parent_subID = (*sm_list_itr)->_getSubID(); - + SG_LOG(SG_GENERAL, SG_DEBUG, "Submodel: Impact " << _impact << " hit! " + << _hit <<" parent_subID " << parent_subID); if ( parent_subID == 0) // this entry in the list has no associated submodel continue; // so we can continue if (_impact || _hit) { - SG_LOG(SG_GENERAL, SG_ALERT, "Submodel: Impact " << _impact << " hit! " << _hit ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Submodel: Impact " << _impact << " hit! " << _hit ); submodel_iterator = submodels.begin(); while (submodel_iterator != submodels.end()) { int child_ID = (*submodel_iterator)->id; - cout << "Impact: parent SubID " << parent_subID << " child_ID " << child_ID << endl; + //cout << "Impact: parent SubID " << parent_subID << " child_ID " << child_ID << endl; if ( parent_subID == child_ID ) { _parent_lat = (*sm_list_itr)->_getImpactLat(); @@ -161,10 +162,9 @@ void FGSubmodelMgr::update(double dt) in_range = true; SG_LOG(SG_GENERAL, SG_DEBUG, - "Submodels: " << (*submodel_iterator)->id - << " name " << (*submodel_iterator)->name - << " in range " << in_range - ); + "Submodels: " << (*submodel_iterator)->id + << " name " << (*submodel_iterator)->name + << " in range " << in_range); if ((*submodel_iterator)->trigger_node != 0) { _trigger_node = (*submodel_iterator)->trigger_node; @@ -188,7 +188,7 @@ void FGSubmodelMgr::update(double dt) if (id == 0) { SG_LOG(SG_GENERAL, SG_DEBUG, - "Submodels: continuing: " << id << " name " << name ); + "Submodels: continuing: " << id << " name " << name ); in_range = true; ++sm_list_itr; continue; @@ -207,21 +207,19 @@ void FGSubmodelMgr::update(double dt) if (range_nm > 15) { SG_LOG(SG_GENERAL, SG_DEBUG, - "Submodels: skipping release: " << id); + "Submodels: skipping release: " << id); in_range = false; } - } ++sm_list_itr; } // end while SG_LOG(SG_GENERAL, SG_DEBUG, - "Submodels end: " << (*submodel_iterator)->id - << " name " << (*submodel_iterator)->name - << " count " << (*submodel_iterator)->count - << " in range " << in_range - ); + "Submodels end: " << (*submodel_iterator)->id + << " name " << (*submodel_iterator)->name + << " count " << (*submodel_iterator)->count + << " in range " << in_range); if ((*submodel_iterator)->count != 0 && in_range) release(*submodel_iterator, dt); @@ -236,7 +234,7 @@ void FGSubmodelMgr::update(double dt) bool FGSubmodelMgr::release(submodel *sm, double dt) { //cout << "release id " << sm->id << " name " << sm->name - //<< " first time " << sm->first_time << " repeat " << (*sm)->repeat << + //<< " first time " << sm->first_time << " repeat " << sm->repeat << // endl; // only run if first time or repeat is set to true @@ -317,8 +315,9 @@ void FGSubmodelMgr::transform(submodel *sm) // set contents to 0 in the parent sm->contents_node->setDoubleValue(0); - } else + } else { IC.mass = sm->weight * lbs_to_slugs; + } // cout << "mass " << IC.mass << endl; @@ -463,22 +462,22 @@ void FGSubmodelMgr::transform(submodel *sm) // calculate the total speed north IC.total_speed_north = sm->speed * cos(IC.elevation * SG_DEGREES_TO_RADIANS) - * cos(IC.azimuth * SG_DEGREES_TO_RADIANS) + IC.speed_north_fps; + * cos(IC.azimuth * SG_DEGREES_TO_RADIANS) + IC.speed_north_fps; // calculate the total speed east IC.total_speed_east = sm->speed * cos(IC.elevation * SG_DEGREES_TO_RADIANS) - * sin(IC.azimuth * SG_DEGREES_TO_RADIANS) + IC.speed_east_fps; + * sin(IC.azimuth * SG_DEGREES_TO_RADIANS) + IC.speed_east_fps; // calculate the total speed down IC.total_speed_down = sm->speed * -sin(IC.elevation * SG_DEGREES_TO_RADIANS) - + IC.speed_down_fps; + + IC.speed_down_fps; // re-calculate speed, elevation and azimuth IC.speed = sqrt(IC.total_speed_north * IC.total_speed_north - + IC.total_speed_east * IC.total_speed_east - + IC.total_speed_down * IC.total_speed_down); + + IC.total_speed_east * IC.total_speed_east + + IC.total_speed_down * IC.total_speed_down); - // if speeds are low these calculations can become unreliable + // if speeds are low this calculation can become unreliable if (IC.speed > 1) { IC.azimuth = atan2(IC.total_speed_east , IC.total_speed_north) * SG_RADIANS_TO_DEGREES; // cout << "azimuth1 " << IC.azimuth<= 360) IC.azimuth -= 360; + } // cout << "azimuth2 " << IC.azimuth<get_fg_root()); config.append(path); SG_LOG(SG_GENERAL, SG_DEBUG, "Submodels: path " << path); - try { SG_LOG(SG_GENERAL, SG_DEBUG, - "Submodels: Trying to read AI submodels file: " << config.str()); + "Submodels: Trying to read AI submodels file: " << config.str()); readProperties(config.str(), &root); - } catch (const sg_exception &e) { SG_LOG(SG_GENERAL, SG_DEBUG, - "Submodels: Unable to read AI submodels file: " << config.str()); + "Submodels: Unable to read AI submodels file: " << config.str()); return; } @@ -610,6 +607,7 @@ void FGSubmodelMgr::setData(int id, string& path, bool serviceable) sm->trigger_node = 0; } + SG_LOG(SG_GENERAL, SG_DEBUG, "Submodels: trigger " << sm->trigger_node->getBoolValue() ); if (sm->speed_node != 0) sm->speed = sm->speed_node->getDoubleValue(); @@ -651,12 +649,12 @@ void FGSubmodelMgr::setSubData(int id, string& path, bool serviceable) "Submodels: path " << path); try { SG_LOG(SG_GENERAL, SG_DEBUG, - "Submodels: Trying to read AI submodels file: " << config.str()); + "Submodels: Trying to read AI submodels file: " << config.str()); readProperties(config.str(), &root); } catch (const sg_exception &e) { SG_LOG(SG_GENERAL, SG_DEBUG, - "Submodels: Unable to read AI submodels file: " << config.str()); + "Submodels: Unable to read AI submodels file: " << config.str()); return; } @@ -749,10 +747,10 @@ void FGSubmodelMgr::loadSubmodels() if (!submodel.empty()) { //int id = (*submodel_iterator)->id; bool serviceable = true; - SG_LOG(SG_GENERAL, SG_ALERT, "found path sub sub " - << submodel - << " index " << index - << "name " << (*submodel_iterator)->name); + SG_LOG(SG_GENERAL, SG_DEBUG, "found path sub sub " + << submodel + << " index " << index + << "name " << (*submodel_iterator)->name); (*submodel_iterator)->sub_id = index; setSubData(index, submodel, serviceable); @@ -773,9 +771,9 @@ void FGSubmodelMgr::loadSubmodels() while (submodel_iterator != submodels.end()) { int id = (*submodel_iterator)->id; SG_LOG(SG_GENERAL, SG_DEBUG,"after pusback " - << " id " << id - << " name " << (*submodel_iterator)->name - << " sub id " << (*submodel_iterator)->sub_id); + << " id " << id + << " name " << (*submodel_iterator)->name + << " sub id " << (*submodel_iterator)->sub_id); ++submodel_iterator; } diff --git a/src/Cockpit/panel.cxx b/src/Cockpit/panel.cxx index 7274e5570..890b9a39d 100644 --- a/src/Cockpit/panel.cxx +++ b/src/Cockpit/panel.cxx @@ -31,7 +31,7 @@ # include #endif -#ifdef HAVE_WINDOWS_H +#ifdef HAVE_WINDOWS_H # include #endif @@ -139,7 +139,11 @@ FGTextureManager::createTexture (const string &relativePath) } - +void FGTextureManager::addTexture(const string &relativePath, + osg::Texture2D* texture) +{ + _textureMap[relativePath] = texture; +} //////////////////////////////////////////////////////////////////////// // Implementation of FGCropped Texture. diff --git a/src/Cockpit/panel.hxx b/src/Cockpit/panel.hxx index 0eb883ddb..2b04559f4 100644 --- a/src/Cockpit/panel.hxx +++ b/src/Cockpit/panel.hxx @@ -76,6 +76,7 @@ class FGTextureManager { public: static osg::Texture2D* createTexture(const string &relativePath); + static void addTexture(const string &relativePath, osg::Texture2D* texture); private: static map > _textureMap; }; diff --git a/src/Instrumentation/od_gauge.cxx b/src/Instrumentation/od_gauge.cxx index 31607aa9c..ce6647259 100644 --- a/src/Instrumentation/od_gauge.cxx +++ b/src/Instrumentation/od_gauge.cxx @@ -4,6 +4,8 @@ // // Copyright (C) 2005 Harald JOHNSEN // +// Ported to OSG by Tim Moore - Jun 2007 +// // 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 @@ -24,12 +26,22 @@ # include "config.h" #endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include -#include #include -#include SG_GLU_H #include
+#include
#include #include "od_gauge.hxx" @@ -39,12 +51,44 @@ FGODGauge::FGODGauge() : { } -// done here and not in init() so we don't allocate a rendering context if it is -// never used void FGODGauge::allocRT () { - GLint colorBits = 0; + camera = new osg::Camera; + camera->setProjectionMatrix(osg::Matrix::ortho2D(-256.0, 256.0, -256.0, + 256.0)); + camera->setViewport(0, 0, textureWH, textureWH); + camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + camera->setRenderOrder(osg::Camera::PRE_RENDER); + camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f)); + camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + 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.valid()) { + texture = new osg::Texture2D; + texture->setTextureSize(textureWH, textureWH); + texture->setInternalFormat(GL_RGBA); + texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); + texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR); + } + camera->attach(osg::Camera::COLOR_BUFFER, texture.get()); + globals->get_renderer()->addCamera(camera.get(), false); + rtAvailable = true; + + // GLint colorBits = 0; // glGetIntegerv( GL_BLUE_BITS, &colorBits ); - textureWH = 256; // rt = new RenderTexture(); // if( colorBits < 8 ) // rt->Reset("rgba=5,5,5,1 ctt"); @@ -94,56 +138,6 @@ void FGODGauge::init () { void FGODGauge::update (double dt) { } -void FGODGauge::beginCapture(int viewSize) { -// if( ! rt ) -// allocRT(); -// if(rtAvailable) { -// rt->BeginCapture(); -// } -// else -// set2D(); -// textureWH = viewSize; -// glViewport(0, 0, textureWH, textureWH); -} - -void FGODGauge::beginCapture(void) { -// if( ! rt ) -// allocRT(); -// if(rtAvailable) { -// rt->BeginCapture(); -// } -// else -// set2D(); -} - -void FGODGauge::Clear(void) { -// if(rtAvailable) { -// glClear(GL_COLOR_BUFFER_BIT); -// } -// else { -// glDisable(GL_BLEND); -// glDisable(GL_ALPHA_TEST); -// glColor4f(0.0f, 0.0f, 0.0f, 0.0f); -// glRectf(-256.0, -256.0, 256.0, 256.0); -// glEnable(GL_BLEND); -// glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); -// glEnable(GL_ALPHA_TEST); -// } -} - -void FGODGauge::endCapture(osg::Texture2D* texID) { - // OSGFIXME -// glBindTexture(GL_TEXTURE_2D, texID); -// // don't use mimaps if we don't update them -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - -// glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, textureWH, textureWH); -// if(rtAvailable) -// rt->EndCapture(); -// else -// set3D(); -// glBindTexture(GL_TEXTURE_2D, 0); -} void FGODGauge::setSize(int viewSize) { textureWH = viewSize; @@ -155,93 +149,60 @@ bool FGODGauge::serviceable(void) { } /** - * Locate a texture SSG node in a branch. + * Replace a texture in the airplane model with the gauge texture. */ -static const char *strip_path(const char *name) { - /* Remove all leading path information. */ - const char* seps = "\\/" ; - const char* fn = & name [ strlen ( name ) - 1 ] ; - for ( ; fn != name && strchr(seps,*fn) == NULL ; fn-- ) - /* Search back for a seperator */ ; - if ( strchr(seps,*fn) != NULL ) - fn++ ; - return fn ; -} -// OSGFIXME -static osg::StateSet* -find_texture_node(osg::Node* node, const char * name) +class ReplaceStaticTextureVisitor : public osg::NodeVisitor { -#if 0 - if( node->isAKindOf( ssgTypeLeaf() ) ) { - ssgLeaf *leaf = (ssgLeaf *) node; - ssgSimpleState *state = (ssgSimpleState *) leaf->getState(); - if( state ) { - ssgTexture *tex = state->getTexture(); - if( tex ) { - const char * texture_name = tex->getFilename(); - if (texture_name) { - texture_name = strip_path( texture_name ); - if ( !strcmp(name, texture_name) ) - return state; - } - } - } - } - else { - int nKids = node->getNumKids(); - for (int i = 0; i < nKids; i++) { - ssgSimpleState * result = - find_texture_node(((ssgBranch*)node)->getKid(i), name); - if (result != 0) - return result; +public: + ReplaceStaticTextureVisitor(const std::string& name, + osg::Texture2D* _newTexture) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + newTexture(_newTexture) + { + textureFileName = osgDB::getSimpleFileName(name); } - } -#endif - return 0; -} -void FGODGauge::set_texture(const char * name, osg::Texture2D* new_texture) { - osg::Group* root = globals->get_scenery()->get_aircraft_branch(); - name = strip_path( name ); - // OSGFIXME -// osg::StateSet* node = find_texture_node( root, name ); -// if( node ) -// node->setTexture( new_texture ); -} + virtual void apply(osg::Node& node) + { + osg::StateSet* ss = node.getStateSet(); + if (ss) + changeStateSetTexture(ss); + traverse(node); + } -void FGODGauge::set2D() { -// glPushAttrib ( GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_TRANSFORM_BIT | GL_LIGHTING_BIT ) ; - -// glDisable(GL_LIGHTING); -// glEnable(GL_COLOR_MATERIAL); -// glDisable(GL_CULL_FACE); -// glDisable(GL_FOG); -// glDisable(GL_DEPTH_TEST); -// glClearColor(0.0, 0.0, 0.0, 0.0); -// glEnable(GL_TEXTURE_2D); -// glDisable(GL_SMOOTH); -// glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); -// glBindTexture(GL_TEXTURE_2D, 0); - -// glViewport ( 0, 0, textureWH, textureWH ) ; -// glMatrixMode ( GL_PROJECTION ) ; -// glPushMatrix () ; -// glLoadIdentity () ; -// gluOrtho2D( -256.0, 256.0, -256.0, 256.0 ); -// glMatrixMode ( GL_MODELVIEW ) ; -// glPushMatrix () ; -// glLoadIdentity () ; - -// glAlphaFunc(GL_GREATER, 0.0f); + virtual void apply(osg::Geode& node) + { + int numDrawables = node.getNumDrawables(); + for (int i = 0; i < numDrawables; i++) { + osg::Drawable* drawable = node.getDrawable(i); + osg::StateSet* ss = drawable->getStateSet(); + if (ss) + changeStateSetTexture(ss); + } + traverse(node); +} +protected: + void changeStateSetTexture(osg::StateSet *ss) + { + osg::Texture2D* tex + = dynamic_cast(ss->getTextureAttribute(0, + osg::StateAttribute::TEXTURE)); + if (!tex || tex == newTexture || !tex->getImage()) + return; + std::string fileName = tex->getImage()->getFileName(); + std::string simpleName = osgDB::getSimpleFileName(fileName); + if (osgDB::equalCaseInsensitive(textureFileName, simpleName)) + ss->setTextureAttribute(0, newTexture); + } + std::string textureFileName; + osg::Texture2D* newTexture; +}; +void FGODGauge::set_texture(const char * name, osg::Texture2D* new_texture) +{ + osg::Group* root = globals->get_scenery()->get_aircraft_branch(); + ReplaceStaticTextureVisitor visitor(name, new_texture); + root->accept(visitor); } -void FGODGauge::set3D() { -// glMatrixMode ( GL_PROJECTION ) ; -// glPopMatrix () ; -// glMatrixMode ( GL_MODELVIEW ) ; -// glPopMatrix () ; -// glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; -// glPopAttrib () ; -} diff --git a/src/Instrumentation/od_gauge.hxx b/src/Instrumentation/od_gauge.hxx index caf18a41a..2c957d429 100644 --- a/src/Instrumentation/od_gauge.hxx +++ b/src/Instrumentation/od_gauge.hxx @@ -4,6 +4,8 @@ // // Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net // +// Ported to OSG by Tim Moore - Jun 2007 +// // 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 @@ -23,6 +25,7 @@ #ifndef _OD_GAUGE_HXX #define _OD_GAUGE_HXX +#include #include #include @@ -39,34 +42,14 @@ public: virtual void init (); virtual void update (double dt); - /** - * Start the rendering of the RTT context. - * @param viewSize size of the destination texture - */ - void beginCapture(int viewSize); - /** - * Start the rendering of the RTT context. - */ - void beginCapture(void); - /** - * Clear the background. - */ - void Clear(void); - /** - * Finish rendering and save the buffer to a texture. - * @param texID name of a gl texture - */ - void endCapture(osg::Texture2D*); - /** - * Set the size of the destination texture. - * @param viewSize size of the destination texture - */ void setSize(int viewSize); + /** * 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 @@ -75,13 +58,23 @@ public: */ void set_texture(const char * name, osg::Texture2D* new_texture); + /** + * Get the OSG camera for drawing this gauge. + */ + osg::Camera* getCamera() { return camera.get(); } + + osg::Texture2D* getTexture() { return texture.get(); } + void setTexture(osg::Texture2D* t) { texture = t; } + + // Real initialization function. Bad name. + void allocRT(void); + private: int textureWH; bool rtAvailable; + osg::ref_ptr camera; + osg::ref_ptr texture; - void allocRT(void); - void set2D(void); - void set3D(void); }; #endif // _OD_GAUGE_HXX diff --git a/src/Instrumentation/wxradar.cxx b/src/Instrumentation/wxradar.cxx index 7319a5b41..0cee87b23 100644 --- a/src/Instrumentation/wxradar.cxx +++ b/src/Instrumentation/wxradar.cxx @@ -1,6 +1,9 @@ // 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 // @@ -24,30 +27,45 @@ # 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 -#include -#include -#include #include "instrument_mgr.hxx" #include "od_gauge.hxx" #include "wxradar.hxx" + // texture name to use in 2D and 3D instruments static const char *odgauge_name = "Aircraft/Instruments/Textures/od_wxradar.rgb"; wxRadarBg::wxRadarBg ( SGPropertyNode *node) : _name(node->getStringValue("name", "wxRadar")), _num(node->getIntValue("number", 0)), + _last_switchKnob( "off" ), + _sim_init_done ( false ), resultTexture( 0 ), wxEcho( 0 ), - last_switchKnob( "off" ), - sim_init_done ( false ), - odg( 0 ) + _odg( 0 ) { } @@ -63,14 +81,11 @@ wxRadarBg::init () _Instrument = fgGetNode(branch.c_str(), _num, true ); _serviceable_node = _Instrument->getChild("serviceable", 0, true); - resultTexture = FGTextureManager::createTexture( odgauge_name ); + SGPath tpath(globals->get_fg_root()); tpath.append("Aircraft/Instruments/Textures/wxecho.rgb"); // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect - - // OSGFIXME -// wxEcho = new ssgTexture( tpath.c_str(), false, false, false); - wxEcho = new osg::Texture2D; + wxEcho = SGLoadTexture2D(tpath.c_str(), false, false); _Instrument->setFloatValue("trk", 0.0); _Instrument->setFloatValue("tilt", 0.0); @@ -78,7 +93,7 @@ wxRadarBg::init () // those properties are used by a radar instrument of a MFD // input switch = OFF | TST | STBY | ON // input mode = WX | WXA | MAP - // ouput status = STBY | TEST | WX | WXA | MAP | blank + // output status = STBY | TEST | WX | WXA | MAP | blank // input lightning = true | false // input TRK = +/- n degrees // input TILT = +/- n degree @@ -87,172 +102,463 @@ wxRadarBg::init () // input display-mode = arc | rose | map | plan FGInstrumentMgr *imgr = (FGInstrumentMgr *) globals->get_subsystem("instrumentation"); - odg = (FGODGauge *) imgr->get_subsystem("od_gauge"); + _odg = (FGODGauge *) imgr->get_subsystem("od_gauge"); + _odg->setSize(256); + + _ai = (FGAIManager*)globals->get_subsystem("ai_model"); + + _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 = fgGetNode("/instrumentation/tacan", _num, 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 = fgGetNode("/instrumentation/radar/display-controls", _num, true); + _radar_weather_node = _Radar->getNode("WX", true); + _radar_position_node = _Radar->getNode("pos", true); + _radar_data_node = _Radar->getNode("data", true); + _radar_centre_node = _Radar->getNode("centre", true); + + _radar_centre_node->setBoolValue(false); + + _Radar = fgGetNode("/instrumentation/radar/", _num, true); + _radar_mode_control_node = _Radar->getNode("mode-control", true); + _radar_coverage_node = _Radar->getNode("limit-deg", true); + _radar_ref_rng_node = _Radar->getNode("reference-range-nm", true); + + _radar_coverage_node->setFloatValue(120); + _radar_ref_rng_node->setDoubleValue(35); + + _ai_enabled_node = fgGetNode("/sim/ai/enabled", true); + + _x_displacement = 0; + _y_displacement = 0; + _x_sym_displacement = 0; + _y_sym_displacement = 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()); + osg::Geometry* geom = new osg::Geometry; + geom->setUseDisplayList(false); + // Initially allocate space for 128 quads + osg::Vec2Array* vertices = new osg::Vec2Array; + vertices->setDataVariance(osg::Object::DYNAMIC); + vertices->reserve(128 * 4); + geom->setVertexArray(vertices); + osg::Vec2Array* 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(odgauge_name, _odg->getTexture()); + + osg::Camera* camera = _odg->getCamera(); + camera->addChild(radarGeode.get()); +} + +// 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 float symbolSize = 1.0f / 8.0f; +const osg::Vec2f echoTexCoords[4] = { + osg::Vec2f(0.0f, 0.0f), osg::Vec2f(symbolSize, 0.0f), + osg::Vec2f(symbolSize, symbolSize), osg::Vec2f(0.0f, symbolSize) +}; + +// 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) { - //OSGFIXME -#if 0 - if ( ! sim_init_done ) { + if ( ! _sim_init_done ) { if ( ! fgGetBool("sim/sceneryloaded", false) ) return; - sim_init_done = true; + + _sim_init_done = true; } - if ( !odg || ! _serviceable_node->getBoolValue() ) { + + if ( !_odg || ! _serviceable_node->getBoolValue() ) { _Instrument->setStringValue("status",""); return; } + string switchKnob = _Instrument->getStringValue("switch", "on"); string modeButton = _Instrument->getStringValue("mode", "wx"); bool drawLightning = _Instrument->getBoolValue("lightning", true); float range_nm = _Instrument->getFloatValue("range", 40.0); float range_m = range_nm * SG_NM_TO_METER; - if ( last_switchKnob != switchKnob ) { + _user_speed_east_fps = _user_speed_east_fps_node->getDoubleValue(); + _user_speed_north_fps = _user_speed_north_fps_node->getDoubleValue(); + + if ( _last_switchKnob != switchKnob ) { // since 3D models don't share textures with the rest of the world // we must locate them and replace their handle by hand // only do that when the instrument is turned on - if ( last_switchKnob == "off" ) - odg->set_texture( odgauge_name, resultTexture.get()); - last_switchKnob = switchKnob; - } - FGViewer *current__view = globals->get_current_view(); - if ( current__view->getInternal() && - (current__view->getHeadingOffset_deg() <= 15.0 || current__view->getHeadingOffset_deg() >= 345.0) && - (current__view->getPitchOffset_deg() <= 15.0 || current__view->getPitchOffset_deg() >= 350.0) ) { - - // we don't update the radar echo if the pilot looks around - // this is a copy - radarEchoBuffer = *sgEnviro.get_radar_echo(); + //if (last_switchKnob == "off") + //_odg->set_texture(odgauge_name, resultTexture.get()); + _last_switchKnob = switchKnob; } - odg->beginCapture(256); - odg->Clear(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glPushMatrix(); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glBindTexture(GL_TEXTURE_2D, 0); + _radarEchoBuffer = *sgEnviro.get_radar_echo(); + updateRadar(); + //FGViewer *current__view = globals->get_current_view(); if ( switchKnob == "off" ) { _Instrument->setStringValue("status",""); + return; } else if ( switchKnob == "stby" ) { _Instrument->setStringValue("status","STBY"); + return; } else if ( switchKnob == "tst" ) { _Instrument->setStringValue("status","TST"); - // find something interesting to do... - } else { - string display_mode = _Instrument->getStringValue("display-mode", "arc"); - - // 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; - float view_heading = get_heading() * SG_DEGREES_TO_RADIANS; - float range = 200.0f / range_nm; - _Instrument->setStringValue("status", modeButton.c_str()); - if ( display_mode == "arc" ) { - glTranslatef(0.0f, -180.0f, 0.0f); - range = 2*180.0f / range_nm; - } else if ( display_mode == "map" ) { -// float view_heading = get_track() * SG_DEGREES_TO_RADIANS; - } else if ( display_mode == "plan" ) { - // no sense I presume - view_heading = 0; + return; + } + + // find something interesting to do... + string display_mode = _Instrument->getStringValue("display-mode", "arc"); + // 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; + float view_heading = get_heading() * SG_DEGREES_TO_RADIANS; + float range = 200.0f / range_nm; + _Instrument->setStringValue("status", modeButton.c_str()); + osg::Matrixf centerTrans; + + if ( display_mode == "arc" ) { + centerTrans.makeTranslate(0.0f, -180.0f, 0.0f); + range = 2*180.0f / range_nm; + + } else if ( display_mode == "map" ) { + view_heading = 0; + + if (_radar_centre_node->getBoolValue()) { + _x_displacement =_y_displacement = 0; } else { - // rose + _x_displacement += range * _user_speed_east_fps * SG_FPS_TO_KT + * delta_time_sec / (60*60); + _y_displacement += range * _user_speed_north_fps * SG_FPS_TO_KT + * delta_time_sec / (60*60); } - range /= SG_NM_TO_METER; - // we will rotate the echo quads, this gives a better rendering - const float rot_x = cos ( view_heading ); - const float rot_y = sin ( view_heading ); - - list_of_SGWxRadarEcho *radarEcho = &radarEchoBuffer; - list_of_SGWxRadarEcho::iterator iradarEcho; - const float LWClevel[] = { 0.1f, 0.5f, 2.1f }; - const float symbolSize = 1.0f / 8.0f ; - // draw the radar echo, we do that in 3 passes, one for each color level - // this is to 'merge' same colors together - // OSGFIXME -// glBindTexture(GL_TEXTURE_2D, wxEcho->getHandle() ); - glColor3f(1.0f, 1.0f, 1.0f); - glBegin( GL_QUADS ); - for (int level = 0; level <= 2 ; level++ ) { + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: displacement " + << _x_displacement << ", "<<_y_displacement + << " _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 + << " centre " << _radar_centre_node->getBoolValue()); + + centerTrans.makeTranslate(_x_displacement, _y_displacement, 0.0f); + + } else if ( display_mode == "plan" ) { + // no sense I presume + view_heading = 0; + } else { + // rose + view_heading = 0; + } + + range /= SG_NM_TO_METER; + + list_of_SGWxRadarEcho *radarEcho = &_radarEchoBuffer; + list_of_SGWxRadarEcho::iterator iradarEcho; + const float LWClevel[] = { 0.1f, 0.5f, 2.1f }; + float dist = 0; + float size = 0; + + osg::Geometry* geom + = static_cast(radarGeode->getDrawable(0)); + osg::Vec2Array* vertices + = static_cast(geom->getVertexArray()); + osg::Vec2Array* texCoords + = static_cast(geom->getTexCoordArray(0)); + vertices->clear(); + texCoords->clear(); + + // 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 * symbolSize; - for (iradarEcho = radarEcho->begin() ; iradarEcho != radarEcho->end() ; iradarEcho++ ) { - int cloudId = (iradarEcho->cloudId) ; + + for (iradarEcho = radarEcho->begin(); iradarEcho != radarEcho->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) + if (iradarEcho->LWC >= 0.5 && iradarEcho->LWC <= 0.6) continue; - if ( (! iradarEcho->lightning) && ( lwc >= LWClevel[level]) ) { - float dist = sgSqrt( iradarEcho->dist ); - float size = iradarEcho->radius * 2.0; + + if (iradarEcho->lightning || lwc < LWClevel[level] + || iradarEcho->aircraft) + continue; + + dist = sgSqrt(iradarEcho->dist); + size = iradarEcho->radius * 2.0; + if ( dist - size > range_m ) continue; - dist = dist * range; - size = size * range; + + dist *= range; + size *= range; + // Translate echo to proper distance on screen + osg::Matrixf distTrans + = osg::Matrixf::translate(0.0f, dist * range, 0.0f); + // Scale echo + osg::Matrixf scaleEcho = osg::Matrixf::scale(size, size, 1.0f); // compute the relative angle from the view direction - float angle = ( view_heading + iradarEcho->heading ); - if ( angle > SG_PI ) - angle -= 2.0*SG_PI; - if ( angle < - SG_PI ) - angle += 2.0*SG_PI; - // and apply a fov factor to simulate a greater scan angle - angle = angle * fovFactor + SG_PI / 2.0; - float x = cos( angle ) * dist; - float y = sin( angle ) * dist; + float angle = calcRelBearing(iradarEcho->bearing, view_heading); + + // we will rotate the echo quads, this gives a better rendering + //const float rot_x = cos (view_heading); + //const float rot_y = sin (view_heading); + + // and apply a fov factor to simulate a greater scan + // angle + angle *= fovFactor; + // 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. + osg::Matrixf rotEcho = wxRotate(angle); // use different shapes so the display is less boring - float row = symbolSize * (float) (4 + (cloudId & 3) ); - float size_x = rot_x * size; - float size_y = rot_y * size; - glTexCoord2f( col, row); - glVertex2f( x - size_x, y - size_y); - glTexCoord2f( col+symbolSize, row); - glVertex2f( x + size_y, y - size_x); - glTexCoord2f( col+symbolSize, row+symbolSize); - glVertex2f( x + size_x, y + size_y); - glTexCoord2f( col, row+symbolSize); - glVertex2f( x - size_y, y + size_x); + //float row = symbolSize * (float) (4 + (cloudId & 3) ); + const osg::Vec2f texBase(col, (symbolSize + * (float) (4 + (cloudId & 3)))); + osg::Matrixf m(scaleEcho * distTrans * rotEcho * centerTrans); + addQuad(vertices, texCoords, m, texBase); + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: drawing clouds" + << " ID " << iradarEcho->cloudId + << " dist" << dist + << " view_heading" << view_heading / SG_DEGREES_TO_RADIANS + << " heading " << iradarEcho->heading / SG_DEGREES_TO_RADIANS + << " angle " << angle / SG_DEGREES_TO_RADIANS); } } } - glEnd(); // GL_QUADS // draw lightning echos if ( drawLightning ) { - float col = 3 * symbolSize; - float row = 4 * symbolSize; - for (iradarEcho = radarEcho->begin() ; iradarEcho != radarEcho->end() ; iradarEcho++ ) { - if ( iradarEcho->lightning ) { - float dist = iradarEcho->dist; - dist = dist * range; - float angle = (view_heading - iradarEcho->heading); - if ( angle > SG_PI ) - angle -= 2.0*SG_PI; - if ( angle < - SG_PI ) - angle += 2.0*SG_PI; - angle = angle * fovFactor - SG_PI / 2.0; - float x = cos( angle ) * dist; - float y = sin( angle ) * dist; - glColor3f(1.0f, 1.0f, 1.0f); - float size = symbolSize * 0.5f; - glBegin( GL_QUADS ); - glTexCoord2f( col, row); - glVertex2f( x - size, y - size); - glTexCoord2f( col+symbolSize, row); - glVertex2f( x + size, y - size); - glTexCoord2f( col+symbolSize, row+symbolSize); - glVertex2f( x + size, y + size); - glTexCoord2f( col, row+symbolSize); - glVertex2f( x - size, y + size); - glEnd(); - } + const osg::Vec2f texBase(3 * symbolSize, 4 * symbolSize); + for (iradarEcho = radarEcho->begin(); + iradarEcho != radarEcho->end(); + ++iradarEcho) { + + if (!iradarEcho->lightning) + continue; + + float dist = iradarEcho->dist * range; + float angle = calcRelBearing(iradarEcho->bearing, view_heading); + + if ( angle > SG_PI ) + angle -= 2.0*SG_PI; + if ( angle < - SG_PI ) + angle += 2.0*SG_PI; + + angle *= fovFactor; + // Rotate the symbol into position without rotating the + // symbol itself + osg::Vec3f trans(0.0f, dist, 0.0f); + trans = wxRotate(angle).preMult(trans); + osg::Matrixf m(osg::Matrixf::scale(symbolSize, symbolSize, 1.0) + * osg::Matrixf::translate(trans) * centerTrans); + addQuad(vertices, texCoords, m, texBase); } } + + //draw aircraft echoes + if (_radar_position_node->getBoolValue()) { + const osg::Vec2f texBase(3 * symbolSize, 3 * symbolSize); + for (iradarEcho = radarEcho->begin(); + iradarEcho != radarEcho->end(); + ++iradarEcho) { + + if (!iradarEcho->aircraft) + continue; + + dist = iradarEcho->dist * range; + + // calculate relative bearing + float angle = calcRelBearing(iradarEcho->bearing, view_heading); + float limit = _radar_coverage_node->getFloatValue(); + + if (limit > 180) + limit = 180; + else if (limit < 0) + limit = 0; + + // if it's in coverage, draw it + if (angle >= limit * SG_DEGREES_TO_RADIANS + || angle < -limit * SG_DEGREES_TO_RADIANS) + continue; + + size = symbolSize * iradarEcho->radius; + osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f) + * osg::Matrixf::translate(0.0f, dist, 0.0f) + * wxRotate(angle) * centerTrans); + addQuad(vertices, texCoords, m, texBase); + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: drawing AI" + << " ID " << iradarEcho->cloudId + << " dist" << dist + << " view_heading" << view_heading / SG_DEGREES_TO_RADIANS + << " heading " << iradarEcho->heading / SG_DEGREES_TO_RADIANS + << " angle " << angle / SG_DEGREES_TO_RADIANS); + } + } + + // Draw aircraft data + if (_radar_data_node->getBoolValue()) { + const osg::Vec2f texBase(0, 3 * symbolSize); + + for (iradarEcho = radarEcho->begin(); + iradarEcho != radarEcho->end(); + ++iradarEcho) { + + if (!iradarEcho->aircraft) + continue; + + dist = iradarEcho->dist; + dist *= range; + // calculate relative bearing + float angle = calcRelBearing(iradarEcho->bearing, view_heading); + float limit = _radar_coverage_node->getFloatValue(); + + if (limit > 180) + limit = 180; + else if (limit < 0) + limit = 0; + + // if it's in coverage, draw it + if (angle >= limit * SG_DEGREES_TO_RADIANS + || angle < -limit * SG_DEGREES_TO_RADIANS) + continue; + + size = symbolSize * 750; + // Rotate symbol to indicate relative heading iradarEcho->bearing + // - view_heading - angle + //cout << "heading " << iradarEcho->heading << endl; + + osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f) + * wxRotate(iradarEcho->heading - view_heading - angle) + * osg::Matrixf::translate(0.0f, dist, 0.0f) + * wxRotate(angle) * centerTrans); + addQuad(vertices, texCoords, m, texBase); + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: drawing data" + << " ID " << iradarEcho->cloudId + << " view_heading " << view_heading / SG_DEGREES_TO_RADIANS + << " bearing " << angle / SG_DEGREES_TO_RADIANS + << " dist" << dist + << " heading " << iradarEcho->heading / SG_DEGREES_TO_RADIANS + << " rotation " << (iradarEcho->heading - view_heading - angle) + / SG_DEGREES_TO_RADIANS); + } + } + + //draw TACAN symbol + int mode = _radar_mode_control_node->getIntValue(); + bool inRange = _tacan_in_range_node->getBoolValue(); + + if (mode == 1 && inRange) { + const osg::Vec2f texBase(1 * symbolSize, 3 * symbolSize); + dist = _tacan_distance_node->getFloatValue() * SG_NM_TO_METER; + dist *= range; + // calculate relative bearing + float angle = calcRelBearing(_tacan_bearing_node->getFloatValue() + * SG_DEGREES_TO_RADIANS, view_heading); + + // it's always in coverage, so draw it + osg::Vec3f trans(osg::Vec3f(0.0f, dist, 0.0f) * wxRotate(angle)); + size = symbolSize * 750; + osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f) + * osg::Matrixf::translate(trans) * centerTrans); + addQuad(vertices, texCoords, m, texBase); + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: drawing TACAN" + << " dist" << dist + << " view_heading " << view_heading / SG_DEGREES_TO_RADIANS + << " heading " << _tacan_bearing_node->getDoubleValue() + << " angle " << angle / SG_DEGREES_TO_RADIANS + << " size " << size); + } + + //draw aircraft symbol + const osg::Vec2f texBase(2 * symbolSize, 3 * symbolSize); + size = symbolSize * 750; + view_heading = get_heading() * SG_DEGREES_TO_RADIANS; + osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f) + * wxRotate(view_heading)); + + if (display_mode == "map") { + //cout << "Map Mode " << range << endl; + m *= osg::Matrixf::translate(range, range, 0.0f); + } + + m *= centerTrans; + + addQuad(vertices, texCoords, m, texBase); + osg::DrawArrays* quadPSet + = static_cast(geom->getPrimitiveSet(0)); + quadPSet->set(osg::PrimitiveSet::QUADS, 0, vertices->size()); + quadPSet->dirty(); // erase what is out of sight of antenna /* |\ /| @@ -263,64 +569,211 @@ wxRadarBg::update (double delta_time_sec) | | --------- */ - float yOffset = 180.0f, xOffset = 256.0f; + osg::DrawArrays* maskPSet + = static_cast(geom->getPrimitiveSet(1)); + osg::DrawArrays* trimaskPSet + = static_cast(geom->getPrimitiveSet(2)); + + float xOffset = 256.0f, yOffset = 180.0f; + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: display mode " << display_mode); +#if 0 if ( display_mode != "arc" ) { - yOffset = 40.0f; xOffset = 240.0f; - } + yOffset = 40.0f; + } +#endif - if ( display_mode != "plan" ) { - glDisable(GL_BLEND); - glColor4f(1.0f, 0.0f, 0.0f, 0.01f); - glBegin( GL_QUADS ); - glTexCoord2f( 0.5f, 0.25f); - glVertex2f(-xOffset, 0.0 + yOffset); - glTexCoord2f( 1.0f, 0.25f); - glVertex2f(xOffset, 0.0 + yOffset); - glTexCoord2f( 1.0f, 0.5f); - glVertex2f(xOffset, 256.0 + yOffset); - glTexCoord2f( 0.5f, 0.5f); - glVertex2f(-xOffset, 256.0 + yOffset); - glEnd(); - - glColor4f(0.0f, 0.0f, 0.0f, 0.0f); -// glColor4f(0.0f, 1.0f, 0.0f, 1.0f); - glDisable(GL_ALPHA_TEST); - glBindTexture(GL_TEXTURE_2D, 0); - - glBegin( GL_TRIANGLES ); - glVertex2f(0.0, 0.0); - glVertex2f(-256.0, 0.0); - glVertex2f(-256.0, 256.0 * tan(30*SG_DEGREES_TO_RADIANS)); - - glVertex2f(0.0, 0.0); - glVertex2f(256.0, 0.0); - glVertex2f(256.0, 256.0 * tan(30*SG_DEGREES_TO_RADIANS)); - - glVertex2f(-256, 0.0); - glVertex2f(256.0, 0.0); - glVertex2f(-256.0, -256.0); - - glVertex2f(256, 0.0); - glVertex2f(256.0, -256.0); - glVertex2f(-256.0, -256.0); - glEnd(); - } + if (display_mode == "arc" ) { + 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); + // 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); + const osg::Vec2f whiteSpot(1.0f, 0.0f); + 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)); - // DEBUG only -/* glColor4f(1.0f, 0.0f, 0.0f, 1.0f); - glBegin( GL_LINES ); - glVertex2f(0.0, 0.0); - glVertex2f(-256.0, 256.0); - glVertex2f(0.0, 0.0); - glVertex2f(256.0, 256.0); - glEnd();*/ - - glEnable(GL_BLEND); - glEnable(GL_ALPHA_TEST); + 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)); + + for (int i = 0; i < 3 * 4; i++) + texCoords->push_back(whiteSpot); + + trimaskPSet->set(osg::PrimitiveSet::TRIANGLES, firstQuadVert + 4, + 3 * 4); + + } else { + maskPSet->set(osg::PrimitiveSet::QUADS, 0, 0); + trimaskPSet->set(osg::PrimitiveSet::TRIANGLES, 0, 0); } - glPopMatrix(); - odg->endCapture( resultTexture.get() ); -#endif + maskPSet->dirty(); + trimaskPSet->dirty(); +} + +void +wxRadarBg::updateRadar() +{ + bool ai_enabled = _ai_enabled_node->getBoolValue(); + + if (!ai_enabled) + return; + + double radius[] = {0, 1, 1.5, 1.5, 0.001, 0.1, 1.5, 2, 1.5, 1.5}; + bool isDetected = false; + + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: Loading AI submodels "); + _radar_list = _ai->get_ai_list(); + + if (_radar_list.empty()) { + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: Unable to read AI submodel list"); + return; + } + + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: AI submodel list size" << _radar_list.size()); + + double user_alt = _user_alt_node->getDoubleValue(); + double user_lat = _user_lat_node->getDoubleValue(); + double user_lon = _user_lon_node->getDoubleValue(); + + radar_list_iterator radar_list_itr = _radar_list.begin(); + radar_list_iterator end = _radar_list.end(); + + while (radar_list_itr != end) { + double range = (*radar_list_itr)->_getRange(); + double bearing = (*radar_list_itr)->_getBearing(); + double lat = (*radar_list_itr)->_getLatitude(); + double lon = (*radar_list_itr)->_getLongitude(); + double alt = (*radar_list_itr)->_getAltitude(); + double heading = (*radar_list_itr)->_getHeading(); + int id = (*radar_list_itr)->getID(); + int type = (*radar_list_itr)->getType(); + + calcRngBrg(user_lat, user_lon, lat, lon, range, bearing); + + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: AI list size" << _radar_list.size() + << " type " << type + << " ID " << id + << " radar range " << range + << " bearing " << bearing + << " alt " << alt); + + bool isVisible = calcRadarHorizon(user_alt, alt, range); + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: visible " << isVisible); + + if (isVisible) + isDetected = calcMaxRange(type, range); + + //(float _heading, float _alt, float _radius, float _dist, double _LWC, bool _lightning, + // int _cloudId, bool _aircraft) + if (isDetected) + _radarEchoBuffer.push_back(SGWxRadarEcho ( + bearing * SG_DEGREES_TO_RADIANS, + alt, + radius[type] * 120, + range * SG_NM_TO_METER, + heading * SG_DEGREES_TO_RADIANS, + 1, + false, + id, + true)); + + ++radar_list_itr; + } +} + +bool +wxRadarBg::calcRadarHorizon(double user_alt, double alt, double range) +{ + // Radar Horizon = 1.23(ht^1/2 + hr^1/2), + + //don't allow negative altitudes (an approximation - yes altitudes can be negative) + + if (user_alt < 0) + user_alt = 0; + + if (alt < 0) + alt = 0; + + double radarhorizon = 1.23 * (sqrt(alt) + sqrt(user_alt)); + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: horizon " << radarhorizon); + + return radarhorizon >= range; } + +bool +wxRadarBg::calcMaxRange(int type, double range) +{ + //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 target and assume that this + // will provide a maximum range of 35nm; + // + // The reference range is adjustable at runtime + + double sigma[] = {0, 1, 100, 100, 0.001, 0.1, 100, 100, 1, 1}; + double constant = _radar_ref_rng_node->getDoubleValue(); + + if (constant <= 0) + constant = 35; + + double maxrange = constant * pow(sigma[type], 0.25); + + SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: max range " << maxrange); + + return maxrange >= range; +} + +void +wxRadarBg::calcRngBrg(double lat, double lon, double lat2, double lon2, double &range, + double &bearing ) const +{ + double az2, distance; + + // calculate the bearing and range of the second pos from the first + 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; +} + diff --git a/src/Instrumentation/wxradar.hxx b/src/Instrumentation/wxradar.hxx index 575519015..4d285a374 100644 --- a/src/Instrumentation/wxradar.hxx +++ b/src/Instrumentation/wxradar.hxx @@ -1,6 +1,8 @@ // 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 // @@ -23,15 +25,28 @@ #ifndef _INST_WXRADAR_HXX #define _INST_WXRADAR_HXX +#include + #include +#include #include #include #include #include +#include +#include +#include +#include +SG_USING_STD(vector); +SG_USING_STD(string); + +class FGAIBase; + class FGODGauge; + class wxRadarBg : public SGSubsystem { @@ -49,14 +64,71 @@ private: string _name; int _num; + string _last_switchKnob; + bool _sim_init_done; + + double _user_speed_east_fps; + double _user_speed_north_fps; + + float _x_displacement; + float _y_displacement; + float _x_sym_displacement; + float _y_sym_displacement; + SGPropertyNode_ptr _serviceable_node; SGPropertyNode_ptr _Instrument; + SGPropertyNode_ptr _Tacan; + SGPropertyNode_ptr _Radar; + SGPropertyNode_ptr _Radar_controls; + + SGPropertyNode_ptr _user_lat_node; + SGPropertyNode_ptr _user_lon_node; + SGPropertyNode_ptr _user_heading_node; + SGPropertyNode_ptr _user_alt_node; + 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_mode_control_node; + SGPropertyNode_ptr _radar_centre_node; + SGPropertyNode_ptr _radar_coverage_node; + SGPropertyNode_ptr _radar_ref_rng_node; + + SGPropertyNode_ptr _ai_enabled_node; + osg::ref_ptr resultTexture; osg::ref_ptr wxEcho; - string last_switchKnob; - bool sim_init_done; - FGODGauge *odg; - list_of_SGWxRadarEcho radarEchoBuffer; + osg::ref_ptr radarGeode; + + list_of_SGWxRadarEcho _radarEchoBuffer; + + FGODGauge *_odg; + FGAIManager* _ai; + + bool calcRadarHorizon(double user_alt, double alt, double range); + bool calcMaxRange(int type, double range); + + void calcRngBrg(double lat, double lon, double lat2, double lon2, + double &range, double &bearing) const; + + void updateRadar(); + + float calcRelBearing(float bearing, float heading); + + // A list of pointers to AI objects + typedef list > radar_list_type; + typedef radar_list_type::iterator radar_list_iterator; + typedef radar_list_type::const_iterator radar_list_const_iterator; + + radar_list_type _radar_list; }; #endif // _INST_WXRADAR_HXX -- 2.39.5