FlightGear next
AIBase.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: AIBase.cxx
3 * SPDX-FileComment: abstract base class for AI objects, based on David Luff's FGAIEntity class.
4 * SPDX-FileCopyrightText: Written by David Culp, started Nov 2003 - davidculp2@comcast.net
5 * SPDX-FileContributor: With additions by Mathias Froehlich & Vivian Meazza 2004-2007
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include <config.h>
10
11#include <string.h>
12
13#include <simgear/compiler.h>
14
15#include <string>
16
17#include <osg/ref_ptr>
18#include <osg/Node>
19#include <osgDB/FileUtils>
20
21#include <simgear/debug/ErrorReportingCallback.hxx>
22#include <simgear/debug/logstream.hxx>
23#include <simgear/misc/sg_path.hxx>
24#include <simgear/props/props.hxx>
25#include <simgear/scene/model/modellib.hxx>
26#include <simgear/scene/util/SGNodeMasks.hxx>
27
29#include <Main/fg_props.hxx>
30#include <Main/globals.hxx>
31#include <Scenery/scenery.hxx>
34#include <Sound/fg_fx.hxx>
35
36#include "AIFlightPlan.hxx"
37#include "AIBase.hxx"
38#include "AIManager.hxx"
39
40static std::string default_model = "Models/Geometry/glider.ac";
41const double FGAIBase::e = 2.71828183;
42const double FGAIBase::lbs_to_slugs = 0.031080950172; //conversion factor
43
44using std::string;
45using namespace simgear;
46
47class FGAIModelData : public simgear::SGModelData {
48public:
49 explicit FGAIModelData(SGPropertyNode *root = nullptr)
50 : _root (root)
51 {
52 }
53
54
55 ~FGAIModelData() = default;
56
57 FGAIModelData* clone() const override { return new FGAIModelData(); }
58
59 ErrorContext getErrorContext() const override
60 {
61 return _errorContext;
62 }
63
64 void addErrorContext(const std::string& key, const std::string& value)
65 {
66 _errorContext[key] = value;
67 }
68
69 void captureErrorContext(const std::string& key)
70 {
72 if (!v.empty()) {
73 addErrorContext(key, v);
74 }
75 }
76
78 void modelLoaded(const std::string& path, SGPropertyNode *prop, osg::Node *n)
79 {
80 // WARNING: Called in a separate OSG thread! Only use thread-safe stuff here...
81 if (_ready && _modelLoaded.count(path) > 0)
82 return;
83
84 _modelLoaded[path] = true;
85
86 if(prop->hasChild("interior-path")){
87 _interiorPath = prop->getStringValue("interior-path");
88 _hasInteriorPath = true;
89 }
90
91 // save radius of loaded model for updating LOD
92 auto bs = n->getBound();
93 if (bs.valid()) {
94 _radius = bs.radius();
95 }
96
97 _fxpath = prop->getStringValue("sound/path");
98 _nasal[path] = std::unique_ptr<FGNasalModelDataProxy>(new FGNasalModelDataProxy(_root));
99 _nasal[path]->modelLoaded(path, prop, n);
100 _ready = true;
101 _initialized = false;
102 }
103
106 void init(void) { _initialized = true; }
107
108 bool needInitialization(void) { return _ready && !_initialized;}
109 bool isInitialized(void) { return _initialized;}
110 inline std::string& get_sound_path() { return _fxpath;}
111
112 void setInteriorLoaded(const bool state) { _interiorLoaded = state;}
113 bool getInteriorLoaded(void) { return _interiorLoaded;}
114 bool hasInteriorPath(void) { return _hasInteriorPath;}
115 inline std::string& getInteriorPath() { return _interiorPath; }
116 inline float getRadius() { return _radius; }
117
118 bool hasHighResolutionModel(void) { return _hasHighResolutionModel;}
120
121 // The indices used by the LoD node depend on what models are actually available.
122 // The maximal case is a list of [low, high, interior], but [low, interior] and
123 // [low, high] and [low] are also possible.
124 int getInteriorLoDIndex(void) { assert(_hasInteriorPath); return _hasHighResolutionModel ? 2 : 1; }
125 int getHighResolutionLoDIndex(void) { assert(_hasHighResolutionModel); return 1; }
126 int getLowResolutionLoDIndex(void) { return 0; }
127
128private:
129 std::string _fxpath;
130 std::string _interiorPath;
131
132 std::map<string, bool> _modelLoaded;
133 std::map<string, std::unique_ptr<FGNasalModelDataProxy>> _nasal;
134 bool _ready = false;
135 bool _initialized = false;
136 bool _hasInteriorPath = false;
137 bool _hasHighResolutionModel = false;
138 bool _interiorLoaded = false;
139
140 float _radius = -1.0;
141 SGPropertyNode* _root;
142
143 ErrorContext _errorContext;
144};
145
146FGAIBase::FGAIBase(object_type ot, bool enableHot) : replay_time(fgGetNode("sim/replay/time", true)),
147 model_removed(fgGetNode("/ai/models/model-removed", true)),
148 pos(SGGeod::fromDeg(0.0, 0.0)),
149 _impact_lat(0),
150 _impact_lon(0),
151 _impact_elev(0),
152 _impact_hdg(0),
153 _impact_pitch(0),
154 _impact_roll(0),
155 _impact_speed(0),
156 _refID(_newAIModelID()),
157 _otype(ot)
158{
160 tgt_roll = roll = tgt_pitch = tgt_yaw = tgt_vs = vs_fps = pitch = 0.0;
161 bearing = elevation = range = rdot = 0.0;
162 x_shift = y_shift = rotation = 0.0;
163 in_range = false;
164 invisible = false;
165 no_roll = true;
166 life = 900;
167 delete_me = false;
168 _impact_reported = false;
169 _collision_reported = false;
170 _expiry_reported = false;
171
172 _x_offset = 0;
173 _y_offset = 0;
174 _z_offset = 0;
175
176 _pitch_offset = 0;
177 _roll_offset = 0;
178 _yaw_offset = 0;
179
180 speed = 0;
181 altitude_ft = 0;
184 turn_radius_ft = 0;
185
186 ft_per_deg_lon = 0;
187 ft_per_deg_lat = 0;
188
189 horiz_offset = 0;
190 vert_offset = 0;
191 ht_diff = 0;
192
193 serviceable = false;
194
195 // explicitly disable HOT for (most) AI models
196 if (!enableHot)
197 aip.getSceneGraph()->setNodeMask(~SG_NODEMASK_TERRAIN_BIT);
198}
199
201 // Unregister that one at the scenery manager
202 removeModel();
203
204 if (props) {
205 SGPropertyNode* parent = props->getParent();
206
207 if (parent)
208 model_removed->setStringValue(props->getPath());
209 }
210
212}
213
217void
219{
220 if (!_model.valid())
221 return;
222
223 FGScenery* pSceneryManager = globals->get_scenery();
224 if (pSceneryManager && pSceneryManager->get_models_branch())
225 {
226 osg::ref_ptr<osg::Object> temp = _model.get();
227 pSceneryManager->get_models_branch()->removeChild(aip.getSceneGraph());
228 // withdraw from SGModelPlacement and drop own reference (unref)
229 aip.clear();
230 _modeldata = nullptr;
231 _model = nullptr;
232
233 // pass it on to the pager, to be be deleted in the pager thread
234 pSceneryManager->getPager()->queueDeleteRequest(temp);
235 }
236 else
237 {
238 aip.clear();
239 _model = nullptr;
240 _modeldata = nullptr;
241 }
242}
243
244void FGAIBase::setScenarioPath(const std::string& scenarioPath)
245{
246 _scenarioPath = scenarioPath;
247}
248
249void FGAIBase::readFromScenario(SGPropertyNode* scFileNode)
250{
251 if (!scFileNode)
252 return;
253
254 setPath(scFileNode->getStringValue("model",
255 fgGetString("/sim/multiplay/default-model", default_model).c_str()).c_str());
256
257 setFallbackModelIndex(scFileNode->getIntValue("fallback-model-index", 0));
258
259 setHeading(scFileNode->getDoubleValue("heading", 0.0));
260 setSpeed(scFileNode->getDoubleValue("speed", 0.0));
261 setAltitude(scFileNode->getDoubleValue("altitude", 0.0));
262 setLongitude(scFileNode->getDoubleValue("longitude", 0.0));
263 setLatitude(scFileNode->getDoubleValue("latitude", 0.0));
264 setBank(scFileNode->getDoubleValue("roll", 0.0));
265 setPitch(scFileNode->getDoubleValue("pitch", 0.0));
266 setCollisionHeight(scFileNode->getDoubleValue("collision-height", 0.0));
267 setCollisionLength(scFileNode->getDoubleValue("collision-length", 0.0));
268
269 SGPropertyNode* submodels = scFileNode->getChild("submodels");
270
271 if (submodels) {
272 setServiceable(submodels->getBoolValue("serviceable", false));
273 setSMPath(submodels->getStringValue("path", ""));
274 }
275
276 string searchOrder = scFileNode->getStringValue("search-order", "");
277 if (!searchOrder.empty()) {
278 if (searchOrder == "DATA_ONLY") {
280 } else if (searchOrder == "PREFER_AI") {
282 } else if (searchOrder == "PREFER_DATA") {
284 } else
285 SG_LOG(SG_AI, SG_WARN, "invalid model search order " << searchOrder << ". Use either DATA_ONLY, PREFER_AI or PREFER_DATA");
286 }
287
288 const string modelLowres = scFileNode->getStringValue("model-lowres", "");
289 if (!modelLowres.empty()) {
290 setPathLowres(modelLowres);
291 }
292}
293
294void FGAIBase::update(double dt) {
295
296 SG_UNUSED(dt);
297 if (replay_time->getDoubleValue() > 0)
298 return;
299 if (_otype == object_type::otStatic)
300 return;
301
302 ft_per_deg_lat = 366468.96 - 3717.12 * cos(pos.getLatitudeRad());
303 ft_per_deg_lon = 365228.16 * cos(pos.getLatitudeRad());
304
305 if (_modeldata && _modeldata->needInitialization()) {
306 // process deferred nasal initialization,
307 // which must be done in main thread
308 _modeldata->init();
309
310 // update LOD radius from loaded modeldata
311 const auto radius = _modeldata->getRadius();
312 if (radius > 0) {
313 _model->setRadius(radius);
314 _model->dirtyBound();
315 } else {
316 SG_LOG(SG_AI, SG_WARN, "AIBase: model radius not set.");
317 }
318
319 // sound initialization
320 if (fgGetBool("/sim/sound/aimodels/enabled",false))
321 {
322 const string& fxpath = _modeldata->get_sound_path();
323 if (!fxpath.empty()) {
324 simgear::ErrorReportContext ec("ai-model", _name);
325 if (!_scenarioPath.empty()) {
326 ec.add("scenario-path", _scenarioPath);
327 }
328
329 if (_otype == object_type::otMultiplayer) {
330 _modeldata->addErrorContext("multiplayer", getCallSign());
331 }
332
333 props->setStringValue("sim/sound/path", fxpath.c_str());
334
335 // Remove any existing sound FX (e.g. from another model)
337
338 // initialize the sound configuration
339 std::stringstream name;
340 name << "aifx:";
341 name << _refID;
342 _fx = new FGFX(name.str(), props);
343 _fx->init();
344 }
345 }
346 }
347
348 if ( _fx )
349 {
350 // update model's audio sample values
351 _fx->set_position_geod( pos );
352
353 SGQuatd orient = SGQuatd::fromYawPitchRollDeg(hdg, pitch, roll);
354 _fx->set_orientation( orient );
355
356 SGVec3d velocity;
357 velocity = SGVec3d( speed_north_deg_sec, speed_east_deg_sec,
358 pitch*speed );
359 _fx->set_velocity( velocity );
360 }
361
363}
364
366{
367 if(!_modeldata || !_modeldata->hasInteriorPath())
368 return;
369
370 if (!_modeldata->getInteriorLoaded()) { // interior is not yet load
371 _model->setFileName(_model->getNumRanges(), _modeldata->getInteriorPath());
372
373 bool distance_mode = fgGetBool("/sim/rendering/static-lod/aimp-range-mode-distance", false);
374 double maxRangeInterior = fgGetDouble("/sim/rendering/static-lod/aimp-interior", 50.0);
375
376 if (distance_mode) {
377 _model->setRange(0, 0.0, maxRangeInterior);
378 } else {
379 _model->setRange(0, maxRangeInterior, FLT_MAX);
380 }
381
382 _modeldata->setInteriorLoaded(true);
383 SG_LOG(SG_AI, SG_INFO, "AIBase: Loading interior model " << _modeldata->getInteriorPath());
384 }
385}
386
389{
390 double maxRangeDetail = fgGetDouble("/sim/rendering/static-lod/aimp-detailed", 3000.0);
391 double maxRangeBare = fgGetDouble("/sim/rendering/static-lod/aimp-bare", 10000.0);
392 double maxRangeInterior = fgGetDouble("/sim/rendering/static-lod/aimp-interior", 50.0);
393
394 if (_model.valid()) {
395 bool distance_mode = fgGetBool("/sim/rendering/static-lod/aimp-range-mode-distance", false);
396 if (distance_mode) {
397 _model->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
398
399 // In distance mode we simple display a different LoD model depending on how far away the object is.
400
401 if (maxRangeDetail < 0) {
402 // Only use the highest level detail model (interior handled later)
403 if (_modeldata->hasHighResolutionModel()) {
404 // Both high and low resolution models available, so disable the low resolution one.
405 _model->setRange(_modeldata->getLowResolutionLoDIndex(), 0.0, 0.0);
406 _model->setRange(_modeldata->getHighResolutionLoDIndex(), 0.0, maxRangeBare);
407 } else {
408 // Only a single resolution model available.
409 _model->setRange(_modeldata->getLowResolutionLoDIndex(), 0.0, maxRangeBare);
410 }
411 } else if ((int)maxRangeBare == (int)maxRangeDetail) {
412 // Only use the low detail model if available, otherwise we will use whatever is available.
413 if (_modeldata->hasHighResolutionModel()) {
414 _model->setRange(_modeldata->getLowResolutionLoDIndex(), 0.0, maxRangeBare);
415 _model->setRange(_modeldata->getHighResolutionLoDIndex(), 0.0, 0.0);
416 } else {
417 // Only a single resolution model available.
418 _model->setRange(_modeldata->getLowResolutionLoDIndex(), 0.0, maxRangeBare);
419 }
420 } else {
421 // We have 3 visibility ranges:
422 // 1) 0 to maxRangeDetail we use the high resolution model (if available)
423 // 2) maxRangeDetail to (maxRangeDetail+maxRangeBare) we use the low resolution model
424 // 3) from (maxRangeDetail+maxRangeBare) to infinity we do not display anything
425 if (maxRangeBare <= 0) {
426 // Sanity check that we have sensible values.
427 maxRangeBare = 1;
428 SG_LOG(SG_AI,
429 SG_ALERT,
430 "/sim/rendering/static-lod/aimp-bare is <= 0. This should be a delta on top of aimp-detailed in meters mode. setting to 1.");
431 }
432
433 if (_modeldata->hasHighResolutionModel()) {
434 _model->setRange(_modeldata->getLowResolutionLoDIndex(), maxRangeDetail, maxRangeDetail+maxRangeBare);
435 _model->setRange(_modeldata->getHighResolutionLoDIndex(), 0.0, maxRangeDetail);
436 } else {
437 // Only a single resolution model available.
438 _model->setRange(_modeldata->getLowResolutionLoDIndex(), 0.0, maxRangeDetail+maxRangeBare);
439 }
440 }
441
442 if (_modeldata->getInteriorLoaded()) {
443 // Handle the separate interior model.
444 _model->setRange(_modeldata->getInteriorLoDIndex(), 0.0, maxRangeInterior);
445 }
446 } else {
447 _model->setRangeMode(osg::LOD::PIXEL_SIZE_ON_SCREEN);
448
449 /* In pixel size mode, the range sense is reversed, so we want the
450 * detailed model [0] to be displayed when the "range" is really
451 * large (i.e. the object is taking up a large number of pixels on screen),
452 * and the less detailed model [1] to be displayed if the
453 * "range" is between the detailed range and the bare range.
454 * When the "range" is less than the bare value, the aircraft
455 * represents too few pixels to be worth displaying.
456 */
457
458 if (maxRangeDetail < 0) {
459 // Only use the highest level detail model available
460 if (_modeldata->hasHighResolutionModel()) {
461 // Both high and low resolution models available, so disable the low resolution one.
462 _model->setRange(_modeldata->getLowResolutionLoDIndex(), 0.0, 0.0);
463 _model->setRange(_modeldata->getHighResolutionLoDIndex(), maxRangeBare, FLT_MAX);
464 } else {
465 //Only a single resolution model available.
466 _model->setRange(_modeldata->getLowResolutionLoDIndex(), maxRangeBare, FLT_MAX);
467 }
468 } else if ((int)maxRangeBare == (int)maxRangeDetail) {
469 // Only use the low detail model if available, otherwise we will use whatever is available.
470 if (_modeldata->hasHighResolutionModel()) {
471 // Both high and low resolution models available, so disable the high resolution one.
472 _model->setRange(_modeldata->getLowResolutionLoDIndex(), maxRangeBare, FLT_MAX);
473 _model->setRange(_modeldata->getHighResolutionLoDIndex(), 0.0, 0.0);
474 } else {
475 // Only a single resolution model available.
476 _model->setRange(_modeldata->getLowResolutionLoDIndex(), maxRangeBare, FLT_MAX);
477 }
478 } else {
479 // We have three visibility ranges:
480 // 1) From closeby to a size of maxRangeDetail we will use the high resolution model
481 // 2) from maxRangeDetail to maxRangeBare we will use the low resolution model.
482 // 3) If the aircraft is smaller than maxRangeBare we don't display anything
483
484 if (maxRangeBare > maxRangeDetail) {
485 // Sanity check that we have sensible values.
486 maxRangeBare = maxRangeDetail;
487 SG_LOG(SG_AI,
488 SG_WARN,
489 "/sim/rendering/static-lod/aimp-bare greater " <<
490 "than /sim/rendering/static-lod/aimp-detailed when using " <<
491 "/sim/rendering/static-lod/aimp-range-mode-distance=false. Ignoring ai-bare."
492 );
493 }
494
495 if (_modeldata->hasHighResolutionModel()) {
496 _model->setRange(_modeldata->getLowResolutionLoDIndex(), maxRangeBare, maxRangeDetail);
497 _model->setRange(_modeldata->getHighResolutionLoDIndex(), maxRangeDetail, FLT_MAX);
498 } else {
499 // We only have a single model, so display it from the maxRangeBare
500 _model->setRange(_modeldata->getLowResolutionLoDIndex(), maxRangeBare, FLT_MAX );
501 }
502 }
503
504 if (_modeldata->getInteriorLoaded()) {
505 // Handle the separate interior model.
506 _model->setRange(_modeldata->getInteriorLoDIndex(), maxRangeInterior, FLT_MAX);
507 }
508 }
509
510 // Irrespective of the mode, we want to set appropriate priority weighting. AI models
511 // are low priority compared with scenery etc, and this scales the priority down.
512 _model->setPriorityScale(_modeldata->getLowResolutionLoDIndex(), 0.2);
513 if (_modeldata->hasHighResolutionModel()) _model->setPriorityScale(_modeldata->getHighResolutionLoDIndex(), 0.2);
514 if (_modeldata->getInteriorLoaded()) _model->setPriorityScale(_modeldata->getInteriorLoDIndex(), 0.2);
515 }
516}
517
519
520 if (!invisible) {
521 aip.setVisible(true);
522 aip.setPosition(pos);
523
524 if (no_roll)
525 aip.setOrientation(0.0, pitch, hdg);
526 else
527 aip.setOrientation(roll, pitch, hdg);
528
529 aip.update();
530 } else {
531 aip.setVisible(false);
532 aip.update();
533 }
534
535}
536
537/*
538 * Find a set of paths to the model, in order of LOD from least detailed to
539 * most, and accounting for the user preference of detailed models vs. AI
540 * low resolution models.
541 *
542 * This returns a vector of size 1 or 2.
543 */
544std::vector<std::string> FGAIBase::resolveModelPath(ModelSearchOrder searchOrder)
545{
546 string_list path_list;
547
548 if (searchOrder == ModelSearchOrder::DATA_ONLY) {
549 SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: DATA only");
550 auto p = simgear::SGModelLib::findDataFile(model_path);
551 if (!p.empty()) {
552 // We've got a model, use it
553 _installed = true;
554 SG_LOG(SG_AI, SG_DEBUG, "Found model " << p);
555 path_list.push_back(p);
556
557 if (!model_path_lowres.empty()) {
558 p = simgear::SGModelLib::findDataFile(model_path_lowres);
559 if (!p.empty()) {
560 //lowres model needs to be the first in the list
561 path_list.insert(path_list.begin(),p);
562 }
563 }
564 } else {
565 // No model, so fall back to the default
566 const SGPath defaultModelPath = SGPath::fromUtf8(fgGetString("/sim/multiplay/default-model", default_model));
567 path_list.push_back(defaultModelPath.utf8Str());
568 }
569 } else {
570 SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: PREFER_AI/PREFER_DATA");
571 // We're either PREFER_AI or PREFER_DATA. Find an AI model first.
572 for (SGPath p : globals->get_data_paths("AI")) {
573 p.append(model_path);
574 if (p.exists()) {
575 SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p);
576 path_list.push_back(p.utf8Str());
577 break;
578 }
579 }
580
581 if (path_list.empty()) {
582 // Fall back on the fallback-model-index which is a lookup into
583 // /sim/multiplay/fallback-models/model[]
584 std::string fallback_path;
585 const SGPropertyNode* fallbackNode =
586 globals->get_props()->getNode("/sim/multiplay/fallback-models/model", _getFallbackModelIndex(), false);
587
588 if (fallbackNode != nullptr) {
589 fallback_path = fallbackNode->getStringValue();
590 } else {
591 fallback_path = globals->get_props()->getNode("/sim/multiplay/fallback-models/model", 0, true)->getStringValue();
592 }
593
594 for (SGPath p : globals->get_data_paths()) {
595 p.append(fallback_path);
596 if (p.exists()) {
597 SG_LOG(SG_AI, SG_DEBUG, "Found fallback model path for index " << _fallback_model_index << ": " << p);
598 path_list.push_back(p.utf8Str());
599 break;
600 }
601 }
602 }
603
604 if ((searchOrder == ModelSearchOrder::PREFER_AI) && !path_list.empty()) {
605 // if we prefer AI, and we've got a valid AI path from above, then use it, we're done
606 _installed = true;
607 return path_list;
608 }
609
610 // At this point we're looking for a regular model to display at closer range.
611 // From experimentation it seems to work best if the LODs are in the range list in terms of detail
612 // from lowest to highest - so insert this at the end.
613 auto p = simgear::SGModelLib::findDataFile(model_path);
614 if (!p.empty()) {
615 _installed = true;
616 SG_LOG(SG_AI, SG_DEBUG, "Found DATA model " << p);
617 path_list.insert(path_list.end(), p);
618 }
619 }
620
621 /*
622 * We return either one or two models. LoD logic elsewhere relies on this,
623 * so anything else is a logic error in the above code.
624 */
625 assert(path_list.size() != 0);
626 assert(path_list.size() < 3);
627
628 _modeldata->setHasHighResolutionModel(path_list.size() == 2);
629
630 return path_list;
631}
632
634{
635 if (_model.valid())
636 {
637 SG_LOG(SG_AI, SG_ALERT, "AIBase: Cannot initialize a model multiple times! " << model_path);
638 return false;
639 }
640
641 props->addChild("type")->setStringValue("AI");
642 _modeldata = new FGAIModelData(props);
643 _modeldata->addErrorContext("ai", _name);
644 _modeldata->captureErrorContext("scenario-path");
645
646 // set by FGAISchedule::createAIAircraft
647 _modeldata->captureErrorContext("traffic-aircraft-callsign");
648
649 if (_otype == object_type::otMultiplayer) {
650 _modeldata->addErrorContext("multiplayer", getCallSign());
651 }
652
653 // Load models
654 std::vector<string> model_list = resolveModelPath(searchOrder);
655
656 // Now load the set of models as an LoD range.
657 _model = SGModelLib::loadPagedModel(model_list, props, _modeldata);
658 _model->setName("AI-model range animation node");
659 _model->setRadius(getDefaultModelRadius());
660
661 updateLOD();
662 initModel();
663
664 if (_model.valid() && _initialized == false) {
665 aip.init( _model.get() );
666 aip.setVisible(true);
667 invisible = false;
668
669 auto scenery = globals->get_scenery();
670 if (scenery) {
671 scenery->get_models_branch()->addChild(aip.getSceneGraph());
672 }
673 _initialized = true;
674
675 SG_LOG(SG_AI, SG_DEBUG, "AIBase: Loaded model " << model_path);
676
677 } else if (!model_path.empty()) {
678 SG_LOG(SG_AI, SG_WARN, "AIBase: Could not load model " << model_path);
679 // not properly installed...
680 _installed = false;
681 }
682
683 setDie(false);
684 return true;
685}
686
688{
689 if (_model.valid()) {
690
691 if( _path != ""){
692 props->setStringValue("submodels/path", _path.c_str());
693 SG_LOG(SG_AI, SG_DEBUG, "AIBase: submodels/path " << _path);
694 }
695
696 if( _parent!= ""){
697 props->setStringValue("parent-name", _parent.c_str());
698 }
699
700 fgSetString("/ai/models/model-added", props->getPath().c_str());
701 } else if (!model_path.empty()) {
702 SG_LOG(SG_AI, SG_WARN, "AIBase: Could not load model " << model_path);
703 }
704
705 setDie(false);
706}
707
708
710 return otype == _otype;
711}
712
714 _tiedProperties.setRoot(props);
715 tie("id", SGRawValueMethods<FGAIBase,int>(*this,
717 tie("velocities/true-airspeed-kt", SGRawValuePointer<double>(&speed));
718 tie("velocities/vertical-speed-fps",
719 SGRawValueMethods<FGAIBase,double>(*this,
722
723 tie("position/altitude-ft",
724 SGRawValueMethods<FGAIBase,double>(*this,
727 tie("position/latitude-deg",
728 SGRawValueMethods<FGAIBase,double>(*this,
731 tie("position/longitude-deg",
732 SGRawValueMethods<FGAIBase,double>(*this,
735
736 tie("position/global-x",
737 SGRawValueMethods<FGAIBase,double>(*this,
739 nullptr));
740 tie("position/global-y",
741 SGRawValueMethods<FGAIBase,double>(*this,
743 nullptr));
744 tie("position/global-z",
745 SGRawValueMethods<FGAIBase,double>(*this,
747 nullptr));
748 tie("callsign",
749 SGRawValueMethods<FGAIBase,const char*>(*this,
751 nullptr));
752 // 2018.2 - to ensure consistent properties also tie the callsign to where it would be in a local model.
753 tie("sim/multiplay/callsign",
754 SGRawValueMethods<FGAIBase, const char*>(*this, &FGAIBase::_getCallsign, nullptr));
755
756 tie("orientation/pitch-deg", SGRawValuePointer<double>(&pitch));
757 tie("orientation/roll-deg", SGRawValuePointer<double>(&roll));
758 tie("orientation/true-heading-deg", SGRawValuePointer<double>(&hdg));
759
760 tie("radar/in-range", SGRawValuePointer<bool>(&in_range));
761 tie("radar/bearing-deg", SGRawValuePointer<double>(&bearing));
762 tie("radar/elevation-deg", SGRawValuePointer<double>(&elevation));
763 tie("radar/range-nm", SGRawValuePointer<double>(&range));
764 tie("radar/h-offset", SGRawValuePointer<double>(&horiz_offset));
765 tie("radar/v-offset", SGRawValuePointer<double>(&vert_offset));
766 tie("radar/x-shift", SGRawValuePointer<double>(&x_shift));
767 tie("radar/y-shift", SGRawValuePointer<double>(&y_shift));
768 tie("radar/rotation", SGRawValuePointer<double>(&rotation));
769 tie("radar/ht-diff-ft", SGRawValuePointer<double>(&ht_diff));
770 tie("subID", SGRawValuePointer<int>(&_subID));
771
772 props->setStringValue("sim/model/path", model_path);
773
774 // note: AIAircraft creates real SGPropertyNodes for these, we don't do
775 // that here because it would bloat AIBase slightly
776 props->setBoolValue("controls/glide-path", true);
777
778 props->setStringValue("controls/flight/lateral-mode", "roll");
779 props->setDoubleValue("controls/flight/target-hdg", hdg);
780 props->setDoubleValue("controls/flight/target-roll", roll);
781
782 props->setStringValue("controls/flight/vertical-mode", "alt");
783
784 // The property above was incorrectly labelled 'longitude-mode' up until
785 // FG 2018.4, so create an alias in case anyone is relying on the old name
786 auto node = props->getNode("controls/flight/longitude-mode", true);
787 node->alias(props->getNode("controls/flight/vertical-mode"), false);
788
789 props->setDoubleValue("controls/flight/target-alt", altitude_ft);
790 props->setDoubleValue("controls/flight/target-pitch", pitch);
791
792 props->setDoubleValue("controls/flight/target-spd", speed);
793
794 props->setBoolValue("sim/sound/avionics/enabled", false);
795 props->setDoubleValue("sim/sound/avionics/volume", 0.0);
796 props->setBoolValue("sim/sound/avionics/external-view", false);
797 props->setBoolValue("sim/current-view/internal", false);
798}
799
801 _tiedProperties.Untie();
802
803 props->setBoolValue("/sim/controls/radar", true);
804
806}
807
809 // drop reference to sound effects now
810 if (_fx)
811 {
812 // must remove explicitly - since the sound manager also keeps a reference
813 _fx->unbind();
814 // now drop last reference - kill the object
815 _fx = 0;
816 }
817}
818
820{
821 if (!manager->isRadarEnabled())
822 return 0.0;
823
824 const double radar_range_m = manager->radarRangeM() * 1.1; // + 10%
825 bool force_on = manager->enableRadarDebug();
826 double d = dist(SGVec3d::fromGeod(pos), globals->get_aircraft_position_cart());
827 double dFt = d * SG_METER_TO_FEET;
828 in_range = (d < radar_range_m);
829
830 if (!force_on && !in_range) {
831 return dFt * dFt;
832 }
833
834 // copy values from the AIManager
835 double user_heading = manager->get_user_heading();
836 double user_pitch = manager->get_user_pitch();
837
838 range = d * SG_METER_TO_NM;
839 // calculate bearing to target
840 bearing = SGGeodesy::courseDeg(globals->get_aircraft_position(), pos);
841
842 // calculate look left/right to target, without yaw correction
843 horiz_offset = bearing - user_heading;
844 SG_NORMALIZE_RANGE(horiz_offset, -180.0, 180.0);
845
846 // calculate elevation to target
847 ht_diff = altitude_ft - globals->get_aircraft_position().getElevationFt();
848 elevation = atan2( ht_diff, dFt ) * SG_RADIANS_TO_DEGREES;
849
850 // calculate look up/down to target
851 vert_offset = elevation - user_pitch;
852
853 /* this calculation needs to be fixed, but it isn't important anyway
854 // calculate range rate
855 double recip_bearing = bearing + 180.0;
856 if (recip_bearing > 360.0) recip_bearing -= 360.0;
857 double my_horiz_offset = recip_bearing - hdg;
858 if (my_horiz_offset > 180.0) my_horiz_offset -= 360.0;
859 if (my_horiz_offset < -180.0) my_horiz_offset += 360.0;
860 rdot = (-user_speed * cos( horiz_offset * SG_DEGREES_TO_RADIANS ))
861 +(-speed * 1.686 * cos( my_horiz_offset * SG_DEGREES_TO_RADIANS ));
862 */
863
864 // now correct look left/right for yaw
865 // horiz_offset += user_yaw; // FIXME: WHY WOULD WE WANT TO ADD IN SIDE-SLIP HERE?
866
867 // calculate values for radar display
868 y_shift = range * cos( horiz_offset * SG_DEGREES_TO_RADIANS);
869 x_shift = range * sin( horiz_offset * SG_DEGREES_TO_RADIANS);
870
871 rotation = hdg - user_heading;
872 SG_NORMALIZE_RANGE(rotation, 0.0, 360.0);
873
874 return dFt * dFt;
875}
876
877/*
878* Getters and Setters
879*/
880
881SGVec3d FGAIBase::getCartPosAt(const SGVec3d& _off) const {
882 // Transform that one to the horizontal local coordinate system.
883 SGQuatd hlTrans = SGQuatd::fromLonLat(pos);
884
885 // and postrotate the orientation of the AIModel wrt the horizontal
886 // local frame
887 hlTrans *= SGQuatd::fromYawPitchRollDeg(hdg, pitch, roll);
888
889 // The offset converted to the usual body fixed coordinate system
890 // rotated to the earth fixed coordinates axis
891 SGVec3d off = hlTrans.backTransform(_off);
892
893 // Add the position offset of the AIModel to gain the earth centered position
894 SGVec3d cartPos = SGVec3d::fromGeod(pos);
895
896 return cartPos + off;
897}
898
899SGVec3d FGAIBase::getCartPos() const {
900 SGVec3d cartPos = SGVec3d::fromGeod(pos);
901 return cartPos;
902}
903
904bool FGAIBase::getGroundElevationM(const SGGeod& pos, double& elev,
905 const simgear::BVHMaterial** material) const {
906 return globals->get_scenery()->get_elevation_m(pos, elev, material,
907 _model.get());
908}
909
910SGPropertyNode* FGAIBase::getPositionFromNode(SGPropertyNode* scFileNode, const std::string &key, SGVec3d &position) {
911 SGPropertyNode* positionNode = scFileNode->getChild(key);
912 if (positionNode) {
913 // Transform to the right coordinate frame, configuration is done in
914 // the usual x-back, y-right, z-up coordinates, computations
915 // in the simulation usual body x-forward, y-right, z-down coordinates
916 position(0) = -positionNode->getDoubleValue("x-offset-m", 0);
917 position(1) = positionNode->getDoubleValue("y-offset-m", 0);
918 position(2) = -positionNode->getDoubleValue("z-offset-m", 0);
919 return positionNode;
920 }
921 else
922 position = SGVec3d::zeros();
923 return nullptr;
924}
925
927 SGVec3d cartPos = getCartPos();
928 return cartPos.x();
929}
930
932 SGVec3d cartPos = getCartPos();
933 return cartPos.y();
934}
935
937 SGVec3d cartPos = getCartPos();
938 return cartPos.z();
939}
940
942 pos.setLongitudeDeg(longitude);
943}
944
946 pos.setLatitudeDeg(latitude);
947}
948
949void FGAIBase::_setSubID( int s ) {
950 _subID = s;
951}
952
954 if (_parent == ""){
955 SG_LOG(SG_AI, SG_ALERT, "AIBase: " << _name << " parent not set ");
956 return false;
957 }
958
959 const SGPropertyNode_ptr ai = fgGetNode("/ai/models", true);
960
961 for (int i = ai->nChildren() - 1; i >= -1; i--) {
962 SGPropertyNode_ptr model;
963
964 if (i < 0) { // last iteration: selected model
965 model = _selected_ac;
966 } else {
967 model = ai->getChild(i);
968 const string name = model->getStringValue("name");
969
970 if (!model->nChildren()){
971 continue;
972 }
973
974 if (name == _parent) {
975 _selected_ac = model; // save selected model for last iteration
976 break;
977 }
978 }
979
980 if (!model)
981 continue;
982 }// end for loop
983
984 if (_selected_ac != 0){
985 // DEADCODE: const string name = _selected_ac->getStringValue("name");
986 return true;
987 } else {
988 SG_LOG(SG_AI, SG_ALERT, "AIBase: " << _name << " parent not found: dying ");
989 setDie(true);
990 return false;
991 }
992}
993
995 return pos.getLongitudeDeg();
996}
997
999 return pos.getLatitudeDeg();
1000}
1001
1003 return pos.getElevationFt();
1004}
1005
1006double FGAIBase::_getRdot() const {
1007 return rdot;
1008}
1009
1010double FGAIBase::_getVS_fps() const {
1011 return vs_fps;
1012}
1013
1017
1021
1022void FGAIBase::_setVS_fps( double _vs ) {
1023 vs_fps = _vs;
1024}
1025
1027 return altitude_ft;
1028}
1029
1030double FGAIBase::_getAltitudeAGL(SGGeod inpos, double start){
1031 getGroundElevationM(SGGeod::fromGeodM(inpos, start),
1032 _elevation_m, NULL);
1033 return inpos.getElevationFt() - _elevation_m * SG_METER_TO_FEET;
1034}
1035
1037 return serviceable;
1038}
1039
1040SGPropertyNode* FGAIBase::_getProps() const {
1041 return props;
1042}
1043
1044void FGAIBase::_setAltitude( double _alt ) {
1045 setAltitude( _alt );
1046}
1047
1049 return (fgGetFloat("/sim/time/sun-angle-rad") > 1.57);
1050}
1051
1055
1057 return _expiry_reported;
1058}
1059
1061 return _impact_reported;
1062}
1063
1065 return _impact_lat;
1066}
1067
1069 return _impact_lon;
1070}
1071
1073 return _impact_elev * SG_METER_TO_FEET;
1074}
1075
1077 return _impact_pitch;
1078}
1079
1081 return _impact_roll;
1082}
1083
1085 return _impact_hdg;
1086}
1087
1089 return _impact_speed;
1090}
1091
1092int FGAIBase::getID() const {
1093 return _refID;
1094}
1095
1097 return _subID;
1098}
1099
1100double FGAIBase::_getSpeed() const {
1101 return speed;
1102}
1103
1104double FGAIBase::_getRoll() const {
1105 return roll;
1106}
1107
1108double FGAIBase::_getPitch() const {
1109 return pitch;
1110}
1111
1113 return hdg;
1114}
1115
1117 return _x_offset;
1118}
1119
1121 return _y_offset;
1122}
1123
1125 return _z_offset;
1126}
1127
1128const char* FGAIBase::_getPath() const {
1129 return model_path.c_str();
1130}
1131
1132const char* FGAIBase::_getSMPath() const {
1133 return _path.c_str();
1134}
1135
1136const char* FGAIBase::_getName() const {
1137 return _name.c_str();
1138}
1139
1140const char* FGAIBase::_getCallsign() const {
1141 return _callsign.c_str();
1142}
1143
1144const char* FGAIBase::_getSubmodel() const {
1145 return _submodel.c_str();
1146}
1147
1151
1153 static int id = 0;
1154
1155 if (!++id)
1156 id++; // id = 0 is not allowed.
1157
1158 return id;
1159}
1160
1161void FGAIBase::setFlightPlan(std::unique_ptr<FGAIFlightPlan> f)
1162{
1163 fp = std::move(f);
1164}
1165
1167{
1168 //Either no flightplan or it is valid
1169 return !fp || fp->isValidPlan();
1170}
1171
1173{
1174 return _model;
1175}
1176
1178{
1179 return _model->getNumChildren() > 0;
1180}
1181
1183{
1184 return pos;
1185}
1186
1187void FGAIBase::setGeodPos(const SGGeod& geod)
1188{
1189 pos = geod;
1190}
double latitude
Definition ADA.cxx:53
double longitude
Definition ADA.cxx:54
static std::string default_model
Definition AIBase.cxx:40
#define p(x)
#define i(x)
std::string _parent
Definition AIBase.hxx:191
double _getAltitudeAGL(SGGeod inpos, double start)
Definition AIBase.cxx:1030
bool _installed
Definition AIBase.hxx:259
void setSpeed(double speed_KTAS)
Definition AIBase.hxx:404
SGPropertyNode * getPositionFromNode(SGPropertyNode *scFileNode, const std::string &key, SGVec3d &position)
Definition AIBase.cxx:910
int _subID
Definition AIBase.hxx:260
double tgt_pitch
Definition AIBase.hxx:233
simgear::TiedPropertyList _tiedProperties
Definition AIBase.hxx:203
double tgt_heading
Definition AIBase.hxx:229
static const double lbs_to_slugs
Definition AIBase.hxx:360
double altitude_ft
Definition AIBase.hxx:218
double _getZOffset() const
Definition AIBase.cxx:1124
double turn_radius_ft
Definition AIBase.hxx:222
double vert_offset
Definition AIBase.hxx:244
bool serviceable
Definition AIBase.hxx:258
void setFallbackModelIndex(const int i)
Definition AIBase.hxx:389
SGGeod pos
Definition AIBase.hxx:212
void setLatitude(double latitude)
Definition AIBase.hxx:446
bool _getExpiryData()
Definition AIBase.cxx:1056
virtual void readFromScenario(SGPropertyNode *scFileNode)
Definition AIBase.cxx:249
double tgt_yaw
Definition AIBase.hxx:234
double _yaw_offset
Definition AIBase.hxx:180
virtual double getDefaultModelRadius()
Definition AIBase.hxx:77
int _fallback_model_index
Definition AIBase.hxx:252
double _getElevationFt() const
Definition AIBase.cxx:1002
double ht_diff
Definition AIBase.hxx:248
std::string _callsign
Definition AIBase.hxx:188
virtual void unbind()
Definition AIBase.cxx:800
double _getYOffset() const
Definition AIBase.cxx:1120
void setDie(bool die)
Definition AIBase.hxx:506
SGVec3d getCartPos() const
Definition AIBase.cxx:899
const std::string & getCallSign() const
Definition AIBase.hxx:367
double _getLatitude() const
Definition AIBase.cxx:998
void _setAltitude(double _alt)
Definition AIBase.cxx:1044
SGVec3d getCartPosAt(const SGVec3d &off) const
Definition AIBase.cxx:881
std::string _name
Definition AIBase.hxx:190
SGPropertyNode * _getProps() const
Definition AIBase.cxx:1040
double _getCartPosX() const
Definition AIBase.cxx:926
double _get_speed_north_fps() const
Definition AIBase.cxx:1018
int getID() const
Definition AIBase.cxx:1092
bool isValid() const
Definition AIBase.cxx:1166
void setFlightPlan(std::unique_ptr< FGAIFlightPlan > f)
Definition AIBase.cxx:1161
bool invisible
Definition AIBase.hxx:256
double _impact_elev
Definition AIBase.hxx:272
void setPathLowres(std::string model)
Definition AIBase.hxx:384
bool _impact_reported
Definition AIBase.hxx:266
int _getSubID() const
Definition AIBase.cxx:1096
void updateLOD()
update LOD properties of the model
Definition AIBase.cxx:388
void setCollisionLength(int range)
Definition AIBase.hxx:496
FGAIBase(object_type ot, bool enableHot)
Definition AIBase.cxx:146
double rdot
Definition AIBase.hxx:242
void setServiceable(bool serviceable)
Definition AIBase.hxx:399
bool in_range
Definition AIBase.hxx:238
bool modelLoaded() const
Definition AIBase.cxx:1177
std::string model_path
Definition AIBase.hxx:250
bool _expiry_reported
Definition AIBase.hxx:268
double _pitch_offset
Definition AIBase.hxx:178
const char * _getSMPath() const
Definition AIBase.cxx:1132
double _impact_pitch
Definition AIBase.hxx:274
double _getImpactElevFt() const
Definition AIBase.cxx:1072
bool delete_me
Definition AIBase.hxx:255
bool _getImpactData()
Definition AIBase.cxx:1060
bool getGroundElevationM(const SGGeod &pos, double &elev, const simgear::BVHMaterial **material) const
Definition AIBase.cxx:904
void _setSubID(int s)
Definition AIBase.cxx:949
void setBank(double bank)
Definition AIBase.hxx:430
SGPropertyNode_ptr _selected_ac
Definition AIBase.hxx:204
SGGeod getGeodPos() const
Definition AIBase.cxx:1182
double _x_offset
Definition AIBase.hxx:174
void updateInterior()
Definition AIBase.cxx:365
double _getCartPosY() const
Definition AIBase.cxx:931
double horiz_offset
Definition AIBase.hxx:243
virtual void initModel()
Definition AIBase.cxx:687
double _getPitch() const
Definition AIBase.cxx:1108
double _getHeading() const
Definition AIBase.cxx:1112
std::string _scenarioPath
Definition AIBase.hxx:192
virtual bool init(ModelSearchOrder searchOrder)
Definition AIBase.cxx:633
double _getImpactHdg() const
Definition AIBase.cxx:1084
void setAltitude(double altitude_ft)
Definition AIBase.hxx:419
virtual void update(double dt)
Definition AIBase.cxx:294
double speed
Definition AIBase.hxx:216
double bearing
Definition AIBase.hxx:239
double _getXOffset() const
Definition AIBase.cxx:1116
double _getSpeed() const
Definition AIBase.cxx:1100
std::unique_ptr< FGAIFlightPlan > fp
Definition AIBase.hxx:264
static bool _isNight()
Definition AIBase.cxx:1048
double _impact_speed
Definition AIBase.hxx:276
bool _collision_reported
Definition AIBase.hxx:267
double _getRdot() const
Definition AIBase.cxx:1006
double UpdateRadar(FGAIManager *manager)
Definition AIBase.cxx:819
void setGeodPos(const SGGeod &pos)
Definition AIBase.cxx:1187
double _getImpactRoll() const
Definition AIBase.cxx:1080
static int _newAIModelID()
Definition AIBase.cxx:1152
double range
Definition AIBase.hxx:241
double _impact_roll
Definition AIBase.hxx:275
double _getAltitude() const
Definition AIBase.cxx:1026
double _getCartPosZ() const
Definition AIBase.cxx:936
SGPropertyNode_ptr model_removed
Definition AIBase.hxx:208
bool isa(object_type otype)
Definition AIBase.cxx:709
double _getLongitude() const
Definition AIBase.cxx:994
void _setLatitude(double latitude)
Definition AIBase.cxx:945
bool setParentNode()
Definition AIBase.cxx:953
double vs_fps
Definition AIBase.hxx:219
const char * _getName() const
Definition AIBase.cxx:1136
double hdg
Definition AIBase.hxx:213
double elevation
Definition AIBase.hxx:240
const char * _getCallsign() const
Definition AIBase.cxx:1140
void Transform()
Definition AIBase.cxx:518
double _impact_lat
Definition AIBase.hxx:270
int _getFallbackModelIndex() const
Definition AIBase.cxx:1148
double _impact_lon
Definition AIBase.hxx:271
bool _getCollisionData()
Definition AIBase.cxx:1052
double x_shift
Definition AIBase.hxx:245
static const double e
Definition AIBase.hxx:359
void setSMPath(const std::string &p)
Definition AIBase.hxx:394
double tgt_speed
Definition AIBase.hxx:231
double _z_offset
Definition AIBase.hxx:176
double pitch
Definition AIBase.hxx:215
SGModelPlacement aip
Definition AIBase.hxx:253
std::string _path
Definition AIBase.hxx:187
const char * _getPath() const
Definition AIBase.cxx:1128
double ft_per_deg_lon
Definition AIBase.hxx:225
void setLongitude(double longitude)
Definition AIBase.hxx:441
void setPath(const char *model)
Definition AIBase.hxx:379
double y_shift
Definition AIBase.hxx:246
void removeSoundFx()
Definition AIBase.cxx:808
void setScenarioPath(const std::string &scenarioPath)
Definition AIBase.cxx:244
double speed_east_deg_sec
Definition AIBase.hxx:221
ModelSearchOrder
Definition AIBase.hxx:63
std::string _submodel
Definition AIBase.hxx:189
void setCollisionHeight(int height)
Definition AIBase.hxx:501
osg::LOD * getSceneBranch() const
Definition AIBase.cxx:1172
double _y_offset
Definition AIBase.hxx:175
double _getImpactPitch() const
Definition AIBase.cxx:1076
bool _getServiceable() const
Definition AIBase.cxx:1036
double ft_per_deg_lat
Definition AIBase.hxx:226
double _getImpactLon() const
Definition AIBase.cxx:1068
double _elevation_m
Definition AIBase.hxx:172
double life
Definition AIBase.hxx:262
std::string model_path_lowres
Definition AIBase.hxx:251
void removeModel()
Cleanly remove the model and let the scenery database pager do the clean-up work.
Definition AIBase.cxx:218
double tgt_altitude_ft
Definition AIBase.hxx:230
double _impact_hdg
Definition AIBase.hxx:273
const char * _getSubmodel() const
Definition AIBase.cxx:1144
void _setVS_fps(double _vs)
Definition AIBase.cxx:1022
double rotation
Definition AIBase.hxx:247
void setHeading(double heading)
Definition AIBase.hxx:414
double _getRoll() const
Definition AIBase.cxx:1104
void _setLongitude(double longitude)
Definition AIBase.cxx:941
virtual ~FGAIBase()
Definition AIBase.cxx:200
double speed_north_deg_sec
Definition AIBase.hxx:220
double _get_speed_east_fps() const
Definition AIBase.cxx:1014
double _getImpactSpeed() const
Definition AIBase.cxx:1088
double _getVS_fps() const
Definition AIBase.cxx:1010
void setPitch(double newpitch)
Definition AIBase.hxx:436
double tgt_roll
Definition AIBase.hxx:232
double _roll_offset
Definition AIBase.hxx:179
ModelSearchOrder _searchOrder
Definition AIBase.hxx:278
SGPropertyNode_ptr replay_time
Definition AIBase.hxx:207
double roll
Definition AIBase.hxx:214
void tie(const char *aRelPath, const SGRawValue< T > &aRawValue)
Tied-properties helper, record nodes which are tied for easy un-tie-ing.
Definition AIBase.hxx:198
SGPropertyNode_ptr props
Definition AIBase.hxx:205
FGAIManager * manager
Definition AIBase.hxx:209
virtual void bind()
Definition AIBase.cxx:713
double tgt_vs
Definition AIBase.hxx:235
double _getImpactLat() const
Definition AIBase.cxx:1064
bool no_roll
Definition AIBase.hxx:257
ErrorContext getErrorContext() const override
Definition AIBase.cxx:59
FGAIModelData * clone() const override
Definition AIBase.cxx:57
std::string & getInteriorPath()
Definition AIBase.cxx:115
bool getInteriorLoaded(void)
Definition AIBase.cxx:113
bool hasHighResolutionModel(void)
Definition AIBase.cxx:118
int getHighResolutionLoDIndex(void)
Definition AIBase.cxx:125
void setHasHighResolutionModel(bool hasHighResolutionModel)
Definition AIBase.cxx:119
bool needInitialization(void)
Definition AIBase.cxx:108
void init(void)
init hook to be called after model is loaded.
Definition AIBase.cxx:106
void captureErrorContext(const std::string &key)
Definition AIBase.cxx:69
std::string & get_sound_path()
Definition AIBase.cxx:110
int getInteriorLoDIndex(void)
Definition AIBase.cxx:124
bool hasInteriorPath(void)
Definition AIBase.cxx:114
void modelLoaded(const std::string &path, SGPropertyNode *prop, osg::Node *n)
osg callback, thread-safe
Definition AIBase.cxx:78
~FGAIModelData()=default
FGAIModelData(SGPropertyNode *root=nullptr)
Definition AIBase.cxx:49
void addErrorContext(const std::string &key, const std::string &value)
Definition AIBase.cxx:64
bool isInitialized(void)
Definition AIBase.cxx:109
int getLowResolutionLoDIndex(void)
Definition AIBase.cxx:126
float getRadius()
Definition AIBase.cxx:116
void setInteriorLoaded(const bool state)
Definition AIBase.cxx:112
Generator for FlightGear model sound effects.
Definition fg_fx.hxx:48
PathList get_data_paths() const
Get list of data locations.
Definition globals.cxx:308
SGPropertyNode * get_props()
Definition globals.hxx:320
Thread-safe proxy for FGNasalModelData.
osg::Group * get_models_branch() const
Definition scenery.hxx:131
flightgear::SceneryPager * getPager()
Definition scenery.hxx:147
static std::string threadSpecificContextValue(const std::string &key)
void queueDeleteRequest(osg::ref_ptr< osg::Object > &objptr)
const char * name
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
Definition fg_props.cxx:556
FGGlobals * globals
Definition globals.cxx:142
std::vector< std::string > string_list
Definition globals.hxx:36
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
Definition proptest.cpp:25
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
Definition proptest.cpp:30
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
Definition proptest.cpp:26
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
float fgGetFloat(const char *name, float defaultValue)
Get a float value for a property.
Definition proptest.cpp:29