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