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>
24 #include <Main/fg_props.hxx>
25 #include <Main/globals.hxx>
26 #include <Main/location.hxx>
27 #include <Scenery/scenery.hxx>
30 #include "panelnode.hxx"
34 ////////////////////////////////////////////////////////////////////////
35 // Static utility functions.
36 ////////////////////////////////////////////////////////////////////////
39 * Callback to update an animation.
42 animation_callback (ssgEntity * entity, int mask)
44 ((Animation *)entity->getUserData())->update();
50 * Locate a named SSG node in a branch.
53 find_named_node (ssgEntity * node, const char * name)
55 char * node_name = node->getName();
56 if (node_name != 0 && !strcmp(name, node_name))
58 else if (node->isAKindOf(ssgTypeBranch())) {
59 int nKids = node->getNumKids();
60 for (int i = 0; i < nKids; i++) {
62 find_named_node(((ssgBranch*)node)->getKid(i), name);
71 * Splice a branch in between all child nodes and their parents.
74 splice_branch (ssgBranch * branch, ssgEntity * child)
76 int nParents = child->getNumParents();
77 branch->addKid(child);
78 for (int i = 0; i < nParents; i++) {
79 ssgBranch * parent = child->getParent(i);
80 parent->replaceKid(child, branch);
85 * Set up the transform matrix for a spin or rotation.
88 set_rotation (sgMat4 &matrix, double position_deg,
89 sgVec3 ¢er, sgVec3 &axis)
91 float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
93 float s = (float) sin ( temp_angle ) ;
94 float c = (float) cos ( temp_angle ) ;
95 float t = SG_ONE - c ;
97 // axis was normalized at load time
98 // hint to the compiler to put these into FP registers
103 matrix[0][0] = t * x * x + c ;
104 matrix[0][1] = t * y * x - s * z ;
105 matrix[0][2] = t * z * x + s * y ;
106 matrix[0][3] = SG_ZERO;
108 matrix[1][0] = t * x * y + s * z ;
109 matrix[1][1] = t * y * y + c ;
110 matrix[1][2] = t * z * y - s * x ;
111 matrix[1][3] = SG_ZERO;
113 matrix[2][0] = t * x * z - s * y ;
114 matrix[2][1] = t * y * z + s * x ;
115 matrix[2][2] = t * z * z + c ;
116 matrix[2][3] = SG_ZERO;
118 // hint to the compiler to put these into FP registers
123 matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
124 matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
125 matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
126 matrix[3][3] = SG_ONE;
130 * Set up the transform matrix for a translation.
133 set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis)
136 sgScaleVec3(xyz, axis, position_m);
137 sgMakeTransMat4(matrix, xyz);
142 * Make an offset matrix from rotations and position offset.
145 make_offsets_matrix (sgMat4 * result, double h_rot, double p_rot, double r_rot,
146 double x_off, double y_off, double z_off)
150 sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
151 sgMakeTransMat4(pos_matrix, x_off, y_off, z_off);
152 sgMultMat4(*result, pos_matrix, rot_matrix);
157 * Read an interpolation table from properties.
159 static SGInterpTable *
160 read_interpolation_table (SGPropertyNode_ptr props)
162 SGPropertyNode_ptr table_node = props->getNode("interpolation");
163 if (table_node != 0) {
164 SGInterpTable * table = new SGInterpTable();
165 vector<SGPropertyNode_ptr> entries = table_node->getChildren("entry");
166 for (unsigned int i = 0; i < entries.size(); i++)
167 table->addEntry(entries[i]->getDoubleValue("ind", 0.0),
168 entries[i]->getDoubleValue("dep", 0.0));
177 make_animation (ssgBranch * model,
179 vector<SGPropertyNode_ptr> &name_nodes,
180 SGPropertyNode_ptr node)
182 Animation * animation = 0;
183 const char * type = node->getStringValue("type", "none");
184 if (!strcmp("none", type)) {
185 animation = new NullAnimation(node);
186 } else if (!strcmp("range", type)) {
187 animation = new RangeAnimation(node);
188 } else if (!strcmp("billboard", type)) {
189 animation = new BillboardAnimation(node);
190 } else if (!strcmp("select", type)) {
191 animation = new SelectAnimation(node);
192 } else if (!strcmp("spin", type)) {
193 animation = new SpinAnimation(node);
194 } else if (!strcmp("timed", type)) {
195 animation = new TimedAnimation(node);
196 } else if (!strcmp("rotate", type)) {
197 animation = new RotateAnimation(node);
198 } else if (!strcmp("translate", type)) {
199 animation = new TranslateAnimation(node);
201 animation = new NullAnimation(node);
202 SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
206 animation->setName((char *)name);
209 if (name_nodes.size() > 0) {
210 object = find_named_node(model, name_nodes[0]->getStringValue());
212 SG_LOG(SG_INPUT, SG_WARN, "Object " << name_nodes[0]->getStringValue()
221 ssgBranch * branch = animation->getBranch();
222 splice_branch(branch, object);
224 for (int i = 1; i < name_nodes.size(); i++) {
225 const char * name = name_nodes[i]->getStringValue();
226 object = find_named_node(model, name);
228 SG_LOG(SG_INPUT, SG_WARN, "Object " << name << " not found");
232 ssgBranch * oldParent = object->getParent(0);
233 branch->addKid(object);
234 oldParent->removeKid(object);
238 branch->setUserData(animation);
239 branch->setTravCallback(SSG_CALLBACK_PRETRAV, animation_callback);
244 ////////////////////////////////////////////////////////////////////////
246 ////////////////////////////////////////////////////////////////////////
249 fgLoad3DModel (const string &path)
251 ssgBranch * model = 0;
252 SGPropertyNode props;
254 // Load the 3D aircraft object itself
256 SGPath modelpath = path;
257 if ( ulIsAbsolutePathName( path.c_str() ) ) {
261 xmlpath = globals->get_fg_root();
262 xmlpath.append(modelpath.str());
265 // Check for an XML wrapper
266 if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
267 readProperties(xmlpath.str(), &props);
268 if (props.hasValue("/path")) {
269 modelpath = modelpath.dir();
270 modelpath.append(props.getStringValue("/path"));
273 model = new ssgBranch;
277 // Assume that textures are in
278 // the same location as the XML file.
280 ssgTexturePath((char *)xmlpath.dir().c_str());
281 model = (ssgBranch *)ssgLoad((char *)modelpath.c_str());
283 throw sg_exception("Failed to load 3D model");
286 // Set up the alignment node
287 ssgTransform * alignmainmodel = new ssgTransform;
288 alignmainmodel->addKid(model);
290 make_offsets_matrix(&res_matrix,
291 props.getFloatValue("/offsets/heading-deg", 0.0),
292 props.getFloatValue("/offsets/roll-deg", 0.0),
293 props.getFloatValue("/offsets/pitch-deg", 0.0),
294 props.getFloatValue("/offsets/x-m", 0.0),
295 props.getFloatValue("/offsets/y-m", 0.0),
296 props.getFloatValue("/offsets/z-m", 0.0));
297 alignmainmodel->setTransform(res_matrix);
301 vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
302 for (i = 0; i < panel_nodes.size(); i++) {
303 SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
304 FGPanelNode * panel = new FGPanelNode(panel_nodes[i]);
305 if (panel_nodes[i]->hasValue("name"))
306 panel->setName((char *)panel_nodes[i]->getStringValue("name"));
307 model->addKid(panel);
311 vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
312 for (i = 0; i < animation_nodes.size(); i++) {
313 const char * name = animation_nodes[i]->getStringValue("name", 0);
314 vector<SGPropertyNode_ptr> name_nodes =
315 animation_nodes[i]->getChildren("object-name");
316 make_animation(model, name, name_nodes, animation_nodes[i]);
320 vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
321 for (i = 0; i < model_nodes.size(); i++) {
322 SGPropertyNode_ptr node = model_nodes[i];
323 ssgTransform * align = new ssgTransform;
325 make_offsets_matrix(&res_matrix,
326 node->getFloatValue("offsets/heading-deg", 0.0),
327 node->getFloatValue("offsets/roll-deg", 0.0),
328 node->getFloatValue("offsets/pitch-deg", 0.0),
329 node->getFloatValue("offsets/x-m", 0.0),
330 node->getFloatValue("offsets/y-m", 0.0),
331 node->getFloatValue("offsets/z-m", 0.0));
332 align->setTransform(res_matrix);
334 ssgBranch * kid = fgLoad3DModel(node->getStringValue("path"));
336 model->addKid(align);
339 return alignmainmodel;
344 ////////////////////////////////////////////////////////////////////////
345 // Implementation of Animation
346 ////////////////////////////////////////////////////////////////////////
348 Animation::Animation (SGPropertyNode_ptr props, ssgBranch * branch)
351 _branch->setName(props->getStringValue("name", 0));
354 Animation::~Animation ()
370 ////////////////////////////////////////////////////////////////////////
371 // Implementation of NullAnimation
372 ////////////////////////////////////////////////////////////////////////
374 NullAnimation::NullAnimation (SGPropertyNode_ptr props)
375 : Animation(props, new ssgBranch)
379 NullAnimation::~NullAnimation ()
385 ////////////////////////////////////////////////////////////////////////
386 // Implementation of RangeAnimation
387 ////////////////////////////////////////////////////////////////////////
389 RangeAnimation::RangeAnimation (SGPropertyNode_ptr props)
390 : Animation(props, new ssgRangeSelector)
392 float ranges[] = { props->getFloatValue("min-m", 0),
393 props->getFloatValue("max-m", 5000) };
394 ((ssgRangeSelector *)_branch)->setRanges(ranges, 2);
398 RangeAnimation::~RangeAnimation ()
404 ////////////////////////////////////////////////////////////////////////
405 // Implementation of BillboardAnimation
406 ////////////////////////////////////////////////////////////////////////
408 BillboardAnimation::BillboardAnimation (SGPropertyNode_ptr props)
409 : Animation(props, new ssgCutout(props->getBoolValue("spherical", true)))
413 BillboardAnimation::~BillboardAnimation ()
419 ////////////////////////////////////////////////////////////////////////
420 // Implementation of SelectAnimation
421 ////////////////////////////////////////////////////////////////////////
423 SelectAnimation::SelectAnimation (SGPropertyNode_ptr props)
424 : Animation(props, new ssgSelector),
427 SGPropertyNode_ptr node = props->getChild("condition");
429 _condition = fgReadCondition(node);
432 SelectAnimation::~SelectAnimation ()
438 SelectAnimation::update ()
440 if (_condition != 0 && _condition->test())
441 ((ssgSelector *)_branch)->select(0xffff);
443 ((ssgSelector *)_branch)->select(0x0000);
448 ////////////////////////////////////////////////////////////////////////
449 // Implementation of SpinAnimation
450 ////////////////////////////////////////////////////////////////////////
452 SpinAnimation::SpinAnimation (SGPropertyNode_ptr props)
453 : Animation(props, new ssgTransform),
454 _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
455 _factor(props->getDoubleValue("factor", 1.0)),
456 _position_deg(props->getDoubleValue("starting-position-deg", 0)),
457 _last_time_sec(globals->get_sim_time_sec())
459 _center[0] = props->getFloatValue("center/x-m", 0);
460 _center[1] = props->getFloatValue("center/y-m", 0);
461 _center[2] = props->getFloatValue("center/z-m", 0);
462 _axis[0] = props->getFloatValue("axis/x", 0);
463 _axis[1] = props->getFloatValue("axis/y", 0);
464 _axis[2] = props->getFloatValue("axis/z", 0);
465 sgNormalizeVec3(_axis);
468 SpinAnimation::~SpinAnimation ()
473 SpinAnimation::update ()
475 double sim_time = globals->get_sim_time_sec();
476 double dt = sim_time - _last_time_sec;
477 _last_time_sec = sim_time;
479 float velocity_rpms = (_prop->getDoubleValue() * _factor / 60.0);
480 _position_deg += (dt * velocity_rpms * 360);
481 while (_position_deg < 0)
482 _position_deg += 360.0;
483 while (_position_deg >= 360.0)
484 _position_deg -= 360.0;
485 set_rotation(_matrix, _position_deg, _center, _axis);
486 ((ssgTransform *)_branch)->setTransform(_matrix);
491 ////////////////////////////////////////////////////////////////////////
492 // Implementation of TimedAnimation
493 ////////////////////////////////////////////////////////////////////////
495 TimedAnimation::TimedAnimation (SGPropertyNode_ptr props)
496 : Animation(props, new ssgSelector),
497 _duration_sec(props->getDoubleValue("duration-sec", 1.0)),
503 TimedAnimation::~TimedAnimation ()
508 TimedAnimation::update ()
510 float sim_time_sec = globals->get_sim_time_sec();
511 if ((sim_time_sec - _last_time_sec) >= _duration_sec) {
512 _last_time_sec = sim_time_sec;
514 if (_step >= getBranch()->getNumKids())
516 ((ssgSelector *)getBranch())->selectStep(_step);
522 ////////////////////////////////////////////////////////////////////////
523 // Implementation of RotateAnimation
524 ////////////////////////////////////////////////////////////////////////
526 RotateAnimation::RotateAnimation (SGPropertyNode_ptr props)
527 : Animation(props, new ssgTransform),
528 _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
529 _offset_deg(props->getDoubleValue("offset-deg", 0.0)),
530 _factor(props->getDoubleValue("factor", 1.0)),
531 _table(read_interpolation_table(props)),
532 _has_min(props->hasValue("min-deg")),
533 _min_deg(props->getDoubleValue("min-deg")),
534 _has_max(props->hasValue("max-deg")),
535 _max_deg(props->getDoubleValue("max-deg")),
536 _position_deg(props->getDoubleValue("starting-position-deg", 0))
538 _center[0] = props->getFloatValue("center/x-m", 0);
539 _center[1] = props->getFloatValue("center/y-m", 0);
540 _center[2] = props->getFloatValue("center/z-m", 0);
541 _axis[0] = props->getFloatValue("axis/x", 0);
542 _axis[1] = props->getFloatValue("axis/y", 0);
543 _axis[2] = props->getFloatValue("axis/z", 0);
544 sgNormalizeVec3(_axis);
547 RotateAnimation::~RotateAnimation ()
553 RotateAnimation::update ()
556 _position_deg = _prop->getDoubleValue() * _factor + _offset_deg;
557 if (_has_min && _position_deg < _min_deg)
558 _position_deg = _min_deg;
559 if (_has_max && _position_deg > _max_deg)
560 _position_deg = _max_deg;
562 _position_deg = _table->interpolate(_prop->getDoubleValue());
564 set_rotation(_matrix, _position_deg, _center, _axis);
565 ((ssgTransform *)_branch)->setTransform(_matrix);
570 ////////////////////////////////////////////////////////////////////////
571 // Implementation of TranslateAnimation
572 ////////////////////////////////////////////////////////////////////////
574 TranslateAnimation::TranslateAnimation (SGPropertyNode_ptr props)
575 : Animation(props, new ssgTransform),
576 _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
577 _offset_m(props->getDoubleValue("offset-m", 0.0)),
578 _factor(props->getDoubleValue("factor", 1.0)),
579 _table(read_interpolation_table(props)),
580 _has_min(props->hasValue("min-m")),
581 _min_m(props->getDoubleValue("min-m")),
582 _has_max(props->hasValue("max-m")),
583 _max_m(props->getDoubleValue("max-m")),
584 _position_m(props->getDoubleValue("starting-position-m", 0))
586 _axis[0] = props->getFloatValue("axis/x", 0);
587 _axis[1] = props->getFloatValue("axis/y", 0);
588 _axis[2] = props->getFloatValue("axis/z", 0);
589 sgNormalizeVec3(_axis);
592 TranslateAnimation::~TranslateAnimation ()
598 TranslateAnimation::update ()
601 _position_m = (_prop->getDoubleValue() + _offset_m) * _factor;
602 if (_has_min && _position_m < _min_m)
603 _position_m = _min_m;
604 if (_has_max && _position_m > _max_m)
605 _position_m = _max_m;
607 _position_m = _table->interpolate(_prop->getDoubleValue());
609 set_translation(_matrix, _position_m, _axis);
610 ((ssgTransform *)_branch)->setTransform(_matrix);
615 ////////////////////////////////////////////////////////////////////////
616 // Implementation of FGModelPlacement.
617 ////////////////////////////////////////////////////////////////////////
619 FGModelPlacement::FGModelPlacement ()
626 _selector(new ssgSelector),
627 _position(new ssgTransform),
628 _location(new FGLocation)
632 FGModelPlacement::~FGModelPlacement ()
637 FGModelPlacement::init (const string &path)
639 ssgBranch * model = fgLoad3DModel(path);
641 _position->addKid(model);
642 _selector->addKid(_position);
643 _selector->clrTraversalMaskBits(SSGTRAV_HOT);
647 FGModelPlacement::update ()
649 _location->setPosition( _lon_deg, _lat_deg, _elev_ft );
650 _location->setOrientation( _roll_deg, _pitch_deg, _heading_deg );
653 sgCopyMat4(POS, _location->getTransformMatrix());
656 sgCopyVec3(trans, _location->get_view_pos());
658 for(int i = 0; i < 4; i++) {
659 float tmp = POS[i][3];
660 for( int j=0; j<3; j++ ) {
661 POS[i][j] += (tmp * trans[j]);
664 _position->setTransform(POS);
668 FGModelPlacement::getVisible () const
670 return (_selector->getSelect() != 0);
674 FGModelPlacement::setVisible (bool visible)
676 _selector->select(visible);
680 FGModelPlacement::setLongitudeDeg (double lon_deg)
686 FGModelPlacement::setLatitudeDeg (double lat_deg)
692 FGModelPlacement::setElevationFt (double elev_ft)
698 FGModelPlacement::setPosition (double lon_deg, double lat_deg, double elev_ft)
706 FGModelPlacement::setRollDeg (double roll_deg)
708 _roll_deg = roll_deg;
712 FGModelPlacement::setPitchDeg (double pitch_deg)
714 _pitch_deg = pitch_deg;
718 FGModelPlacement::setHeadingDeg (double heading_deg)
720 _heading_deg = heading_deg;
724 FGModelPlacement::setOrientation (double roll_deg, double pitch_deg,
727 _roll_deg = roll_deg;
728 _pitch_deg = pitch_deg;
729 _heading_deg = heading_deg;