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 * Callback to update an animation.
41 animation_callback (ssgEntity * entity, int mask)
43 ((Animation *)entity->getUserData())->update();
49 * Locate a named SSG node in a branch.
52 find_named_node (ssgEntity * node, const char * name)
54 char * node_name = node->getName();
55 if (node_name != 0 && !strcmp(name, node_name))
57 else if (node->isAKindOf(ssgTypeBranch())) {
58 int nKids = node->getNumKids();
59 for (int i = 0; i < nKids; i++) {
61 find_named_node(((ssgBranch*)node)->getKid(i), name);
70 * Splice a branch in between all child nodes and their parents.
73 splice_branch (ssgBranch * branch, ssgEntity * child)
75 int nParents = child->getNumParents();
76 branch->addKid(child);
77 for (int i = 0; i < nParents; i++) {
78 ssgBranch * parent = child->getParent(i);
79 parent->replaceKid(child, branch);
84 * Set up the transform matrix for a spin or rotation.
87 set_rotation (sgMat4 &matrix, double position_deg,
88 sgVec3 ¢er, sgVec3 &axis)
90 float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
92 float s = (float) sin ( temp_angle ) ;
93 float c = (float) cos ( temp_angle ) ;
94 float t = SG_ONE - c ;
96 // axis was normalized at load time
97 // hint to the compiler to put these into FP registers
102 matrix[0][0] = t * x * x + c ;
103 matrix[0][1] = t * y * x - s * z ;
104 matrix[0][2] = t * z * x + s * y ;
105 matrix[0][3] = SG_ZERO;
107 matrix[1][0] = t * x * y + s * z ;
108 matrix[1][1] = t * y * y + c ;
109 matrix[1][2] = t * z * y - s * x ;
110 matrix[1][3] = SG_ZERO;
112 matrix[2][0] = t * x * z - s * y ;
113 matrix[2][1] = t * y * z + s * x ;
114 matrix[2][2] = t * z * z + c ;
115 matrix[2][3] = SG_ZERO;
117 // hint to the compiler to put these into FP registers
122 matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
123 matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
124 matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
125 matrix[3][3] = SG_ONE;
129 * Set up the transform matrix for a translation.
132 set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis)
135 sgScaleVec3(xyz, axis, position_m);
136 sgMakeTransMat4(matrix, xyz);
141 * Make an offset matrix from rotations and position offset.
144 make_offsets_matrix (sgMat4 * result, double h_rot, double p_rot, double r_rot,
145 double x_off, double y_off, double z_off)
149 sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
150 sgMakeTransMat4(pos_matrix, x_off, y_off, z_off);
151 sgMultMat4(*result, pos_matrix, rot_matrix);
156 * Read an interpolation table from properties.
158 static SGInterpTable *
159 read_interpolation_table (const SGPropertyNode * props)
161 const SGPropertyNode * table_node = props->getNode("interpolation");
162 if (table_node != 0) {
163 SGInterpTable * table = new SGInterpTable();
164 vector<SGPropertyNode_ptr> entries = table_node->getChildren("entry");
165 for (unsigned int i = 0; i < entries.size(); i++)
166 table->addEntry(entries[i]->getDoubleValue("ind", 0.0),
167 entries[i]->getDoubleValue("dep", 0.0));
176 make_animation (ssgBranch * model,
177 const char * object_name,
178 SGPropertyNode * node)
180 Animation * animation = 0;
181 const char * type = node->getStringValue("type");
182 if (!strcmp("none", type)) {
183 animation = new NullAnimation(node);
184 } else if (!strcmp("range", type)) {
185 animation = new RangeAnimation(node);
186 } else if (!strcmp("billboard", type)) {
187 animation = new BillboardAnimation(node);
188 } else if (!strcmp("select", type)) {
189 animation = new SelectAnimation(node);
190 } else if (!strcmp("spin", type)) {
191 animation = new SpinAnimation(node);
192 } else if (!strcmp("rotate", type)) {
193 animation = new RotateAnimation(node);
194 } else if (!strcmp("translate", type)) {
195 animation = new TranslateAnimation(node);
197 animation = new NullAnimation(node);
198 SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
202 if (object_name != 0) {
203 object = find_named_node(model, object_name);
205 SG_LOG(SG_INPUT, SG_WARN, "Object " << object_name << " not found");
213 ssgBranch * branch = animation->getBranch();
214 splice_branch(branch, object);
215 branch->setUserData(animation);
216 branch->setTravCallback(SSG_CALLBACK_PRETRAV, animation_callback);
221 ////////////////////////////////////////////////////////////////////////
223 ////////////////////////////////////////////////////////////////////////
226 fgLoad3DModel (const string &path)
228 ssgBranch * model = 0;
229 SGPropertyNode props;
231 // Load the 3D aircraft object itself
233 SGPath modelpath = path;
234 if ( path[ 0 ] == '/' || path[ 0 ] == '\\' || ( isalpha( path[ 0 ] ) && path[ 1 ] == ':' ) ) {
238 xmlpath = globals->get_fg_root();
239 xmlpath.append(modelpath.str());
242 // Check for an XML wrapper
243 if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
244 readProperties(xmlpath.str(), &props);
245 if (props.hasValue("/path")) {
246 modelpath = modelpath.dir();
247 modelpath.append(props.getStringValue("/path"));
250 model = new ssgBranch;
254 // Assume that textures are in
255 // the same location as the XML file.
257 ssgTexturePath((char *)xmlpath.dir().c_str());
258 model = (ssgBranch *)ssgLoad((char *)modelpath.c_str());
260 throw sg_exception("Failed to load 3D model");
263 // Set up the alignment node
264 ssgTransform * align = new ssgTransform;
265 align->addKid(model);
267 make_offsets_matrix(&res_matrix,
268 props.getFloatValue("/offsets/heading-deg", 0.0),
269 props.getFloatValue("/offsets/roll-deg", 0.0),
270 props.getFloatValue("/offsets/pitch-deg", 0.0),
271 props.getFloatValue("/offsets/x-m", 0.0),
272 props.getFloatValue("/offsets/y-m", 0.0),
273 props.getFloatValue("/offsets/z-m", 0.0));
274 align->setTransform(res_matrix);
277 vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
279 for (i = 0; i < animation_nodes.size(); i++) {
280 vector<SGPropertyNode_ptr> name_nodes =
281 animation_nodes[i]->getChildren("object-name");
282 if (name_nodes.size() < 1) {
283 make_animation(model, 0, animation_nodes[i]);
285 for (unsigned int j = 0; j < name_nodes.size(); j++) {
286 make_animation(model, name_nodes[j]->getStringValue(), animation_nodes[i]);
292 vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
293 for (i = 0; i < panel_nodes.size(); i++) {
294 printf("Reading a panel in model.cxx\n");
295 FGPanelNode * panel = new FGPanelNode(panel_nodes[i]);
296 model->addKid(panel);
300 vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
301 for (i = 0; i < model_nodes.size(); i++) {
302 SGPropertyNode_ptr node = model_nodes[i];
303 ssgTransform * align = new ssgTransform;
305 make_offsets_matrix(&res_matrix,
306 node->getFloatValue("offsets/heading-deg", 0.0),
307 node->getFloatValue("offsets/roll-deg", 0.0),
308 node->getFloatValue("offsets/pitch-deg", 0.0),
309 node->getFloatValue("offsets/x-m", 0.0),
310 node->getFloatValue("offsets/y-m", 0.0),
311 node->getFloatValue("offsets/z-m", 0.0));
312 align->setTransform(res_matrix);
314 ssgBranch * kid = fgLoad3DModel(node->getStringValue("path"));
316 model->addKid(align);
324 ////////////////////////////////////////////////////////////////////////
325 // Implementation of Animation
326 ////////////////////////////////////////////////////////////////////////
328 Animation::Animation (SGPropertyNode_ptr props, ssgBranch * branch)
331 _branch->setName(props->getStringValue("name", 0));
334 Animation::~Animation ()
340 ////////////////////////////////////////////////////////////////////////
341 // Implementation of NullAnimation
342 ////////////////////////////////////////////////////////////////////////
344 NullAnimation::NullAnimation (SGPropertyNode_ptr props)
345 : Animation(props, new ssgBranch)
349 NullAnimation::~NullAnimation ()
354 NullAnimation::update ()
360 ////////////////////////////////////////////////////////////////////////
361 // Implementation of RangeAnimation
362 ////////////////////////////////////////////////////////////////////////
364 RangeAnimation::RangeAnimation (SGPropertyNode_ptr props)
365 : Animation(props, new ssgRangeSelector)
367 float ranges[] = { props->getFloatValue("min-m", 0),
368 props->getFloatValue("max-m", 5000) };
369 ((ssgRangeSelector *)_branch)->setRanges(ranges, 2);
373 RangeAnimation::~RangeAnimation ()
378 RangeAnimation::update ()
384 ////////////////////////////////////////////////////////////////////////
385 // Implementation of BillboardAnimation
386 ////////////////////////////////////////////////////////////////////////
388 BillboardAnimation::BillboardAnimation (SGPropertyNode_ptr props)
389 : Animation(props, new ssgCutout(props->getBoolValue("spherical", true)))
393 BillboardAnimation::~BillboardAnimation ()
398 BillboardAnimation::update ()
404 ////////////////////////////////////////////////////////////////////////
405 // Implementation of SelectAnimation
406 ////////////////////////////////////////////////////////////////////////
408 SelectAnimation::SelectAnimation (SGPropertyNode_ptr props)
409 : Animation(props, new ssgSelector),
412 SGPropertyNode * node = props->getChild("condition");
414 _condition = fgReadCondition(node);
417 SelectAnimation::~SelectAnimation ()
423 SelectAnimation::update ()
425 if (_condition != 0 && _condition->test())
426 ((ssgSelector *)_branch)->select(0xffff);
428 ((ssgSelector *)_branch)->select(0x0000);
433 ////////////////////////////////////////////////////////////////////////
434 // Implementation of SpinAnimation
435 ////////////////////////////////////////////////////////////////////////
437 SpinAnimation::SpinAnimation (SGPropertyNode_ptr props)
438 : Animation(props, new ssgTransform),
439 _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
440 _factor(props->getDoubleValue("factor", 1.0)),
441 _position_deg(props->getDoubleValue("starting-position-deg", 0)),
442 _last_time_sec(globals->get_sim_time_sec())
444 _center[0] = props->getFloatValue("center/x-m", 0);
445 _center[1] = props->getFloatValue("center/y-m", 0);
446 _center[2] = props->getFloatValue("center/z-m", 0);
447 _axis[0] = props->getFloatValue("axis/x", 0);
448 _axis[1] = props->getFloatValue("axis/y", 0);
449 _axis[2] = props->getFloatValue("axis/z", 0);
450 sgNormalizeVec3(_axis);
453 SpinAnimation::~SpinAnimation ()
458 SpinAnimation::update ()
460 double sim_time = globals->get_sim_time_sec();
461 double dt = sim_time - _last_time_sec;
462 _last_time_sec = sim_time;
464 float velocity_rpms = (_prop->getDoubleValue() * _factor / 60.0);
465 _position_deg += (dt * velocity_rpms * 360);
466 while (_position_deg < 0)
467 _position_deg += 360.0;
468 while (_position_deg >= 360.0)
469 _position_deg -= 360.0;
470 set_rotation(_matrix, _position_deg, _center, _axis);
471 ((ssgTransform *)_branch)->setTransform(_matrix);
476 ////////////////////////////////////////////////////////////////////////
477 // Implementation of RotateAnimation
478 ////////////////////////////////////////////////////////////////////////
480 RotateAnimation::RotateAnimation (SGPropertyNode_ptr props)
481 : Animation(props, new ssgTransform),
482 _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
483 _offset_deg(props->getDoubleValue("offset-deg", 0.0)),
484 _factor(props->getDoubleValue("factor", 1.0)),
485 _table(read_interpolation_table(props)),
486 _has_min(props->hasValue("min-deg")),
487 _min_deg(props->getDoubleValue("min-deg")),
488 _has_max(props->hasValue("max-deg")),
489 _max_deg(props->getDoubleValue("max-deg")),
490 _position_deg(props->getDoubleValue("starting-position-deg", 0))
492 _center[0] = props->getFloatValue("center/x-m", 0);
493 _center[1] = props->getFloatValue("center/y-m", 0);
494 _center[2] = props->getFloatValue("center/z-m", 0);
495 _axis[0] = props->getFloatValue("axis/x", 0);
496 _axis[1] = props->getFloatValue("axis/y", 0);
497 _axis[2] = props->getFloatValue("axis/z", 0);
498 sgNormalizeVec3(_axis);
501 RotateAnimation::~RotateAnimation ()
507 RotateAnimation::update ()
510 _position_deg = (_prop->getDoubleValue() + _offset_deg) * _factor;
511 if (_has_min && _position_deg < _min_deg)
512 _position_deg = _min_deg;
513 if (_has_max && _position_deg > _max_deg)
514 _position_deg = _max_deg;
516 _position_deg = _table->interpolate(_prop->getDoubleValue());
518 set_rotation(_matrix, _position_deg, _center, _axis);
519 ((ssgTransform *)_branch)->setTransform(_matrix);
524 ////////////////////////////////////////////////////////////////////////
525 // Implementation of TranslateAnimation
526 ////////////////////////////////////////////////////////////////////////
528 TranslateAnimation::TranslateAnimation (SGPropertyNode_ptr props)
529 : Animation(props, new ssgTransform),
530 _prop(fgGetNode(props->getStringValue("property", "/null"), true)),
531 _offset_m(props->getDoubleValue("offset-m", 0.0)),
532 _factor(props->getDoubleValue("factor", 1.0)),
533 _table(read_interpolation_table(props)),
534 _has_min(props->hasValue("min-m")),
535 _min_m(props->getDoubleValue("min-m")),
536 _has_max(props->hasValue("max-m")),
537 _max_m(props->getDoubleValue("max-m")),
538 _position_m(props->getDoubleValue("starting-position-m", 0))
540 _axis[0] = props->getFloatValue("axis/x", 0);
541 _axis[1] = props->getFloatValue("axis/y", 0);
542 _axis[2] = props->getFloatValue("axis/z", 0);
543 sgNormalizeVec3(_axis);
546 TranslateAnimation::~TranslateAnimation ()
552 TranslateAnimation::update ()
555 _position_m = (_prop->getDoubleValue() + _offset_m) * _factor;
556 if (_has_min && _position_m < _min_m)
557 _position_m = _min_m;
558 if (_has_max && _position_m > _max_m)
559 _position_m = _max_m;
561 _position_m = _table->interpolate(_prop->getDoubleValue());
563 set_translation(_matrix, _position_m, _axis);
564 ((ssgTransform *)_branch)->setTransform(_matrix);
569 ////////////////////////////////////////////////////////////////////////
570 // Implementation of FGModelPlacement.
571 ////////////////////////////////////////////////////////////////////////
573 FGModelPlacement::FGModelPlacement ()
580 _selector(new ssgSelector),
581 _position(new ssgTransform),
582 _location(new FGLocation)
586 FGModelPlacement::~FGModelPlacement ()
591 FGModelPlacement::init (const string &path)
593 ssgBranch * model = fgLoad3DModel(path);
595 _position->addKid(model);
596 _selector->addKid(_position);
597 _selector->clrTraversalMaskBits(SSGTRAV_HOT);
601 FGModelPlacement::update ()
603 _location->setPosition( _lon_deg, _lat_deg, _elev_ft );
604 _location->setOrientation( _roll_deg, _pitch_deg, _heading_deg );
607 sgCopyMat4(POS, _location->getTransformMatrix());
610 sgCopyVec3(trans, _location->get_view_pos());
612 for(int i = 0; i < 4; i++) {
613 float tmp = POS[i][3];
614 for( int j=0; j<3; j++ ) {
615 POS[i][j] += (tmp * trans[j]);
618 _position->setTransform(POS);
622 FGModelPlacement::getVisible () const
624 return (_selector->getSelect() != 0);
628 FGModelPlacement::setVisible (bool visible)
630 _selector->select(visible);
634 FGModelPlacement::setLongitudeDeg (double lon_deg)
640 FGModelPlacement::setLatitudeDeg (double lat_deg)
646 FGModelPlacement::setElevationFt (double elev_ft)
652 FGModelPlacement::setPosition (double lon_deg, double lat_deg, double elev_ft)
660 FGModelPlacement::setRollDeg (double roll_deg)
662 _roll_deg = roll_deg;
666 FGModelPlacement::setPitchDeg (double pitch_deg)
668 _pitch_deg = pitch_deg;
672 FGModelPlacement::setHeadingDeg (double heading_deg)
674 _heading_deg = heading_deg;
678 FGModelPlacement::setOrientation (double roll_deg, double pitch_deg,
681 _roll_deg = roll_deg;
682 _pitch_deg = pitch_deg;
683 _heading_deg = heading_deg;