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