]> git.mxchange.org Git - flightgear.git/blob - src/Model/model.cxx
Check point commit:
[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 (const SGPropertyNode * props)
160 {
161   const SGPropertyNode * 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 * object_name,
178                 SGPropertyNode * node)
179 {
180   Animation * animation = 0;
181   const char * type = node->getStringValue("type");
182   if (!strcmp("none", type)) {
183     animation = new NullAnimation(node);
184   } else if (!strcmp("range", type)) {
185     animation = new RangeAnimation(node);
186   } else if (!strcmp("billboard", type)) {
187     animation = new BillboardAnimation(node);
188   } else if (!strcmp("select", type)) {
189     animation = new SelectAnimation(node);
190   } else if (!strcmp("spin", type)) {
191     animation = new SpinAnimation(node);
192   } else if (!strcmp("rotate", type)) {
193     animation = new RotateAnimation(node);
194   } else if (!strcmp("translate", type)) {
195     animation = new TranslateAnimation(node);
196   } else {
197     animation = new NullAnimation(node);
198     SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
199   }
200
201   ssgEntity * object;
202   if (object_name != 0) {
203     object = find_named_node(model, object_name);
204     if (object == 0) {
205       SG_LOG(SG_INPUT, SG_WARN, "Object " << object_name << " not found");
206       delete animation;
207       animation = 0;
208     }
209   } else {
210     object = model;
211   }
212   
213   ssgBranch * branch = animation->getBranch();
214   splice_branch(branch, object);
215   branch->setUserData(animation);
216   branch->setTravCallback(SSG_CALLBACK_PRETRAV, animation_callback);
217 }
218
219
220 \f
221 ////////////////////////////////////////////////////////////////////////
222 // Global functions.
223 ////////////////////////////////////////////////////////////////////////
224
225 ssgBranch *
226 fgLoad3DModel (const string &path)
227 {
228   ssgBranch * model = 0;
229   SGPropertyNode props;
230
231                                 // Load the 3D aircraft object itself
232   SGPath xmlpath = globals->get_fg_root();
233   SGPath modelpath = path;
234   xmlpath.append(modelpath.str());
235
236                                 // Check for an XML wrapper
237   if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
238     readProperties(xmlpath.str(), &props);
239     if (props.hasValue("/path")) {
240       modelpath = modelpath.dir();
241       modelpath.append(props.getStringValue("/path"));
242     } else {
243       if (model == 0)
244         model = new ssgBranch;
245     }
246   }
247
248                                 // Assume that textures are in
249                                 // the same location as the XML file.
250   if (model == 0) {
251     ssgTexturePath((char *)xmlpath.dir().c_str());
252     model = (ssgBranch *)ssgLoad((char *)modelpath.c_str());
253     if (model == 0)
254       throw sg_exception("Failed to load 3D model");
255   }
256
257                                 // Set up the alignment node
258   ssgTransform * align = new ssgTransform;
259   align->addKid(model);
260   sgMat4 res_matrix;
261   make_offsets_matrix(&res_matrix,
262                       props.getFloatValue("/offsets/heading-deg", 0.0),
263                       props.getFloatValue("/offsets/roll-deg", 0.0),
264                       props.getFloatValue("/offsets/pitch-deg", 0.0),
265                       props.getFloatValue("/offsets/x-m", 0.0),
266                       props.getFloatValue("/offsets/y-m", 0.0),
267                       props.getFloatValue("/offsets/z-m", 0.0));
268   align->setTransform(res_matrix);
269
270                                 // Load animations
271   vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
272   unsigned int i;
273   for (i = 0; i < animation_nodes.size(); i++) {
274     vector<SGPropertyNode_ptr> name_nodes =
275       animation_nodes[i]->getChildren("object-name");
276     if (name_nodes.size() < 1) {
277         make_animation(model, 0, animation_nodes[i]);
278     } else {
279       for (unsigned int j = 0; j < name_nodes.size(); j++) {
280         make_animation(model, name_nodes[j]->getStringValue(), animation_nodes[i]);
281       }
282     }
283   }
284
285                                 // Load panels
286   vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
287   for (i = 0; i < panel_nodes.size(); i++) {
288     printf("Reading a panel in model.cxx\n");
289     FGPanelNode * panel = new FGPanelNode(panel_nodes[i]);
290     model->addKid(panel);
291   }
292
293                                 // Load sub-models
294   vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
295   for (i = 0; i < model_nodes.size(); i++) {
296     SGPropertyNode_ptr node = model_nodes[i];
297     ssgTransform * align = new ssgTransform;
298     sgMat4 res_matrix;
299     make_offsets_matrix(&res_matrix,
300                         node->getFloatValue("offsets/heading-deg", 0.0),
301                         node->getFloatValue("offsets/roll-deg", 0.0),
302                         node->getFloatValue("offsets/pitch-deg", 0.0),
303                         node->getFloatValue("offsets/x-m", 0.0),
304                         node->getFloatValue("offsets/y-m", 0.0),
305                         node->getFloatValue("offsets/z-m", 0.0));
306     align->setTransform(res_matrix);
307
308     ssgBranch * kid = fgLoad3DModel(node->getStringValue("path"));
309     align->addKid(kid);
310     model->addKid(align);
311   }
312
313   return model;
314 }
315
316
317 \f
318 ////////////////////////////////////////////////////////////////////////
319 // Implementation of Animation
320 ////////////////////////////////////////////////////////////////////////
321
322 Animation::Animation (SGPropertyNode_ptr props, ssgBranch * branch)
323     : _branch(branch)
324 {
325     _branch->setName(props->getStringValue("name", 0));
326 }
327
328 Animation::~Animation ()
329 {
330 }
331
332
333 \f
334 ////////////////////////////////////////////////////////////////////////
335 // Implementation of NullAnimation
336 ////////////////////////////////////////////////////////////////////////
337
338 NullAnimation::NullAnimation (SGPropertyNode_ptr props)
339   : Animation(props, new ssgBranch)
340 {
341 }
342
343 NullAnimation::~NullAnimation ()
344 {
345 }
346
347 void
348 NullAnimation::update ()
349 {
350 }
351
352
353 \f
354 ////////////////////////////////////////////////////////////////////////
355 // Implementation of RangeAnimation
356 ////////////////////////////////////////////////////////////////////////
357
358 RangeAnimation::RangeAnimation (SGPropertyNode_ptr props)
359   : Animation(props, new ssgRangeSelector)
360 {
361     float ranges[] = { props->getFloatValue("min-m", 0),
362                        props->getFloatValue("max-m", 5000) };
363     ((ssgRangeSelector *)_branch)->setRanges(ranges, 2);
364                        
365 }
366
367 RangeAnimation::~RangeAnimation ()
368 {
369 }
370
371 void
372 RangeAnimation::update ()
373 {
374 }
375
376
377 \f
378 ////////////////////////////////////////////////////////////////////////
379 // Implementation of BillboardAnimation
380 ////////////////////////////////////////////////////////////////////////
381
382 BillboardAnimation::BillboardAnimation (SGPropertyNode_ptr props)
383     : Animation(props, new ssgCutout(props->getBoolValue("spherical", true)))
384 {
385 }
386
387 BillboardAnimation::~BillboardAnimation ()
388 {
389 }
390
391 void
392 BillboardAnimation::update ()
393 {
394 }
395
396
397 \f
398 ////////////////////////////////////////////////////////////////////////
399 // Implementation of SelectAnimation
400 ////////////////////////////////////////////////////////////////////////
401
402 SelectAnimation::SelectAnimation (SGPropertyNode_ptr props)
403   : Animation(props, new ssgSelector),
404     _condition(0)
405 {
406   SGPropertyNode * node = props->getChild("condition");
407   if (node != 0)
408     _condition = fgReadCondition(node);
409 }
410
411 SelectAnimation::~SelectAnimation ()
412 {
413   delete _condition;
414 }
415
416 void
417 SelectAnimation::update ()
418 {
419   if (_condition != 0 && _condition->test()) 
420       ((ssgSelector *)_branch)->select(0xffff);
421   else
422       ((ssgSelector *)_branch)->select(0x0000);
423 }
424
425
426 \f
427 ////////////////////////////////////////////////////////////////////////
428 // Implementation of SpinAnimation
429 ////////////////////////////////////////////////////////////////////////
430
431 SpinAnimation::SpinAnimation (SGPropertyNode_ptr props)
432   : Animation(props, new ssgTransform),
433     _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
434     _factor(props->getDoubleValue("factor", 1.0)),
435     _position_deg(props->getDoubleValue("starting-position-deg", 0)),
436     _last_time_sec(globals->get_sim_time_sec())
437 {
438     _center[0] = props->getFloatValue("center/x-m", 0);
439     _center[1] = props->getFloatValue("center/y-m", 0);
440     _center[2] = props->getFloatValue("center/z-m", 0);
441     _axis[0] = props->getFloatValue("axis/x", 0);
442     _axis[1] = props->getFloatValue("axis/y", 0);
443     _axis[2] = props->getFloatValue("axis/z", 0);
444     sgNormalizeVec3(_axis);
445 }
446
447 SpinAnimation::~SpinAnimation ()
448 {
449 }
450
451 void
452 SpinAnimation::update ()
453 {
454   double sim_time = globals->get_sim_time_sec();
455   double dt = sim_time - _last_time_sec;
456   _last_time_sec = sim_time;
457
458   float velocity_rpms = (_prop->getDoubleValue() * _factor / 60.0);
459   _position_deg += (dt * velocity_rpms * 360);
460   while (_position_deg < 0)
461     _position_deg += 360.0;
462   while (_position_deg >= 360.0)
463     _position_deg -= 360.0;
464   set_rotation(_matrix, _position_deg, _center, _axis);
465   ((ssgTransform *)_branch)->setTransform(_matrix);
466 }
467
468
469 \f
470 ////////////////////////////////////////////////////////////////////////
471 // Implementation of RotateAnimation
472 ////////////////////////////////////////////////////////////////////////
473
474 RotateAnimation::RotateAnimation (SGPropertyNode_ptr props)
475     : Animation(props, new ssgTransform),
476       _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
477       _offset_deg(props->getDoubleValue("offset-deg", 0.0)),
478       _factor(props->getDoubleValue("factor", 1.0)),
479       _table(read_interpolation_table(props)),
480       _has_min(props->hasValue("min-deg")),
481       _min_deg(props->getDoubleValue("min-deg")),
482       _has_max(props->hasValue("max-deg")),
483       _max_deg(props->getDoubleValue("max-deg")),
484       _position_deg(props->getDoubleValue("starting-position-deg", 0))
485 {
486   _center[0] = props->getFloatValue("center/x-m", 0);
487   _center[1] = props->getFloatValue("center/y-m", 0);
488   _center[2] = props->getFloatValue("center/z-m", 0);
489   _axis[0] = props->getFloatValue("axis/x", 0);
490   _axis[1] = props->getFloatValue("axis/y", 0);
491   _axis[2] = props->getFloatValue("axis/z", 0);
492   sgNormalizeVec3(_axis);
493 }
494
495 RotateAnimation::~RotateAnimation ()
496 {
497   delete _table;
498 }
499
500 void
501 RotateAnimation::update ()
502 {
503   if (_table == 0) {
504     _position_deg = (_prop->getDoubleValue() + _offset_deg) * _factor;
505    if (_has_min && _position_deg < _min_deg)
506      _position_deg = _min_deg;
507    if (_has_max && _position_deg > _max_deg)
508      _position_deg = _max_deg;
509   } else {
510     _position_deg = _table->interpolate(_prop->getDoubleValue());
511   }
512   set_rotation(_matrix, _position_deg, _center, _axis);
513   ((ssgTransform *)_branch)->setTransform(_matrix);
514 }
515
516
517 \f
518 ////////////////////////////////////////////////////////////////////////
519 // Implementation of TranslateAnimation
520 ////////////////////////////////////////////////////////////////////////
521
522 TranslateAnimation::TranslateAnimation (SGPropertyNode_ptr props)
523   : Animation(props, new ssgTransform),
524     _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
525     _offset_m(props->getDoubleValue("offset-m", 0.0)),
526     _factor(props->getDoubleValue("factor", 1.0)),
527     _table(read_interpolation_table(props)),
528     _has_min(props->hasValue("min-m")),
529     _min_m(props->getDoubleValue("min-m")),
530     _has_max(props->hasValue("max-m")),
531     _max_m(props->getDoubleValue("max-m")),
532     _position_m(props->getDoubleValue("starting-position-m", 0))
533 {
534   _axis[0] = props->getFloatValue("axis/x", 0);
535   _axis[1] = props->getFloatValue("axis/y", 0);
536   _axis[2] = props->getFloatValue("axis/z", 0);
537   sgNormalizeVec3(_axis);
538 }
539
540 TranslateAnimation::~TranslateAnimation ()
541 {
542   delete _table;
543 }
544
545 void
546 TranslateAnimation::update ()
547 {
548   if (_table == 0) {
549     _position_m = (_prop->getDoubleValue() + _offset_m) * _factor;
550     if (_has_min && _position_m < _min_m)
551       _position_m = _min_m;
552     if (_has_max && _position_m > _max_m)
553       _position_m = _max_m;
554   } else {
555     _position_m = _table->interpolate(_prop->getDoubleValue());
556   }
557   set_translation(_matrix, _position_m, _axis);
558   ((ssgTransform *)_branch)->setTransform(_matrix);
559 }
560
561
562 \f
563 ////////////////////////////////////////////////////////////////////////
564 // Implementation of FGModelPlacement.
565 ////////////////////////////////////////////////////////////////////////
566
567 FGModelPlacement::FGModelPlacement ()
568   : _lon_deg(0),
569     _lat_deg(0),
570     _elev_ft(0),
571     _roll_deg(0),
572     _pitch_deg(0),
573     _heading_deg(0),
574     _selector(new ssgSelector),
575     _position(new ssgTransform),
576     _location(new FGLocation)
577 {
578 }
579
580 FGModelPlacement::~FGModelPlacement ()
581 {
582 }
583
584 void
585 FGModelPlacement::init (const string &path)
586 {
587   ssgBranch * model = fgLoad3DModel(path);
588   if (model != 0)
589       _position->addKid(model);
590   _selector->addKid(_position);
591   _selector->clrTraversalMaskBits(SSGTRAV_HOT);
592 }
593
594 void
595 FGModelPlacement::update ()
596 {
597   _location->setPosition( _lon_deg, _lat_deg, _elev_ft );
598   _location->setOrientation( _roll_deg, _pitch_deg, _heading_deg );
599
600   sgMat4 POS;
601   sgCopyMat4(POS, _location->getTransformMatrix());
602   
603   sgVec3 trans;
604   sgCopyVec3(trans, _location->get_view_pos());
605
606   for(int i = 0; i < 4; i++) {
607     float tmp = POS[i][3];
608     for( int j=0; j<3; j++ ) {
609       POS[i][j] += (tmp * trans[j]);
610     }
611   }
612   _position->setTransform(POS);
613 }
614
615 bool
616 FGModelPlacement::getVisible () const
617 {
618   return (_selector->getSelect() != 0);
619 }
620
621 void
622 FGModelPlacement::setVisible (bool visible)
623 {
624   _selector->select(visible);
625 }
626
627 void
628 FGModelPlacement::setLongitudeDeg (double lon_deg)
629 {
630   _lon_deg = lon_deg;
631 }
632
633 void
634 FGModelPlacement::setLatitudeDeg (double lat_deg)
635 {
636   _lat_deg = lat_deg;
637 }
638
639 void
640 FGModelPlacement::setElevationFt (double elev_ft)
641 {
642   _elev_ft = elev_ft;
643 }
644
645 void
646 FGModelPlacement::setPosition (double lon_deg, double lat_deg, double elev_ft)
647 {
648   _lon_deg = lon_deg;
649   _lat_deg = lat_deg;
650   _elev_ft = elev_ft;
651 }
652
653 void
654 FGModelPlacement::setRollDeg (double roll_deg)
655 {
656   _roll_deg = roll_deg;
657 }
658
659 void
660 FGModelPlacement::setPitchDeg (double pitch_deg)
661 {
662   _pitch_deg = pitch_deg;
663 }
664
665 void
666 FGModelPlacement::setHeadingDeg (double heading_deg)
667 {
668   _heading_deg = heading_deg;
669 }
670
671 void
672 FGModelPlacement::setOrientation (double roll_deg, double pitch_deg,
673                                   double heading_deg)
674 {
675   _roll_deg = roll_deg;
676   _pitch_deg = pitch_deg;
677   _heading_deg = heading_deg;
678 }
679
680 // end of model.cxx