]> git.mxchange.org Git - flightgear.git/blobdiff - src/AIModel/AIBase.cxx
Interim windows build fix
[flightgear.git] / src / AIModel / AIBase.cxx
index c6a967f8d578e5ddc9f6f6adfe6371912a35c4fd..339de5f27f17557dd42b435722d19b238b4b42e8 100644 (file)
 
 #include <simgear/compiler.h>
 
+#include <boost/foreach.hpp>
 #include <string>
 
 #include <osg/ref_ptr>
 #include <osg/Node>
 #include <osgDB/FileUtils>
 
-#include <simgear/math/SGMath.hxx>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/scene/model/modellib.hxx>
 #include <simgear/scene/util/SGNodeMasks.hxx>
-#include <simgear/sound/soundmgr_openal.hxx>
 #include <simgear/debug/logstream.hxx>
 #include <simgear/props/props.hxx>
 
 #include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
 #include <Scenery/scenery.hxx>
 #include <Scripting/NasalSys.hxx>
+#include <Scripting/NasalModelData.hxx>
 #include <Sound/fg_fx.hxx>
 
+#include "AIFlightPlan.hxx"
 #include "AIBase.hxx"
 #include "AIManager.hxx"
 
@@ -54,8 +56,69 @@ const char *default_model = "Models/Geometry/glider.ac";
 const double FGAIBase::e = 2.71828183;
 const double FGAIBase::lbs_to_slugs = 0.031080950172;   //conversion factor
 
+using std::string;
 using namespace simgear;
 
+class FGAIModelData : public simgear::SGModelData {
+public:
+    FGAIModelData(SGPropertyNode *root = NULL)
+        : _nasal( new FGNasalModelDataProxy(root) ),
+        _ready(false),
+        _initialized(false),
+        _hasInteriorPath(false),
+        _interiorLoaded(false)
+    {
+    }
+
+    
+    ~FGAIModelData()
+    {
+    }
+    
+    virtual FGAIModelData* clone() const { return new FGAIModelData(); }
+
+    /** osg callback, thread-safe */
+    void modelLoaded(const std::string& path, SGPropertyNode *prop, osg::Node *n)
+    {
+        // WARNING: Called in a separate OSG thread! Only use thread-safe stuff here...
+        if (_ready)
+            return;
+        
+        if(prop->hasChild("interior-path")){
+            _interiorPath = prop->getStringValue("interior-path");
+            _hasInteriorPath = true;
+        }
+
+        _fxpath = prop->getStringValue("sound/path");
+        _nasal->modelLoaded(path, prop, n);
+        
+        _ready = true;
+
+    }
+    
+    /** init hook to be called after model is loaded.
+     * Not thread-safe. Call from main thread only. */
+    void init(void) { _initialized = true; }
+    
+    bool needInitilization(void) { return _ready && !_initialized;}
+    bool isInitialized(void) { return _initialized;}
+    inline std::string& get_sound_path() { return _fxpath;}
+
+    void setInteriorLoaded(const bool state) { _interiorLoaded = state;}
+    bool getInteriorLoaded(void) { return _interiorLoaded;}
+    bool hasInteriorPath(void) { return _hasInteriorPath;}
+    inline std::string& getInteriorPath() { return _interiorPath; }
+private:
+    std::auto_ptr<FGNasalModelDataProxy> _nasal;
+    std::string _fxpath;
+    bool _ready;
+    bool _initialized;
+
+    std::string _interiorPath;
+    bool _hasInteriorPath;
+    bool _interiorLoaded;
+};
+
 FGAIBase::FGAIBase(object_type ot, bool enableHot) :
     _max_speed(300),
     _name(""),
@@ -75,10 +138,8 @@ FGAIBase::FGAIBase(object_type ot, bool enableHot) :
     _refID( _newAIModelID() ),
     _otype(ot),
     _initialized(false),
-    _aimodel(0),
-    _fxpath(""),
+    _modeldata(0),
     _fx(0)
