]> git.mxchange.org Git - flightgear.git/blob - src/Model/model.cxx
Moved FGAircraftModel subsystem out into its own directory, and
[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 <string.h>             // for strcmp()
11
12 #include <plib/sg.h>
13 #include <plib/ssg.h>
14
15 #include <simgear/compiler.h>
16 #include <simgear/debug/logstream.hxx>
17 #include <simgear/math/point3d.hxx>
18 #include <simgear/math/sg_geodesy.hxx>
19 #include <simgear/misc/exception.hxx>
20 #include <simgear/misc/sg_path.hxx>
21
22 #include <Main/globals.hxx>
23 #include <Scenery/scenery.hxx>
24
25 #include "model.hxx"
26
27
28 \f
29 ////////////////////////////////////////////////////////////////////////
30 // Static utility functions.
31 ////////////////////////////////////////////////////////////////////////
32
33 /**
34  * Locate a named SSG node in a branch.
35  */
36 static ssgEntity *
37 find_named_node (ssgEntity * node, const char * name)
38 {
39   char * node_name = node->getName();
40   if (node_name != 0 && !strcmp(name, node_name))
41     return node;
42   else if (node->isAKindOf(ssgTypeBranch())) {
43     int nKids = node->getNumKids();
44     for (int i = 0; i < nKids; i++) {
45       ssgEntity * result =
46         find_named_node(((ssgBranch*)node)->getKid(i), name);
47       if (result != 0)
48         return result;
49     }
50   } 
51   return 0;
52 }
53
54 /**
55  * Splice a branch in between all child nodes and their parents.
56  */
57 static void
58 splice_branch (ssgBranch * branch, ssgEntity * child)
59 {
60   int nParents = child->getNumParents();
61   branch->addKid(child);
62   for (int i = 0; i < nParents; i++) {
63     ssgBranch * parent = child->getParent(i);
64     parent->replaceKid(child, branch);
65   }
66 }
67
68 /**
69  * Set up the transform matrix for a spin or rotation.
70  */
71 static void
72 set_rotation (sgMat4 &matrix, double position_deg,
73               sgVec3 &center, sgVec3 &axis)
74 {
75  float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
76  
77  float s = (float) sin ( temp_angle ) ;
78  float c = (float) cos ( temp_angle ) ;
79  float t = SG_ONE - c ;
80
81  // axis was normalized at load time 
82  // hint to the compiler to put these into FP registers
83  float x = axis[0];
84  float y = axis[1];
85  float z = axis[2];
86
87  matrix[0][0] = t * x * x + c ;
88  matrix[0][1] = t * y * x - s * z ;
89  matrix[0][2] = t * z * x + s * y ;
90  matrix[0][3] = SG_ZERO;
91  
92  matrix[1][0] = t * x * y + s * z ;
93  matrix[1][1] = t * y * y + c ;
94  matrix[1][2] = t * z * y - s * x ;
95  matrix[1][3] = SG_ZERO;
96  
97  matrix[2][0] = t * x * z - s * y ;
98  matrix[2][1] = t * y * z + s * x ;
99  matrix[2][2] = t * z * z + c ;
100  matrix[2][3] = SG_ZERO;
101
102   // hint to the compiler to put these into FP registers
103  x = center[0];
104  y = center[1];
105  z = center[2];
106  
107  matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
108  matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
109  matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
110  matrix[3][3] = SG_ONE;
111 }
112
113 /**
114  * Set up the transform matrix for a translation.
115  */
116 static void
117 set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis)
118 {
119   sgVec3 xyz;
120   sgScaleVec3(xyz, axis, position_m);
121   sgMakeTransMat4(matrix, xyz);
122 }
123
124
125 // TODO: once this is working, look at Norm's optimized version
126 static void
127 world_coordinate( sgCoord *obj_pos,
128                   double lat_deg, double lon_deg, double elev_ft,
129                   double roll_deg, double pitch_deg, double hdg_deg)
130 {
131   Point3D center = scenery.get_center();
132
133   // setup transforms
134   Point3D geod( lon_deg * SGD_DEGREES_TO_RADIANS,
135                 lat_deg * SGD_DEGREES_TO_RADIANS,
136                 elev_ft * SG_FEET_TO_METER);
137         
138   Point3D world_pos = sgGeodToCart( geod );
139   Point3D offset = world_pos - center;
140         
141   sgMat4 POS;
142   sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
143
144   sgVec3 obj_bk, obj_rt, obj_up;
145   sgSetVec3( obj_bk, 1.0, 0.0, 0.0); // X axis
146   sgSetVec3( obj_rt, 0.0, 1.0, 0.0); // Y axis
147   sgSetVec3( obj_up, 0.0, 0.0, 1.0); // Z axis
148
149   sgMat4 ROT_lon, ROT_lat, ROT_hdg, ROT_pitch, ROT_roll;
150   sgMakeRotMat4( ROT_lon, lon_deg, obj_up );
151   sgMakeRotMat4( ROT_lat, 90 - lat_deg, obj_rt );
152   sgMakeRotMat4( ROT_hdg, -hdg_deg, obj_up );
153   sgMakeRotMat4( ROT_pitch, pitch_deg, obj_rt );
154   sgMakeRotMat4( ROT_roll, -roll_deg, obj_bk );
155
156   sgMat4 TRANS;
157   sgCopyMat4( TRANS, ROT_roll );
158   sgPostMultMat4( TRANS, ROT_pitch );
159   sgPostMultMat4( TRANS, ROT_hdg );
160   sgPostMultMat4( TRANS, ROT_lat );
161   sgPostMultMat4( TRANS, ROT_lon );
162   sgPostMultMat4( TRANS, POS );
163
164   sgSetCoord( obj_pos, TRANS );
165 }
166
167
168 \f
169 ////////////////////////////////////////////////////////////////////////
170 // Implementation of FG3DModel
171 ////////////////////////////////////////////////////////////////////////
172
173 FG3DModel::FG3DModel ()
174   : _model(0),
175     _selector(new ssgSelector),
176     _position(new ssgTransform)
177 {
178 }
179
180 FG3DModel::~FG3DModel ()
181 {
182   // since the nodes are attached to the scene graph, they'll be
183   // deleted automatically
184
185   for (int i = 0; i < _animations.size(); i++) {
186     Animation * tmp = _animations[i];
187     _animations[i] = 0;
188     delete tmp;
189   }
190
191 }
192
193 void 
194 FG3DModel::init (const string &path)
195 {
196   SGPropertyNode props;
197
198                                 // Load the 3D aircraft object itself
199   SGPath xmlpath = globals->get_fg_root();
200   SGPath modelpath = path;
201   xmlpath.append(modelpath.str());
202
203                                 // Check for an XML wrapper
204   if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
205     readProperties(xmlpath.str(), &props);
206     if (props.hasValue("/path")) {
207       modelpath = modelpath.dir();
208       modelpath.append(props.getStringValue("/path"));
209     } else {
210       throw sg_exception("No path for model");
211     }
212   }
213
214                                 // Assume that textures are in
215                                 // the same location as the XML file.
216   ssgTexturePath((char *)xmlpath.dir().c_str());
217   _model = ssgLoad((char *)modelpath.c_str());
218   if (_model == 0)
219     throw sg_exception("Failed to load 3D model");
220
221                                 // Load animations
222   vector<SGPropertyNode *> animation_nodes = props.getChildren("animation");
223   for (unsigned int i = 0; i < animation_nodes.size(); i++) {
224     vector<SGPropertyNode *> name_nodes =
225       animation_nodes[i]->getChildren("object-name");
226     if (name_nodes.size() < 1) {
227       SG_LOG(SG_INPUT, SG_ALERT, "No object-name given for transformation");
228     } else {
229       for (unsigned int j = 0; j < name_nodes.size(); j++) {
230         Animation * animation =
231           make_animation(name_nodes[j]->getStringValue(), animation_nodes[i]);
232         if (animation != 0)
233           _animations.push_back(animation);
234       }
235     }
236   }
237
238                                 // Set up the alignment node
239   ssgTransform * align = new ssgTransform;
240   align->addKid(_model);
241   sgMat4 rot_matrix;
242   sgMat4 off_matrix;
243   sgMat4 res_matrix;
244   float h_rot = props.getFloatValue("/offsets/heading-deg", 0.0);
245   float p_rot = props.getFloatValue("/offsets/roll-deg", 0.0);
246   float r_rot = props.getFloatValue("/offsets/pitch-deg", 0.0);
247   float x_off = props.getFloatValue("/offsets/x-m", 0.0);
248   float y_off = props.getFloatValue("/offsets/y-m", 0.0);
249   float z_off = props.getFloatValue("/offsets/z-m", 0.0);
250   sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
251   sgMakeTransMat4(off_matrix, x_off, y_off, z_off);
252   sgMultMat4(res_matrix, off_matrix, rot_matrix);
253   align->setTransform(res_matrix);
254
255                                 // Set up the position node
256   _position->addKid(align);
257
258                                 // Set up the selector node
259   _selector->addKid(_position);
260   _selector->clrTraversalMaskBits(SSGTRAV_HOT);
261 }
262
263 void
264 FG3DModel::update (int dt)
265 {
266   for (unsigned int i = 0; i < _animations.size(); i++)
267     _animations[i]->update(dt);
268
269   _selector->select(true);
270
271   sgCoord obj_pos;
272   world_coordinate(&obj_pos, _lat_deg, _lon_deg, _elev_ft,
273                    _roll_deg, _pitch_deg, _heading_deg);
274   _position->setTransform(&obj_pos);
275 }
276
277 bool
278 FG3DModel::getVisible () const
279 {
280   return _selector->getSelect();
281 }
282
283 void
284 FG3DModel::setVisible (bool visible)
285 {
286   _selector->select(visible);
287 }
288
289 void
290 FG3DModel::setPosition (double lon_deg, double lat_deg, double elev_ft)
291 {
292   _lon_deg = lon_deg;
293   _lat_deg = lat_deg;
294   _elev_ft = elev_ft;
295 }
296
297 void
298 FG3DModel::setOrientation (double roll_deg, double pitch_deg,
299                            double heading_deg)
300 {
301   _roll_deg = roll_deg;
302   _pitch_deg = pitch_deg;
303   _heading_deg = heading_deg;
304 }
305
306 FG3DModel::Animation *
307 FG3DModel::make_animation (const char * object_name,
308                                  SGPropertyNode * node)
309 {
310   Animation * animation = 0;
311   const char * type = node->getStringValue("type");
312   if (!strcmp("none", type)) {
313     animation = new NullAnimation();
314   } else if (!strcmp("select", type)) {
315     animation = new SelectAnimation();
316   } else if (!strcmp("spin", type)) {
317     animation = new SpinAnimation();
318   } else if (!strcmp("rotate", type)) {
319     animation = new RotateAnimation();
320   } else if (!strcmp("translate", type)) {
321     animation = new TranslateAnimation();
322   } else {
323     animation = new NullAnimation();
324     SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
325   }
326
327   ssgEntity * object = find_named_node(_model, object_name);
328   if (object == 0) {
329     SG_LOG(SG_INPUT, SG_WARN, "Object " << object_name << " not found");
330     delete animation;
331     animation = 0;
332   } else {
333     animation->init(object, node);
334   }
335
336   return animation;
337 }
338
339
340 \f
341 ////////////////////////////////////////////////////////////////////////
342 // Implementation of FG3DModel::Animation
343 ////////////////////////////////////////////////////////////////////////
344
345 FG3DModel::Animation::Animation ()
346 {
347 }
348
349 FG3DModel::Animation::~Animation ()
350 {
351 }
352
353
354 \f
355 ////////////////////////////////////////////////////////////////////////
356 // Implementation of FG3DModel::NullAnimation
357 ////////////////////////////////////////////////////////////////////////
358
359 FG3DModel::NullAnimation::NullAnimation ()
360   : _branch(new ssgBranch)
361 {
362 }
363
364 FG3DModel::NullAnimation::~NullAnimation ()
365 {
366   _branch = 0;
367 }
368
369 void
370 FG3DModel::NullAnimation::init (ssgEntity * object,
371                                       SGPropertyNode * props)
372 {
373   splice_branch(_branch, object);
374   _branch->setName(props->getStringValue("name", 0));
375 }
376
377 void
378 FG3DModel::NullAnimation::update (int dt)
379 {
380 }
381
382
383 \f
384 ////////////////////////////////////////////////////////////////////////
385 // Implementation of FG3DModel::SelectAnimation
386 ////////////////////////////////////////////////////////////////////////
387
388 FG3DModel::SelectAnimation::SelectAnimation ()
389   : _condition(0),
390     _selector(new ssgSelector)
391 {
392 }
393
394 FG3DModel::SelectAnimation::~SelectAnimation ()
395 {
396   delete _condition;
397   _selector = 0;
398 }
399
400 void
401 FG3DModel::SelectAnimation::init (ssgEntity * object,
402                                       SGPropertyNode * props)
403 {
404   splice_branch(_selector, object);
405   _selector->setName(props->getStringValue("name", 0));
406   SGPropertyNode * node = props->getChild("condition");
407   if (node != 0) {
408     _condition = fgReadCondition(node);
409   }
410 }
411
412 void
413 FG3DModel::SelectAnimation::update (int dt)
414 {
415   if (_condition != 0 && _condition->test()) 
416     _selector->select(0xffff);
417   else
418     _selector->select(0x0000);
419 }
420
421
422 \f
423 ////////////////////////////////////////////////////////////////////////
424 // Implementation of FG3DModel::SpinAnimation
425 ////////////////////////////////////////////////////////////////////////
426
427 FG3DModel::SpinAnimation::SpinAnimation ()
428   : _prop(0),
429     _factor(0),
430     _position_deg(0),
431     _transform(new ssgTransform)
432 {
433 }
434
435 FG3DModel::SpinAnimation::~SpinAnimation ()
436 {
437   _transform = 0;
438 }
439
440 void
441 FG3DModel::SpinAnimation::init (ssgEntity * object,
442                                       SGPropertyNode * props)
443 {
444                                 // Splice in the new transform node
445   splice_branch(_transform, object);
446   _transform->setName(props->getStringValue("name", 0));
447   _prop = fgGetNode(props->getStringValue("property", "/null"), true);
448   _factor = props->getDoubleValue("factor", 1.0);
449   _position_deg = props->getDoubleValue("starting-position-deg", 0);
450   _center[0] = props->getFloatValue("center/x-m", 0);
451   _center[1] = props->getFloatValue("center/y-m", 0);
452   _center[2] = props->getFloatValue("center/z-m", 0);
453   _axis[0] = props->getFloatValue("axis/x", 0);
454   _axis[1] = props->getFloatValue("axis/y", 0);
455   _axis[2] = props->getFloatValue("axis/z", 0);
456   sgNormalizeVec3(_axis);
457 }
458
459 void
460 FG3DModel::SpinAnimation::update (int dt)
461 {
462   float velocity_rpms = (_prop->getDoubleValue() * _factor / 60000.0);
463   _position_deg += (dt * velocity_rpms * 360);
464   while (_position_deg < 0)
465     _position_deg += 360.0;
466   while (_position_deg >= 360.0)
467     _position_deg -= 360.0;
468   set_rotation(_matrix, _position_deg, _center, _axis);
469   _transform->setTransform(_matrix);
470 }
471
472
473 \f
474 ////////////////////////////////////////////////////////////////////////
475 // Implementation of FG3DModel::RotateAnimation
476 ////////////////////////////////////////////////////////////////////////
477
478 FG3DModel::RotateAnimation::RotateAnimation ()
479   : _prop(0),
480     _offset_deg(0.0),
481     _factor(1.0),
482     _has_min(false),
483     _min_deg(0.0),
484     _has_max(false),
485     _max_deg(1.0),
486     _position_deg(0.0),
487     _transform(new ssgTransform)
488 {
489 }
490
491 FG3DModel::RotateAnimation::~RotateAnimation ()
492 {
493   _transform = 0;
494 }
495
496 void
497 FG3DModel::RotateAnimation::init (ssgEntity * object,
498                                         SGPropertyNode * props)
499 {
500                                 // Splice in the new transform node
501   splice_branch(_transform, object);
502   _transform->setName(props->getStringValue("name", 0));
503   _prop = fgGetNode(props->getStringValue("property", "/null"), true);
504   _offset_deg = props->getDoubleValue("offset-deg", 0.0);
505   _factor = props->getDoubleValue("factor", 1.0);
506   if (props->hasValue("min-deg")) {
507     _has_min = true;
508     _min_deg = props->getDoubleValue("min-deg");
509   }
510   if (props->hasValue("max-deg")) {
511     _has_max = true;
512     _max_deg = props->getDoubleValue("max-deg");
513   }
514   _position_deg = props->getDoubleValue("starting-position-deg", 0);
515   _center[0] = props->getFloatValue("center/x-m", 0);
516   _center[1] = props->getFloatValue("center/y-m", 0);
517   _center[2] = props->getFloatValue("center/z-m", 0);
518   _axis[0] = props->getFloatValue("axis/x", 0);
519   _axis[1] = props->getFloatValue("axis/y", 0);
520   _axis[2] = props->getFloatValue("axis/z", 0);
521   sgNormalizeVec3(_axis);
522 }
523
524 void
525 FG3DModel::RotateAnimation::update (int dt)
526 {
527   _position_deg = ((_prop->getDoubleValue() + _offset_deg) * _factor);
528   if (_has_min && _position_deg < _min_deg)
529     _position_deg = _min_deg;
530   if (_has_max && _position_deg > _max_deg)
531     _position_deg = _max_deg;
532   set_rotation(_matrix, _position_deg, _center, _axis);
533   _transform->setTransform(_matrix);
534 }
535
536
537 \f
538 ////////////////////////////////////////////////////////////////////////
539 // Implementation of FG3DModel::TranslateAnimation
540 ////////////////////////////////////////////////////////////////////////
541
542 FG3DModel::TranslateAnimation::TranslateAnimation ()
543   : _prop(0),
544     _offset_m(0.0),
545     _factor(1.0),
546     _has_min(false),
547     _min_m(0.0),
548     _has_max(false),
549     _max_m(1.0),
550     _position_m(0.0),
551     _transform(new ssgTransform)
552 {
553 }
554
555 FG3DModel::TranslateAnimation::~TranslateAnimation ()
556 {
557   _transform = 0;
558 }
559
560 void
561 FG3DModel::TranslateAnimation::init (ssgEntity * object,
562                                         SGPropertyNode * props)
563 {
564                                 // Splice in the new transform node
565   splice_branch(_transform, object);
566   _transform->setName(props->getStringValue("name", 0));
567   _prop = fgGetNode(props->getStringValue("property", "/null"), true);
568   _offset_m = props->getDoubleValue("offset-m", 0.0);
569   _factor = props->getDoubleValue("factor", 1.0);
570   if (props->hasValue("min-m")) {
571     _has_min = true;
572     _min_m = props->getDoubleValue("min-m");
573   }
574   if (props->hasValue("max-m")) {
575     _has_max = true;
576     _max_m = props->getDoubleValue("max-m");
577   }
578   _position_m = props->getDoubleValue("starting-position-m", 0);
579   _axis[0] = props->getFloatValue("axis/x", 0);
580   _axis[1] = props->getFloatValue("axis/y", 0);
581   _axis[2] = props->getFloatValue("axis/z", 0);
582   sgNormalizeVec3(_axis);
583 }
584
585 void
586 FG3DModel::TranslateAnimation::update (int dt)
587 {
588   _position_m = ((_prop->getDoubleValue() + _offset_m) * _factor);
589   if (_has_min && _position_m < _min_m)
590     _position_m = _min_m;
591   if (_has_max && _position_m > _max_m)
592     _position_m = _max_m;
593   set_translation(_matrix, _position_m, _axis);
594   _transform->setTransform(_matrix);
595 }
596
597
598 // end of model.cxx