1 // model.cxx - manage a 3D aircraft model.
2 // Written by David Megginson, started 2002.
4 // This file is in the Public Domain, and comes with no warranty.
10 #include <string.h> // for strcmp()
16 #include <simgear/compiler.h>
17 #include <simgear/debug/logstream.hxx>
18 #include <simgear/math/interpolater.hxx>
19 #include <simgear/math/point3d.hxx>
20 #include <simgear/math/sg_geodesy.hxx>
21 #include <simgear/misc/exception.hxx>
22 #include <simgear/misc/sg_path.hxx>
23 #include <simgear/props/condition.hxx>
24 #include <simgear/props/props_io.hxx>
25 #include <simgear/scene/model/location.hxx>
27 // #include <Main/fg_props.hxx>
28 // #include <Main/globals.hxx>
29 // #include <Scenery/scenery.hxx>
32 #include "panelnode.hxx"
36 ////////////////////////////////////////////////////////////////////////
37 // Static utility functions.
38 ////////////////////////////////////////////////////////////////////////
41 * Callback to update an animation.
44 animation_callback (ssgEntity * entity, int mask)
46 ((Animation *)entity->getUserData())->update();
52 * Locate a named SSG node in a branch.
55 find_named_node (ssgEntity * node, const char * name)
57 char * node_name = node->getName();
58 if (node_name != 0 && !strcmp(name, node_name))
60 else if (node->isAKindOf(ssgTypeBranch())) {
61 int nKids = node->getNumKids();
62 for (int i = 0; i < nKids; i++) {
64 find_named_node(((ssgBranch*)node)->getKid(i), name);
73 * Splice a branch in between all child nodes and their parents.
76 splice_branch (ssgBranch * branch, ssgEntity * child)
78 int nParents = child->getNumParents();
79 branch->addKid(child);
80 for (int i = 0; i < nParents; i++) {
81 ssgBranch * parent = child->getParent(i);
82 parent->replaceKid(child, branch);
87 * Set up the transform matrix for a spin or rotation.
90 set_rotation (sgMat4 &matrix, double position_deg,
91 sgVec3 ¢er, sgVec3 &axis)
93 float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
95 float s = (float) sin ( temp_angle ) ;
96 float c = (float) cos ( temp_angle ) ;
97 float t = SG_ONE - c ;
99 // axis was normalized at load time
100 // hint to the compiler to put these into FP registers
105 matrix[0][0] = t * x * x + c ;
106 matrix[0][1] = t * y * x - s * z ;
107 matrix[0][2] = t * z * x + s * y ;
108 matrix[0][3] = SG_ZERO;
110 matrix[1][0] = t * x * y + s * z ;
111 matrix[1][1] = t * y * y + c ;
112 matrix[1][2] = t * z * y - s * x ;
113 matrix[1][3] = SG_ZERO;
115 matrix[2][0] = t * x * z - s * y ;
116 matrix[2][1] = t * y * z + s * x ;
117 matrix[2][2] = t * z * z + c ;
118 matrix[2][3] = SG_ZERO;
120 // hint to the compiler to put these into FP registers
125 matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
126 matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
127 matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
128 matrix[3][3] = SG_ONE;
132 * Set up the transform matrix for a translation.
135 set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis)
138 sgScaleVec3(xyz, axis, position_m);
139 sgMakeTransMat4(matrix, xyz);
144 * Make an offset matrix from rotations and position offset.
147 make_offsets_matrix (sgMat4 * result, double h_rot, double p_rot, double r_rot,
148 double x_off, double y_off, double z_off)
152 sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
153 sgMakeTransMat4(pos_matrix, x_off, y_off, z_off);
154 sgMultMat4(*result, pos_matrix, rot_matrix);
159 * Read an interpolation table from properties.
161 static SGInterpTable *
162 read_interpolation_table (SGPropertyNode_ptr props)
164 SGPropertyNode_ptr table_node = props->getNode("interpolation");
165 if (table_node != 0) {
166 SGInterpTable * table = new SGInterpTable();
167 vector<SGPropertyNode_ptr> entries = table_node->getChildren("entry");
168 for (unsigned int i = 0; i < entries.size(); i++)
169 table->addEntry(entries[i]->getDoubleValue("ind", 0.0),
170 entries[i]->getDoubleValue("dep", 0.0));
179 make_animation (ssgBranch * model,
181 vector<SGPropertyNode_ptr> &name_nodes,
182 SGPropertyNode *prop_root,
183 SGPropertyNode_ptr node,
184 double sim_time_sec )
186 Animation * animation = 0;
187 const char * type = node->getStringValue("type", "none");
188 if (!strcmp("none", type)) {
189 animation = new NullAnimation(node);
190 } else if (!strcmp("range", type)) {
191 animation = new RangeAnimation(node);
192 } else if (!strcmp("billboard", type)) {
193 animation = new BillboardAnimation(node);
194 } else if (!strcmp("select", type)) {
195 animation = new SelectAnimation(prop_root, node);
196 } else if (!strcmp("spin", type)) {
197 animation = new SpinAnimation(prop_root, node, sim_time_sec );
198 } else if (!strcmp("timed", type)) {
199 animation = new TimedAnimation(node);
200 } else if (!strcmp("rotate", type)) {
201 animation = new RotateAnimation(prop_root, node);
202 } else if (!strcmp("translate", type)) {
203 animation = new TranslateAnimation(prop_root, node);
205 animation = new NullAnimation(node);
206 SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
210 animation->setName((char *)name);
213 if (name_nodes.size() > 0) {
214 object = find_named_node(model, name_nodes[0]->getStringValue());
216 SG_LOG(SG_INPUT, SG_WARN, "Object " << name_nodes[0]->getStringValue()
225 ssgBranch * branch = animation->getBranch();
226 splice_branch(branch, object);
228 for (unsigned int i = 1; i < name_nodes.size(); i++) {
229 const char * name = name_nodes[i]->getStringValue();
230 object = find_named_node(model, name);
232 SG_LOG(SG_INPUT, SG_WARN, "Object " << name << " not found");
236 ssgBranch * oldParent = object->getParent(0);
237 branch->addKid(object);
238 oldParent->removeKid(object);
242 branch->setUserData(animation);
243 branch->setTravCallback(SSG_CALLBACK_PRETRAV, animation_callback);
248 ////////////////////////////////////////////////////////////////////////
250 ////////////////////////////////////////////////////////////////////////
253 fgLoad3DModel( const string &fg_root, const string &path,
254 SGPropertyNode *prop_root,
255 double sim_time_sec )
257 ssgBranch * model = 0;
258 SGPropertyNode props;
260 // Load the 3D aircraft object itself
262 SGPath modelpath = path;
263 if ( ulIsAbsolutePathName( path.c_str() ) ) {
268 xmlpath.append(modelpath.str());
271 // Check for an XML wrapper
272 if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
273 readProperties(xmlpath.str(), &props);
274 if (props.hasValue("/path")) {
275 modelpath = modelpath.dir();
276 modelpath.append(props.getStringValue("/path"));
279 model = new ssgBranch;
283 // Assume that textures are in
284 // the same location as the XML file.
286 ssgTexturePath((char *)xmlpath.dir().c_str());
287 model = (ssgBranch *)ssgLoad((char *)modelpath.c_str());
289 throw sg_exception("Failed to load 3D model");
292 // Set up the alignment node
293 ssgTransform * alignmainmodel = new ssgTransform;
294 alignmainmodel->addKid(model);
296 make_offsets_matrix(&res_matrix,
297 props.getFloatValue("/offsets/heading-deg", 0.0),
298 props.getFloatValue("/offsets/roll-deg", 0.0),
299 props.getFloatValue("/offsets/pitch-deg", 0.0),
300 props.getFloatValue("/offsets/x-m", 0.0),
301 props.getFloatValue("/offsets/y-m", 0.0),
302 props.getFloatValue("/offsets/z-m", 0.0));
303 alignmainmodel->setTransform(res_matrix);
307 vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
308 for (i = 0; i < panel_nodes.size(); i++) {
309 SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
310 FGPanelNode * panel = new FGPanelNode(panel_nodes[i]);
311 if (panel_nodes[i]->hasValue("name"))
312 panel->setName((char *)panel_nodes[i]->getStringValue("name"));
313 model->addKid(panel);
317 vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
318 for (i = 0; i < animation_nodes.size(); i++) {
319 const char * name = animation_nodes[i]->getStringValue("name", 0);
320 vector<SGPropertyNode_ptr> name_nodes =
321 animation_nodes[i]->getChildren("object-name");
322 make_animation( model, name, name_nodes, prop_root, animation_nodes[i],
327 vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
328 for (i = 0; i < model_nodes.size(); i++) {
329 SGPropertyNode_ptr node = model_nodes[i];
330 ssgTransform * align = new ssgTransform;
332 make_offsets_matrix(&res_matrix,
333 node->getFloatValue("offsets/heading-deg", 0.0),
334 node->getFloatValue("offsets/roll-deg", 0.0),
335 node->getFloatValue("offsets/pitch-deg", 0.0),
336 node->getFloatValue("offsets/x-m", 0.0),
337 node->getFloatValue("offsets/y-m", 0.0),
338 node->getFloatValue("offsets/z-m", 0.0));
339 align->setTransform(res_matrix);
341 ssgBranch * kid = fgLoad3DModel( fg_root, node->getStringValue("path"),
342 prop_root, sim_time_sec );
344 model->addKid(align);
347 return alignmainmodel;
352 ////////////////////////////////////////////////////////////////////////
353 // Implementation of Animation
354 ////////////////////////////////////////////////////////////////////////
356 Animation::Animation (SGPropertyNode_ptr props, ssgBranch * branch)
359 _branch->setName(props->getStringValue("name", 0));
362 Animation::~Animation ()
378 ////////////////////////////////////////////////////////////////////////
379 // Implementation of NullAnimation
380 ////////////////////////////////////////////////////////////////////////
382 NullAnimation::NullAnimation (SGPropertyNode_ptr props)
383 : Animation(props, new ssgBranch)
387 NullAnimation::~NullAnimation ()
393 ////////////////////////////////////////////////////////////////////////
394 // Implementation of RangeAnimation
395 ////////////////////////////////////////////////////////////////////////
397 RangeAnimation::RangeAnimation (SGPropertyNode_ptr props)
398 : Animation(props, new ssgRangeSelector)
400 float ranges[] = { props->getFloatValue("min-m", 0),
401 props->getFloatValue("max-m", 5000) };
402 ((ssgRangeSelector *)_branch)->setRanges(ranges, 2);
406 RangeAnimation::~RangeAnimation ()
412 ////////////////////////////////////////////////////////////////////////
413 // Implementation of BillboardAnimation
414 ////////////////////////////////////////////////////////////////////////
416 BillboardAnimation::BillboardAnimation (SGPropertyNode_ptr props)
417 : Animation(props, new ssgCutout(props->getBoolValue("spherical", true)))
421 BillboardAnimation::~BillboardAnimation ()
427 ////////////////////////////////////////////////////////////////////////
428 // Implementation of SelectAnimation
429 ////////////////////////////////////////////////////////////////////////
431 SelectAnimation::SelectAnimation( SGPropertyNode *prop_root,
432 SGPropertyNode_ptr props )
433 : Animation(props, new ssgSelector),
436 SGPropertyNode_ptr node = props->getChild("condition");
438 _condition = fgReadCondition(prop_root, node);
441 SelectAnimation::~SelectAnimation ()
447 SelectAnimation::update ()
449 if (_condition != 0 && _condition->test())
450 ((ssgSelector *)_branch)->select(0xffff);
452 ((ssgSelector *)_branch)->select(0x0000);
457 ////////////////////////////////////////////////////////////////////////
458 // Implementation of SpinAnimation
459 ////////////////////////////////////////////////////////////////////////
461 SpinAnimation::SpinAnimation( SGPropertyNode *prop_root,
462 SGPropertyNode_ptr props,
463 double sim_time_sec )
464 : Animation(props, new ssgTransform),
465 _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
466 _factor(props->getDoubleValue("factor", 1.0)),
467 _position_deg(props->getDoubleValue("starting-position-deg", 0)),
468 _last_time_sec( sim_time_sec /* globals->get_sim_time_sec() */ )
470 _center[0] = props->getFloatValue("center/x-m", 0);
471 _center[1] = props->getFloatValue("center/y-m", 0);
472 _center[2] = props->getFloatValue("center/z-m", 0);
473 _axis[0] = props->getFloatValue("axis/x", 0);
474 _axis[1] = props->getFloatValue("axis/y", 0);
475 _axis[2] = props->getFloatValue("axis/z", 0);
476 sgNormalizeVec3(_axis);
479 SpinAnimation::~SpinAnimation ()
484 SpinAnimation::update( double sim_time_sec )
486 double dt = sim_time_sec - _last_time_sec;
487 _last_time_sec = sim_time_sec;
489 float velocity_rpms = (_prop->getDoubleValue() * _factor / 60.0);
490 _position_deg += (dt * velocity_rpms * 360);
491 while (_position_deg < 0)
492 _position_deg += 360.0;
493 while (_position_deg >= 360.0)
494 _position_deg -= 360.0;
495 set_rotation(_matrix, _position_deg, _center, _axis);
496 ((ssgTransform *)_branch)->setTransform(_matrix);
501 ////////////////////////////////////////////////////////////////////////
502 // Implementation of TimedAnimation
503 ////////////////////////////////////////////////////////////////////////
505 TimedAnimation::TimedAnimation (SGPropertyNode_ptr props)
506 : Animation(props, new ssgSelector),
507 _duration_sec(props->getDoubleValue("duration-sec", 1.0)),
513 TimedAnimation::~TimedAnimation ()
518 TimedAnimation::update( double sim_time_sec )
520 if ((sim_time_sec - _last_time_sec) >= _duration_sec) {
521 _last_time_sec = sim_time_sec;
523 if (_step >= getBranch()->getNumKids())
525 ((ssgSelector *)getBranch())->selectStep(_step);
531 ////////////////////////////////////////////////////////////////////////
532 // Implementation of RotateAnimation
533 ////////////////////////////////////////////////////////////////////////
535 RotateAnimation::RotateAnimation( SGPropertyNode *prop_root,
536 SGPropertyNode_ptr props )
537 : Animation(props, new ssgTransform),
538 _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
539 _offset_deg(props->getDoubleValue("offset-deg", 0.0)),
540 _factor(props->getDoubleValue("factor", 1.0)),
541 _table(read_interpolation_table(props)),
542 _has_min(props->hasValue("min-deg")),
543 _min_deg(props->getDoubleValue("min-deg")),
544 _has_max(props->hasValue("max-deg")),
545 _max_deg(props->getDoubleValue("max-deg")),
546 _position_deg(props->getDoubleValue("starting-position-deg", 0))
548 _center[0] = props->getFloatValue("center/x-m", 0);
549 _center[1] = props->getFloatValue("center/y-m", 0);
550 _center[2] = props->getFloatValue("center/z-m", 0);
551 _axis[0] = props->getFloatValue("axis/x", 0);
552 _axis[1] = props->getFloatValue("axis/y", 0);
553 _axis[2] = props->getFloatValue("axis/z", 0);
554 sgNormalizeVec3(_axis);
557 RotateAnimation::~RotateAnimation ()
563 RotateAnimation::update()
566 _position_deg = _prop->getDoubleValue() * _factor + _offset_deg;
567 if (_has_min && _position_deg < _min_deg)
568 _position_deg = _min_deg;
569 if (_has_max && _position_deg > _max_deg)
570 _position_deg = _max_deg;
572 _position_deg = _table->interpolate(_prop->getDoubleValue());
574 set_rotation(_matrix, _position_deg, _center, _axis);
575 ((ssgTransform *)_branch)->setTransform(_matrix);
580 ////////////////////////////////////////////////////////////////////////
581 // Implementation of TranslateAnimation
582 ////////////////////////////////////////////////////////////////////////
584 TranslateAnimation::TranslateAnimation( SGPropertyNode *prop_root,
585 SGPropertyNode_ptr props )
586 : Animation(props, new ssgTransform),
587 _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
588 _offset_m(props->getDoubleValue("offset-m", 0.0)),
589 _factor(props->getDoubleValue("factor", 1.0)),
590 _table(read_interpolation_table(props)),
591 _has_min(props->hasValue("min-m")),
592 _min_m(props->getDoubleValue("min-m")),
593 _has_max(props->hasValue("max-m")),
594 _max_m(props->getDoubleValue("max-m")),
595 _position_m(props->getDoubleValue("starting-position-m", 0))
597 _axis[0] = props->getFloatValue("axis/x", 0);
598 _axis[1] = props->getFloatValue("axis/y", 0);
599 _axis[2] = props->getFloatValue("axis/z", 0);
600 sgNormalizeVec3(_axis);
603 TranslateAnimation::~TranslateAnimation ()
609 TranslateAnimation::update ()
612 _position_m = (_prop->getDoubleValue() + _offset_m) * _factor;
613 if (_has_min && _position_m < _min_m)
614 _position_m = _min_m;
615 if (_has_max && _position_m > _max_m)
616 _position_m = _max_m;
618 _position_m = _table->interpolate(_prop->getDoubleValue());
620 set_translation(_matrix, _position_m, _axis);
621 ((ssgTransform *)_branch)->setTransform(_matrix);
626 ////////////////////////////////////////////////////////////////////////
627 // Implementation of FGModelPlacement.
628 ////////////////////////////////////////////////////////////////////////
630 FGModelPlacement::FGModelPlacement ()
637 _selector(new ssgSelector),
638 _position(new ssgTransform),
639 _location(new FGLocation)
643 FGModelPlacement::~FGModelPlacement ()
648 FGModelPlacement::init( const string &fg_root,
650 SGPropertyNode *prop_root,
651 double sim_time_sec )
653 ssgBranch * model = fgLoad3DModel( fg_root, path, prop_root, sim_time_sec );
655 _position->addKid(model);
656 _selector->addKid(_position);
657 _selector->clrTraversalMaskBits(SSGTRAV_HOT);
661 FGModelPlacement::update( const Point3D scenery_center )
663 _location->setPosition( _lon_deg, _lat_deg, _elev_ft );
664 _location->setOrientation( _roll_deg, _pitch_deg, _heading_deg );
666 sgCopyMat4( POS, _location->getTransformMatrix(scenery_center) );
669 sgCopyVec3(trans, _location->get_view_pos());
671 for(int i = 0; i < 4; i++) {
672 float tmp = POS[i][3];
673 for( int j=0; j<3; j++ ) {
674 POS[i][j] += (tmp * trans[j]);
677 _position->setTransform(POS);
681 FGModelPlacement::getVisible () const
683 return (_selector->getSelect() != 0);
687 FGModelPlacement::setVisible (bool visible)
689 _selector->select(visible);
693 FGModelPlacement::setLongitudeDeg (double lon_deg)
699 FGModelPlacement::setLatitudeDeg (double lat_deg)
705 FGModelPlacement::setElevationFt (double elev_ft)
711 FGModelPlacement::setPosition (double lon_deg, double lat_deg, double elev_ft)
719 FGModelPlacement::setRollDeg (double roll_deg)
721 _roll_deg = roll_deg;
725 FGModelPlacement::setPitchDeg (double pitch_deg)
727 _pitch_deg = pitch_deg;
731 FGModelPlacement::setHeadingDeg (double heading_deg)
733 _heading_deg = heading_deg;
737 FGModelPlacement::setOrientation (double roll_deg, double pitch_deg,
740 _roll_deg = roll_deg;
741 _pitch_deg = pitch_deg;
742 _heading_deg = heading_deg;