-
 {
     tgt_heading = hdg = tgt_altitude_ft = tgt_speed = 0.0;
     tgt_roll = roll = tgt_pitch = tgt_yaw = tgt_vs = vs = pitch = 0.0;
@@ -103,8 +164,6 @@ FGAIBase::FGAIBase(object_type ot, bool enableHot) :
     _roll_offset = 0;
     _yaw_offset = 0;
 
-    userpos = SGGeod::fromDeg(0, 0);
-
     pos = SGGeod::fromDeg(0, 0);
     speed = 0;
     altitude_ft = 0;
@@ -145,13 +204,7 @@ FGAIBase::~FGAIBase() {
             model_removed->setStringValue(props->getPath());
     }
 
-    if (_fx && _refID != 0 && _refID !=  1) {
-        SGSoundMgr *smgr = globals->get_soundmgr();
-        stringstream name; 
-        name <<  "aifx:";
-        name << _refID;
-        smgr->remove(name.str());
-    }
+    removeSoundFx();
 
     if (fp)
         delete fp;
@@ -164,20 +217,27 @@ FGAIBase::~FGAIBase() {
 void
 FGAIBase::removeModel()
 {
+    if (!_model.valid())
+        return;
+
     FGScenery* pSceneryManager = globals->get_scenery();
-    if (pSceneryManager)
+    if (pSceneryManager && pSceneryManager->get_models_branch())
     {
         osg::ref_ptr<osg::Object> temp = _model.get();
-        pSceneryManager->get_scene_graph()->removeChild(aip.getSceneGraph());
+        pSceneryManager->get_models_branch()->removeChild(aip.getSceneGraph());
         // withdraw from SGModelPlacement and drop own reference (unref)
-        aip.init( 0 );
+        aip.clear();
+        _modeldata = 0;
         _model = 0;
+        
         // pass it on to the pager, to be be deleted in the pager thread
-        pSceneryManager->getPagerSingleton()->queueDeleteRequest(temp);
+        pSceneryManager->getPager()->queueDeleteRequest(temp);
     }
     else
     {
-        SG_LOG(SG_AI, SG_ALERT, "AIBase: Could not unload model. Missing scenery manager!");
+        aip.clear();
+        _model = 0;
+        _modeldata = 0;
     }
 }
 
@@ -229,21 +289,48 @@ void FGAIBase::update(double dt) {
                             pitch*speed );
         _fx->set_velocity( velocity );
     }
-    else if ((_aimodel)&&(fgGetBool("/sim/sound/aimodels/enabled",false)))
+    else if ((_modeldata)&&(_modeldata->needInitilization()))
     {
-        string fxpath = _aimodel->get_sound_path();
-        if (fxpath != "")
+        // process deferred nasal initialization,
+        // which must be done in main thread
+        _modeldata->init();
+
+        // sound initialization
+        if (fgGetBool("/sim/sound/aimodels/enabled",false))
         {
-            _fxpath = fxpath;
-            props->setStringValue("sim/sound/path", _fxpath.c_str());
-
-            // initialize the sound configuration
-            SGSoundMgr *smgr = globals->get_soundmgr();
-            stringstream name;
-            name <<  "aifx:";
-            name << _refID;
-            _fx = new FGFX(smgr, name.str(), props);
-            _fx->init();
+            const string& fxpath = _modeldata->get_sound_path();
+            if (fxpath != "")
+            {
+                props->setStringValue("sim/sound/path", fxpath.c_str());
+
+                // initialize the sound configuration
+                std::stringstream name;
+                name <<  "aifx:";
+                name << _refID;
+                _fx = new FGFX(name.str(), props);
+                _fx->init();
+            }
+        }
+    }
+
+    updateInterior();
+}
+
+void FGAIBase::updateInterior()
+{
+    if(!_modeldata || !_modeldata->hasInteriorPath())
+        return;
+
+    if(!_modeldata->getInteriorLoaded()){ // interior is not yet load
+        double d2 = dist(SGVec3d::fromGeod(pos), globals->get_aircraft_position_cart());
+        if(d2 <= _maxRangeInterior){ // if the AI is in-range we load the interior
+            _interior = SGModelLib::loadPagedModel(_modeldata->getInteriorPath(), props, _modeldata);
+            if(_interior.valid()){
+                _interior->setRange(0, 0.0, _maxRangeInterior);
+                aip.add(_interior.get());
+                _modeldata->setInteriorLoaded(true);
+                SG_LOG(SG_AI, SG_INFO, "AIBase: Loaded interior model " << _interior->getName());
+            }
         }
     }
 }
