]> git.mxchange.org Git - flightgear.git/blob - src/Model/model.cxx
Working at unraveling and breaking dependencies inside of src/Model.
[flightgear.git] / src / 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 <config.h>
8 #endif
9
10 #include <simgear/compiler.h>
11
12 #include <string.h>             // for strcmp()
13
14 #include <vector>
15
16 #include <plib/sg.h>
17 #include <plib/ssg.h>
18 #include <plib/ul.h>
19
20 #include <simgear/misc/exception.hxx>
21 #include <simgear/misc/sg_path.hxx>
22 #include <simgear/props/props.hxx>
23 #include <simgear/props/props_io.hxx>
24 #include <simgear/scene/model/animation.hxx>
25
26 #include "model.hxx"
27
28 SG_USING_STD(vector);
29
30
31 \f
32 ////////////////////////////////////////////////////////////////////////
33 // Static utility functions.
34 ////////////////////////////////////////////////////////////////////////
35
36 /**
37  * Callback to update an animation.
38  */
39 static int
40 animation_callback (ssgEntity * entity, int mask)
41 {
42     ((Animation *)entity->getUserData())->update();
43     return true;
44 }
45
46
47 /**
48  * Locate a named SSG node in a branch.
49  */
50 static ssgEntity *
51 find_named_node (ssgEntity * node, const char * name)
52 {
53   char * node_name = node->getName();
54   if (node_name != 0 && !strcmp(name, node_name))
55     return node;
56   else if (node->isAKindOf(ssgTypeBranch())) {
57     int nKids = node->getNumKids();
58     for (int i = 0; i < nKids; i++) {
59       ssgEntity * result =
60         find_named_node(((ssgBranch*)node)->getKid(i), name);
61       if (result != 0)
62         return result;
63     }
64   } 
65   return 0;
66 }
67
68 /**
69  * Splice a branch in between all child nodes and their parents.
70  */
71 static void
72 splice_branch (ssgBranch * branch, ssgEntity * child)
73 {
74   int nParents = child->getNumParents();
75   branch->addKid(child);
76   for (int i = 0; i < nParents; i++) {
77     ssgBranch * parent = child->getParent(i);
78     parent->replaceKid(child, branch);
79   }
80 }
81
82 /**
83  * Make an offset matrix from rotations and position offset.
84  */
85 void
86 fgMakeOffsetsMatrix( sgMat4 * result, double h_rot, double p_rot, double r_rot,
87                      double x_off, double y_off, double z_off )
88 {
89   sgMat4 rot_matrix;
90   sgMat4 pos_matrix;
91   sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
92   sgMakeTransMat4(pos_matrix, x_off, y_off, z_off);
93   sgMultMat4(*result, pos_matrix, rot_matrix);
94 }
95
96
97 void
98 fgMakeAnimation( ssgBranch * model,
99                  const char * name,
100                  vector<SGPropertyNode_ptr> &name_nodes,
101                  SGPropertyNode *prop_root,
102                  SGPropertyNode_ptr node,
103                  double sim_time_sec )
104 {
105   Animation * animation = 0;
106   const char * type = node->getStringValue("type", "none");
107   if (!strcmp("none", type)) {
108     animation = new NullAnimation(node);
109   } else if (!strcmp("range", type)) {
110     animation = new RangeAnimation(node);
111   } else if (!strcmp("billboard", type)) {
112     animation = new BillboardAnimation(node);
113   } else if (!strcmp("select", type)) {
114     animation = new SelectAnimation(prop_root, node);
115   } else if (!strcmp("spin", type)) {
116     animation = new SpinAnimation(prop_root, node, sim_time_sec );
117   } else if (!strcmp("timed", type)) {
118     animation = new TimedAnimation(node);
119   } else if (!strcmp("rotate", type)) {
120     animation = new RotateAnimation(prop_root, node);
121   } else if (!strcmp("translate", type)) {
122     animation = new TranslateAnimation(prop_root, node);
123   } else {
124     animation = new NullAnimation(node);
125     SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
126   }
127
128   if (name != 0)
129       animation->setName((char *)name);
130
131   ssgEntity * object;
132   if (name_nodes.size() > 0) {
133     object = find_named_node(model, name_nodes[0]->getStringValue());
134     if (object == 0) {
135       SG_LOG(SG_INPUT, SG_WARN, "Object " << name_nodes[0]->getStringValue()
136              << " not found");
137       delete animation;
138       animation = 0;
139     }
140   } else {
141     object = model;
142   }
143   
144   ssgBranch * branch = animation->getBranch();
145   splice_branch(branch, object);
146
147   for (unsigned int i = 1; i < name_nodes.size(); i++) {
148       const char * name = name_nodes[i]->getStringValue();
149       object = find_named_node(model, name);
150       if (object == 0) {
151           SG_LOG(SG_INPUT, SG_WARN, "Object " << name << " not found");
152           delete animation;
153           animation = 0;
154       }
155       ssgBranch * oldParent = object->getParent(0);
156       branch->addKid(object);
157       oldParent->removeKid(object);
158   }
159
160   animation->init();
161   branch->setUserData(animation);
162   branch->setTravCallback(SSG_CALLBACK_PRETRAV, animation_callback);
163 }
164
165
166
167 \f
168 ////////////////////////////////////////////////////////////////////////
169 // Global functions.
170 ////////////////////////////////////////////////////////////////////////
171
172 ssgBranch *
173 fgLoad3DModel( const string &fg_root, const string &path,
174                SGPropertyNode *prop_root,
175                double sim_time_sec )
176 {
177   ssgBranch * model = 0;
178   SGPropertyNode props;
179
180                                 // Load the 3D aircraft object itself
181   SGPath xmlpath;
182   SGPath modelpath = path;
183   if ( ulIsAbsolutePathName( path.c_str() ) ) {
184     xmlpath = modelpath;
185   }
186   else {
187     xmlpath = fg_root;
188     xmlpath.append(modelpath.str());
189   }
190
191                                 // Check for an XML wrapper
192   if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
193     readProperties(xmlpath.str(), &props);
194     if (props.hasValue("/path")) {
195       modelpath = modelpath.dir();
196       modelpath.append(props.getStringValue("/path"));
197     } else {
198       if (model == 0)
199         model = new ssgBranch;
200     }
201   }
202
203                                 // Assume that textures are in
204                                 // the same location as the XML file.
205   if (model == 0) {
206     ssgTexturePath((char *)xmlpath.dir().c_str());
207     model = (ssgBranch *)ssgLoad((char *)modelpath.c_str());
208     if (model == 0)
209       throw sg_exception("Failed to load 3D model");
210   }
211
212                                 // Set up the alignment node
213   ssgTransform * alignmainmodel = new ssgTransform;
214   alignmainmodel->addKid(model);
215   sgMat4 res_matrix;
216   fgMakeOffsetsMatrix(&res_matrix,
217                       props.getFloatValue("/offsets/heading-deg", 0.0),
218                       props.getFloatValue("/offsets/roll-deg", 0.0),
219                       props.getFloatValue("/offsets/pitch-deg", 0.0),
220                       props.getFloatValue("/offsets/x-m", 0.0),
221                       props.getFloatValue("/offsets/y-m", 0.0),
222                       props.getFloatValue("/offsets/z-m", 0.0));
223   alignmainmodel->setTransform(res_matrix);
224
225   unsigned int i;
226
227                                 // Load animations
228   vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
229   for (i = 0; i < animation_nodes.size(); i++) {
230     const char * name = animation_nodes[i]->getStringValue("name", 0);
231     vector<SGPropertyNode_ptr> name_nodes =
232       animation_nodes[i]->getChildren("object-name");
233     fgMakeAnimation( model, name, name_nodes, prop_root, animation_nodes[i],
234                      sim_time_sec);
235   }
236
237                                 // Load sub-models
238   vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
239   for (i = 0; i < model_nodes.size(); i++) {
240     SGPropertyNode_ptr node = model_nodes[i];
241     ssgTransform * align = new ssgTransform;
242     sgMat4 res_matrix;
243     fgMakeOffsetsMatrix(&res_matrix,
244                         node->getFloatValue("offsets/heading-deg", 0.0),
245                         node->getFloatValue("offsets/roll-deg", 0.0),
246                         node->getFloatValue("offsets/pitch-deg", 0.0),
247                         node->getFloatValue("offsets/x-m", 0.0),
248                         node->getFloatValue("offsets/y-m", 0.0),
249                         node->getFloatValue("offsets/z-m", 0.0));
250     align->setTransform(res_matrix);
251
252     ssgBranch * kid = fgLoad3DModel( fg_root, node->getStringValue("path"),
253                                      prop_root, sim_time_sec );
254     align->addKid(kid);
255     model->addKid(align);
256   }
257
258   return alignmainmodel;
259 }
260
261
262 // end of model.cxx