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 <Scenery/scenery.hxx>
29 ////////////////////////////////////////////////////////////////////////
30 // Static utility functions.
31 ////////////////////////////////////////////////////////////////////////
34 * Locate a named SSG node in a branch.
37 find_named_node (ssgEntity * node, const char * name)
39 char * node_name = node->getName();
40 if (node_name != 0 && !strcmp(name, node_name))
42 else if (node->isAKindOf(ssgTypeBranch())) {
43 int nKids = node->getNumKids();
44 for (int i = 0; i < nKids; i++) {
46 find_named_node(((ssgBranch*)node)->getKid(i), name);
55 * Splice a branch in between all child nodes and their parents.
58 splice_branch (ssgBranch * branch, ssgEntity * child)
60 int nParents = child->getNumParents();
61 branch->addKid(child);
62 for (int i = 0; i < nParents; i++) {
63 ssgBranch * parent = child->getParent(i);
64 parent->replaceKid(child, branch);
69 * Set up the transform matrix for a spin or rotation.
72 set_rotation (sgMat4 &matrix, double position_deg,
73 sgVec3 ¢er, sgVec3 &axis)
75 float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
77 float s = (float) sin ( temp_angle ) ;
78 float c = (float) cos ( temp_angle ) ;
79 float t = SG_ONE - c ;
81 // axis was normalized at load time
82 // hint to the compiler to put these into FP registers
87 matrix[0][0] = t * x * x + c ;
88 matrix[0][1] = t * y * x - s * z ;
89 matrix[0][2] = t * z * x + s * y ;
90 matrix[0][3] = SG_ZERO;
92 matrix[1][0] = t * x * y + s * z ;
93 matrix[1][1] = t * y * y + c ;
94 matrix[1][2] = t * z * y - s * x ;
95 matrix[1][3] = SG_ZERO;
97 matrix[2][0] = t * x * z - s * y ;
98 matrix[2][1] = t * y * z + s * x ;
99 matrix[2][2] = t * z * z + c ;
100 matrix[2][3] = SG_ZERO;
102 // hint to the compiler to put these into FP registers
107 matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
108 matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
109 matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
110 matrix[3][3] = SG_ONE;
114 * Set up the transform matrix for a translation.
117 set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis)
120 sgScaleVec3(xyz, axis, position_m);
121 sgMakeTransMat4(matrix, xyz);
125 * make model transformation Matrix - based on optimizations by NHV
127 static void MakeTRANS( sgMat4 dst, const double Theta,
128 const double Phi, const double Psi,
129 const double lon, const double lat)
131 SGfloat cosTheta = (SGfloat) cos(Theta);
132 SGfloat sinTheta = (SGfloat) sin(Theta);
133 SGfloat cosPhi = (SGfloat) cos(Phi);
134 SGfloat sinPhi = (SGfloat) sin(Phi);
135 SGfloat sinPsi = (SGfloat) sin(Psi) ;
136 SGfloat cosPsi = (SGfloat) cos(Psi) ;
138 SGfloat cosLon = SGD_ONE;
139 SGfloat sinLon = SGD_ZERO;
140 SGfloat cosLat = SGD_ONE;
141 SGfloat sinLat = SGD_ZERO;
143 if ( lon != SG_ZERO ) {
144 sinLon = (SGfloat) sin( lon ) ;
145 cosLon = (SGfloat) cos( lon ) ;
147 if ( lat != SG_ZERO ) {
148 sinLat = (SGfloat) sin( lat ) ;
149 cosLat = (SGfloat) cos( lat ) ;
155 tmp[0][0] = cosPhi * cosTheta;
156 tmp[0][1] = sinPhi * cosPsi + cosPhi * -sinTheta * -sinPsi;
157 tmp[0][2] = sinPhi * sinPsi + cosPhi * -sinTheta * cosPsi;
159 tmp[1][0] = -sinPhi * cosTheta;
160 tmp[1][1] = cosPhi * cosPsi + -sinPhi * -sinTheta * -sinPsi;
161 tmp[1][2] = cosPhi * sinPsi + -sinPhi * -sinTheta * cosPsi;
163 tmp[2][0] = sinTheta;
164 tmp[2][1] = cosTheta * -sinPsi;
165 tmp[2][2] = cosTheta * cosPsi;
167 float a = cosLon * cosLat; // world up [0]
169 float c = sinLat * cosLon;
170 dst[2][0] = a*tmp[0][0] + b*tmp[0][1] + c*tmp[0][2] ;
171 dst[1][0] = a*tmp[1][0] + b*tmp[1][1] + c*tmp[1][2] ;
172 dst[0][0] = -(a*tmp[2][0] + b*tmp[2][1] + c*tmp[2][2]) ;
173 dst[3][0] = SG_ZERO ;
175 a = cosLat * sinLon; // world up [1]
178 dst[2][1] = a*tmp[0][0] + b*tmp[0][1] + c*tmp[0][2] ;
179 dst[1][1] = a*tmp[1][0] + b*tmp[1][1] + c*tmp[1][2] ;
180 dst[0][1] = -(a*tmp[2][0] + b*tmp[2][1] + c*tmp[2][2]) ;
181 dst[3][1] = SG_ZERO ;
183 a = -sinLat; // world up [2]
185 dst[2][2] = a*tmp[0][0] + c*tmp[0][2] ;
186 dst[1][2] = a*tmp[1][0] + c*tmp[1][2] ;
187 dst[0][2] = -(a*tmp[2][0] + c*tmp[2][2]) ;
188 dst[3][2] = SG_ZERO ;
190 dst[2][3] = SG_ZERO ;
191 dst[1][3] = SG_ZERO ;
192 dst[0][3] = SG_ZERO ;
198 // TODO: once this is working, look at Norm's optimized version
200 world_coordinate( sgCoord *obj_pos,
201 double lat_deg, double lon_deg, double elev_ft,
202 double roll_deg, double pitch_deg, double hdg_deg)
205 double sea_level_radius_m;
208 // Convert from geodetic to geocentric
210 sgGeodToGeoc(lat_deg * SGD_DEGREES_TO_RADIANS,
211 elev_ft * SG_FEET_TO_METER,
215 Point3D center = scenery.get_center();
217 Point3D geod = Point3D(lon_deg * SG_DEGREES_TO_RADIANS,
220 geod.setz(geod.radius() + elev_ft * SG_FEET_TO_METER);
222 Point3D world_pos = sgPolarToCart3d( geod );
223 Point3D offset = world_pos - center;
226 sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
230 MakeTRANS( TRANS, pitch_deg * SG_DEGREES_TO_RADIANS,
231 roll_deg * SG_DEGREES_TO_RADIANS,
232 -hdg_deg * SG_DEGREES_TO_RADIANS,
233 lon_deg * SG_DEGREES_TO_RADIANS,
234 -lat_deg * SG_DEGREES_TO_RADIANS );
236 sgPostMultMat4( TRANS, POS );
238 sgSetCoord( obj_pos, TRANS );
243 ////////////////////////////////////////////////////////////////////////
244 // Implementation of FG3DModel
245 ////////////////////////////////////////////////////////////////////////
247 FG3DModel::FG3DModel ()
249 _selector(new ssgSelector),
250 _position(new ssgTransform)
254 FG3DModel::~FG3DModel ()
256 // since the nodes are attached to the scene graph, they'll be
257 // deleted automatically
259 for (int i = 0; i < _animations.size(); i++) {
260 Animation * tmp = _animations[i];
268 FG3DModel::init (const string &path)
270 SGPropertyNode props;
272 // Load the 3D aircraft object itself
273 SGPath xmlpath = globals->get_fg_root();
274 SGPath modelpath = path;
275 xmlpath.append(modelpath.str());
277 // Check for an XML wrapper
278 if (xmlpath.str().substr(xmlpath.str().size() - 4, 4) == ".xml") {
279 readProperties(xmlpath.str(), &props);
280 if (props.hasValue("/path")) {
281 modelpath = modelpath.dir();
282 modelpath.append(props.getStringValue("/path"));
284 throw sg_exception("No path for model");
288 // Assume that textures are in
289 // the same location as the XML file.
290 ssgTexturePath((char *)xmlpath.dir().c_str());
291 _model = ssgLoad((char *)modelpath.c_str());
293 throw sg_exception("Failed to load 3D model");
296 vector<SGPropertyNode *> animation_nodes = props.getChildren("animation");
297 for (unsigned int i = 0; i < animation_nodes.size(); i++) {
298 vector<SGPropertyNode *> name_nodes =
299 animation_nodes[i]->getChildren("object-name");
300 if (name_nodes.size() < 1) {
301 SG_LOG(SG_INPUT, SG_ALERT, "No object-name given for transformation");
303 for (unsigned int j = 0; j < name_nodes.size(); j++) {
304 Animation * animation =
305 make_animation(name_nodes[j]->getStringValue(), animation_nodes[i]);
307 _animations.push_back(animation);
312 // Set up the alignment node
313 ssgTransform * align = new ssgTransform;
314 align->addKid(_model);
318 float h_rot = props.getFloatValue("/offsets/heading-deg", 0.0);
319 float p_rot = props.getFloatValue("/offsets/roll-deg", 0.0);
320 float r_rot = props.getFloatValue("/offsets/pitch-deg", 0.0);
321 float x_off = props.getFloatValue("/offsets/x-m", 0.0);
322 float y_off = props.getFloatValue("/offsets/y-m", 0.0);
323 float z_off = props.getFloatValue("/offsets/z-m", 0.0);
324 sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
325 sgMakeTransMat4(off_matrix, x_off, y_off, z_off);
326 sgMultMat4(res_matrix, off_matrix, rot_matrix);
327 align->setTransform(res_matrix);
329 // Set up the position node
330 _position->addKid(align);
332 // Set up the selector node
333 _selector->addKid(_position);
334 _selector->clrTraversalMaskBits(SSGTRAV_HOT);
338 FG3DModel::update (int dt)
340 for (unsigned int i = 0; i < _animations.size(); i++)
341 _animations[i]->update(dt);
343 _selector->select(true);
346 world_coordinate(&obj_pos, _lat_deg, _lon_deg, _elev_ft,
347 _roll_deg, _pitch_deg, _heading_deg);
348 _position->setTransform(&obj_pos);
352 FG3DModel::getVisible () const
354 return _selector->getSelect();
358 FG3DModel::setVisible (bool visible)
360 _selector->select(visible);
364 FG3DModel::setPosition (double lon_deg, double lat_deg, double elev_ft)
372 FG3DModel::setOrientation (double roll_deg, double pitch_deg,
375 _roll_deg = roll_deg;
376 _pitch_deg = pitch_deg;
377 _heading_deg = heading_deg;
380 FG3DModel::Animation *
381 FG3DModel::make_animation (const char * object_name,
382 SGPropertyNode * node)
384 Animation * animation = 0;
385 const char * type = node->getStringValue("type");
386 if (!strcmp("none", type)) {
387 animation = new NullAnimation();
388 } else if (!strcmp("select", type)) {
389 animation = new SelectAnimation();
390 } else if (!strcmp("spin", type)) {
391 animation = new SpinAnimation();
392 } else if (!strcmp("rotate", type)) {
393 animation = new RotateAnimation();
394 } else if (!strcmp("translate", type)) {
395 animation = new TranslateAnimation();
397 animation = new NullAnimation();
398 SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
401 ssgEntity * object = find_named_node(_model, object_name);
403 SG_LOG(SG_INPUT, SG_WARN, "Object " << object_name << " not found");
407 animation->init(object, node);
415 ////////////////////////////////////////////////////////////////////////
416 // Implementation of FG3DModel::Animation
417 ////////////////////////////////////////////////////////////////////////
419 FG3DModel::Animation::Animation ()
423 FG3DModel::Animation::~Animation ()
429 ////////////////////////////////////////////////////////////////////////
430 // Implementation of FG3DModel::NullAnimation
431 ////////////////////////////////////////////////////////////////////////
433 FG3DModel::NullAnimation::NullAnimation ()
434 : _branch(new ssgBranch)
438 FG3DModel::NullAnimation::~NullAnimation ()
444 FG3DModel::NullAnimation::init (ssgEntity * object,
445 SGPropertyNode * props)
447 splice_branch(_branch, object);
448 _branch->setName(props->getStringValue("name", 0));
452 FG3DModel::NullAnimation::update (int dt)
458 ////////////////////////////////////////////////////////////////////////
459 // Implementation of FG3DModel::SelectAnimation
460 ////////////////////////////////////////////////////////////////////////
462 FG3DModel::SelectAnimation::SelectAnimation ()
464 _selector(new ssgSelector)
468 FG3DModel::SelectAnimation::~SelectAnimation ()
475 FG3DModel::SelectAnimation::init (ssgEntity * object,
476 SGPropertyNode * props)
478 splice_branch(_selector, object);
479 _selector->setName(props->getStringValue("name", 0));
480 SGPropertyNode * node = props->getChild("condition");
482 _condition = fgReadCondition(node);
487 FG3DModel::SelectAnimation::update (int dt)
489 if (_condition != 0 && _condition->test())
490 _selector->select(0xffff);
492 _selector->select(0x0000);
497 ////////////////////////////////////////////////////////////////////////
498 // Implementation of FG3DModel::SpinAnimation
499 ////////////////////////////////////////////////////////////////////////
501 FG3DModel::SpinAnimation::SpinAnimation ()
505 _transform(new ssgTransform)
509 FG3DModel::SpinAnimation::~SpinAnimation ()
515 FG3DModel::SpinAnimation::init (ssgEntity * object,
516 SGPropertyNode * props)
518 // Splice in the new transform node
519 splice_branch(_transform, object);
520 _transform->setName(props->getStringValue("name", 0));
521 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
522 _factor = props->getDoubleValue("factor", 1.0);
523 _position_deg = props->getDoubleValue("starting-position-deg", 0);
524 _center[0] = props->getFloatValue("center/x-m", 0);
525 _center[1] = props->getFloatValue("center/y-m", 0);
526 _center[2] = props->getFloatValue("center/z-m", 0);
527 _axis[0] = props->getFloatValue("axis/x", 0);
528 _axis[1] = props->getFloatValue("axis/y", 0);
529 _axis[2] = props->getFloatValue("axis/z", 0);
530 sgNormalizeVec3(_axis);
534 FG3DModel::SpinAnimation::update (int dt)
536 float velocity_rpms = (_prop->getDoubleValue() * _factor / 60000.0);
537 _position_deg += (dt * velocity_rpms * 360);
538 while (_position_deg < 0)
539 _position_deg += 360.0;
540 while (_position_deg >= 360.0)
541 _position_deg -= 360.0;
542 set_rotation(_matrix, _position_deg, _center, _axis);
543 _transform->setTransform(_matrix);
548 ////////////////////////////////////////////////////////////////////////
549 // Implementation of FG3DModel::RotateAnimation
550 ////////////////////////////////////////////////////////////////////////
552 FG3DModel::RotateAnimation::RotateAnimation ()
561 _transform(new ssgTransform)
565 FG3DModel::RotateAnimation::~RotateAnimation ()
571 FG3DModel::RotateAnimation::init (ssgEntity * object,
572 SGPropertyNode * props)
574 // Splice in the new transform node
575 splice_branch(_transform, object);
576 _transform->setName(props->getStringValue("name", 0));
577 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
578 _offset_deg = props->getDoubleValue("offset-deg", 0.0);
579 _factor = props->getDoubleValue("factor", 1.0);
580 if (props->hasValue("min-deg")) {
582 _min_deg = props->getDoubleValue("min-deg");
584 if (props->hasValue("max-deg")) {
586 _max_deg = props->getDoubleValue("max-deg");
588 _position_deg = props->getDoubleValue("starting-position-deg", 0);
589 _center[0] = props->getFloatValue("center/x-m", 0);
590 _center[1] = props->getFloatValue("center/y-m", 0);
591 _center[2] = props->getFloatValue("center/z-m", 0);
592 _axis[0] = props->getFloatValue("axis/x", 0);
593 _axis[1] = props->getFloatValue("axis/y", 0);
594 _axis[2] = props->getFloatValue("axis/z", 0);
595 sgNormalizeVec3(_axis);
599 FG3DModel::RotateAnimation::update (int dt)
601 _position_deg = ((_prop->getDoubleValue() + _offset_deg) * _factor);
602 if (_has_min && _position_deg < _min_deg)
603 _position_deg = _min_deg;
604 if (_has_max && _position_deg > _max_deg)
605 _position_deg = _max_deg;
606 set_rotation(_matrix, _position_deg, _center, _axis);
607 _transform->setTransform(_matrix);
612 ////////////////////////////////////////////////////////////////////////
613 // Implementation of FG3DModel::TranslateAnimation
614 ////////////////////////////////////////////////////////////////////////
616 FG3DModel::TranslateAnimation::TranslateAnimation ()
625 _transform(new ssgTransform)
629 FG3DModel::TranslateAnimation::~TranslateAnimation ()
635 FG3DModel::TranslateAnimation::init (ssgEntity * object,
636 SGPropertyNode * props)
638 // Splice in the new transform node
639 splice_branch(_transform, object);
640 _transform->setName(props->getStringValue("name", 0));
641 _prop = fgGetNode(props->getStringValue("property", "/null"), true);
642 _offset_m = props->getDoubleValue("offset-m", 0.0);
643 _factor = props->getDoubleValue("factor", 1.0);
644 if (props->hasValue("min-m")) {
646 _min_m = props->getDoubleValue("min-m");
648 if (props->hasValue("max-m")) {
650 _max_m = props->getDoubleValue("max-m");
652 _position_m = props->getDoubleValue("starting-position-m", 0);
653 _axis[0] = props->getFloatValue("axis/x", 0);
654 _axis[1] = props->getFloatValue("axis/y", 0);
655 _axis[2] = props->getFloatValue("axis/z", 0);
656 sgNormalizeVec3(_axis);
660 FG3DModel::TranslateAnimation::update (int dt)
662 _position_m = ((_prop->getDoubleValue() + _offset_m) * _factor);
663 if (_has_min && _position_m < _min_m)
664 _position_m = _min_m;
665 if (_has_max && _position_m > _max_m)
666 _position_m = _max_m;
667 set_translation(_matrix, _position_m, _axis);
668 _transform->setTransform(_matrix);