@@ -252,7 +339,9 @@ void FGAIBase::update(double dt) {
 void FGAIBase::updateLOD()
 {
     double maxRangeDetail = fgGetDouble("/sim/rendering/static-lod/ai-detailed", 10000.0);
-    double maxRangeBare   = fgGetDouble("/sim/rendering/static-lod/ai-bare", 20000.0);
+//    double maxRangeBare   = fgGetDouble("/sim/rendering/static-lod/ai-bare", 20000.0);
+
+    _maxRangeInterior     = fgGetDouble("/sim/rendering/static-lod/ai-interior", 50.0);
     if (_model.valid())
     {
         if( maxRangeDetail == 0.0 )
@@ -263,8 +352,14 @@ void FGAIBase::updateLOD()
         }
         else
         {
-            _model->setRange(0, 0.0, maxRangeDetail);
-            _model->setRange(1, maxRangeDetail,maxRangeBare);
+            if( fgGetBool("/sim/rendering/static-lod/ai-range-mode-pixel", false ) ) 
+            {
+                _model->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN );
+                _model->setRange(0, maxRangeDetail, 100000 );
+            } else {
+                _model->setRangeMode( osg::LOD:: DISTANCE_FROM_EYE_POINT);
+                _model->setRange(0, 0.0, maxRangeDetail);
+            }
         }
     }
 }
@@ -292,28 +387,24 @@ bool FGAIBase::init(bool search_in_AI_path)
 {
     if (_model.valid())
     {
-        SG_LOG(SG_AI, SG_WARN, "AIBase: Cannot initialize a model multiple times! " << model_path);
+        SG_LOG(SG_AI, SG_ALERT, "AIBase: Cannot initialize a model multiple times! " << model_path);
         return false;
     }
 
     string f;
     if(search_in_AI_path)
     {
-    // setup a modified Options structure, with only the $fg-root/AI defined;
-    // we'll check that first, then give the normal search logic a chance.
-    // this ensures that models in AI/ are preferred to normal models, where
-    // both exist.
-        osg::ref_ptr<osgDB::ReaderWriter::Options> 
-          opt(osg::clone(osgDB::Registry::instance()->getOptions(), osg::CopyOp::SHALLOW_COPY));
-
-        SGPath ai_path(globals->get_fg_root(), "AI");
-        opt->setDatabasePath(ai_path.str());
-        
-        f = osgDB::findDataFile(model_path, opt.get());
-    }
+        BOOST_FOREACH(SGPath p, globals->get_data_paths("AI")) {
+            p.append(model_path);
+            if (p.exists()) {
+                f = p.str();
+                break;
+            }
+        } // of AI data paths iteration
+    } // of search in AI path
 
     if (f.empty()) {
-      f = simgear::SGModelLib::findDataFile(model_path);
+        f = simgear::SGModelLib::findDataFile(model_path);
     }
     
     if(f.empty())
@@ -321,32 +412,31 @@ bool FGAIBase::init(bool search_in_AI_path)
     else
         _installed = true;
 
-    _aimodel = new FGAIModelData(props);
-    osg::Node * mdl = SGModelLib::loadDeferredModel(f, props, _aimodel);
+    props->addChild("type")->setStringValue("AI");
+    _modeldata = new FGAIModelData(props);
+    _model= SGModelLib::loadPagedModel(f, props, _modeldata);
 
-    _model = new osg::LOD;
     _model->setName("AI-model range animation node");
 
-    _model->addChild( mdl, 0, FLT_MAX );
-    _model->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
-    _model->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
+  //  _model->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
+  //  _model->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
+  
 //    We really need low-resolution versions of AI/MP aircraft.
 //    Or at least dummy "stubs" with some default silhouette.
 //        _model->addChild( SGModelLib::loadPagedModel(fgGetString("/sim/multiplay/default-model", default_model),
 //                                                    props, new FGNasalModelData(props)), FLT_MAX, FLT_MAX);
     updateLOD();
-
-    initModel(mdl);
+    initModel();
+  
     if (_model.valid() && _initialized == false) {
         aip.init( _model.get() );
         aip.setVisible(true);
         invisible = false;
-        globals->get_scenery()->get_scene_graph()->addChild(aip.getSceneGraph());
-
-        // Get the sound-path tag from the configuration file and store it
-        // in the property tree.
+        globals->get_scenery()->get_models_branch()->addChild(aip.getSceneGraph());
         _initialized = true;
 
+        SG_LOG(SG_AI, SG_DEBUG, "AIBase: Loaded model " << model_path);
+
     } else if (!model_path.empty()) {
         SG_LOG(SG_AI, SG_WARN, "AIBase: Could not load model " << model_path);
         // not properly installed...
@@ -357,7 +447,7 @@ bool FGAIBase::init(bool search_in_AI_path)
     return true;
 }
 
