]> git.mxchange.org Git - flightgear.git/blob - src/Model/model.cxx
Make sure that all elapsed time gets passed to update when a subsystem
[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 #include <plib/ul.h>
15
16 #include <simgear/compiler.h>
17 #include <simgear/debug/logstream.hxx>
18 #include <simgear/math/interpolater.hxx>
19 #include <simgear/math/point3d.hxx>
20 #include <simgear/math/sg_geodesy.hxx>
21 #include <simgear/misc/exception.hxx>
22 #include <simgear/misc/sg_path.hxx>
23
24 #include <Main/fg_props.hxx>
25 #include <Main/globals.hxx>
26 #include <Main/location.hxx>
27 #include <Scenery/scenery.hxx>
28
29 #include "model.hxx"
30 #include "panelnode.hxx"
31
32
33 \f
34 ////////////////////////////////////////////////////////////////////////
35 // Static utility functions.
36 ////////////////////////////////////////////////////////////////////////
37
38 /**
39  * Callback to update an animation.
40  */
41 static int
42 animation_callback (ssgEntity * entity, int mask)
43 {
44     ((Animation *)entity->getUserData())->update();
45     return true;
46 }
47
48
49 /**
50  * Locate a named SSG node in a branch.
51  */
52 static ssgEntity *
53 find_named_node (ssgEntity * node, const char * name)
54 {
55   char * node_name = node->getName();
56   if (node_name != 0 && !strcmp(name, node_name))
57     return node;
58   else if (node->isAKindOf(ssgTypeBranch())) {
59     int nKids = node->getNumKids();
60     for (int i = 0; i < nKids; i++) {
61       ssgEntity * result =
62         find_named_node(((ssgBranch*)node)->getKid(i), name);
63       if (result != 0)
64         return result;
65     }
66   } 
67   return 0;
68 }
69
70 /**
71  * Splice a branch in between all child nodes and their parents.
72  */
73 static void
74 splice_branch (ssgBranch * branch, ssgEntity * child)
75 {
76   int nParents = child->getNumParents();
77   branch->addKid(child);
78   for (int i = 0; i < nParents; i++) {
79     ssgBranch * parent = child->getParent(i);
80     parent->replaceKid(child, branch);
81   }
82 }
83
84 /**
85  * Set up the transform matrix for a spin or rotation.
86  */
87 static void
88 set_rotation (sgMat4 &matrix, double position_deg,
89               sgVec3 &center, sgVec3 &axis)
90 {
91  float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
92  
93  float s = (float) sin ( temp_angle ) ;
94  float c = (float) cos ( temp_angle ) ;
95  float t = SG_ONE - c ;
96
97  // axis was normalized at load time 
98  // hint to the compiler to put these into FP registers
99  float x = axis[0];
100  float y = axis[1];
101  float z = axis[2];
102
103  matrix[0][0] = t * x * x + c ;
104  matrix[0][1] = t * y * x - s * z ;
105  matrix[0][2] = t * z * x + s * y ;
106  matrix[0][3] = SG_ZERO;
107  
108  matrix[1][0] = t * x * y + s * z ;
109  matrix[1][1] = t * y * y + c ;
110  matrix[1][2] = t * z * y - s * x ;
111  matrix[1][3] = SG_ZERO;
112  
113  matrix[2][0] = t * x * z - s * y ;
114  matrix[2][1] = t * y * z + s * x ;
115  matrix[2][2] = t * z * z + c ;
116  matrix[2][3] = SG_ZERO;
117
118   // hint to the compiler to put these into FP registers
119  x = center[0];
120  y = center[1];
121  z = center[2];
122  
123  matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
124  matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
125  matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
126  matrix[3][3] = SG_ONE;
127 }
128
129 /**
130  * Set up the transform matrix for a translation.
131  */
132 static void
133 set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis)
134 {
135   sgVec3 xyz;
136   sgScaleVec3(xyz, axis, position_m);
137   sgMakeTransMat4(matrix, xyz);
138 }
139
140
141 /**
142  * Make an offset matrix from rotations and position offset.
143  */
144 static void
145 make_offsets_matrix (sgMat4 * result, double h_rot, double p_rot, double r_rot,
146                      double x_off, double y_off, double z_off)
147 {
148   sgMat4 rot_matrix;
149   sgMat4 pos_matrix;
150   sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
151   sgMakeTransMat4(pos_matrix, x_off, y_off, z_off);
152   sgMultMat4(*result, pos_matrix, rot_matrix);
153 }
154
155
156 /**
157  * Read an interpolation table from properties.
158  */
159 static SGInterpTable *
160 read_interpolation_table (SGPropertyNode_ptr props)
161 {
162   SGPropertyNode_ptr table_node = props->getNode("interpolation");
163   if (table_node != 0) {
164     SGInterpTable * table = new SGInterpTable();
165     vector<SGPropertyNode_ptr> entries = table_node->getChildren("entry");
166     for (unsigned int i = 0; i < entries.size(); i++)
167       table->addEntry(entries[i]->getDoubleValue("ind", 0.0),
168                       entries[i]->getDoubleValue("dep", 0.0));
169     return table;
170   } else {
171     return 0;
172   }
173 }
174
175
176 static void
177 make_animation (ssgBranch * model,
178                 const char * name,
179                 vector<SGPropertyNode_ptr> &name_nodes,
180                 SGPropertyNode_ptr node)
181 {
182   Animation * animation = 0;
183   const char * type = node->getStringValue("type", "none");
184   if (!strcmp("none", type)) {
185     animation = new NullAnimation(node);
186   } else if (!strcmp("range", type)) {
187     animation = new RangeAnimation(node);
188   } else if (!strcmp("billboard", type)) {
189     animation = new BillboardAnimation(node);
190   } else if (!strcmp("select", type)) {
191     animation = new SelectAnimation(node);
192   } else if (!strcmp("spin", type)) {
193     animation = new SpinAnimation(node);
194   } else if (!strcmp("timed", type)) {
195     animation = new TimedAnimation(node);
196   } else if (!strcmp("rotate", type)) {
197     animation = new RotateAnimation(node);
198   } else if (!strcmp("translate", type)) {
199     animation = new TranslateAnimation(node);
200   } else {
201     animation = new NullAnimation(node);
202     SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
203   }
204
205   if (name != 0)
206       animation->setName((char *)name);
207
208   ssgEntity * object;
209   if (name_nodes.size() > 0) {
210     object = find_named_node(model, name_nodes[0]->getStringValue());
211     if (object == 0) {
212       SG_LOG(SG_INPUT, SG_WARN, "Object " << name_nodes[0]->getStringValue()
213              << " not found");
214       delete animation;
215       animation = 0;
216     }
217   } else {
218     object = model;
219   }
220   
221   ssgBranch * branch = animation->getBranch();
222   splice_branch(branch, object);
223
224   for (int i = 1; i < name_nodes.size(); i++) {
225       const char * name = name_nodes[i]->getStringValue();
226       object = find_named_node(model, name);
227       if (object == 0) {
228           SG_LOG(SG_INPUT, SG_WARN, "Object " << name << " not found");
229           delete animation;
230           animation = 0;
231       }
232       ssgBranch * oldParent = object->getParent(0);
233       branch->addKid(object);
234       oldParent->removeKid(object);
235   }
236
237   animation->init();
238   branch->setUserData(animation);
239   branch->setTravCallback(SSG_CALLBACK_PRETRAV, animation_callback);
240 }
241
242
243 \f
244 ////////////////////////////////////////////////////////////////////////
245 // Global functions.
246 ////////////////////////////////////////////////////////////////////////
247
248 ssgBranch *
249 fgLoad3DModel (const string &path)
250 {
251   ssgBranch * model = 0;
252   SGPropertyNode props;
253
254                                 // Load the 3D aircraft object itself
255   SGPath xmlpath;
256   SGPath modelpath = path;
257   if ( ulIsAbsolutePathName( path.c_str() ) ) {
258     xmlpath = modelpath;
259   }
260   else {
261     xmlpath = globals->get_fg_root();
262     xmlpath.append(modelpath.str());
263   }
264
265                                 // Check for an XML wrapper
266   if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
267     readProperties(xmlpath.str(), &props);
268     if (props.hasValue("/path")) {
269       modelpath = modelpath.dir();
270       modelpath.append(props.getStringValue("/path"));
271     } else {
272       if (model == 0)
273         model = new ssgBranch;
274     }
275   }
276
277                                 // Assume that textures are in
278                                 // the same location as the XML file.
279   if (model == 0) {
280     ssgTexturePath((char *)xmlpath.dir().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
286                                 // Set up the alignment node
287   ssgTransform * alignmainmodel = new ssgTransform;
288   alignmainmodel->addKid(model);
289   sgMat4 res_matrix;
290   make_offsets_matrix(&res_matrix,
291                       props.getFloatValue("/offsets/heading-deg", 0.0),
292                       props.getFloatValue("/offsets/roll-deg", 0.0),
293                       props.getFloatValue("/offsets/pitch-deg", 0.0),
294                       props.getFloatValue("/offsets/x-m", 0.0),
295                       props.getFloatValue("/offsets/y-m", 0.0),
296                       props.getFloatValue("/offsets/z-m", 0.0));
297   alignmainmodel->setTransform(res_matrix);
298
299                                 // Load panels
300   unsigned int i;
301   vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
302   for (i = 0; i < panel_nodes.size(); i++) {
303     SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
304     FGPanelNode * panel = new FGPanelNode(panel_nodes[i]);
305     if (panel_nodes[i]->hasValue("name"))
306         panel->setName((char *)panel_nodes[i]->getStringValue("name"));
307     model->addKid(panel);
308   }
309
310                                 // Load animations
311   vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
312   for (i = 0; i < animation_nodes.size(); i++) {
313     const char * name = animation_nodes[i]->getStringValue("name", 0);
314     vector<SGPropertyNode_ptr> name_nodes =
315       animation_nodes[i]->getChildren("object-name");
316     make_animation(model, name, name_nodes, animation_nodes[i]);
317   }
318
319                                 // Load sub-models
320   vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
321   for (i = 0; i < model_nodes.size(); i++) {
322     SGPropertyNode_ptr node = model_nodes[i];
323     ssgTransform * align = new ssgTransform;
324     sgMat4 res_matrix;
325     make_offsets_matrix(&res_matrix,
326                         node->getFloatValue("offsets/heading-deg", 0.0),
327                         node->getFloatValue("offsets/roll-deg", 0.0),
328                         node->getFloatValue("offsets/pitch-deg", 0.0),
329                         node->getFloatValue("offsets/x-m", 0.0),
330                         node->getFloatValue("offsets/y-m", 0.0),
331                         node->getFloatValue("offsets/z-m", 0.0));
332     align->setTransform(res_matrix);
333
334     ssgBranch * kid = fgLoad3DModel(node->getStringValue("path"));
335     align->addKid(kid);
336     model->addKid(align);
337   }
338
339   return alignmainmodel;
340 }
341
342
343 \f
344 ////////////////////////////////////////////////////////////////////////
345 // Implementation of Animation
346 ////////////////////////////////////////////////////////////////////////
347
348 Animation::Animation (SGPropertyNode_ptr props, ssgBranch * branch)
349     : _branch(branch)
350 {
351     _branch->setName(props->getStringValue("name", 0));
352 }
353
354 Animation::~Animation ()
355 {
356 }
357
358 void
359 Animation::init ()
360 {
361 }
362
363 void
364 Animation::update ()
365 {
366 }
367
368
369 \f
370 ////////////////////////////////////////////////////////////////////////
371 // Implementation of NullAnimation
372 ////////////////////////////////////////////////////////////////////////
373
374 NullAnimation::NullAnimation (SGPropertyNode_ptr props)
375   : Animation(props, new ssgBranch)
376 {
377 }
378
379 NullAnimation::~NullAnimation ()
380 {
381 }
382
383
384 \f
385 ////////////////////////////////////////////////////////////////////////
386 // Implementation of RangeAnimation
387 ////////////////////////////////////////////////////////////////////////
388
389 RangeAnimation::RangeAnimation (SGPropertyNode_ptr props)
390   : Animation(props, new ssgRangeSelector)
391 {
392     float ranges[] = { props->getFloatValue("min-m", 0),
393                        props->getFloatValue("max-m", 5000) };
394     ((ssgRangeSelector *)_branch)->setRanges(ranges, 2);
395                        
396 }
397
398 RangeAnimation::~RangeAnimation ()
399 {
400 }
401
402
403 \f
404 ////////////////////////////////////////////////////////////////////////
405 // Implementation of BillboardAnimation
406 ////////////////////////////////////////////////////////////////////////
407
408 BillboardAnimation::BillboardAnimation (SGPropertyNode_ptr props)
409     : Animation(props, new ssgCutout(props->getBoolValue("spherical", true)))
410 {
411 }
412
413 BillboardAnimation::~BillboardAnimation ()
414 {
415 }
416
417
418 \f
419 ////////////////////////////////////////////////////////////////////////
420 // Implementation of SelectAnimation
421 ////////////////////////////////////////////////////////////////////////
422
423 SelectAnimation::SelectAnimation (SGPropertyNode_ptr props)
424   : Animation(props, new ssgSelector),
425     _condition(0)
426 {
427   SGPropertyNode_ptr node = props->getChild("condition");
428   if (node != 0)
429     _condition = fgReadCondition(node);
430 }
431
432 SelectAnimation::~SelectAnimation ()
433 {
434   delete _condition;
435 }
436
437 void
438 SelectAnimation::update ()
439 {
440   if (_condition != 0 && _condition->test()) 
441       ((ssgSelector *)_branch)->select(0xffff);
442   else
443       ((ssgSelector *)_branch)->select(0x0000);
444 }
445
446
447 \f
448 ////////////////////////////////////////////////////////////////////////
449 // Implementation of SpinAnimation
450 ////////////////////////////////////////////////////////////////////////
451
452 SpinAnimation::SpinAnimation (SGPropertyNode_ptr props)
453   : Animation(props, new ssgTransform),
454     _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
455     _factor(props->getDoubleValue("factor", 1.0)),
456     _position_deg(props->getDoubleValue("starting-position-deg", 0)),
457     _last_time_sec(globals->get_sim_time_sec())
458 {
459     _center[0] = props->getFloatValue("center/x-m", 0);
460     _center[1] = props->getFloatValue("center/y-m", 0);
461     _center[2] = props->getFloatValue("center/z-m", 0);
462     _axis[0] = props->getFloatValue("axis/x", 0);
463     _axis[1] = props->getFloatValue("axis/y", 0);
464     _axis[2] = props->getFloatValue("axis/z", 0);
465     sgNormalizeVec3(_axis);
466 }
467
468 SpinAnimation::~SpinAnimation ()
469 {
470 }
471
472 void
473 SpinAnimation::update ()
474 {
475   double sim_time = globals->get_sim_time_sec();
476   double dt = sim_time - _last_time_sec;
477   _last_time_sec = sim_time;
478
479   float velocity_rpms = (_prop->getDoubleValue() * _factor / 60.0);
480   _position_deg += (dt * velocity_rpms * 360);
481   while (_position_deg < 0)
482     _position_deg += 360.0;
483   while (_position_deg >= 360.0)
484     _position_deg -= 360.0;
485   set_rotation(_matrix, _position_deg, _center, _axis);
486   ((ssgTransform *)_branch)->setTransform(_matrix);
487 }
488
489
490 \f
491 ////////////////////////////////////////////////////////////////////////
492 // Implementation of TimedAnimation
493 ////////////////////////////////////////////////////////////////////////
494
495 TimedAnimation::TimedAnimation (SGPropertyNode_ptr props)
496   : Animation(props, new ssgSelector),
497     _duration_sec(props->getDoubleValue("duration-sec", 1.0)),
498     _last_time_sec(0),
499     _step(-1)
500 {
501 }
502
503 TimedAnimation::~TimedAnimation ()
504 {
505 }
506
507 void
508 TimedAnimation::update ()
509 {
510     float sim_time_sec = globals->get_sim_time_sec();
511     if ((sim_time_sec - _last_time_sec) >= _duration_sec) {
512         _last_time_sec = sim_time_sec;
513         _step++;
514         if (_step >= getBranch()->getNumKids())
515             _step = 0;
516         ((ssgSelector *)getBranch())->selectStep(_step);
517     }
518 }
519
520
521 \f
522 ////////////////////////////////////////////////////////////////////////
523 // Implementation of RotateAnimation
524 ////////////////////////////////////////////////////////////////////////
525
526 RotateAnimation::RotateAnimation (SGPropertyNode_ptr props)
527     : Animation(props, new ssgTransform),
528       _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
529       _offset_deg(props->getDoubleValue("offset-deg", 0.0)),
530       _factor(props->getDoubleValue("factor", 1.0)),
531       _table(read_interpolation_table(props)),
532       _has_min(props->hasValue("min-deg")),
533       _min_deg(props->getDoubleValue("min-deg")),
534       _has_max(props->hasValue("max-deg")),
535       _max_deg(props->getDoubleValue("max-deg")),
536       _position_deg(props->getDoubleValue("starting-position-deg", 0))
537 {
538   _center[0] = props->getFloatValue("center/x-m", 0);
539   _center[1] = props->getFloatValue("center/y-m", 0);
540   _center[2] = props->getFloatValue("center/z-m", 0);
541   _axis[0] = props->getFloatValue("axis/x", 0);
542   _axis[1] = props->getFloatValue("axis/y", 0);
543   _axis[2] = props->getFloatValue("axis/z", 0);
544   sgNormalizeVec3(_axis);
545 }
546
547 RotateAnimation::~RotateAnimation ()
548 {
549   delete _table;
550 }
551
552 void
553 RotateAnimation::update ()
554 {
555   if (_table == 0) {
556    _position_deg = _prop->getDoubleValue() * _factor + _offset_deg;
557    if (_has_min && _position_deg < _min_deg)
558      _position_deg = _min_deg;
559    if (_has_max && _position_deg > _max_deg)
560      _position_deg = _max_deg;
561   } else {
562     _position_deg = _table->interpolate(_prop->getDoubleValue());
563   }
564   set_rotation(_matrix, _position_deg, _center, _axis);
565   ((ssgTransform *)_branch)->setTransform(_matrix);
566 }
567
568
569 \f
570 ////////////////////////////////////////////////////////////////////////
571 // Implementation of TranslateAnimation
572 ////////////////////////////////////////////////////////////////////////
573
574 TranslateAnimation::TranslateAnimation (SGPropertyNode_ptr props)
575   : Animation(props, new ssgTransform),
576     _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
577     _offset_m(props->getDoubleValue("offset-m", 0.0)),
578     _factor(props->getDoubleValue("factor", 1.0)),
579     _table(read_interpolation_table(props)),
580     _has_min(props->hasValue("min-m")),
581     _min_m(props->getDoubleValue("min-m")),
582     _has_max(props->hasValue("max-m")),
583     _max_m(props->getDoubleValue("max-m")),
584     _position_m(props->getDoubleValue("starting-position-m", 0))
585 {
586   _axis[0] = props->getFloatValue("axis/x", 0);
587   _axis[1] = props->getFloatValue("axis/y", 0);
588   _axis[2] = props->getFloatValue("axis/z", 0);
589   sgNormalizeVec3(_axis);
590 }
591
592 TranslateAnimation::~TranslateAnimation ()
593 {
594   delete _table;
595 }
596
597 void
598 TranslateAnimation::update ()
599 {
600   if (_table == 0) {
601     _position_m = (_prop->getDoubleValue() + _offset_m) * _factor;
602     if (_has_min && _position_m < _min_m)
603       _position_m = _min_m;
604     if (_has_max && _position_m > _max_m)
605       _position_m = _max_m;
606   } else {
607     _position_m = _table->interpolate(_prop->getDoubleValue());
608   }
609   set_translation(_matrix, _position_m, _axis);
610   ((ssgTransform *)_branch)->setTransform(_matrix);
611 }
612
613
614 \f
615 ////////////////////////////////////////////////////////////////////////
616 // Implementation of FGModelPlacement.
617 ////////////////////////////////////////////////////////////////////////
618
619 FGModelPlacement::FGModelPlacement ()
620   : _lon_deg(0),
621     _lat_deg(0),
622     _elev_ft(0),
623     _roll_deg(0),
624     _pitch_deg(0),
625     _heading_deg(0),
626     _selector(new ssgSelector),
627     _position(new ssgTransform),
628     _location(new FGLocation)
629 {
630 }
631
632 FGModelPlacement::~FGModelPlacement ()
633 {
634 }
635
636 void
637 FGModelPlacement::init (const string &path)
638 {
639   ssgBranch * model = fgLoad3DModel(path);
640   if (model != 0)
641       _position->addKid(model);
642   _selector->addKid(_position);
643   _selector->clrTraversalMaskBits(SSGTRAV_HOT);
644 }
645
646 void
647 FGModelPlacement::update ()
648 {
649   _location->setPosition( _lon_deg, _lat_deg, _elev_ft );
650   _location->setOrientation( _roll_deg, _pitch_deg, _heading_deg );
651
652   sgMat4 POS;
653   sgCopyMat4(POS, _location->getTransformMatrix());
654   
655   sgVec3 trans;
656   sgCopyVec3(trans, _location->get_view_pos());
657
658   for(int i = 0; i < 4; i++) {
659     float tmp = POS[i][3];
660     for( int j=0; j<3; j++ ) {
661       POS[i][j] += (tmp * trans[j]);
662     }
663   }
664   _position->setTransform(POS);
665 }
666
667 bool
668 FGModelPlacement::getVisible () const
669 {
670   return (_selector->getSelect() != 0);
671 }
672
673 void
674 FGModelPlacement::setVisible (bool visible)
675 {
676   _selector->select(visible);
677 }
678
679 void
680 FGModelPlacement::setLongitudeDeg (double lon_deg)
681 {
682   _lon_deg = lon_deg;
683 }
684
685 void
686 FGModelPlacement::setLatitudeDeg (double lat_deg)
687 {
688   _lat_deg = lat_deg;
689 }
690
691 void
692 FGModelPlacement::setElevationFt (double elev_ft)
693 {
694   _elev_ft = elev_ft;
695 }
696
697 void
698 FGModelPlacement::setPosition (double lon_deg, double lat_deg, double elev_ft)
699 {
700   _lon_deg = lon_deg;
701   _lat_deg = lat_deg;
702   _elev_ft = elev_ft;
703 }
704
705 void
706 FGModelPlacement::setRollDeg (double roll_deg)
707 {
708   _roll_deg = roll_deg;
709 }
710
711 void
712 FGModelPlacement::setPitchDeg (double pitch_deg)
713 {
714   _pitch_deg = pitch_deg;
715 }
716
717 void
718 FGModelPlacement::setHeadingDeg (double heading_deg)
719 {
720   _heading_deg = heading_deg;
721 }
722
723 void
724 FGModelPlacement::setOrientation (double roll_deg, double pitch_deg,
725                                   double heading_deg)
726 {
727   _roll_deg = roll_deg;
728   _pitch_deg = pitch_deg;
729   _heading_deg = heading_deg;
730 }
731
732 // end of model.cxx