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>
32 ////////////////////////////////////////////////////////////////////////
33 // Static utility functions.
34 ////////////////////////////////////////////////////////////////////////
37 * Locate a named SSG node in a branch.
40 find_named_node (ssgEntity * node, const char * name)
42 char * node_name = node->getName();
43 if (node_name != 0 && !strcmp(name, node_name))
45 else if (node->isAKindOf(ssgTypeBranch())) {
46 int nKids = node->getNumKids();
47 for (int i = 0; i < nKids; i++) {
49 find_named_node(((ssgBranch*)node)->getKid(i), name);
58 * Splice a branch in between all child nodes and their parents.
61 splice_branch (ssgBranch * branch, ssgEntity * child)
63 int nParents = child->getNumParents();
64 branch->addKid(child);
65 for (int i = 0; i < nParents; i++) {
66 ssgBranch * parent = child->getParent(i);
67 parent->replaceKid(child, branch);
72 * Set up the transform matrix for a spin or rotation.
75 set_rotation (sgMat4 &matrix, double position_deg,
76 sgVec3 ¢er, sgVec3 &axis)
78 float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
80 float s = (float) sin ( temp_angle ) ;
81 float c = (float) cos ( temp_angle ) ;
82 float t = SG_ONE - c ;
84 // axis was normalized at load time
85 // hint to the compiler to put these into FP registers
90 matrix[0][0] = t * x * x + c ;
91 matrix[0][1] = t * y * x - s * z ;
92 matrix[0][2] = t * z * x + s * y ;
93 matrix[0][3] = SG_ZERO;
95 matrix[1][0] = t * x * y + s * z ;
96 matrix[1][1] = t * y * y + c ;
97 matrix[1][2] = t * z * y - s * x ;
98 matrix[1][3] = SG_ZERO;
100 matrix[2][0] = t * x * z - s * y ;
101 matrix[2][1] = t * y * z + s * x ;
102 matrix[2][2] = t * z * z + c ;
103 matrix[2][3] = SG_ZERO;
105 // hint to the compiler to put these into FP registers
110 matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
111 matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
112 matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
113 matrix[3][3] = SG_ONE;
117 * Set up the transform matrix for a translation.
120 set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis)
123 sgScaleVec3(xyz, axis, position_m);
124 sgMakeTransMat4(matrix, xyz);
129 * Read an interpolation table from properties.
131 static SGInterpTable *
132 read_interpolation_table (const SGPropertyNode * props)
134 const SGPropertyNode * table_node = props->getNode("interpolation");
135 if (table_node != 0) {
136 SGInterpTable * table = new SGInterpTable();
137 vector<const SGPropertyNode *> entries = table_node->getChildren("entry");
138 for (int i = 0; i < entries.size(); i++)
139 table->addEntry(entries[i]->getDoubleValue("ind", 0.0),
140 entries[i]->getDoubleValue("dep", 0.0));
149 ////////////////////////////////////////////////////////////////////////
150 // Implementation of FG3DModel
151 ////////////////////////////////////////////////////////////////////////
153 FG3DModel::FG3DModel ()
155 _selector(new ssgSelector),
156 _position(new ssgTransform)
160 FG3DModel::~FG3DModel ()
162 // since the nodes are attached to the scene graph, they'll be
163 // deleted automatically
165 for (int i = 0; i < _animations.size(); i++) {
166 Animation * tmp = _animations[i];
174 FG3DModel::init (const string &path)
176 SGPropertyNode props;
178 // Load the 3D aircraft object itself
179 SGPath xmlpath = globals->get_fg_root();
180 SGPath modelpath = path;
181 xmlpath.append(modelpath.str());
183 // Check for an XML wrapper
184 if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
185 readProperties(xmlpath.str(), &props);
186 if (props.hasValue("/path")) {
187 modelpath = modelpath.dir();
188 modelpath.append(props.getStringValue("/path"));
190 throw sg_exception("No path for model");
194 // Assume that textures are in
195 // the same location as the XML file.
196 ssgTexturePath((char *)xmlpath.dir().c_str());
197 _model = ssgLoad((char *)modelpath.c_str());
199 throw sg_exception("Failed to load 3D model");
202 vector<SGPropertyNode *> animation_nodes = props.getChildren("animation");
203 for (unsigned int i = 0; i < animation_nodes.size(); i++) {
204 vector<SGPropertyNode *> name_nodes =
205 animation_nodes[i]->getChildren("object-name");
206 if (name_nodes.size() < 1) {
207 SG_LOG(SG_INPUT, SG_ALERT, "No object-name given for transformation");
209 for (unsigned int j = 0; j < name_nodes.size(); j++) {
210 Animation * animation =
211 make_animation(name_nodes[j]->getStringValue(), animation_nodes[i]);
213 _animations.push_back(animation);
218 // Set up the range selector node
220 ssgRangeSelector * lod = new ssgRangeSelector;
222 ranges[0] = props.getFloatValue("range/min-m", 0);
223 ranges[1] = props.getFloatValue("range/max-m", 5000);
224 lod->setRanges(ranges, 2);
227 // Set up the alignment node
228 ssgTransform * align = new ssgTransform;
233 float h_rot = props.getFloatValue("/offsets/heading-deg", 0.0);
234 float p_rot = props.getFloatValue("/offsets/roll-deg", 0.0);
235 float r_rot = props.getFloatValue("/offsets/pitch-deg", 0.0);
236 float x_off = props.getFloatValue("/offsets/x-m", 0.0);
237 float y_off = props.getFloatValue("/offsets/y-m", 0.0);
238 float z_off = props.getFloatValue("/offsets/z-m", 0.0);
239 sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
240 sgMakeTransMat4(off_matrix, x_off, y_off, z_off);
241 sgMultMat4(res_matrix, off_matrix, rot_matrix);
242 align->setTransform(res_matrix);
244 // Set up the position node
245 _position->addKid(align);
247 // Set up the selector node
248 _selector->addKid(_position);
249 _selector->clrTraversalMaskBits(SSGTRAV_HOT);
251 // Set up a location class
252 _location = (FGLocation *) new FGLocation;
257 FG3DModel::update (int dt)
261 for (i = 0; i < _animations.size(); i++)
262 _animations[i]->update(dt);
264 _location->setPosition( _lon_deg, _lat_deg, _elev_ft );
265 _location->setOrientation( _roll_deg, _pitch_deg, _heading_deg );
268 sgCopyMat4(POS, _location->getTransformMatrix());
271 sgCopyVec3(trans, _location->get_view_pos());
273 for(i = 0; i < 4; i++) {
274 float tmp = POS[i][3];
275 for( int j=0; j<3; j++ ) {
276 POS[i][j] += (tmp * trans[j]);
279 _position->setTransform(POS);
283 FG3DModel::getVisible () const
285 return _selector->getSelect();
289 FG3DModel::setVisible (bool visible)
291 _selector->select(visible);
295 FG3DModel::setLongitudeDeg (double lon_deg)
301 FG3DModel::setLatitudeDeg (double lat_deg)
307 FG3DModel::setElevationFt (double elev_ft)
313 FG3DModel::setPosition (double lon_deg, double lat_deg, double elev_ft)
321 FG3DModel::setRollDeg (double roll_deg)
323 _roll_deg = roll_deg;
327 FG3DModel::setPitchDeg (double pitch_deg)
329 _pitch_deg = pitch_deg;
333 FG3DModel::setHeadingDeg (double heading_deg)
335 _heading_deg = heading_deg;
339 FG3DModel::setOrientation (double roll_deg, double pitch_deg,
342 _roll_deg = roll_deg;
343 _pitch_deg = pitch_deg;
344 _heading_deg = heading_deg;
347 FG3DModel::Animation *
348 FG3DModel::make_animation (const char * object_name,
349 SGPropertyNode * node)
351 Animation * animation = 0;
352 const char * type = node->getStringValue("type");
353 if (!strcmp("none", type)) {
354 animation = new NullAnimation();
355 } else if (!strcmp("range", type)) {
356 animation = new RangeAnimation();
357 } else if (!strcmp("select", type)) {
358 animation = new SelectAnimation();
359 } else if (!strcmp("spin", type)) {
360 animation = new SpinAnimation();
361 } else if (!strcmp("rotate", type)) {
362 animation = new RotateAnimation();
363 } else if (!strcmp("translate", type)) {
364 animation = new TranslateAnimation();
366 animation = new NullAnimation();
367 SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
370 ssgEntity * object = find_named_node(_model, object_name);
372 SG_LOG(SG_INPUT, SG_WARN, "Object " << object_name << " not found");
376 animation->init(object, node);
384 ////////////////////////////////////////////////////////////////////////
385 // Implementation of FG3DModel::Animation
386 ////////////////////////////////////////////////////////////////////////
388 FG3DModel::Animation::Animation ()
392 FG3DModel::Animation::~Animation ()
398 ////////////////////////////////////////////////////////////////////////
399 // Implementation of FG3DModel::NullAnimation
400 ////////////////////////////////////////////////////////////////////////
402 FG3DModel::NullAnimation::NullAnimation ()
403 : _branch(new ssgBranch)
407 FG3DModel::NullAnimation::~NullAnimation ()
413 FG3DModel::NullAnimation::init (ssgEntity * object,
414 SGPropertyNode * props)
416 splice_branch(_branch, object);
417 _branch->setName(props->getStringValue("name", 0));
421 FG3DModel::NullAnimation::update (int dt)
427 ////////////////////////////////////////////////////////////////////////
428 // Implementation of FG3DModel::RangeAnimation
429 ////////////////////////////////////////////////////////////////////////
431 FG3DModel::RangeAnimation::RangeAnimation ()
432 : _branch(new ssgRangeSelector)
436 FG3DModel::RangeAnimation::~RangeAnimation ()
442 FG3DModel::RangeAnimation::init (ssgEntity * object,
443 SGPropertyNode * props)
446 splice_branch(_branch, object);
447 _branch->setName(props->getStringValue("name", 0));
448 ranges[0] = props->getFloatValue("min-m", 0);
449 ranges[1] = props->getFloatValue("max-m", 5000);
450 _branch->setRanges(ranges, 2);
454 FG3DModel::RangeAnimation::update (int dt)
460 ////////////////////////////////////////////////////////////////////////
461 // Implementation of FG3DModel::SelectAnimation
462 ////////////////////////////////////////////////////////////////////////
464 FG3DModel::SelectAnimation::SelectAnimation ()
466 _selector(new ssgSelector)
470 FG3DModel::SelectAnimation::~SelectAnimation ()
477 FG3DModel::SelectAnimation::init (ssgEntity * object,
478 SGPropertyNode * props)
480 splice_branch(_selector, object);
481 _selector->setName(props->getStringValue("name", 0));
482 SGPropertyNode * node = props->getChild("condition");
484 _condition = fgReadCondition(node);
489 FG3DModel::SelectAnimation::update (int dt)
491 if (_condition != 0 && _condition->test())
492 _selector->select(0xffff);
494 _selector->select(0x0000);
499 ////////////////////////////////////////////////////////////////////////
500 // Implementation of FG3DModel::SpinAnimation
501 ////////////////////////////////////////////////////////////////////////
503 FG3DModel::SpinAnimation::SpinAnimation ()
507 _transform(new ssgTransform)
511 FG3DModel::SpinAnimation::~SpinAnimation ()
517 FG3DModel::SpinAnimation::init (ssgEntity * object,
518 SGPropertyNode * props)
520 // Splice in the new transform node
521 splice_branch(_transform, object);
522 _transform->setName(props->getStringValue("name", 0));
523 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
524 _factor = props->getDoubleValue("factor", 1.0);
525 _position_deg = props->getDoubleValue("starting-position-deg", 0);
526 _center[0] = props->getFloatValue("center/x-m", 0);
527 _center[1] = props->getFloatValue("center/y-m", 0);
528 _center[2] = props->getFloatValue("center/z-m", 0);
529 _axis[0] = props->getFloatValue("axis/x", 0);
530 _axis[1] = props->getFloatValue("axis/y", 0);
531 _axis[2] = props->getFloatValue("axis/z", 0);
532 sgNormalizeVec3(_axis);
536 FG3DModel::SpinAnimation::update (int dt)
538 float velocity_rpms = (_prop->getDoubleValue() * _factor / 60000.0);
539 _position_deg += (dt * velocity_rpms * 360);
540 while (_position_deg < 0)
541 _position_deg += 360.0;
542 while (_position_deg >= 360.0)
543 _position_deg -= 360.0;
544 set_rotation(_matrix, _position_deg, _center, _axis);
545 _transform->setTransform(_matrix);
550 ////////////////////////////////////////////////////////////////////////
551 // Implementation of FG3DModel::RotateAnimation
552 ////////////////////////////////////////////////////////////////////////
554 FG3DModel::RotateAnimation::RotateAnimation ()
564 _transform(new ssgTransform)
568 FG3DModel::RotateAnimation::~RotateAnimation ()
575 FG3DModel::RotateAnimation::init (ssgEntity * object,
576 SGPropertyNode * props)
578 // Splice in the new transform node
579 splice_branch(_transform, object);
580 _transform->setName(props->getStringValue("name", 0));
581 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
582 _offset_deg = props->getDoubleValue("offset-deg", 0.0);
583 _factor = props->getDoubleValue("factor", 1.0);
584 _table = read_interpolation_table(props);
585 if (props->hasValue("min-deg")) {
587 _min_deg = props->getDoubleValue("min-deg");
589 if (props->hasValue("max-deg")) {
591 _max_deg = props->getDoubleValue("max-deg");
593 _position_deg = props->getDoubleValue("starting-position-deg", 0);
594 _center[0] = props->getFloatValue("center/x-m", 0);
595 _center[1] = props->getFloatValue("center/y-m", 0);
596 _center[2] = props->getFloatValue("center/z-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);
604 FG3DModel::RotateAnimation::update (int dt)
607 _position_deg = (_prop->getDoubleValue() + _offset_deg) * _factor;
608 if (_has_min && _position_deg < _min_deg)
609 _position_deg = _min_deg;
610 if (_has_max && _position_deg > _max_deg)
611 _position_deg = _max_deg;
613 _position_deg = _table->interpolate(_prop->getDoubleValue());
615 set_rotation(_matrix, _position_deg, _center, _axis);
616 _transform->setTransform(_matrix);
621 ////////////////////////////////////////////////////////////////////////
622 // Implementation of FG3DModel::TranslateAnimation
623 ////////////////////////////////////////////////////////////////////////
625 FG3DModel::TranslateAnimation::TranslateAnimation ()
635 _transform(new ssgTransform)
639 FG3DModel::TranslateAnimation::~TranslateAnimation ()
646 FG3DModel::TranslateAnimation::init (ssgEntity * object,
647 SGPropertyNode * props)
649 // Splice in the new transform node
650 splice_branch(_transform, object);
651 _transform->setName(props->getStringValue("name", 0));
652 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
653 _offset_m = props->getDoubleValue("offset-m", 0.0);
654 _factor = props->getDoubleValue("factor", 1.0);
655 _table = read_interpolation_table(props);
656 if (props->hasValue("min-m")) {
658 _min_m = props->getDoubleValue("min-m");
660 if (props->hasValue("max-m")) {
662 _max_m = props->getDoubleValue("max-m");
664 _position_m = props->getDoubleValue("starting-position-m", 0);
665 _axis[0] = props->getFloatValue("axis/x", 0);
666 _axis[1] = props->getFloatValue("axis/y", 0);
667 _axis[2] = props->getFloatValue("axis/z", 0);
668 sgNormalizeVec3(_axis);
672 FG3DModel::TranslateAnimation::update (int dt)
675 _position_m = (_prop->getDoubleValue() + _offset_m) * _factor;
676 if (_has_min && _position_m < _min_m)
677 _position_m = _min_m;
678 if (_has_max && _position_m > _max_m)
679 _position_m = _max_m;
681 _position_m = _table->interpolate(_prop->getDoubleValue());
683 set_translation(_matrix, _position_m, _axis);
684 _transform->setTransform(_matrix);