-void FGAIBase::initModel(osg::Node *node)
+void FGAIBase::initModel()
 {
     if (_model.valid()) { 
 
@@ -385,61 +475,62 @@ bool FGAIBase::isa( object_type otype ) {
 
 
 void FGAIBase::bind() {
-    props->tie("id", SGRawValueMethods<FGAIBase,int>(*this,
+    _tiedProperties.setRoot(props);
+    tie("id", SGRawValueMethods<FGAIBase,int>(*this,
         &FGAIBase::getID));
-    props->tie("velocities/true-airspeed-kt",  SGRawValuePointer<double>(&speed));
-    props->tie("velocities/vertical-speed-fps",
+    tie("velocities/true-airspeed-kt",  SGRawValuePointer<double>(&speed));
+    tie("velocities/vertical-speed-fps",
         SGRawValueMethods<FGAIBase,double>(*this,
         &FGAIBase::_getVS_fps,
         &FGAIBase::_setVS_fps));
 
-    props->tie("position/altitude-ft",
+    tie("position/altitude-ft",
         SGRawValueMethods<FGAIBase,double>(*this,
         &FGAIBase::_getAltitude,
         &FGAIBase::_setAltitude));
-    props->tie("position/latitude-deg",
+    tie("position/latitude-deg",
         SGRawValueMethods<FGAIBase,double>(*this,
         &FGAIBase::_getLatitude,
         &FGAIBase::_setLatitude));
-    props->tie("position/longitude-deg",
+    tie("position/longitude-deg",
         SGRawValueMethods<FGAIBase,double>(*this,
         &FGAIBase::_getLongitude,
         &FGAIBase::_setLongitude));
 
-    props->tie("position/global-x",
+    tie("position/global-x",
         SGRawValueMethods<FGAIBase,double>(*this,
         &FGAIBase::_getCartPosX,
         0));
-    props->tie("position/global-y",
+    tie("position/global-y",
         SGRawValueMethods<FGAIBase,double>(*this,
         &FGAIBase::_getCartPosY,
         0));
-    props->tie("position/global-z",
+    tie("position/global-z",
         SGRawValueMethods<FGAIBase,double>(*this,
         &FGAIBase::_getCartPosZ,
         0));
-    props->tie("callsign",
+    tie("callsign",
         SGRawValueMethods<FGAIBase,const char*>(*this,
         &FGAIBase::_getCallsign,
         0));
 
-    props->tie("orientation/pitch-deg",   SGRawValuePointer<double>(&pitch));
-    props->tie("orientation/roll-deg",    SGRawValuePointer<double>(&roll));
-    props->tie("orientation/true-heading-deg", SGRawValuePointer<double>(&hdg));
-
-    props->tie("radar/in-range", SGRawValuePointer<bool>(&in_range));
-    props->tie("radar/bearing-deg",   SGRawValuePointer<double>(&bearing));
-    props->tie("radar/elevation-deg", SGRawValuePointer<double>(&elevation));
-    props->tie("radar/range-nm", SGRawValuePointer<double>(&range));
-    props->tie("radar/h-offset", SGRawValuePointer<double>(&horiz_offset));
-    props->tie("radar/v-offset", SGRawValuePointer<double>(&vert_offset));
-    props->tie("radar/x-shift", SGRawValuePointer<double>(&x_shift));
-    props->tie("radar/y-shift", SGRawValuePointer<double>(&y_shift));
-    props->tie("radar/rotation", SGRawValuePointer<double>(&rotation));
-    props->tie("radar/ht-diff-ft", SGRawValuePointer<double>(&ht_diff));
-    props->tie("subID", SGRawValuePointer<int>(&_subID));
-    props->tie("controls/lighting/nav-lights",
-        SGRawValueFunctions<bool>(_isNight));
+    tie("orientation/pitch-deg",   SGRawValuePointer<double>(&pitch));
+    tie("orientation/roll-deg",    SGRawValuePointer<double>(&roll));
+    tie("orientation/true-heading-deg", SGRawValuePointer<double>(&hdg));
+
+    tie("radar/in-range", SGRawValuePointer<bool>(&in_range));
+    tie("radar/bearing-deg",   SGRawValuePointer<double>(&bearing));
+    tie("radar/elevation-deg", SGRawValuePointer<double>(&elevation));
+    tie("radar/range-nm", SGRawValuePointer<double>(&range));
+    tie("radar/h-offset", SGRawValuePointer<double>(&horiz_offset));
+    tie("radar/v-offset", SGRawValuePointer<double>(&vert_offset));
+    tie("radar/x-shift", SGRawValuePointer<double>(&x_shift));
+    tie("radar/y-shift", SGRawValuePointer<double>(&y_shift));
+    tie("radar/rotation", SGRawValuePointer<double>(&rotation));
+    tie("radar/ht-diff-ft", SGRawValuePointer<double>(&ht_diff));
+    tie("subID", SGRawValuePointer<int>(&_subID));
+    tie("controls/lighting/nav-lights", SGRawValueFunctions<bool>(_isNight));
+
     props->setBoolValue("controls/lighting/beacon", true);
     props->setBoolValue("controls/lighting/strobe", true);
     props->setBoolValue("controls/glide-path", true);
@@ -458,145 +549,89 @@ void FGAIBase::bind() {
     props->setDoubleValue("sim/sound/avionics/volume", 0.0);
     props->setBoolValue("sim/sound/avionics/external-view", false);
     props->setBoolValue("sim/current-view/internal", false);
-
 }
 
 void FGAIBase::unbind() {
-    props->untie("id");
-    props->untie("velocities/true-airspeed-kt");
-    props->untie("velocities/vertical-speed-fps");
-
-    props->untie("position/altitude-ft");
-    props->untie("position/latitude-deg");
-    props->untie("position/longitude-deg");
-    props->untie("position/global-x");
-    props->untie("position/global-y");
-    props->untie("position/global-z");
-    props->untie("callsign");
-
-    props->untie("orientation/pitch-deg");
-    props->untie("orientation/roll-deg");
-    props->untie("orientation/true-heading-deg");
-
-    props->untie("radar/in-range");
-    props->untie("radar/bearing-deg");
-    props->untie("radar/elevation-deg");
-    props->untie("radar/range-nm");
-    props->untie("radar/h-offset");
-    props->untie("radar/v-offset");
-    props->untie("radar/x-shift");
-    props->untie("radar/y-shift");
-    props->untie("radar/rotation");
-    props->untie("radar/ht-diff-ft");
-
-    props->untie("controls/lighting/nav-lights");
-
-    props->setBoolValue("/sim/controls/radar/", true);
+    _tiedProperties.Untie();
+
+    props->setBoolValue("/sim/controls/radar", true);
 
+    removeSoundFx();
+}
+
+void FGAIBase::removeSoundFx() {
     // drop reference to sound effects now
-    _fx = 0;
+    if (_fx)
+    {
+        // must remove explicitly - since the sound manager also keeps a reference
+        _fx->unbind();
+        // now drop last reference - kill the object
+        _fx = 0;
+    }
 }
 
-double FGAIBase::UpdateRadar(FGAIManager* manager) {
+double FGAIBase::UpdateRadar(FGAIManager* manager)
+{
     bool control = fgGetBool("/sim/controls/radar", true);
 
     if(!control) return 0;
 
-    double radar_range_ft2 = fgGetDouble("/instrumentation/radar/range");
+    double radar_range_m = fgGetDouble("/instrumentation/radar/range");
     bool force_on = fgGetBool("/instrumentation/radar/debug-mode", false);
-    radar_range_ft2 *= SG_NM_TO_METER * SG_METER_TO_FEET * 1.1; // + 10%
-    radar_range_ft2 *= radar_range_ft2;
-
-    double user_latitude  = manager->get_user_latitude();
-    double user_longitude = manager->get_user_longitude();
-    double lat_range = fabs(pos.getLatitudeDeg() - user_latitude) * ft_per_deg_lat;
-    double lon_range = fabs(pos.getLongitudeDeg() - user_longitude) * ft_per_deg_lon;
-    double range_ft2 = lat_range*lat_range + lon_range*lon_range;
-
-    //
-    // Test whether the target is within radar range.
-    //
-    in_range = (range_ft2 && (range_ft2 <= radar_range_ft2));
-
-    if ( in_range || force_on ) {
-        props->setBoolValue("radar/in-range", true);
-
-        // copy values from the AIManager
-        double user_altitude  = manager->get_user_altitude();
-        double user_heading   = manager->get_user_heading();
-        double user_pitch     = manager->get_user_pitch();
-        //double user_yaw       = manager->get_user_yaw();
-        //double user_speed     = manager->get_user_speed();
-
-        // calculate range to target in feet and nautical miles
-        double range_ft = sqrt( range_ft2 );
-        range = range_ft / 6076.11549;
-
-        // calculate bearing to target
-        if (pos.getLatitudeDeg() >= user_latitude) {
-            bearing = atan2(lat_range, lon_range) * SG_RADIANS_TO_DEGREES;
-            if (pos.getLongitudeDeg() >= user_longitude) {
-                bearing = 90.0 - bearing;
-            } else {
-                bearing = 270.0 + bearing;
-            }
-        } else {
-            bearing = atan2(lon_range, lat_range) * SG_RADIANS_TO_DEGREES;
-            if (pos.getLongitudeDeg() >= user_longitude) {
-                bearing = 180.0 - bearing;
-            } else {
-                bearing = 180.0 + bearing;
-            }
-        }
-
-        // This is an alternate way to compute bearing and distance which
-        // agrees with the original scheme within about 0.1 degrees.
-        //
-        // Point3D start( user_longitude * SGD_DEGREES_TO_RADIANS,
-        //                user_latitude * SGD_DEGREES_TO_RADIANS, 0 );
-        // Point3D dest( pos.getLongitudeRad(), pos.getLatitudeRad(), 0 );
-        // double gc_bearing, gc_range;
-        // calc_gc_course_dist( start, dest, &gc_bearing, &gc_range );
-        // gc_range *= SG_METER_TO_NM;
-        // gc_bearing *= SGD_RADIANS_TO_DEGREES;
-        // printf("orig b = %.3f %.2f  gc b= %.3f, %.2f\n",
-        //        bearing, range, gc_bearing, gc_range);
-
-        // calculate look left/right to target, without yaw correction
-        horiz_offset = bearing - user_heading;
-        if (horiz_offset > 180.0) horiz_offset -= 360.0;
-        if (horiz_offset < -180.0) horiz_offset += 360.0;
-
-        // calculate elevation to target
-        elevation = atan2( altitude_ft - user_altitude, range_ft ) * SG_RADIANS_TO_DEGREES;
-
-        // calculate look up/down to target
-        vert_offset = elevation - user_pitch;
-
-        /* this calculation needs to be fixed, but it isn't important anyway
-        // calculate range rate
-        double recip_bearing = bearing + 180.0;
-        if (recip_bearing > 360.0) recip_bearing -= 360.0;
-        double my_horiz_offset = recip_bearing - hdg;
-        if (my_horiz_offset > 180.0) my_horiz_offset -= 360.0;
-        if (my_horiz_offset < -180.0) my_horiz_offset += 360.0;
-        rdot = (-user_speed * cos( horiz_offset * SG_DEGREES_TO_RADIANS ))
-        +(-speed * 1.686 * cos( my_horiz_offset * SG_DEGREES_TO_RADIANS ));
-        */
-
-        // now correct look left/right for yaw
-        // horiz_offset += user_yaw; // FIXME: WHY WOULD WE WANT TO ADD IN SIDE-SLIP HERE?
-
-        // calculate values for radar display
-        y_shift = range * cos( horiz_offset * SG_DEGREES_TO_RADIANS);
-        x_shift = range * sin( horiz_offset * SG_DEGREES_TO_RADIANS);
-        rotation = hdg - user_heading;
-        if (rotation < 0.0) rotation += 360.0;
-        ht_diff = altitude_ft - user_altitude;
-
+    radar_range_m *= SG_NM_TO_METER  * 1.1; // + 10%
+    radar_range_m *= radar_range_m; // squared
+    
+    double d2 = distSqr(SGVec3d::fromGeod(pos), globals->get_aircraft_position_cart());
+    double range_ft = sqrt(d2) * SG_METER_TO_FEET;
+    
+    if (!force_on && (d2 > radar_range_m)) {
+        return range_ft * range_ft;
     }
+    
+    props->setBoolValue("radar/in-range", true);
+
+    // copy values from the AIManager
+    double user_heading   = manager->get_user_heading();
+    double user_pitch     = manager->get_user_pitch();
+  
+    range = range_ft * SG_FEET_TO_METER * SG_METER_TO_NM;
+
+    // calculate bearing to target
+    bearing = SGGeodesy::courseDeg(globals->get_aircraft_position(), pos);
+
+    // calculate look left/right to target, without yaw correction
+    horiz_offset = bearing - user_heading;
+    SG_NORMALIZE_RANGE(horiz_offset, -180.0, 180.0);
+   
+    // calculate elevation to target
+    ht_diff = altitude_ft - globals->get_aircraft_position().getElevationFt();
+    elevation = atan2( ht_diff, range_ft ) * SG_RADIANS_TO_DEGREES;
+
+    // calculate look up/down to target
+    vert_offset = elevation - user_pitch;
+
+    /* this calculation needs to be fixed, but it isn't important anyway
+    // calculate range rate
+    double recip_bearing = bearing + 180.0;
+    if (recip_bearing > 360.0) recip_bearing -= 360.0;
+    double my_horiz_offset = recip_bearing - hdg;
+    if (my_horiz_offset > 180.0) my_horiz_offset -= 360.0;
+    if (my_horiz_offset < -180.0) my_horiz_offset += 360.0;
+    rdot = (-user_speed * cos( horiz_offset * SG_DEGREES_TO_RADIANS ))
+    +(-speed * 1.686 * cos( my_horiz_offset * SG_DEGREES_TO_RADIANS ));
+    */
+
+    // now correct look left/right for yaw
+    // horiz_offset += user_yaw; // FIXME: WHY WOULD WE WANT TO ADD IN SIDE-SLIP HERE?
+
+    // calculate values for radar display
+    y_shift = range * cos( horiz_offset * SG_DEGREES_TO_RADIANS);
+    x_shift = range * sin( horiz_offset * SG_DEGREES_TO_RADIANS);
+    
+    rotation = hdg - user_heading;
+    SG_NORMALIZE_RANGE(rotation, 0.0, 360.0);
 
-    return range_ft2;
+    return range_ft * range_ft;
 }
 
 /*
@@ -627,7 +662,7 @@ SGVec3d FGAIBase::getCartPos() const {
 }
 
 bool FGAIBase::getGroundElevationM(const SGGeod& pos, double& elev,
-                                   const SGMaterial** material) const {
+                                   const simgear::BVHMaterial** material) const {
     return globals->get_scenery()->get_elevation_m(pos, elev, material,
                                                    _model.get());
 }
@@ -655,12 +690,6 @@ void FGAIBase::_setLatitude ( double latitude )  {
     pos.setLatitudeDeg(latitude);
 }
 
-void FGAIBase::_setUserPos(){
-    userpos.setLatitudeDeg(manager->get_user_latitude());
-    userpos.setLongitudeDeg(manager->get_user_longitude());
-    userpos.setElevationM(manager->get_user_altitude() * SG_FEET_TO_METER);
-}
-
 void FGAIBase::_setSubID( int s ) {
     _subID = s;
 }
@@ -682,7 +711,7 @@ bool FGAIBase::setParentNode() {
             model = _selected_ac;
         } else {
             model = ai->getChild(i);
-            string path = ai->getPath();
+            //const string& path = ai->getPath();
             const string name = model->getStringValue("name");
 
             if (!model->nChildren()){
@@ -749,7 +778,7 @@ double FGAIBase::_getAltitude() const {
 
 double FGAIBase::_getAltitudeAGL(SGGeod inpos, double start){
     getGroundElevationM(SGGeod::fromGeodM(inpos, start),
-        _elevation_m, &_material);
+        _elevation_m, NULL);
     return inpos.getElevationFt() - _elevation_m * SG_METER_TO_FEET;
 }
 
@@ -906,26 +935,7 @@ int FGAIBase::_newAIModelID() {
     return id;
 }
 
-
-FGAIModelData::FGAIModelData(SGPropertyNode *root)
-  : _nasal( new FGNasalModelData(root) ),
-    _path("")
-{
-}
-
-FGAIModelData::~FGAIModelData()
-{
-    delete _nasal;
-}
-
-void FGAIModelData::modelLoaded(const string& path, SGPropertyNode *prop, osg::Node *n)
-{
-    const char* fxpath = prop->getStringValue("sound/path");
-    if (fxpath) {
-        string sound_path = string(fxpath);
-        if (sound_path != "") {
-            _path = "/AI/"+sound_path;
-        }
-    }
-    _nasal->modelLoaded(path, prop, n);
+bool FGAIBase::isValid() { 
+       //Either no flightplan or it is valid
+       return !fp || fp->isValidPlan(); 
 }