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