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