Provide debug information on thread safety of osg::Referenced.
[fg:hoorays-flightgear.git] / src / Scripting / NasalModelData.cxx
1
2 #include "NasalModelData.hxx"
3 #include "NasalSys.hxx"
4 #include <Main/globals.hxx>
5
6 #include <simgear/math/SGMath.hxx>
7 #include <simgear/nasal/cppbind/Ghost.hxx>
8 #include <simgear/scene/util/OsgDebug.hxx>
9 #include <simgear/scene/util/OsgMath.hxx>
10 #include <simgear/debug/logstream.hxx>
11
12 #include <osg/Transform>
13
14 #include <boost/bind.hpp>
15
16 #include <algorithm>
17 #include <cstring> // for strlen
18
19 // FGNasalModelData class.  If sgLoad3DModel() is called with a pointer to
20 // such a class, then it lets modelLoaded() run the <load> script, and the
21 // destructor the <unload> script. The latter happens when the model branch
22 // is removed from the scene graph.
23
24 unsigned int FGNasalModelData::_max_module_id = 0;
25 FGNasalModelDataList FGNasalModelData::_loaded_models;
26
27 typedef osg::ref_ptr<osg::Node> NodeRef;
28 typedef nasal::Ghost<NodeRef> NasalNode;
29
30 /**
31  * Get position (lat, lon, elevation) and orientation (heading, pitch, roll) of
32  * model.
33  */
34 static naRef f_node_getPose( const osg::Node& node,
35                              const nasal::CallContext& ctx )
36 {
37   osg::NodePathList parent_paths = node.getParentalNodePaths();
38   for( osg::NodePathList::const_iterator path = parent_paths.begin();
39                                          path != parent_paths.end();
40                                        ++path )
41   {
42     osg::Matrix local_to_world = osg::computeLocalToWorld(*path);
43     if( !local_to_world.valid() )
44       continue;
45
46     SGGeod coord = SGGeod::fromCart( toSG(local_to_world.getTrans()) );
47     if( !coord.isValid() )
48       continue;
49
50     osg::Matrix local_frame = makeZUpFrameRelative(coord),
51                 inv_local;
52     inv_local.invert_4x3(local_frame);
53     local_to_world.postMult(inv_local);
54
55     SGQuatd rotate = toSG(local_to_world.getRotate());
56     double hdg, pitch, roll;
57     rotate.getEulerDeg(hdg, pitch, roll);
58
59     nasal::Hash pose(ctx.to_nasal(coord), ctx.c);
60     pose.set("heading", hdg);
61     pose.set("pitch", pitch);
62     pose.set("roll", roll);
63     return pose.get_naRef();
64   }
65
66   return naNil();
67 }
68
69 //------------------------------------------------------------------------------
70 FGNasalModelData::FGNasalModelData( SGPropertyNode *root,
71                                     const std::string& path,
72                                     SGPropertyNode *prop,
73                                     SGPropertyNode* load,
74                                     SGPropertyNode* unload,
75                                     osg::Node* branch ):
76   _path(path),
77   _root(root), _prop(prop),
78   _load(load), _unload(unload),
79   _branch(branch),
80   _module_id( _max_module_id++ )
81 {
82   _loaded_models.push_back(this);
83
84   SG_LOG
85   (
86     SG_NASAL,
87     SG_INFO,
88     "New model with attached script(s) "
89     "(branch = " << branch << ","
90     " path = " << simgear::getNodePathString(branch) <<
91     " thread-safe = " << branch->getThreadSafeRefUnref() << ")"
92   );
93 }
94
95 //------------------------------------------------------------------------------
96 FGNasalModelData::~FGNasalModelData()
97 {
98   _loaded_models.remove(this);
99
100   SG_LOG
101   (
102     SG_NASAL,
103     SG_INFO,
104     "Removed model with script(s) (branch = " << _branch << ")"
105   );
106 }
107
108 //------------------------------------------------------------------------------
109 void FGNasalModelData::load()
110 {
111   std::stringstream m;
112   m << "__model" << _module_id;
113   _module = m.str();
114
115   SG_LOG(SG_NASAL, SG_DEBUG, "Loading nasal module " << _module.c_str());
116
117   const char *s = _load ? _load->getStringValue() : "";
118   FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
119
120   // Add _module_id to script local hash to allow placing canvasses on objects
121   // inside the model.
122   nasal::Hash module = nasalSys->getGlobals().createHash(_module);
123   module.set("_module_id", _module_id);
124
125   if( !NasalNode::isInit() )
126   {
127     NasalNode::init("osg.Node")
128       .method("getPose", &f_node_getPose);
129   }
130
131   module.set("_model", NodeRef(_branch));
132
133   naRef arg[2];
134   arg[0] = nasalSys->propNodeGhost(_root);
135   arg[1] = nasalSys->propNodeGhost(_prop);
136   nasalSys->createModule(_module.c_str(), _path.c_str(), s, strlen(s),
137                          _root, 2, arg);
138 }
139
140 //------------------------------------------------------------------------------
141 void FGNasalModelData::unload()
142 {
143     if (_module.empty())
144         return;
145
146     FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
147     if(!nasalSys) {
148         SG_LOG(SG_NASAL, SG_WARN, "Trying to run an <unload> script "
149                "without Nasal subsystem present.");
150         return;
151     }
152
153     SG_LOG(SG_NASAL, SG_DEBUG, "Unloading nasal module " << _module.c_str());
154
155     if (_unload)
156     {
157         const char *s = _unload->getStringValue();
158         nasalSys->createModule(_module.c_str(), _module.c_str(), s, strlen(s), _root);
159     }
160
161     nasalSys->deleteModule(_module.c_str());
162 }
163
164 //------------------------------------------------------------------------------
165 osg::Node* FGNasalModelData::getNode()
166 {
167   return _branch;
168 }
169
170 //------------------------------------------------------------------------------
171 FGNasalModelData* FGNasalModelData::getByModuleId(unsigned int id)
172 {
173   FGNasalModelDataList::iterator it = std::find_if
174   (
175     _loaded_models.begin(),
176     _loaded_models.end(),
177     boost::bind(&FGNasalModelData::_module_id, _1) == id
178   );
179
180   if( it != _loaded_models.end() )
181     return *it;
182
183   return 0;
184 }
185
186 //------------------------------------------------------------------------------
187 FGNasalModelDataProxy::~FGNasalModelDataProxy()
188 {
189     FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
190     // when necessary, register Nasal module to be destroyed/unloaded
191     // in the main thread.
192     if ((_data.valid())&&(nasalSys))
193         nasalSys->registerToUnload(_data);
194 }
195
196 //------------------------------------------------------------------------------
197 void FGNasalModelDataProxy::modelLoaded( const std::string& path,
198                                          SGPropertyNode *prop,
199                                          osg::Node *branch )
200 {
201     if(!prop)
202         return;
203     
204     SGPropertyNode *nasal = prop->getNode("nasal");
205     if(!nasal)
206         return;
207     
208     FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
209     if(!nasalSys)
210     {
211         SG_LOG
212         (
213           SG_NASAL,
214           SG_WARN,
215           "Can not load model script(s) (Nasal subsystem not available)."
216         );
217         return;
218     }
219
220     SGPropertyNode* load   = nasal->getNode("load");
221     SGPropertyNode* unload = nasal->getNode("unload");
222     
223     if ((!load) && (!unload))
224         return;
225
226     _data = new FGNasalModelData(_root, path, prop, load, unload, branch);
227     
228     // register Nasal module to be created and loaded in the main thread.
229     nasalSys->registerToLoad(_data);
230 }