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