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