]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/model.cxx
2a27815b411695931715ab24714cb067eb2476aa
[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(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       ((ssgLeaf*)e)->makeDList();
230     } else if (e->isAKindOf(ssgTypeBranch()) && ignore.find((ssgBranch *)e) == ignore.end()) {
231       makeDList( (ssgBranch*)e, ignore );
232     }
233   }
234 }
235
236
237 \f
238 ////////////////////////////////////////////////////////////////////////
239 // Global functions.
240 ////////////////////////////////////////////////////////////////////////
241
242 ssgBranch *
243 sgLoad3DModel( const string &fg_root, const string &path,
244                SGPropertyNode *prop_root,
245                double sim_time_sec, ssgEntity *(*load_panel)(SGPropertyNode *) )
246 {
247   ssgBranch * model = 0;
248   SGPropertyNode props;
249
250                                 // Load the 3D aircraft object itself
251   SGPath modelpath = path, texturepath = path;
252   if ( !ulIsAbsolutePathName( path.c_str() ) ) {
253     SGPath tmp = fg_root;
254     tmp.append(modelpath.str());
255     modelpath = texturepath = tmp;
256   }
257
258                                 // Check for an XML wrapper
259   if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") {
260     readProperties(modelpath.str(), &props);
261     if (props.hasValue("/path")) {
262       modelpath = modelpath.dir();
263       modelpath.append(props.getStringValue("/path"));
264       if (props.hasValue("/texture-path")) {
265         texturepath = texturepath.dir();
266         texturepath.append(props.getStringValue("/texture-path"));
267       }
268     } else {
269       if (model == 0)
270         model = new ssgBranch;
271     }
272   }
273
274                                 // Assume that textures are in
275                                 // the same location as the XML file.
276   if (model == 0) {
277     if (texturepath.extension() != "")
278           texturepath = texturepath.dir();
279
280     ssgTexturePath((char *)texturepath.c_str());
281     model = (ssgBranch *)ssgLoad((char *)modelpath.c_str());
282     if (model == 0)
283       throw sg_exception("Failed to load 3D model");
284   }
285                                 // Set up the alignment node
286   ssgTransform * alignmainmodel = new ssgTransform;
287   if ( load_panel == 0 )
288     alignmainmodel->setTravCallback( SSG_CALLBACK_PRETRAV, model_filter_callback );
289   alignmainmodel->addKid(model);
290   sgMat4 res_matrix;
291   sgMakeOffsetsMatrix(&res_matrix,
292                       props.getFloatValue("/offsets/heading-deg", 0.0),
293                       props.getFloatValue("/offsets/roll-deg", 0.0),
294                       props.getFloatValue("/offsets/pitch-deg", 0.0),
295                       props.getFloatValue("/offsets/x-m", 0.0),
296                       props.getFloatValue("/offsets/y-m", 0.0),
297                       props.getFloatValue("/offsets/z-m", 0.0));
298   alignmainmodel->setTransform(res_matrix);
299
300   unsigned int i;
301
302                                 // Load sub-models
303   vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
304   for (i = 0; i < model_nodes.size(); i++) {
305     SGPropertyNode_ptr node = model_nodes[i];
306     ssgTransform * align = new ssgTransform;
307     sgMat4 res_matrix;
308     sgMakeOffsetsMatrix(&res_matrix,
309                         node->getFloatValue("offsets/heading-deg", 0.0),
310                         node->getFloatValue("offsets/roll-deg", 0.0),
311                         node->getFloatValue("offsets/pitch-deg", 0.0),
312                         node->getFloatValue("offsets/x-m", 0.0),
313                         node->getFloatValue("offsets/y-m", 0.0),
314                         node->getFloatValue("offsets/z-m", 0.0));
315     align->setTransform(res_matrix);
316
317     ssgBranch * kid = sgLoad3DModel( fg_root, node->getStringValue("path"),
318                                      prop_root, sim_time_sec, load_panel );
319     align->addKid(kid);
320     align->setName(node->getStringValue("name", ""));
321     model->addKid(align);
322   }
323
324                                 // Load animations
325   set<ssgBranch *> ignore_branches;
326   vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
327   for (i = 0; i < animation_nodes.size(); i++) {
328     const char * name = animation_nodes[i]->getStringValue("name", 0);
329     vector<SGPropertyNode_ptr> name_nodes =
330       animation_nodes[i]->getChildren("object-name");
331     sgMakeAnimation( model, name, name_nodes, prop_root, animation_nodes[i],
332                      sim_time_sec, texturepath, ignore_branches);
333   }
334
335 #if PLIB_VERSION > 183
336   if ( model != 0 && sgUseDisplayList ) {
337      makeDList( model, ignore_branches );
338   }
339 #endif
340
341   if ( load_panel ) {
342                                 // Load panels
343     vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
344     for (i = 0; i < panel_nodes.size(); i++) {
345         SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
346         ssgEntity * panel = load_panel(panel_nodes[i]);
347         if (panel_nodes[i]->hasValue("name"))
348             panel->setName((char *)panel_nodes[i]->getStringValue("name"));
349         model->addKid(panel);
350     }
351   }
352
353   return alignmainmodel;
354 }
355
356 bool
357 sgSetModelFilter( bool filter )
358 {
359   bool old = model_filter;
360   model_filter = filter;
361   return old;
362 }
363
364 bool 
365 sgCheckAnimationBranch (ssgEntity * entity)
366 {
367     return entity->getTravCallback(SSG_CALLBACK_PRETRAV) == animation_callback;
368 }
369
370 // end of model.cxx