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