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()
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>
23 #include <Main/fg_props.hxx>
24 #include <Main/globals.hxx>
25 #include <Main/location.hxx>
26 #include <Scenery/scenery.hxx>
29 #include "panelnode.hxx"
33 ////////////////////////////////////////////////////////////////////////
34 // Static utility functions.
35 ////////////////////////////////////////////////////////////////////////
38 * Locate a named SSG node in a branch.
41 find_named_node (ssgEntity * node, const char * name)
43 char * node_name = node->getName();
44 if (node_name != 0 && !strcmp(name, node_name))
46 else if (node->isAKindOf(ssgTypeBranch())) {
47 int nKids = node->getNumKids();
48 for (int i = 0; i < nKids; i++) {
50 find_named_node(((ssgBranch*)node)->getKid(i), name);
59 * Splice a branch in between all child nodes and their parents.
62 splice_branch (ssgBranch * branch, ssgEntity * child)
64 int nParents = child->getNumParents();
65 branch->addKid(child);
66 for (int i = 0; i < nParents; i++) {
67 ssgBranch * parent = child->getParent(i);
68 parent->replaceKid(child, branch);
73 * Set up the transform matrix for a spin or rotation.
76 set_rotation (sgMat4 &matrix, double position_deg,
77 sgVec3 ¢er, sgVec3 &axis)
79 float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
81 float s = (float) sin ( temp_angle ) ;
82 float c = (float) cos ( temp_angle ) ;
83 float t = SG_ONE - c ;
85 // axis was normalized at load time
86 // hint to the compiler to put these into FP registers
91 matrix[0][0] = t * x * x + c ;
92 matrix[0][1] = t * y * x - s * z ;
93 matrix[0][2] = t * z * x + s * y ;
94 matrix[0][3] = SG_ZERO;
96 matrix[1][0] = t * x * y + s * z ;
97 matrix[1][1] = t * y * y + c ;
98 matrix[1][2] = t * z * y - s * x ;
99 matrix[1][3] = SG_ZERO;
101 matrix[2][0] = t * x * z - s * y ;
102 matrix[2][1] = t * y * z + s * x ;
103 matrix[2][2] = t * z * z + c ;
104 matrix[2][3] = SG_ZERO;
106 // hint to the compiler to put these into FP registers
111 matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
112 matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
113 matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
114 matrix[3][3] = SG_ONE;
118 * Set up the transform matrix for a translation.
121 set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis)
124 sgScaleVec3(xyz, axis, position_m);
125 sgMakeTransMat4(matrix, xyz);
130 * Make an offset matrix from rotations and position offset.
133 make_offsets_matrix (sgMat4 * result, double h_rot, double p_rot, double r_rot,
134 double x_off, double y_off, double z_off)
138 sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
139 sgMakeTransMat4(pos_matrix, x_off, y_off, z_off);
140 sgMultMat4(*result, pos_matrix, rot_matrix);
145 * Read an interpolation table from properties.
147 static SGInterpTable *
148 read_interpolation_table (const SGPropertyNode * props)
150 const SGPropertyNode * table_node = props->getNode("interpolation");
151 if (table_node != 0) {
152 SGInterpTable * table = new SGInterpTable();
153 vector<SGPropertyNode_ptr> entries = table_node->getChildren("entry");
154 for (unsigned int i = 0; i < entries.size(); i++)
155 table->addEntry(entries[i]->getDoubleValue("ind", 0.0),
156 entries[i]->getDoubleValue("dep", 0.0));
165 ////////////////////////////////////////////////////////////////////////
166 // Implementation of FG3DModel
167 ////////////////////////////////////////////////////////////////////////
169 FG3DModel::FG3DModel ()
174 FG3DModel::~FG3DModel ()
176 // since the nodes are attached to the scene graph, they'll be
177 // deleted automatically
180 for (i = 0; i < _animations.size(); i++)
181 delete _animations[i];
185 FG3DModel::init (const string &path)
187 SGPropertyNode props;
189 // Load the 3D aircraft object itself
190 SGPath xmlpath = globals->get_fg_root();
191 SGPath modelpath = path;
192 xmlpath.append(modelpath.str());
194 // Check for an XML wrapper
195 if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
196 readProperties(xmlpath.str(), &props);
197 if (props.hasValue("/path")) {
198 modelpath = modelpath.dir();
199 modelpath.append(props.getStringValue("/path"));
202 _model = new ssgBranch;
206 // Assume that textures are in
207 // the same location as the XML file.
209 ssgTexturePath((char *)xmlpath.dir().c_str());
210 _model = (ssgBranch *)ssgLoad((char *)modelpath.c_str());
212 throw sg_exception("Failed to load 3D model");
215 // Set up the alignment node
216 ssgTransform * align = new ssgTransform;
217 align->addKid(_model);
219 make_offsets_matrix(&res_matrix,
220 props.getFloatValue("/offsets/heading-deg", 0.0),
221 props.getFloatValue("/offsets/roll-deg", 0.0),
222 props.getFloatValue("/offsets/pitch-deg", 0.0),
223 props.getFloatValue("/offsets/x-m", 0.0),
224 props.getFloatValue("/offsets/y-m", 0.0),
225 props.getFloatValue("/offsets/z-m", 0.0));
226 align->setTransform(res_matrix);
229 vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
231 for (i = 0; i < animation_nodes.size(); i++) {
232 vector<SGPropertyNode_ptr> name_nodes =
233 animation_nodes[i]->getChildren("object-name");
234 if (name_nodes.size() < 1) {
235 Animation * animation = make_animation(0, animation_nodes[i]);
237 _animations.push_back(animation);
239 for (unsigned int j = 0; j < name_nodes.size(); j++) {
240 Animation * animation =
241 make_animation(name_nodes[j]->getStringValue(), animation_nodes[i]);
243 _animations.push_back(animation);
249 vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
250 for (i = 0; i < panel_nodes.size(); i++) {
251 printf("Reading a panel in model.cxx\n");
252 FGPanelNode * panel = new FGPanelNode(panel_nodes[i]);
253 _model->addKid(panel);
257 vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
258 for (i = 0; i < model_nodes.size(); i++) {
259 SGPropertyNode_ptr node = model_nodes[i];
260 ssgTransform * align = new ssgTransform;
262 make_offsets_matrix(&res_matrix,
263 node->getFloatValue("offsets/heading-deg", 0.0),
264 node->getFloatValue("offsets/roll-deg", 0.0),
265 node->getFloatValue("offsets/pitch-deg", 0.0),
266 node->getFloatValue("offsets/x-m", 0.0),
267 node->getFloatValue("offsets/y-m", 0.0),
268 node->getFloatValue("offsets/z-m", 0.0));
269 align->setTransform(res_matrix);
270 FG3DModel * kid = new FG3DModel;
271 kid->init(node->getStringValue("path"));
272 align->addKid(kid->getSceneGraph());
273 _model->addKid(align);
274 _children.push_back(kid);
279 FG3DModel::update (double dt)
283 for (i = 0; i < _children.size(); i++)
284 _children[i]->update(dt);
285 for (i = 0; i < _animations.size(); i++)
286 _animations[i]->update(dt);
289 FG3DModel::Animation *
290 FG3DModel::make_animation (const char * object_name,
291 SGPropertyNode * node)
293 Animation * animation = 0;
294 const char * type = node->getStringValue("type");
295 if (!strcmp("none", type)) {
296 animation = new NullAnimation();
297 } else if (!strcmp("range", type)) {
298 animation = new RangeAnimation();
299 } else if (!strcmp("billboard", type)) {
300 animation = new BillboardAnimation();
301 } else if (!strcmp("select", type)) {
302 animation = new SelectAnimation();
303 } else if (!strcmp("spin", type)) {
304 animation = new SpinAnimation();
305 } else if (!strcmp("rotate", type)) {
306 animation = new RotateAnimation();
307 } else if (!strcmp("translate", type)) {
308 animation = new TranslateAnimation();
310 animation = new NullAnimation();
311 SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
315 if (object_name != 0) {
316 object = find_named_node(_model, object_name);
318 SG_LOG(SG_INPUT, SG_WARN, "Object " << object_name << " not found");
327 animation->init(object, node);
333 ////////////////////////////////////////////////////////////////////////
334 // Implementation of FG3DModel::Animation
335 ////////////////////////////////////////////////////////////////////////
337 FG3DModel::Animation::Animation ()
341 FG3DModel::Animation::~Animation ()
347 ////////////////////////////////////////////////////////////////////////
348 // Implementation of FG3DModel::NullAnimation
349 ////////////////////////////////////////////////////////////////////////
351 FG3DModel::NullAnimation::NullAnimation ()
352 : _branch(new ssgBranch)
356 FG3DModel::NullAnimation::~NullAnimation ()
362 FG3DModel::NullAnimation::init (ssgEntity * object,
363 SGPropertyNode * props)
365 splice_branch(_branch, object);
366 _branch->setName(props->getStringValue("name", 0));
370 FG3DModel::NullAnimation::update (double dt)
376 ////////////////////////////////////////////////////////////////////////
377 // Implementation of FG3DModel::RangeAnimation
378 ////////////////////////////////////////////////////////////////////////
380 FG3DModel::RangeAnimation::RangeAnimation ()
381 : _branch(new ssgRangeSelector)
385 FG3DModel::RangeAnimation::~RangeAnimation ()
391 FG3DModel::RangeAnimation::init (ssgEntity * object,
392 SGPropertyNode * props)
395 splice_branch(_branch, object);
396 _branch->setName(props->getStringValue("name", 0));
397 ranges[0] = props->getFloatValue("min-m", 0);
398 ranges[1] = props->getFloatValue("max-m", 5000);
399 _branch->setRanges(ranges, 2);
403 FG3DModel::RangeAnimation::update (double dt)
409 ////////////////////////////////////////////////////////////////////////
410 // Implementation of FG3DModel::BillboardAnimation
411 ////////////////////////////////////////////////////////////////////////
413 FG3DModel::BillboardAnimation::BillboardAnimation ()
416 // Note: we cannot allocate the branch until we know whether
417 // it can rotate around the x axis as well as the z axis.
420 FG3DModel::BillboardAnimation::~BillboardAnimation ()
426 FG3DModel::BillboardAnimation::init (ssgEntity * object,
427 SGPropertyNode * props)
429 _branch = new ssgCutout(props->getBoolValue("spherical", true));
430 splice_branch(_branch, object);
431 _branch->setName(props->getStringValue("name", 0));
435 FG3DModel::BillboardAnimation::update (double dt)
441 ////////////////////////////////////////////////////////////////////////
442 // Implementation of FG3DModel::SelectAnimation
443 ////////////////////////////////////////////////////////////////////////
445 FG3DModel::SelectAnimation::SelectAnimation ()
447 _selector(new ssgSelector)
451 FG3DModel::SelectAnimation::~SelectAnimation ()
458 FG3DModel::SelectAnimation::init (ssgEntity * object,
459 SGPropertyNode * props)
461 splice_branch(_selector, object);
462 _selector->setName(props->getStringValue("name", 0));
463 SGPropertyNode * node = props->getChild("condition");
465 _condition = fgReadCondition(node);
470 FG3DModel::SelectAnimation::update (double dt)
472 if (_condition != 0 && _condition->test())
473 _selector->select(0xffff);
475 _selector->select(0x0000);
480 ////////////////////////////////////////////////////////////////////////
481 // Implementation of FG3DModel::SpinAnimation
482 ////////////////////////////////////////////////////////////////////////
484 FG3DModel::SpinAnimation::SpinAnimation ()
488 _transform(new ssgTransform)
492 FG3DModel::SpinAnimation::~SpinAnimation ()
498 FG3DModel::SpinAnimation::init (ssgEntity * object,
499 SGPropertyNode * props)
501 // Splice in the new transform node
502 splice_branch(_transform, object);
503 _transform->setName(props->getStringValue("name", 0));
504 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
505 _factor = props->getDoubleValue("factor", 1.0);
506 _position_deg = props->getDoubleValue("starting-position-deg", 0);
507 _center[0] = props->getFloatValue("center/x-m", 0);
508 _center[1] = props->getFloatValue("center/y-m", 0);
509 _center[2] = props->getFloatValue("center/z-m", 0);
510 _axis[0] = props->getFloatValue("axis/x", 0);
511 _axis[1] = props->getFloatValue("axis/y", 0);
512 _axis[2] = props->getFloatValue("axis/z", 0);
513 sgNormalizeVec3(_axis);
517 FG3DModel::SpinAnimation::update (double dt)
519 float velocity_rpms = (_prop->getDoubleValue() * _factor / 60.0);
520 _position_deg += (dt * velocity_rpms * 360);
521 while (_position_deg < 0)
522 _position_deg += 360.0;
523 while (_position_deg >= 360.0)
524 _position_deg -= 360.0;
525 set_rotation(_matrix, _position_deg, _center, _axis);
526 _transform->setTransform(_matrix);
531 ////////////////////////////////////////////////////////////////////////
532 // Implementation of FG3DModel::RotateAnimation
533 ////////////////////////////////////////////////////////////////////////
535 FG3DModel::RotateAnimation::RotateAnimation ()
545 _transform(new ssgTransform)
549 FG3DModel::RotateAnimation::~RotateAnimation ()
556 FG3DModel::RotateAnimation::init (ssgEntity * object,
557 SGPropertyNode * props)
559 // Splice in the new transform node
560 splice_branch(_transform, object);
561 _transform->setName(props->getStringValue("name", 0));
562 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
563 _offset_deg = props->getDoubleValue("offset-deg", 0.0);
564 _factor = props->getDoubleValue("factor", 1.0);
565 _table = read_interpolation_table(props);
566 if (props->hasValue("min-deg")) {
568 _min_deg = props->getDoubleValue("min-deg");
570 if (props->hasValue("max-deg")) {
572 _max_deg = props->getDoubleValue("max-deg");
574 _position_deg = props->getDoubleValue("starting-position-deg", 0);
575 _center[0] = props->getFloatValue("center/x-m", 0);
576 _center[1] = props->getFloatValue("center/y-m", 0);
577 _center[2] = props->getFloatValue("center/z-m", 0);
578 _axis[0] = props->getFloatValue("axis/x", 0);
579 _axis[1] = props->getFloatValue("axis/y", 0);
580 _axis[2] = props->getFloatValue("axis/z", 0);
581 sgNormalizeVec3(_axis);
585 FG3DModel::RotateAnimation::update (double dt)
588 _position_deg = (_prop->getDoubleValue() + _offset_deg) * _factor;
589 if (_has_min && _position_deg < _min_deg)
590 _position_deg = _min_deg;
591 if (_has_max && _position_deg > _max_deg)
592 _position_deg = _max_deg;
594 _position_deg = _table->interpolate(_prop->getDoubleValue());
596 set_rotation(_matrix, _position_deg, _center, _axis);
597 _transform->setTransform(_matrix);
602 ////////////////////////////////////////////////////////////////////////
603 // Implementation of FG3DModel::TranslateAnimation
604 ////////////////////////////////////////////////////////////////////////
606 FG3DModel::TranslateAnimation::TranslateAnimation ()
616 _transform(new ssgTransform)
620 FG3DModel::TranslateAnimation::~TranslateAnimation ()
627 FG3DModel::TranslateAnimation::init (ssgEntity * object,
628 SGPropertyNode * props)
630 // Splice in the new transform node
631 splice_branch(_transform, object);
632 _transform->setName(props->getStringValue("name", 0));
633 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
634 _offset_m = props->getDoubleValue("offset-m", 0.0);
635 _factor = props->getDoubleValue("factor", 1.0);
636 _table = read_interpolation_table(props);
637 if (props->hasValue("min-m")) {
639 _min_m = props->getDoubleValue("min-m");
641 if (props->hasValue("max-m")) {
643 _max_m = props->getDoubleValue("max-m");
645 _position_m = props->getDoubleValue("starting-position-m", 0);
646 _axis[0] = props->getFloatValue("axis/x", 0);
647 _axis[1] = props->getFloatValue("axis/y", 0);
648 _axis[2] = props->getFloatValue("axis/z", 0);
649 sgNormalizeVec3(_axis);
653 FG3DModel::TranslateAnimation::update (double dt)
656 _position_m = (_prop->getDoubleValue() + _offset_m) * _factor;
657 if (_has_min && _position_m < _min_m)
658 _position_m = _min_m;
659 if (_has_max && _position_m > _max_m)
660 _position_m = _max_m;
662 _position_m = _table->interpolate(_prop->getDoubleValue());
664 set_translation(_matrix, _position_m, _axis);
665 _transform->setTransform(_matrix);
670 ////////////////////////////////////////////////////////////////////////
671 // Implementation of FGModelPlacement.
672 ////////////////////////////////////////////////////////////////////////
674 FGModelPlacement::FGModelPlacement ()
675 : _model(new FG3DModel),
682 _selector(new ssgSelector),
683 _position(new ssgTransform),
684 _location(new FGLocation)
688 FGModelPlacement::~FGModelPlacement ()
695 FGModelPlacement::init (const string &path)
698 _position->addKid(_model->getSceneGraph());
699 _selector->addKid(_position);
700 _selector->clrTraversalMaskBits(SSGTRAV_HOT);
704 FGModelPlacement::update (double dt)
708 _location->setPosition( _lon_deg, _lat_deg, _elev_ft );
709 _location->setOrientation( _roll_deg, _pitch_deg, _heading_deg );
712 sgCopyMat4(POS, _location->getTransformMatrix());
715 sgCopyVec3(trans, _location->get_view_pos());
717 for(int i = 0; i < 4; i++) {
718 float tmp = POS[i][3];
719 for( int j=0; j<3; j++ ) {
720 POS[i][j] += (tmp * trans[j]);
723 _position->setTransform(POS);
727 FGModelPlacement::getVisible () const
729 return (_selector->getSelect() != 0);
733 FGModelPlacement::setVisible (bool visible)
735 _selector->select(visible);
739 FGModelPlacement::setLongitudeDeg (double lon_deg)
745 FGModelPlacement::setLatitudeDeg (double lat_deg)
751 FGModelPlacement::setElevationFt (double elev_ft)
757 FGModelPlacement::setPosition (double lon_deg, double lat_deg, double elev_ft)
765 FGModelPlacement::setRollDeg (double roll_deg)
767 _roll_deg = roll_deg;
771 FGModelPlacement::setPitchDeg (double pitch_deg)
773 _pitch_deg = pitch_deg;
777 FGModelPlacement::setHeadingDeg (double heading_deg)
779 _heading_deg = heading_deg;
783 FGModelPlacement::setOrientation (double roll_deg, double pitch_deg,
786 _roll_deg = roll_deg;
787 _pitch_deg = pitch_deg;
788 _heading_deg = heading_deg;