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/point3d.hxx>
18 #include <simgear/math/sg_geodesy.hxx>
19 #include <simgear/misc/exception.hxx>
20 #include <simgear/misc/sg_path.hxx>
22 #include <Main/globals.hxx>
23 #include <Main/location.hxx>
24 #include <Scenery/scenery.hxx>
30 ////////////////////////////////////////////////////////////////////////
31 // Static utility functions.
32 ////////////////////////////////////////////////////////////////////////
35 * Locate a named SSG node in a branch.
38 find_named_node (ssgEntity * node, const char * name)
40 char * node_name = node->getName();
41 if (node_name != 0 && !strcmp(name, node_name))
43 else if (node->isAKindOf(ssgTypeBranch())) {
44 int nKids = node->getNumKids();
45 for (int i = 0; i < nKids; i++) {
47 find_named_node(((ssgBranch*)node)->getKid(i), name);
56 * Splice a branch in between all child nodes and their parents.
59 splice_branch (ssgBranch * branch, ssgEntity * child)
61 int nParents = child->getNumParents();
62 branch->addKid(child);
63 for (int i = 0; i < nParents; i++) {
64 ssgBranch * parent = child->getParent(i);
65 parent->replaceKid(child, branch);
70 * Set up the transform matrix for a spin or rotation.
73 set_rotation (sgMat4 &matrix, double position_deg,
74 sgVec3 ¢er, sgVec3 &axis)
76 float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
78 float s = (float) sin ( temp_angle ) ;
79 float c = (float) cos ( temp_angle ) ;
80 float t = SG_ONE - c ;
82 // axis was normalized at load time
83 // hint to the compiler to put these into FP registers
88 matrix[0][0] = t * x * x + c ;
89 matrix[0][1] = t * y * x - s * z ;
90 matrix[0][2] = t * z * x + s * y ;
91 matrix[0][3] = SG_ZERO;
93 matrix[1][0] = t * x * y + s * z ;
94 matrix[1][1] = t * y * y + c ;
95 matrix[1][2] = t * z * y - s * x ;
96 matrix[1][3] = SG_ZERO;
98 matrix[2][0] = t * x * z - s * y ;
99 matrix[2][1] = t * y * z + s * x ;
100 matrix[2][2] = t * z * z + c ;
101 matrix[2][3] = SG_ZERO;
103 // hint to the compiler to put these into FP registers
108 matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
109 matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
110 matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
111 matrix[3][3] = SG_ONE;
115 * Set up the transform matrix for a translation.
118 set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis)
121 sgScaleVec3(xyz, axis, position_m);
122 sgMakeTransMat4(matrix, xyz);
127 ////////////////////////////////////////////////////////////////////////
128 // Implementation of FG3DModel
129 ////////////////////////////////////////////////////////////////////////
131 FG3DModel::FG3DModel ()
133 _selector(new ssgSelector),
134 _position(new ssgTransform)
138 FG3DModel::~FG3DModel ()
140 // since the nodes are attached to the scene graph, they'll be
141 // deleted automatically
143 for (int i = 0; i < _animations.size(); i++) {
144 Animation * tmp = _animations[i];
152 FG3DModel::init (const string &path)
154 SGPropertyNode props;
156 // Load the 3D aircraft object itself
157 SGPath xmlpath = globals->get_fg_root();
158 SGPath modelpath = path;
159 xmlpath.append(modelpath.str());
161 // Check for an XML wrapper
162 if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
163 readProperties(xmlpath.str(), &props);
164 if (props.hasValue("/path")) {
165 modelpath = modelpath.dir();
166 modelpath.append(props.getStringValue("/path"));
168 throw sg_exception("No path for model");
172 // Assume that textures are in
173 // the same location as the XML file.
174 ssgTexturePath((char *)xmlpath.dir().c_str());
175 _model = ssgLoad((char *)modelpath.c_str());
177 throw sg_exception("Failed to load 3D model");
180 vector<SGPropertyNode *> animation_nodes = props.getChildren("animation");
181 for (unsigned int i = 0; i < animation_nodes.size(); i++) {
182 vector<SGPropertyNode *> name_nodes =
183 animation_nodes[i]->getChildren("object-name");
184 if (name_nodes.size() < 1) {
185 SG_LOG(SG_INPUT, SG_ALERT, "No object-name given for transformation");
187 for (unsigned int j = 0; j < name_nodes.size(); j++) {
188 Animation * animation =
189 make_animation(name_nodes[j]->getStringValue(), animation_nodes[i]);
191 _animations.push_back(animation);
196 // Set up the range selector node
198 ssgRangeSelector * lod = new ssgRangeSelector;
200 ranges[0] = props.getFloatValue("range/min-m", 0);
201 ranges[1] = props.getFloatValue("range/max-m", 5000);
202 lod->setRanges(ranges, 2);
205 // Set up the alignment node
206 ssgTransform * align = new ssgTransform;
211 float h_rot = props.getFloatValue("/offsets/heading-deg", 0.0);
212 float p_rot = props.getFloatValue("/offsets/roll-deg", 0.0);
213 float r_rot = props.getFloatValue("/offsets/pitch-deg", 0.0);
214 float x_off = props.getFloatValue("/offsets/x-m", 0.0);
215 float y_off = props.getFloatValue("/offsets/y-m", 0.0);
216 float z_off = props.getFloatValue("/offsets/z-m", 0.0);
217 sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
218 sgMakeTransMat4(off_matrix, x_off, y_off, z_off);
219 sgMultMat4(res_matrix, off_matrix, rot_matrix);
220 align->setTransform(res_matrix);
222 // Set up the position node
223 _position->addKid(align);
225 // Set up the selector node
226 _selector->addKid(_position);
227 _selector->clrTraversalMaskBits(SSGTRAV_HOT);
229 // Set up a location class
230 _location = (FGLocation *) new FGLocation;
235 FG3DModel::update (int dt)
237 for (unsigned int i = 0; i < _animations.size(); i++)
238 _animations[i]->update(dt);
240 _location->setPosition( _lon_deg, _lat_deg, _elev_ft );
241 _location->setOrientation( _roll_deg, _pitch_deg, _heading_deg );
244 sgCopyMat4(POS, _location->getTransformMatrix());
247 sgCopyVec3(trans, _location->get_view_pos());
249 for( int i=0; i<4; i++) {
250 float tmp = POS[i][3];
251 for( int j=0; j<3; j++ ) {
252 POS[i][j] += (tmp * trans[j]);
255 _position->setTransform(POS);
259 FG3DModel::getVisible () const
261 return _selector->getSelect();
265 FG3DModel::setVisible (bool visible)
267 _selector->select(visible);
271 FG3DModel::setLongitudeDeg (double lon_deg)
277 FG3DModel::setLatitudeDeg (double lat_deg)
283 FG3DModel::setElevationFt (double elev_ft)
289 FG3DModel::setPosition (double lon_deg, double lat_deg, double elev_ft)
297 FG3DModel::setRollDeg (double roll_deg)
299 _roll_deg = roll_deg;
303 FG3DModel::setPitchDeg (double pitch_deg)
305 _pitch_deg = pitch_deg;
309 FG3DModel::setHeadingDeg (double heading_deg)
311 _heading_deg = heading_deg;
315 FG3DModel::setOrientation (double roll_deg, double pitch_deg,
318 _roll_deg = roll_deg;
319 _pitch_deg = pitch_deg;
320 _heading_deg = heading_deg;
323 FG3DModel::Animation *
324 FG3DModel::make_animation (const char * object_name,
325 SGPropertyNode * node)
327 Animation * animation = 0;
328 const char * type = node->getStringValue("type");
329 if (!strcmp("none", type)) {
330 animation = new NullAnimation();
331 } else if (!strcmp("range", type)) {
332 animation = new RangeAnimation();
333 } else if (!strcmp("select", type)) {
334 animation = new SelectAnimation();
335 } else if (!strcmp("spin", type)) {
336 animation = new SpinAnimation();
337 } else if (!strcmp("rotate", type)) {
338 animation = new RotateAnimation();
339 } else if (!strcmp("translate", type)) {
340 animation = new TranslateAnimation();
342 animation = new NullAnimation();
343 SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
346 ssgEntity * object = find_named_node(_model, object_name);
348 SG_LOG(SG_INPUT, SG_WARN, "Object " << object_name << " not found");
352 animation->init(object, node);
360 ////////////////////////////////////////////////////////////////////////
361 // Implementation of FG3DModel::Animation
362 ////////////////////////////////////////////////////////////////////////
364 FG3DModel::Animation::Animation ()
368 FG3DModel::Animation::~Animation ()
374 ////////////////////////////////////////////////////////////////////////
375 // Implementation of FG3DModel::NullAnimation
376 ////////////////////////////////////////////////////////////////////////
378 FG3DModel::NullAnimation::NullAnimation ()
379 : _branch(new ssgBranch)
383 FG3DModel::NullAnimation::~NullAnimation ()
389 FG3DModel::NullAnimation::init (ssgEntity * object,
390 SGPropertyNode * props)
392 splice_branch(_branch, object);
393 _branch->setName(props->getStringValue("name", 0));
397 FG3DModel::NullAnimation::update (int dt)
403 ////////////////////////////////////////////////////////////////////////
404 // Implementation of FG3DModel::RangeAnimation
405 ////////////////////////////////////////////////////////////////////////
407 FG3DModel::RangeAnimation::RangeAnimation ()
408 : _branch(new ssgRangeSelector)
412 FG3DModel::RangeAnimation::~RangeAnimation ()
418 FG3DModel::RangeAnimation::init (ssgEntity * object,
419 SGPropertyNode * props)
422 splice_branch(_branch, object);
423 _branch->setName(props->getStringValue("name", 0));
424 ranges[0] = props->getFloatValue("min-m", 0);
425 ranges[1] = props->getFloatValue("max-m", 5000);
426 _branch->setRanges(ranges, 2);
430 FG3DModel::RangeAnimation::update (int dt)
436 ////////////////////////////////////////////////////////////////////////
437 // Implementation of FG3DModel::SelectAnimation
438 ////////////////////////////////////////////////////////////////////////
440 FG3DModel::SelectAnimation::SelectAnimation ()
442 _selector(new ssgSelector)
446 FG3DModel::SelectAnimation::~SelectAnimation ()
453 FG3DModel::SelectAnimation::init (ssgEntity * object,
454 SGPropertyNode * props)
456 splice_branch(_selector, object);
457 _selector->setName(props->getStringValue("name", 0));
458 SGPropertyNode * node = props->getChild("condition");
460 _condition = fgReadCondition(node);
465 FG3DModel::SelectAnimation::update (int dt)
467 if (_condition != 0 && _condition->test())
468 _selector->select(0xffff);
470 _selector->select(0x0000);
475 ////////////////////////////////////////////////////////////////////////
476 // Implementation of FG3DModel::SpinAnimation
477 ////////////////////////////////////////////////////////////////////////
479 FG3DModel::SpinAnimation::SpinAnimation ()
483 _transform(new ssgTransform)
487 FG3DModel::SpinAnimation::~SpinAnimation ()
493 FG3DModel::SpinAnimation::init (ssgEntity * object,
494 SGPropertyNode * props)
496 // Splice in the new transform node
497 splice_branch(_transform, object);
498 _transform->setName(props->getStringValue("name", 0));
499 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
500 _factor = props->getDoubleValue("factor", 1.0);
501 _position_deg = props->getDoubleValue("starting-position-deg", 0);
502 _center[0] = props->getFloatValue("center/x-m", 0);
503 _center[1] = props->getFloatValue("center/y-m", 0);
504 _center[2] = props->getFloatValue("center/z-m", 0);
505 _axis[0] = props->getFloatValue("axis/x", 0);
506 _axis[1] = props->getFloatValue("axis/y", 0);
507 _axis[2] = props->getFloatValue("axis/z", 0);
508 sgNormalizeVec3(_axis);
512 FG3DModel::SpinAnimation::update (int dt)
514 float velocity_rpms = (_prop->getDoubleValue() * _factor / 60000.0);
515 _position_deg += (dt * velocity_rpms * 360);
516 while (_position_deg < 0)
517 _position_deg += 360.0;
518 while (_position_deg >= 360.0)
519 _position_deg -= 360.0;
520 set_rotation(_matrix, _position_deg, _center, _axis);
521 _transform->setTransform(_matrix);
526 ////////////////////////////////////////////////////////////////////////
527 // Implementation of FG3DModel::RotateAnimation
528 ////////////////////////////////////////////////////////////////////////
530 FG3DModel::RotateAnimation::RotateAnimation ()
539 _transform(new ssgTransform)
543 FG3DModel::RotateAnimation::~RotateAnimation ()
549 FG3DModel::RotateAnimation::init (ssgEntity * object,
550 SGPropertyNode * props)
552 // Splice in the new transform node
553 splice_branch(_transform, object);
554 _transform->setName(props->getStringValue("name", 0));
555 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
556 _offset_deg = props->getDoubleValue("offset-deg", 0.0);
557 _factor = props->getDoubleValue("factor", 1.0);
558 if (props->hasValue("min-deg")) {
560 _min_deg = props->getDoubleValue("min-deg");
562 if (props->hasValue("max-deg")) {
564 _max_deg = props->getDoubleValue("max-deg");
566 _position_deg = props->getDoubleValue("starting-position-deg", 0);
567 _center[0] = props->getFloatValue("center/x-m", 0);
568 _center[1] = props->getFloatValue("center/y-m", 0);
569 _center[2] = props->getFloatValue("center/z-m", 0);
570 _axis[0] = props->getFloatValue("axis/x", 0);
571 _axis[1] = props->getFloatValue("axis/y", 0);
572 _axis[2] = props->getFloatValue("axis/z", 0);
573 sgNormalizeVec3(_axis);
577 FG3DModel::RotateAnimation::update (int dt)
579 _position_deg = ((_prop->getDoubleValue() + _offset_deg) * _factor);
580 if (_has_min && _position_deg < _min_deg)
581 _position_deg = _min_deg;
582 if (_has_max && _position_deg > _max_deg)
583 _position_deg = _max_deg;
584 set_rotation(_matrix, _position_deg, _center, _axis);
585 _transform->setTransform(_matrix);
590 ////////////////////////////////////////////////////////////////////////
591 // Implementation of FG3DModel::TranslateAnimation
592 ////////////////////////////////////////////////////////////////////////
594 FG3DModel::TranslateAnimation::TranslateAnimation ()
603 _transform(new ssgTransform)
607 FG3DModel::TranslateAnimation::~TranslateAnimation ()
613 FG3DModel::TranslateAnimation::init (ssgEntity * object,
614 SGPropertyNode * props)
616 // Splice in the new transform node
617 splice_branch(_transform, object);
618 _transform->setName(props->getStringValue("name", 0));
619 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
620 _offset_m = props->getDoubleValue("offset-m", 0.0);
621 _factor = props->getDoubleValue("factor", 1.0);
622 if (props->hasValue("min-m")) {
624 _min_m = props->getDoubleValue("min-m");
626 if (props->hasValue("max-m")) {
628 _max_m = props->getDoubleValue("max-m");
630 _position_m = props->getDoubleValue("starting-position-m", 0);
631 _axis[0] = props->getFloatValue("axis/x", 0);
632 _axis[1] = props->getFloatValue("axis/y", 0);
633 _axis[2] = props->getFloatValue("axis/z", 0);
634 sgNormalizeVec3(_axis);
638 FG3DModel::TranslateAnimation::update (int dt)
640 _position_m = ((_prop->getDoubleValue() + _offset_m) * _factor);
641 if (_has_min && _position_m < _min_m)
642 _position_m = _min_m;
643 if (_has_max && _position_m > _max_m)
644 _position_m = _max_m;
645 set_translation(_matrix, _position_m, _axis);
646 _transform->setTransform(_matrix);