FlightGear next
modelmgr.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: modelmgr.cxx
3 * SPDX-FileComment: manage a collection of 3D models
4 * SPDX-FileCopyrightText: Written by David Megginson, started 2002.
5 * SPDX-License-Identifier: This file is in the Public Domain, and comes with no warranty.
6 */
7
8#ifdef _MSC_VER
9# pragma warning( disable: 4355 )
10#endif
11
12#ifdef HAVE_CONFIG_H
13# include <config.h>
14#endif
15
16#include <simgear/compiler.h>
17
18#include <algorithm>
19#include <functional>
20#include <vector>
21#include <cstring>
22
23#include <simgear/scene/model/placement.hxx>
24#include <simgear/scene/model/modellib.hxx>
25#include <simgear/structure/exception.hxx>
26
27#include <Main/fg_props.hxx>
28#include <Scenery/scenery.hxx>
29#include <Scenery/marker.hxx>
30
31#include <osg/ProxyNode>
32#include <osgText/String>
33
34#include "modelmgr.hxx"
35
36using namespace simgear;
37
38namespace {
39
40class CheckInstanceModelLoadedVisitor : public osg::NodeVisitor
41{
42public:
43 CheckInstanceModelLoadedVisitor() :
44 osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR, osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
45 {}
46
47 ~CheckInstanceModelLoadedVisitor() = default;
48
49 void apply(osg::Node& node) override
50 {
51 if (!_loaded)
52 return;
53 traverse(node);
54 }
55
56 void apply(osg::ProxyNode& node) override
57 {
58 if (!_loaded)
59 return;
60
61 for (unsigned i = 0; i < node.getNumFileNames(); ++i) {
62 if (node.getFileName(i).empty())
63 continue;
64
65 // Check if this is already loaded.
66 if (i < node.getNumChildren() && node.getChild(i))
67 continue;
68
69 _loaded=false;
70 return;
71 }
72 traverse(node);
73 }
74
75 bool isLoaded() const
76 {
77 return _loaded;
78 }
79
80private:
81 bool _loaded = true;
82};
83
84} // of anonymous namespace
85
89
93
94void
96{
97 std::vector<SGPropertyNode_ptr> model_nodes = _models->getChildren("model");
98
99 for (unsigned int i = 0; i < model_nodes.size(); i++)
100 add_model(model_nodes[i]);
101}
102
104{
105 osg::Group *scene_graph = NULL;
106 if (globals->get_scenery()) {
107 scene_graph = globals->get_scenery()->get_scene_graph();
108 }
109
110 // always delete instances, even if the scene-graph is gone
111 for (unsigned int i = 0; i < _instances.size(); i++) {
112 if (scene_graph) {
113 scene_graph->removeChild(_instances[i]->model->getSceneGraph());
114 }
115
116 delete _instances[i];
117 }
118}
119
120void
121FGModelMgr::add_model (SGPropertyNode * node)
122{
123 const std::string model_path{node->getStringValue("path", "Models/Geometry/glider.ac")};
124 if (model_path.empty()) {
125 SG_LOG(SG_AIRCRAFT, SG_WARN, "add_model called with empty path");
126 return;
127 }
128
129 const std::string internal_model{node->getStringValue("internal-model", "external")};
130
131 osg::Node *object;
132
133 Instance * instance = new Instance;
134 instance->loaded_node = node->addChild("loaded");
135 instance->loaded_node->setBoolValue(false);
136
137 if (internal_model == "marker") {
138 std::string label{node->getStringValue("marker/text", "MARKER")};
139 float r = node->getFloatValue("marker/color[0]", 1.0f);
140 float g = node->getFloatValue("marker/color[1]", 1.0f);
141 float b = node->getFloatValue("marker/color[2]", 1.0f);
142 osg::Vec4f color(r, g, b, 1.0f);
143 float font_size = node->getFloatValue("marker/size", 1.0f);
144 float pin_height = node->getFloatValue("marker/height", 1000.0f);
145 float tip_height = node->getFloatValue("marker/tip-height", 0.0f);
146 object = fgCreateMarkerNode(osgText::String(label, osgText::String::ENCODING_UTF8), font_size, pin_height, tip_height, color);
147 }
148 else if (internal_model == "external") {
149 try {
150 std::string fullPath = simgear::SGModelLib::findDataFile(model_path);
151 if (fullPath.empty()) {
152 SG_LOG(SG_AIRCRAFT, SG_ALERT, "add_model: unable to find model with name '" << model_path << "'");
153 return;
154 }
155 object = SGModelLib::loadDeferredModel(fullPath, globals->get_props());
156 } catch (const sg_throwable& t) {
157 SG_LOG(SG_AIRCRAFT, SG_ALERT, "Error loading " << model_path << ":\n "
158 << t.getFormattedMessage() << t.getOrigin());
159 return;
160 }
161 }
162 else {
163 object = new osg::Node;
164 SG_LOG(SG_AIRCRAFT, SG_WARN, "Unsupported internal-model type " << internal_model);
165 }
166
167 const std::string modelName{node->getStringValue("name", model_path.c_str())};
168 SG_LOG(SG_AIRCRAFT, SG_INFO, "Adding model " << modelName);
169
170 SGModelPlacement *model = new SGModelPlacement;
171 instance->model = model;
172 instance->node = node;
173
174 model->init( object );
175 double lon = node->getDoubleValue("longitude-deg"),
176 lat = node->getDoubleValue("latitude-deg"),
177 elevFt = node->getDoubleValue("elevation-ft");
178
179 model->setPosition(SGGeod::fromDegFt(lon, lat, elevFt));
180// Set position and orientation either
181// indirectly through property refs
182// or directly with static values.
183 SGPropertyNode * child = node->getChild("longitude-deg-prop");
184 if (child != 0)
185 instance->lon_deg_node = fgGetNode(child->getStringValue(), true);
186
187 child = node->getChild("latitude-deg-prop");
188 if (child != 0)
189 instance->lat_deg_node = fgGetNode(child->getStringValue(), true);
190
191 child = node->getChild("elevation-ft-prop");
192 if (child != 0)
193 instance->elev_ft_node = fgGetNode(child->getStringValue(), true);
194
195 child = node->getChild("roll-deg-prop");
196 if (child != 0)
197 instance->roll_deg_node = fgGetNode(child->getStringValue(), true);
198 else
199 model->setRollDeg(node->getDoubleValue("roll-deg"));
200
201 child = node->getChild("pitch-deg-prop");
202 if (child != 0)
203 instance->pitch_deg_node = fgGetNode(child->getStringValue(), true);
204 else
205 model->setPitchDeg(node->getDoubleValue("pitch-deg"));
206
207 child = node->getChild("heading-deg-prop");
208 if (child != 0)
209 instance->heading_deg_node = fgGetNode(child->getStringValue(), true);
210 else
211 model->setHeadingDeg(node->getDoubleValue("heading-deg"));
212
213 if (node->hasChild("enable-hot")) {
214 osg::Node::NodeMask mask = model->getSceneGraph()->getNodeMask();
215 if (node->getBoolValue("enable-hot")) {
216 mask |= SG_NODEMASK_TERRAIN_BIT;
217 } else {
218 mask &= ~SG_NODEMASK_TERRAIN_BIT;
219 }
220 model->getSceneGraph()->setNodeMask(mask);
221 }
222
223 // Add this model to the global scene graph
224 globals->get_scenery()->get_scene_graph()->addChild(model->getSceneGraph());
225
226
227 // Save this instance for updating
228 add_instance(instance);
229}
230
231void
233{
234 _models = fgGetNode("/models", true);
235
236 _listener.reset(new Listener(this));
237 _models->addChangeListener(_listener.get());
238}
239
240void
242{
243 // work-around for FLIGHTGEAR-37D : crash when quitting during
244 // early startup
245 if (_listener) {
246 _models->removeChangeListener(_listener.get());
247 }
248
249 _listener.reset();
250 _models.clear();
251}
252
253namespace
254{
255double testNan(double val)
256{
257 if (SGMisc<double>::isNaN(val))
258 throw sg_range_exception("value is nan");
259
260 return val;
261}
262} // namespace
263
264void FGModelMgr::update(double dt)
265{
266 std::for_each(_instances.begin(), _instances.end(), [](FGModelMgr::Instance* instance) {
267 SGModelPlacement* model = instance->model;
268 double roll, pitch, heading;
269 roll = pitch = heading = 0.0;
270 SGGeod pos = model->getPosition();
271
272 try {
273 // Optionally set position from properties
274 if (instance->lon_deg_node != 0)
275 pos.setLongitudeDeg(testNan(instance->lon_deg_node->getDoubleValue()));
276 if (instance->lat_deg_node != 0)
277 pos.setLatitudeDeg(testNan(instance->lat_deg_node->getDoubleValue()));
278 if (instance->elev_ft_node != 0)
279 pos.setElevationFt(testNan(instance->elev_ft_node->getDoubleValue()));
280
281 // Optionally set orientation from properties
282 if (instance->roll_deg_node != 0)
283 roll = testNan(instance->roll_deg_node->getDoubleValue());
284 if (instance->pitch_deg_node != 0)
285 pitch = testNan(instance->pitch_deg_node->getDoubleValue());
286 if (instance->heading_deg_node != 0)
287 heading = testNan(instance->heading_deg_node->getDoubleValue());
288 } catch (const sg_range_exception&) {
289 std::string path = instance->node->getStringValue("path", "unknown");
290 SG_LOG(SG_AIRCRAFT, SG_INFO, "Instance of model " << path
291 << " has invalid values");
292 return;
293 }
294
295 model->setPosition(pos);
296 // Optionally set orientation from properties
297 if (instance->roll_deg_node != 0)
298 model->setRollDeg(roll);
299 if (instance->pitch_deg_node != 0)
300 model->setPitchDeg(pitch);
301 if (instance->heading_deg_node != 0)
302 model->setHeadingDeg(heading);
303
304 instance->model->update();
305 instance->checkLoaded();
306 });
307}
308
309void
311{
312 _instances.push_back(instance);
313}
314
315void
317{
318 std::vector<Instance *>::iterator it;
319 for (it = _instances.begin(); it != _instances.end(); it++) {
320 if (*it == instance) {
321 _instances.erase(it);
322 delete instance;
323 return;
324 }
325 }
326}
327
329FGModelMgr::findInstanceByNodePath(const std::string& node_path) const
330{
331 if (node_path.empty())
332 return nullptr;
333
334 SGPropertyNode* node = fgGetNode(node_path, false);
335 if (!node)
336 return nullptr;
337
338 auto it = std::find_if(_instances.begin(), _instances.end(),
339 [node](const Instance* instance)
340 { return instance->node == node; });
341
342 if (it == _instances.end()) {
343 return nullptr;
344 }
345
346 return *it;
347}
348
350// Implementation of FGModelMgr::Instance
352
354{
355 delete model;
356}
357
359{
360 if (!model)
361 return false;
362
363 if (loaded_node->getBoolValue()) {
364 return true;
365 }
366
367 CheckInstanceModelLoadedVisitor cilv;
368 model->getSceneGraph()->accept(cilv);
369 const bool loadedNow = cilv.isLoaded();
370
371 if (loadedNow) {
372 loaded_node->setBoolValue(true);
373 }
374 return loadedNow;
375}
376
378// Implementation of FGModelMgr::Listener
380
381void
382FGModelMgr::Listener::childAdded(SGPropertyNode * parent, SGPropertyNode * child)
383{
384 if (parent->getNameString() != "model" || child->getNameString() != "load")
385 return;
386
387 _mgr->add_model(parent);
388}
389
390void
391FGModelMgr::Listener::childRemoved(SGPropertyNode * parent, SGPropertyNode * child)
392{
393 if (parent->getNameString() != "models" || child->getNameString() != "model")
394 return;
395
396 // search instance by node and remove it from scenegraph
397 std::vector<Instance *>::iterator it = _mgr->_instances.begin();
398 std::vector<Instance *>::iterator end = _mgr->_instances.end();
399
400 for (; it != end; ++it) {
401 Instance *instance = *it;
402 if (instance->node != child)
403 continue;
404
405 _mgr->_instances.erase(it);
406 osg::Node *branch = instance->model->getSceneGraph();
407 // OSGFIXME
408// if (shadows && instance->shadow)
409// shadows->deleteOccluder(branch);
410
411 if (globals->get_scenery() && globals->get_scenery()->get_scene_graph()) {
412 globals->get_scenery()->get_scene_graph()->removeChild(branch);
413 }
414
415 delete instance;
416 break;
417 }
418}
419
420
421// Register the subsystem.
422SGSubsystemMgr::Registrant<FGModelMgr> registrantFGModelMgr(
423 SGSubsystemMgr::DISPLAY);
424
425// end of modelmgr.cxx
#define i(x)
virtual ~FGModelMgr()
Definition modelmgr.cxx:90
void init() override
Definition modelmgr.cxx:95
virtual void add_instance(Instance *instance)
Add an instance of a dynamic model to the manager.
Definition modelmgr.cxx:310
virtual void remove_instance(Instance *instance)
Remove an instance of a dynamic model from the manager.
Definition modelmgr.cxx:316
void unbind() override
Definition modelmgr.cxx:241
Instance * findInstanceByNodePath(const std::string &nodePath) const
Finds an instance in the model manager from a given node path in the property tree.
Definition modelmgr.cxx:329
virtual void add_model(SGPropertyNode *node)
Definition modelmgr.cxx:121
void update(double dt) override
Definition modelmgr.cxx:264
void shutdown() override
Definition modelmgr.cxx:103
void bind() override
Definition modelmgr.cxx:232
FGGlobals * globals
Definition globals.cxx:142
osg::Node * fgCreateMarkerNode(const osgText::String &label, float font_size, float pin_height, float tip_height, const osg::Vec4f &color)
Definition marker.cxx:24
SGSubsystemMgr::Registrant< FGModelMgr > registrantFGModelMgr(SGSubsystemMgr::DISPLAY)
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
A dynamically-placed model using properties.
Definition modelmgr.hxx:43
SGPropertyNode_ptr pitch_deg_node
Definition modelmgr.hxx:51
bool checkLoaded() const
Definition modelmgr.cxx:358
SGModelPlacement * model
Definition modelmgr.hxx:45
SGPropertyNode_ptr roll_deg_node
Definition modelmgr.hxx:50
SGPropertyNode_ptr lon_deg_node
Definition modelmgr.hxx:47
SGPropertyNode_ptr node
Definition modelmgr.hxx:46
SGPropertyNode_ptr heading_deg_node
Definition modelmgr.hxx:52
SGPropertyNode_ptr loaded_node
Definition modelmgr.hxx:53
SGPropertyNode_ptr lat_deg_node
Definition modelmgr.hxx:48
SGPropertyNode_ptr elev_ft_node
Definition modelmgr.hxx:49