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