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