]> git.mxchange.org Git - flightgear.git/blob - src/Model/model.cxx
Patch from Jim Wilson:
[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  * make model transformation Matrix - based on optimizations by NHV
126  */
127 static void MakeTRANS( sgMat4 dst, const double Theta,
128                         const double Phi, const double Psi, 
129                         const double lon, const double lat)
130 {
131     SGfloat cosTheta = (SGfloat) cos(Theta);
132     SGfloat sinTheta = (SGfloat) sin(Theta);
133     SGfloat cosPhi   = (SGfloat) cos(Phi);
134     SGfloat sinPhi   = (SGfloat) sin(Phi);
135     SGfloat sinPsi   = (SGfloat) sin(Psi) ;
136     SGfloat cosPsi   = (SGfloat) cos(Psi) ;
137
138     SGfloat cosLon = SGD_ONE;
139     SGfloat sinLon = SGD_ZERO;
140     SGfloat cosLat = SGD_ONE;
141     SGfloat sinLat = SGD_ZERO;
142
143     if ( lon != SG_ZERO ) { 
144       sinLon = (SGfloat) sin( lon ) ;
145       cosLon = (SGfloat) cos( lon ) ;
146     }
147     if ( lat != SG_ZERO ) {
148       sinLat   = (SGfloat) sin( lat ) ;
149       cosLat   = (SGfloat) cos( lat ) ;
150     }
151
152
153     sgMat4 tmp;
154         
155     tmp[0][0] = cosPhi * cosTheta;
156     tmp[0][1] = sinPhi * cosPsi + cosPhi * -sinTheta * -sinPsi;
157     tmp[0][2] = sinPhi * sinPsi + cosPhi * -sinTheta * cosPsi;
158
159     tmp[1][0] = -sinPhi * cosTheta;
160     tmp[1][1] = cosPhi * cosPsi + -sinPhi * -sinTheta * -sinPsi;
161     tmp[1][2] = cosPhi * sinPsi + -sinPhi * -sinTheta * cosPsi;
162         
163     tmp[2][0] = sinTheta;
164     tmp[2][1] = cosTheta * -sinPsi;
165     tmp[2][2] = cosTheta * cosPsi;
166         
167     float a = cosLon * cosLat;  // world up [0]
168     float b = -sinLon;
169     float c = sinLat * cosLon;
170     dst[2][0] = a*tmp[0][0] + b*tmp[0][1] + c*tmp[0][2] ;
171     dst[1][0] = a*tmp[1][0] + b*tmp[1][1] + c*tmp[1][2] ;
172     dst[0][0] = -(a*tmp[2][0] + b*tmp[2][1] + c*tmp[2][2]) ;
173     dst[3][0] = SG_ZERO ;
174
175     a = cosLat * sinLon;  // world up [1]
176     b = cosLon;
177     c = sinLat * sinLon;
178     dst[2][1] = a*tmp[0][0] + b*tmp[0][1] + c*tmp[0][2] ;
179     dst[1][1] = a*tmp[1][0] + b*tmp[1][1] + c*tmp[1][2] ;
180     dst[0][1] = -(a*tmp[2][0] + b*tmp[2][1] + c*tmp[2][2]) ;
181     dst[3][1] = SG_ZERO ;
182
183     a = -sinLat;  // world up [2]
184     c = cosLat;
185     dst[2][2] = a*tmp[0][0] + c*tmp[0][2] ;
186     dst[1][2] = a*tmp[1][0] + c*tmp[1][2] ;
187     dst[0][2] = -(a*tmp[2][0] + c*tmp[2][2]) ;
188     dst[3][2] = SG_ZERO ;
189
190     dst[2][3] = SG_ZERO ;
191     dst[1][3] = SG_ZERO ;
192     dst[0][3] = SG_ZERO ;
193     dst[3][3] = SG_ONE ;
194
195 }
196
197
198 // TODO: once this is working, look at Norm's optimized version
199 static void
200 world_coordinate( sgCoord *obj_pos,
201                   double lat_deg, double lon_deg, double elev_ft,
202                   double roll_deg, double pitch_deg, double hdg_deg)
203 {
204   // setup translation
205   double sea_level_radius_m;
206   double lat_geoc_rad;
207
208   // Convert from geodetic to geocentric
209   // coordinates.
210   sgGeodToGeoc(lat_deg * SGD_DEGREES_TO_RADIANS,
211                elev_ft * SG_FEET_TO_METER,
212                &sea_level_radius_m,
213                &lat_geoc_rad);
214
215   Point3D center = scenery.get_center();
216
217   Point3D geod = Point3D(lon_deg * SG_DEGREES_TO_RADIANS,
218                          lat_geoc_rad,
219                          sea_level_radius_m);
220   geod.setz(geod.radius() + elev_ft * SG_FEET_TO_METER);
221         
222   Point3D world_pos = sgPolarToCart3d( geod );
223   Point3D offset = world_pos - center;
224         
225   sgMat4 POS;
226   sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
227
228   sgMat4 TRANS;
229
230   MakeTRANS( TRANS, pitch_deg * SG_DEGREES_TO_RADIANS, 
231                     roll_deg * SG_DEGREES_TO_RADIANS,
232                     -hdg_deg * SG_DEGREES_TO_RADIANS, 
233                     lon_deg * SG_DEGREES_TO_RADIANS, 
234                     -lat_deg * SG_DEGREES_TO_RADIANS );
235
236   sgPostMultMat4( TRANS, POS );
237
238   sgSetCoord( obj_pos, TRANS );
239 }
240
241
242 \f
243 ////////////////////////////////////////////////////////////////////////
244 // Implementation of FG3DModel
245 ////////////////////////////////////////////////////////////////////////
246
247 FG3DModel::FG3DModel ()
248   : _model(0),
249     _selector(new ssgSelector),
250     _position(new ssgTransform)
251 {
252 }
253
254 FG3DModel::~FG3DModel ()
255 {
256   // since the nodes are attached to the scene graph, they'll be
257   // deleted automatically
258
259   for (int i = 0; i < _animations.size(); i++) {
260     Animation * tmp = _animations[i];
261     _animations[i] = 0;
262     delete tmp;
263   }
264
265 }
266
267 void 
268 FG3DModel::init (const string &path)
269 {
270   SGPropertyNode props;
271
272                                 // Load the 3D aircraft object itself
273   SGPath xmlpath = globals->get_fg_root();
274   SGPath modelpath = path;
275   xmlpath.append(modelpath.str());
276
277                                 // Check for an XML wrapper
278   if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
279     readProperties(xmlpath.str(), &props);
280     if (props.hasValue("/path")) {
281       modelpath = modelpath.dir();
282       modelpath.append(props.getStringValue("/path"));
283     } else {
284       throw sg_exception("No path for model");
285     }
286   }
287
288                                 // Assume that textures are in
289                                 // the same location as the XML file.
290   ssgTexturePath((char *)xmlpath.dir().c_str());
291   _model = ssgLoad((char *)modelpath.c_str());
292   if (_model == 0)
293     throw sg_exception("Failed to load 3D model");
294
295                                 // Load animations
296   vector<SGPropertyNode *> animation_nodes = props.getChildren("animation");
297   for (unsigned int i = 0; i < animation_nodes.size(); i++) {
298     vector<SGPropertyNode *> name_nodes =
299       animation_nodes[i]->getChildren("object-name");
300     if (name_nodes.size() < 1) {
301       SG_LOG(SG_INPUT, SG_ALERT, "No object-name given for transformation");
302     } else {
303       for (unsigned int j = 0; j < name_nodes.size(); j++) {
304         Animation * animation =
305           make_animation(name_nodes[j]->getStringValue(), animation_nodes[i]);
306         if (animation != 0)
307           _animations.push_back(animation);
308       }
309     }
310   }
311
312                                 // Set up the alignment node
313   ssgTransform * align = new ssgTransform;
314   align->addKid(_model);
315   sgMat4 rot_matrix;
316   sgMat4 off_matrix;
317   sgMat4 res_matrix;
318   float h_rot = props.getFloatValue("/offsets/heading-deg", 0.0);
319   float p_rot = props.getFloatValue("/offsets/roll-deg", 0.0);
320   float r_rot = props.getFloatValue("/offsets/pitch-deg", 0.0);
321   float x_off = props.getFloatValue("/offsets/x-m", 0.0);
322   float y_off = props.getFloatValue("/offsets/y-m", 0.0);
323   float z_off = props.getFloatValue("/offsets/z-m", 0.0);
324   sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
325   sgMakeTransMat4(off_matrix, x_off, y_off, z_off);
326   sgMultMat4(res_matrix, off_matrix, rot_matrix);
327   align->setTransform(res_matrix);
328
329                                 // Set up the position node
330   _position->addKid(align);
331
332                                 // Set up the selector node
333   _selector->addKid(_position);
334   _selector->clrTraversalMaskBits(SSGTRAV_HOT);
335 }
336
337 void
338 FG3DModel::update (int dt)
339 {
340   for (unsigned int i = 0; i < _animations.size(); i++)
341     _animations[i]->update(dt);
342
343   _selector->select(true);
344
345   sgCoord obj_pos;
346   world_coordinate(&obj_pos, _lat_deg, _lon_deg, _elev_ft,
347                    _roll_deg, _pitch_deg, _heading_deg);
348   _position->setTransform(&obj_pos);
349 }
350
351 bool
352 FG3DModel::getVisible () const
353 {
354   return _selector->getSelect();
355 }
356
357 void
358 FG3DModel::setVisible (bool visible)
359 {
360   _selector->select(visible);
361 }
362
363 void
364 FG3DModel::setPosition (double lon_deg, double lat_deg, double elev_ft)
365 {
366   _lon_deg = lon_deg;
367   _lat_deg = lat_deg;
368   _elev_ft = elev_ft;
369 }
370
371 void
372 FG3DModel::setOrientation (double roll_deg, double pitch_deg,
373                            double heading_deg)
374 {
375   _roll_deg = roll_deg;
376   _pitch_deg = pitch_deg;
377   _heading_deg = heading_deg;
378 }
379
380 FG3DModel::Animation *
381 FG3DModel::make_animation (const char * object_name,
382                                  SGPropertyNode * node)
383 {
384   Animation * animation = 0;
385   const char * type = node->getStringValue("type");
386   if (!strcmp("none", type)) {
387     animation = new NullAnimation();
388   } else if (!strcmp("select", type)) {
389     animation = new SelectAnimation();
390   } else if (!strcmp("spin", type)) {
391     animation = new SpinAnimation();
392   } else if (!strcmp("rotate", type)) {
393     animation = new RotateAnimation();
394   } else if (!strcmp("translate", type)) {
395     animation = new TranslateAnimation();
396   } else {
397     animation = new NullAnimation();
398     SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
399   }
400
401   ssgEntity * object = find_named_node(_model, object_name);
402   if (object == 0) {
403     SG_LOG(SG_INPUT, SG_WARN, "Object " << object_name << " not found");
404     delete animation;
405     animation = 0;
406   } else {
407     animation->init(object, node);
408   }
409
410   return animation;
411 }
412
413
414 \f
415 ////////////////////////////////////////////////////////////////////////
416 // Implementation of FG3DModel::Animation
417 ////////////////////////////////////////////////////////////////////////
418
419 FG3DModel::Animation::Animation ()
420 {
421 }
422
423 FG3DModel::Animation::~Animation ()
424 {
425 }
426
427
428 \f
429 ////////////////////////////////////////////////////////////////////////
430 // Implementation of FG3DModel::NullAnimation
431 ////////////////////////////////////////////////////////////////////////
432
433 FG3DModel::NullAnimation::NullAnimation ()
434   : _branch(new ssgBranch)
435 {
436 }
437
438 FG3DModel::NullAnimation::~NullAnimation ()
439 {
440   _branch = 0;
441 }
442
443 void
444 FG3DModel::NullAnimation::init (ssgEntity * object,
445                                       SGPropertyNode * props)
446 {
447   splice_branch(_branch, object);
448   _branch->setName(props->getStringValue("name", 0));
449 }
450
451 void
452 FG3DModel::NullAnimation::update (int dt)
453 {
454 }
455
456
457 \f
458 ////////////////////////////////////////////////////////////////////////
459 // Implementation of FG3DModel::SelectAnimation
460 ////////////////////////////////////////////////////////////////////////
461
462 FG3DModel::SelectAnimation::SelectAnimation ()
463   : _condition(0),
464     _selector(new ssgSelector)
465 {
466 }
467
468 FG3DModel::SelectAnimation::~SelectAnimation ()
469 {
470   delete _condition;
471   _selector = 0;
472 }
473
474 void
475 FG3DModel::SelectAnimation::init (ssgEntity * object,
476                                       SGPropertyNode * props)
477 {
478   splice_branch(_selector, object);
479   _selector->setName(props->getStringValue("name", 0));
480   SGPropertyNode * node = props->getChild("condition");
481   if (node != 0) {
482     _condition = fgReadCondition(node);
483   }
484 }
485
486 void
487 FG3DModel::SelectAnimation::update (int dt)
488 {
489   if (_condition != 0 && _condition->test()) 
490     _selector->select(0xffff);
491   else
492     _selector->select(0x0000);
493 }
494
495
496 \f
497 ////////////////////////////////////////////////////////////////////////
498 // Implementation of FG3DModel::SpinAnimation
499 ////////////////////////////////////////////////////////////////////////
500
501 FG3DModel::SpinAnimation::SpinAnimation ()
502   : _prop(0),
503     _factor(0),
504     _position_deg(0),
505     _transform(new ssgTransform)
506 {
507 }
508
509 FG3DModel::SpinAnimation::~SpinAnimation ()
510 {
511   _transform = 0;
512 }
513
514 void
515 FG3DModel::SpinAnimation::init (ssgEntity * object,
516                                       SGPropertyNode * props)
517 {
518                                 // Splice in the new transform node
519   splice_branch(_transform, object);
520   _transform->setName(props->getStringValue("name", 0));
521   _prop = fgGetNode(props->getStringValue("property", "/null"), true);
522   _factor = props->getDoubleValue("factor", 1.0);
523   _position_deg = props->getDoubleValue("starting-position-deg", 0);
524   _center[0] = props->getFloatValue("center/x-m", 0);
525   _center[1] = props->getFloatValue("center/y-m", 0);
526   _center[2] = props->getFloatValue("center/z-m", 0);
527   _axis[0] = props->getFloatValue("axis/x", 0);
528   _axis[1] = props->getFloatValue("axis/y", 0);
529   _axis[2] = props->getFloatValue("axis/z", 0);
530   sgNormalizeVec3(_axis);
531 }
532
533 void
534 FG3DModel::SpinAnimation::update (int dt)
535 {
536   float velocity_rpms = (_prop->getDoubleValue() * _factor / 60000.0);
537   _position_deg += (dt * velocity_rpms * 360);
538   while (_position_deg < 0)
539     _position_deg += 360.0;
540   while (_position_deg >= 360.0)
541     _position_deg -= 360.0;
542   set_rotation(_matrix, _position_deg, _center, _axis);
543   _transform->setTransform(_matrix);
544 }
545
546
547 \f
548 ////////////////////////////////////////////////////////////////////////
549 // Implementation of FG3DModel::RotateAnimation
550 ////////////////////////////////////////////////////////////////////////
551
552 FG3DModel::RotateAnimation::RotateAnimation ()
553   : _prop(0),
554     _offset_deg(0.0),
555     _factor(1.0),
556     _has_min(false),
557     _min_deg(0.0),
558     _has_max(false),
559     _max_deg(1.0),
560     _position_deg(0.0),
561     _transform(new ssgTransform)
562 {
563 }
564
565 FG3DModel::RotateAnimation::~RotateAnimation ()
566 {
567   _transform = 0;
568 }
569
570 void
571 FG3DModel::RotateAnimation::init (ssgEntity * object,
572                                         SGPropertyNode * props)
573 {
574                                 // Splice in the new transform node
575   splice_branch(_transform, object);
576   _transform->setName(props->getStringValue("name", 0));
577   _prop = fgGetNode(props->getStringValue("property", "/null"), true);
578   _offset_deg = props->getDoubleValue("offset-deg", 0.0);
579   _factor = props->getDoubleValue("factor", 1.0);
580   if (props->hasValue("min-deg")) {
581     _has_min = true;
582     _min_deg = props->getDoubleValue("min-deg");
583   }
584   if (props->hasValue("max-deg")) {
585     _has_max = true;
586     _max_deg = props->getDoubleValue("max-deg");
587   }
588   _position_deg = props->getDoubleValue("starting-position-deg", 0);
589   _center[0] = props->getFloatValue("center/x-m", 0);
590   _center[1] = props->getFloatValue("center/y-m", 0);
591   _center[2] = props->getFloatValue("center/z-m", 0);
592   _axis[0] = props->getFloatValue("axis/x", 0);
593   _axis[1] = props->getFloatValue("axis/y", 0);
594   _axis[2] = props->getFloatValue("axis/z", 0);
595   sgNormalizeVec3(_axis);
596 }
597
598 void
599 FG3DModel::RotateAnimation::update (int dt)
600 {
601   _position_deg = ((_prop->getDoubleValue() + _offset_deg) * _factor);
602   if (_has_min && _position_deg < _min_deg)
603     _position_deg = _min_deg;
604   if (_has_max && _position_deg > _max_deg)
605     _position_deg = _max_deg;
606   set_rotation(_matrix, _position_deg, _center, _axis);
607   _transform->setTransform(_matrix);
608 }
609
610
611 \f
612 ////////////////////////////////////////////////////////////////////////
613 // Implementation of FG3DModel::TranslateAnimation
614 ////////////////////////////////////////////////////////////////////////
615
616 FG3DModel::TranslateAnimation::TranslateAnimation ()
617   : _prop(0),
618     _offset_m(0.0),
619     _factor(1.0),
620     _has_min(false),
621     _min_m(0.0),
622     _has_max(false),
623     _max_m(1.0),
624     _position_m(0.0),
625     _transform(new ssgTransform)
626 {
627 }
628
629 FG3DModel::TranslateAnimation::~TranslateAnimation ()
630 {
631   _transform = 0;
632 }
633
634 void
635 FG3DModel::TranslateAnimation::init (ssgEntity * object,
636                                         SGPropertyNode * props)
637 {
638                                 // Splice in the new transform node
639   splice_branch(_transform, object);
640   _transform->setName(props->getStringValue("name", 0));
641   _prop = fgGetNode(props->getStringValue("property", "/null"), true);
642   _offset_m = props->getDoubleValue("offset-m", 0.0);
643   _factor = props->getDoubleValue("factor", 1.0);
644   if (props->hasValue("min-m")) {
645     _has_min = true;
646     _min_m = props->getDoubleValue("min-m");
647   }
648   if (props->hasValue("max-m")) {
649     _has_max = true;
650     _max_m = props->getDoubleValue("max-m");
651   }
652   _position_m = props->getDoubleValue("starting-position-m", 0);
653   _axis[0] = props->getFloatValue("axis/x", 0);
654   _axis[1] = props->getFloatValue("axis/y", 0);
655   _axis[2] = props->getFloatValue("axis/z", 0);
656   sgNormalizeVec3(_axis);
657 }
658
659 void
660 FG3DModel::TranslateAnimation::update (int dt)
661 {
662   _position_m = ((_prop->getDoubleValue() + _offset_m) * _factor);
663   if (_has_min && _position_m < _min_m)
664     _position_m = _min_m;
665   if (_has_max && _position_m > _max_m)
666     _position_m = _max_m;
667   set_translation(_matrix, _position_m, _axis);
668   _transform->setTransform(_matrix);
669 }
670
671
672 // end of model.cxx
673
674