Keep ref_ptr in FGNasalModelData (should fix #1234)
[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   );
92 }
93
94 //------------------------------------------------------------------------------
95 FGNasalModelData::~FGNasalModelData()
96 {
97   _loaded_models.remove(this);
98
99   SG_LOG
100   (
101     SG_NASAL,
102     SG_INFO,
103     "Removed model with script(s) (branch = " << _branch.get() << ")"
104   );
105 }
106
107 //------------------------------------------------------------------------------
108 void FGNasalModelData::load()
109 {
110   std::stringstream m;
111   m << "__model" << _module_id;
112   _module = m.str();
113
114   SG_LOG(SG_NASAL, SG_DEBUG, "Loading nasal module " << _module.c_str());
115
116   const char *s = _load ? _load->getStringValue() : "";
117   FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
118
119   // Add _module_id to script local hash to allow placing canvasses on objects
120   // inside the model.
121   nasal::Hash module = nasalSys->getGlobals().createHash(_module);
122   module.set("_module_id", _module_id);
123
124   if( !NasalNode::isInit() )
125   {
126     NasalNode::init("osg.Node")
127       .method("getPose", &f_node_getPose);
128   }
129
130   module.set("_model", _branch);
131
132   naRef arg[2];
133   arg[0] = nasalSys->propNodeGhost(_root);
134   arg[1] = nasalSys->propNodeGhost(_prop);
135   nasalSys->createModule(_module.c_str(), _path.c_str(), s, strlen(s),
136                          _root, 2, arg);
137 }
138
139 //------------------------------------------------------------------------------
140 void FGNasalModelData::unload()
141 {
142     if (_module.empty())
143         return;
144
145     FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
146     if(!nasalSys) {
147         SG_LOG(SG_NASAL, SG_WARN, "Trying to run an <unload> script "
148                "without Nasal subsystem present.");
149         return;
150     }
151
152     SG_LOG(SG_NASAL, SG_DEBUG, "Unloading nasal module " << _module.c_str());
153
154     if (_unload)
155     {
156         const char *s = _unload->getStringValue();
157         nasalSys->createModule(_module.c_str(), _module.c_str(), s, strlen(s), _root);
158     }
159
160     nasalSys->deleteModule(_module.c_str());
161 }
162
163 //------------------------------------------------------------------------------
164 osg::Node* FGNasalModelData::getNode()
165 {
166   return _branch.get();
167 }
168
169 //------------------------------------------------------------------------------
170 FGNasalModelData* FGNasalModelData::getByModuleId(unsigned int id)
171 {
172   FGNasalModelDataList::iterator it = std::find_if
173   (
174     _loaded_models.begin(),
175     _loaded_models.end(),
176     boost::bind(&FGNasalModelData::_module_id, _1) == id
177   );
178
179   if( it != _loaded_models.end() )
180     return *it;
181
182   return 0;
183 }
184
185 //------------------------------------------------------------------------------
186 FGNasalModelDataProxy::~FGNasalModelDataProxy()
187 {
188     FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
189     // when necessary, register Nasal module to be destroyed/unloaded
190     // in the main thread.
191     if ((_data.valid())&&(nasalSys))
192         nasalSys->registerToUnload(_data);
193 }
194
195 //------------------------------------------------------------------------------
196 void FGNasalModelDataProxy::modelLoaded( const std::string& path,
197                                          SGPropertyNode *prop,
198                                          osg::Node *branch )
199 {
200     if(!prop)
201         return;
202     
203     SGPropertyNode *nasal = prop->getNode("nasal");
204     if(!nasal)
205         return;
206     
207     FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
208     if(!nasalSys)
209     {
210         SG_LOG
211         (
212           SG_NASAL,
213           SG_WARN,
214           "Can not load model script(s) (Nasal subsystem not available)."
215         );
216         return;
217     }
218
219     SGPropertyNode* load   = nasal->getNode("load");
220     SGPropertyNode* unload = nasal->getNode("unload");
221     
222     if ((!load) && (!unload))
223         return;
224
225     _data = new FGNasalModelData(_root, path, prop, load, unload, branch);
226     
227     // register Nasal module to be created and loaded in the main thread.
228     nasalSys->registerToLoad(_data);
229 }