]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/model.cxx
- don't need a guarded pointer here
[simgear.git] / simgear / scene / model / model.cxx
1 // model.cxx - manage a 3D aircraft model.
2 // Written by David Megginson, started 2002.
3 //
4 // This file is in the Public Domain, and comes with no warranty.
5
6 #ifdef HAVE_CONFIG_H
7 #include <simgear_config.h>
8 #endif
9
10 #include <string.h>             // for strcmp()
11
12 #include <vector>
13 #include <set>
14
15 #include <plib/sg.h>
16 #include <plib/ssg.h>
17 #include <plib/ul.h>
18
19 #include <simgear/structure/exception.hxx>
20 #include <simgear/props/props.hxx>
21 #include <simgear/props/props_io.hxx>
22 #include <simgear/props/condition.hxx>
23
24 #include "animation.hxx"
25 #include "model.hxx"
26
27 SG_USING_STD(vector);
28 SG_USING_STD(set);
29
30 bool sgUseDisplayList = true;
31 \f
32 ////////////////////////////////////////////////////////////////////////
33 // Global state
34 ////////////////////////////////////////////////////////////////////////
35 static bool
36 model_filter = true;
37
38 \f
39 ////////////////////////////////////////////////////////////////////////
40 // Static utility functions.
41 ////////////////////////////////////////////////////////////////////////
42
43 static int
44 model_filter_callback (ssgEntity * entity, int mask)
45 {
46   return model_filter ? 1 : 0;
47 }
48
49 /**
50  * Callback to update an animation.
51  */
52 static int
53 animation_callback (ssgEntity * entity, int mask)
54 {
55     return ((SGAnimation *)entity->getUserData())->update();
56 }
57
58 /**
59  * Callback to restore the state after an animation.
60  */
61 static int
62 restore_callback (ssgEntity * entity, int mask)
63 {
64     ((SGAnimation *)entity->getUserData())->restore();
65     return 1;
66 }
67
68 /**
69  * Callback and userData class for conditional rendering.
70  */
71 class SGConditionalRender : public ssgBase {
72 public:
73   SGConditionalRender(SGCondition *c) : _condition(c) {}
74   bool test() { return _condition->test(); }
75 private:
76   SGCondition *_condition;
77 };
78
79 static int
80 model_condition_callback (ssgEntity * entity, int mask)
81 {
82   return ((SGConditionalRender *)entity->getUserData())->test();
83 }
84
85
86 /**
87  * Locate a named SSG node in a branch.
88  */
89 static ssgEntity *
90 find_named_node (ssgEntity * node, const char * name)
91 {
92   char * node_name = node->getName();
93   if (node_name != 0 && !strcmp(name, node_name))
94     return node;
95   else if (node->isAKindOf(ssgTypeBranch())) {
96     int nKids = node->getNumKids();
97     for (int i = 0; i < nKids; i++) {
98       ssgEntity * result =
99         find_named_node(((ssgBranch*)node)->getKid(i), name);
100       if (result != 0)
101         return result;
102     }
103   } 
104   return 0;
105 }
106
107 /**
108  * Splice a branch in between all child nodes and their parents.
109  */
110 static void
111 splice_branch (ssgBranch * branch, ssgEntity * child)
112 {
113   int nParents = child->getNumParents();
114   branch->addKid(child);
115   for (int i = 0; i < nParents; i++) {
116     ssgBranch * parent = child->getParent(i);
117     parent->replaceKid(child, branch);
118   }
119 }
120
121 /**
122  * Make an offset matrix from rotations and position offset.
123  */
124 void
125 sgMakeOffsetsMatrix( sgMat4 * result, double h_rot, double p_rot, double r_rot,
126                      double x_off, double y_off, double z_off )
127 {
128   sgMat4 rot_matrix;
129   sgMat4 pos_matrix;
130   sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
131   sgMakeTransMat4(pos_matrix, x_off, y_off, z_off);
132   sgMultMat4(*result, pos_matrix, rot_matrix);
133 }
134
135
136 void
137 sgMakeAnimation( ssgBranch * model,
138                  const char * name,
139                  vector<SGPropertyNode_ptr> &name_nodes,
140                  SGPropertyNode *prop_root,
141                  SGPropertyNode_ptr node,
142                  double sim_time_sec,
143                  SGPath &texture_path,
144                  set<ssgBranch *> &ignore_branches )
145 {
146   bool ignore = false;
147   SGAnimation * animation = 0;
148   const char * type = node->getStringValue("type", "none");
149   if (!strcmp("none", type)) {
150     animation = new SGNullAnimation(node);
151   } else if (!strcmp("range", type)) {
152     animation = new SGRangeAnimation(prop_root, node);
153   } else if (!strcmp("billboard", type)) {
154     animation = new SGBillboardAnimation(node);
155   } else if (!strcmp("select", type)) {
156     animation = new SGSelectAnimation(prop_root, node);
157   } else if (!strcmp("spin", type)) {
158     animation = new SGSpinAnimation(prop_root, node, sim_time_sec );
159   } else if (!strcmp("timed", type)) {
160     animation = new SGTimedAnimation(node);
161   } else if (!strcmp("rotate", type)) {
162     animation = new SGRotateAnimation(prop_root, node);
163   } else if (!strcmp("translate", type)) {
164     animation = new SGTranslateAnimation(prop_root, node);
165   } else if (!strcmp("scale", type)) {
166     animation = new SGScaleAnimation(prop_root, node);
167   } else if (!strcmp("texrotate", type)) {
168     animation = new SGTexRotateAnimation(prop_root, node);
169   } else if (!strcmp("textranslate", type)) {
170     animation = new SGTexTranslateAnimation(prop_root, node);
171   } else if (!strcmp("texmultiple", type)) {
172     animation = new SGTexMultipleAnimation(prop_root, node);
173   } else if (!strcmp("blend", type)) {
174     animation = new SGBlendAnimation(prop_root, node);
175     ignore = true;
176   } else if (!strcmp("alpha-test", type)) {
177     animation = new SGAlphaTestAnimation(node);
178   } else if (!strcmp("material", type)) {
179     animation = new SGMaterialAnimation(prop_root, node, texture_path);
180   } else if (!strcmp("flash", type)) {
181     animation = new SGFlashAnimation(node);
182   } else if (!strcmp("dist-scale", type)) {
183     animation = new SGDistScaleAnimation(node);
184   } else if (!strcmp("noshadow", type)) {
185     animation = new SGShadowAnimation(prop_root, node);
186   } else if (!strcmp("shader", type)) {
187     animation = new SGShaderAnimation(prop_root, node);
188   } else {
189     animation = new SGNullAnimation(node);
190     SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
191   }
192
193   if (name != 0)
194       animation->setName((char *)name);
195
196   ssgEntity * object;
197   if (name_nodes.size() > 0) {
198     object = find_named_node(model, name_nodes[0]->getStringValue());
199     if (object == 0) {
200       SG_LOG(SG_INPUT, SG_ALERT, "Object " << name_nodes[0]->getStringValue()
201              << " not found");
202       delete animation;
203       animation = 0;
204     }
205   } else {
206     object = model;
207   }
208
209   if ( animation == 0 )
210      return;
211   
212   ssgBranch * branch = animation->getBranch();
213   splice_branch(branch, object);
214
215   for (unsigned int i = 1; i < name_nodes.size(); i++) {
216       const char * name = name_nodes[i]->getStringValue();
217       object = find_named_node(model, name);
218       if (object == 0) {
219           SG_LOG(SG_INPUT, SG_ALERT, "Object " << name << " not found");
220           delete animation;
221           animation = 0;
222       } else {
223           ssgBranch * oldParent = object->getParent(0);
224           branch->addKid(object);
225           oldParent->removeKid(object);
226       }
227   }
228
229   if ( animation != 0 ) {
230     animation->init();
231     branch->setUserData(animation);
232     branch->setTravCallback(SSG_CALLBACK_PRETRAV, animation_callback);
233     branch->setTravCallback(SSG_CALLBACK_POSTTRAV, restore_callback);
234     if ( ignore ) {
235       ignore_branches.insert( branch );
236     }
237   }
238 }
239
240
241 static void makeDList( ssgBranch *b, const set<ssgBranch *> &ignore )
242 {
243   int nb = b->getNumKids();
244   for (int i = 0; i<nb; i++) {
245     ssgEntity *e = b->getKid(i);
246     if (e->isAKindOf(ssgTypeLeaf())) {
247      if( ((ssgLeaf*)e)->getNumVertices() > 0)
248       ((ssgLeaf*)e)->makeDList();
249     } else if (e->isAKindOf(ssgTypeBranch()) && ignore.find((ssgBranch *)e) == ignore.end()) {
250       makeDList( (ssgBranch*)e, ignore );
251     }
252   }
253 }
254
255 class SGLoaderOptions : public ssgLoaderOptions {
256 public:
257   SGLoaderOptions() { ssgSetCurrentOptions( this ); } // Install our own loader options at startup
258   void endLoad() {} // Avoid clearing the texture cache after every model load
259 };
260
261 static SGLoaderOptions loaderOptions;
262
263 \f
264 ////////////////////////////////////////////////////////////////////////
265 // Global functions.
266 ////////////////////////////////////////////////////////////////////////
267
268 ssgBranch *
269 sgLoad3DModel( const string &fg_root, const string &path,
270                SGPropertyNode *prop_root,
271                double sim_time_sec, ssgEntity *(*load_panel)(SGPropertyNode *),
272                SGModelData *data )
273 {
274   ssgBranch * model = 0;
275   SGPropertyNode props;
276
277                                 // Load the 3D aircraft object itself
278   SGPath modelpath = path, texturepath = path;
279   if ( !ulIsAbsolutePathName( path.c_str() ) ) {
280     SGPath tmp = fg_root;
281     tmp.append(modelpath.str());
282     modelpath = texturepath = tmp;
283   }
284
285                                 // Check for an XML wrapper
286   if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") {
287     readProperties(modelpath.str(), &props);
288     if (props.hasValue("/path")) {
289       modelpath = modelpath.dir();
290       modelpath.append(props.getStringValue("/path"));
291       if (props.hasValue("/texture-path")) {
292         texturepath = texturepath.dir();
293         texturepath.append(props.getStringValue("/texture-path"));
294       }
295     } else {
296       if (model == 0)
297         model = new ssgBranch;
298     }
299   }
300
301                                 // Assume that textures are in
302                                 // the same location as the XML file.
303   if (model == 0) {
304     if (texturepath.extension() != "")
305           texturepath = texturepath.dir();
306
307     ssgTexturePath((char *)texturepath.c_str());
308     model = (ssgBranch *)ssgLoad((char *)modelpath.c_str());
309     if (model == 0)
310       throw sg_io_exception("Failed to load 3D model", 
311                           sg_location(modelpath.str()));
312   }
313                                 // Set up the alignment node
314   ssgTransform * alignmainmodel = new ssgTransform;
315   if ( load_panel == 0 )
316     alignmainmodel->setTravCallback( SSG_CALLBACK_PRETRAV, model_filter_callback );
317   alignmainmodel->addKid(model);
318   sgMat4 res_matrix;
319   sgMakeOffsetsMatrix(&res_matrix,
320                       props.getFloatValue("/offsets/heading-deg", 0.0),
321                       props.getFloatValue("/offsets/roll-deg", 0.0),
322                       props.getFloatValue("/offsets/pitch-deg", 0.0),
323                       props.getFloatValue("/offsets/x-m", 0.0),
324                       props.getFloatValue("/offsets/y-m", 0.0),
325                       props.getFloatValue("/offsets/z-m", 0.0));
326   alignmainmodel->setTransform(res_matrix);
327
328   unsigned int i;
329
330                                 // Load sub-models
331   vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
332   for (i = 0; i < model_nodes.size(); i++) {
333     SGPropertyNode_ptr node = model_nodes[i];
334     ssgTransform * align = new ssgTransform;
335     sgMat4 res_matrix;
336     sgMakeOffsetsMatrix(&res_matrix,
337                         node->getFloatValue("offsets/heading-deg", 0.0),
338                         node->getFloatValue("offsets/roll-deg", 0.0),
339                         node->getFloatValue("offsets/pitch-deg", 0.0),
340                         node->getFloatValue("offsets/x-m", 0.0),
341                         node->getFloatValue("offsets/y-m", 0.0),
342                         node->getFloatValue("offsets/z-m", 0.0));
343     align->setTransform(res_matrix);
344
345     ssgBranch * kid;
346     const char * submodel = node->getStringValue("path");
347     try {
348       kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel );
349
350     } catch (const sg_throwable &t) {
351       SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage());
352       throw;
353     }
354
355     SGPropertyNode *cond = node->getNode("condition", false);
356     if (cond) {
357       align->setUserData(new SGConditionalRender(sgReadCondition(prop_root, cond)));
358       align->setTravCallback(SSG_CALLBACK_PRETRAV, model_condition_callback);
359     }
360
361     align->addKid(kid);
362     align->setName(node->getStringValue("name", ""));
363     model->addKid(align);
364   }
365
366   if ( load_panel ) {
367                                 // Load panels
368     vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
369     for (i = 0; i < panel_nodes.size(); i++) {
370         SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
371         ssgEntity * panel = load_panel(panel_nodes[i]);
372         if (panel_nodes[i]->hasValue("name"))
373             panel->setName((char *)panel_nodes[i]->getStringValue("name"));
374         model->addKid(panel);
375     }
376   }
377
378   if (data) {
379     alignmainmodel->setUserData(data);
380     data->modelLoaded(path, &props, alignmainmodel);
381   }
382                                 // Load animations
383   set<ssgBranch *> ignore_branches;
384   vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
385   for (i = 0; i < animation_nodes.size(); i++) {
386     const char * name = animation_nodes[i]->getStringValue("name", 0);
387     vector<SGPropertyNode_ptr> name_nodes =
388       animation_nodes[i]->getChildren("object-name");
389     sgMakeAnimation( model, name, name_nodes, prop_root, animation_nodes[i],
390                      sim_time_sec, texturepath, ignore_branches);
391   }
392
393 #if PLIB_VERSION > 183
394   if ( model != 0 && sgUseDisplayList ) {
395      makeDList( model, ignore_branches );
396   }
397 #endif
398
399   int m = props.getIntValue("dump", 0);
400   if (m > 0)
401     model->print(stderr, "", m - 1);
402
403   return alignmainmodel;
404 }
405
406 bool
407 sgSetModelFilter( bool filter )
408 {
409   bool old = model_filter;
410   model_filter = filter;
411   return old;
412 }
413
414 bool 
415 sgCheckAnimationBranch (ssgEntity * entity)
416 {
417     return entity->getTravCallback(SSG_CALLBACK_PRETRAV) == animation_callback;
418 }
419
420 // end of model.cxx