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