# include <config.h>
#endif
+#include <string.h>
+
#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/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"
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(""),
_impact_speed(0),
_refID( _newAIModelID() ),
_otype(ot),
- _initialized(false)
-
+ _initialized(false),
+ _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;
_roll_offset = 0;
_yaw_offset = 0;
- userpos = SGGeod::fromDeg(0, 0);
-
pos = SGGeod::fromDeg(0, 0);
speed = 0;
altitude_ft = 0;
FGAIBase::~FGAIBase() {
// Unregister that one at the scenery manager
- if (globals->get_scenery()) {
- globals->get_scenery()->get_scene_graph()->removeChild(aip.getSceneGraph());
- }
+ removeModel();
if (props) {
SGPropertyNode* parent = props->getParent();
if (parent)
model_removed->setStringValue(props->getPath());
}
- delete fp;
+
+ removeSoundFx();
+
+ if (fp)
+ delete fp;
fp = 0;
}
+/** Cleanly remove the model
+ * and let the scenery database pager do the clean-up work.
+ */
+void
+FGAIBase::removeModel()
+{
+ if (!_model.valid())
+ return;
+
+ FGScenery* pSceneryManager = globals->get_scenery();
+ if (pSceneryManager && pSceneryManager->get_models_branch())
+ {
+ osg::ref_ptr<osg::Object> temp = _model.get();
+ pSceneryManager->get_models_branch()->removeChild(aip.getSceneGraph());
+ // withdraw from SGModelPlacement and drop own reference (unref)
+ aip.clear();
+ _modeldata = 0;
+ _model = 0;
+
+ // pass it on to the pager, to be be deleted in the pager thread
+ pSceneryManager->getPager()->queueDeleteRequest(temp);
+ }
+ else
+ {
+ aip.clear();
+ _model = 0;
+ _modeldata = 0;
+ }
+}
+
void FGAIBase::readFromScenario(SGPropertyNode* scFileNode)
{
if (!scFileNode)
ft_per_deg_lat = 366468.96 - 3717.12 * cos(pos.getLatitudeRad());
ft_per_deg_lon = 365228.16 * cos(pos.getLatitudeRad());
+
+ if ( _fx )
+ {
+ // update model's audio sample values
+ _fx->set_position_geod( pos );
+
+ SGQuatd orient = SGQuatd::fromYawPitchRollDeg(hdg, pitch, roll);
+ _fx->set_orientation( orient );
+
+ SGVec3d velocity;
+ velocity = SGVec3d( speed_north_deg_sec, speed_east_deg_sec,
+ pitch*speed );
+ _fx->set_velocity( velocity );
+ }
+ else if ((_modeldata)&&(_modeldata->needInitilization()))
+ {
+ // process deferred nasal initialization,
+ // which must be done in main thread
+ _modeldata->init();
+
+ // sound initialization
+ if (fgGetBool("/sim/sound/aimodels/enabled",false))
+ {
+ 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());
+ }
+ }
+ }
+}
+
+/** update LOD properties of the model */
+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);
+
+ _maxRangeInterior = fgGetDouble("/sim/rendering/static-lod/ai-interior", 50.0);
+ if (_model.valid())
+ {
+ if( maxRangeDetail == 0.0 )
+ {
+ // disable LOD
+ _model->setRange(0, 0.0, FLT_MAX);
+ _model->setRange(1, FLT_MAX, FLT_MAX);
+ }
+ else
+ {
+ 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);
+ }
+ }
+ }
}
void FGAIBase::Transform() {
}
-bool FGAIBase::init(bool search_in_AI_path) {
-
+bool FGAIBase::init(bool search_in_AI_path)
+{
+ if (_model.valid())
+ {
+ 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())
else
_installed = true;
- model = load3DModel(f, props);
-
- if (model.valid() && _initialized == false) {
- aip.init( model.get() );
+ props->addChild("type")->setStringValue("AI");
+ _modeldata = new FGAIModelData(props);
+ _model= SGModelLib::loadPagedModel(f, props, _modeldata);
+
+ _model->setName("AI-model range animation node");
+
+ // _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();
+
+ if (_model.valid() && _initialized == false) {
+ aip.init( _model.get() );
aip.setVisible(true);
invisible = false;
- globals->get_scenery()->get_scene_graph()->addChild(aip.getSceneGraph());
+ 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_INPUT, SG_WARN, "AIBase: Could not load model " << model_path);
+ SG_LOG(SG_AI, SG_WARN, "AIBase: Could not load model " << model_path);
// not properly installed...
_installed = false;
}
return true;
}
-void FGAIBase::initModel(osg::Node *node)
+void FGAIBase::initModel()
{
- if (model.valid()) {
+ if (_model.valid()) {
if( _path != ""){
props->setStringValue("submodels/path", _path.c_str());
- SG_LOG(SG_INPUT, SG_DEBUG, "AIBase: submodels/path " << _path);
+ SG_LOG(SG_AI, SG_DEBUG, "AIBase: submodels/path " << _path);
}
if( _parent!= ""){
fgSetString("/ai/models/model-added", props->getPath().c_str());
} else if (!model_path.empty()) {
- SG_LOG(SG_INPUT, SG_WARN, "AIBase: Could not load model " << model_path);
+ SG_LOG(SG_AI, SG_WARN, "AIBase: Could not load model " << model_path);
}
setDie(false);
}
-osg::Node* FGAIBase::load3DModel(const string &path, SGPropertyNode *prop_root)
-{
- model = SGModelLib::loadPagedModel(path, prop_root, new FGNasalModelData(prop_root));
- initModel(model.get());
- return model.get();
-}
-
bool FGAIBase::isa( object_type otype ) {
return otype == _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);
props->setDoubleValue("controls/flight/target-alt", altitude_ft);
props->setDoubleValue("controls/flight/target-pitch", pitch);
- props->setDoubleValue("controls/flight/target-spd", speed);
+ props->setDoubleValue("controls/flight/target-spd", speed);
+ props->setBoolValue("sim/sound/avionics/enabled", false);
+ 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");
+ _tiedProperties.Untie();
- props->untie("orientation/pitch-deg");
- props->untie("orientation/roll-deg");
- props->untie("orientation/true-heading-deg");
+ props->setBoolValue("/sim/controls/radar", true);
- 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);
+ removeSoundFx();
+}
+void FGAIBase::removeSoundFx() {
+ // drop reference to sound effects now
+ 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;
}
/*
}
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());
+ _model.get());
}
double FGAIBase::_getCartPosX() const {
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;
}
bool FGAIBase::setParentNode() {
if (_parent == ""){
- SG_LOG(SG_GENERAL, SG_ALERT, "AIBase: " << _name
+ SG_LOG(SG_AI, SG_ALERT, "AIBase: " << _name
<< " parent not set ");
return false;
}
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()){
const string name = _selected_ac->getStringValue("name");
return true;
} else {
- SG_LOG(SG_GENERAL, SG_ALERT, "AIBase: " << _name
+ SG_LOG(SG_AI, SG_ALERT, "AIBase: " << _name
<< " parent not found: dying ");
setDie(true);
return false;
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;
}
return id;
}
+bool FGAIBase::isValid() {
+ //Either no flightplan or it is valid
+ return !fp || fp->isValidPlan();
+}