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.
13 #include <simgear/compiler.h>
14 #include <simgear/debug/logstream.hxx>
15 #include <simgear/misc/exception.hxx>
16 #include <simgear/misc/sg_path.hxx>
18 #include "globals.hxx"
19 #include "fg_props.hxx"
20 #include "viewmgr.hxx"
23 extern unsigned long int fgSimTime; // FIXME: this is ugly
25 extern ssgRoot * scene; // FIXME: from main.cxx
27 FGAircraftModel current_model; // FIXME: add to globals
31 find_named_node (ssgEntity * node, const string &name)
33 char * node_name = node->getName();
34 if (node_name != 0 && name == node_name)
36 else if (node->isAKindOf(ssgTypeBranch())) {
37 int nKids = node->getNumKids();
38 for (int i = 0; i < nKids; i++) {
40 find_named_node(((ssgBranch*)node)->getKid(i), name);
48 FGAircraftModel::FGAircraftModel ()
50 _selector(new ssgSelector),
51 _position(new ssgTransform)
55 FGAircraftModel::~FGAircraftModel ()
57 // since the nodes are attached to the scene graph, they'll be
58 // deleted automatically
62 FGAircraftModel::init ()
64 // TODO: optionally load an XML file with a pointer to the 3D object
65 // and placement and animation info
69 SG_LOG(SG_INPUT, SG_INFO, "Initializing aircraft 3D model");
71 // Load the 3D aircraft object itself
72 SGPath path = globals->get_fg_root();
73 path.append(fgGetString("/sim/model/path", "Models/Geometry/glider.ac"));
75 if (path.str().substr(path.str().size() - 4, 4) == ".xml") {
76 readProperties(path.str(), &props);
77 if (props.hasValue("/path")) {
79 path.append(props.getStringValue("/path"));
81 path = globals->get_fg_root();
82 path.append("Models/Geometry/glider.ac");
86 ssgTexturePath((char *)path.dir().c_str());
87 _model = ssgLoad((char *)path.c_str());
89 _model = ssgLoad((char *)"Models/Geometry/glider.ac");
91 throw sg_exception("Failed to load an aircraft model");
95 vector<SGPropertyNode *> animation_nodes =
96 props.getChildren("animation");
97 for (unsigned int i = 0; i < animation_nodes.size(); i++) {
98 vector<SGPropertyNode *> name_nodes =
99 animation_nodes[i]->getChildren("object-name");
100 if (name_nodes.size() < 1) {
101 SG_LOG(SG_INPUT, SG_ALERT, "No object-name given for transformation");
103 for (unsigned int j = 0; j < name_nodes.size(); j++) {
104 _animations.push_back(read_animation(name_nodes[j]->getStringValue(),
105 animation_nodes[i]));
110 // Set up the alignment node
111 ssgTransform * align = new ssgTransform;
112 align->addKid(_model);
116 float h_rot = props.getFloatValue("/offsets/heading-deg", 0.0);
117 float p_rot = props.getFloatValue("/offsets/roll-deg", 0.0);
118 float r_rot = props.getFloatValue("/offsets/pitch-deg", 0.0);
119 float x_off = props.getFloatValue("/offsets/x-m", 0.0);
120 float y_off = props.getFloatValue("/offsets/y-m", 0.0);
121 float z_off = props.getFloatValue("/offsets/z-m", 0.0);
122 sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
123 sgMakeTransMat4(off_matrix, x_off, y_off, z_off);
124 sgMultMat4(res_matrix, off_matrix, rot_matrix);
125 align->setTransform(res_matrix);
127 // Set up the position node
128 _position->addKid(align);
130 // Set up the selector node
131 _selector->addKid(_position);
132 _selector->clrTraversalMaskBits(SSGTRAV_HOT);
133 scene->addKid(_selector);
137 FGAircraftModel::bind ()
142 FGAircraftModel::unbind ()
147 FGAircraftModel::update (int dt)
149 _current_timestamp.stamp();
150 long elapsed_ms = (_current_timestamp - _last_timestamp) / 1000;
151 _last_timestamp.stamp();
153 int view_number = globals->get_viewmgr()->get_current();
155 if (view_number == 0 && !fgGetBool("/sim/view/internal")) {
156 _selector->select(false);
158 for (unsigned int i = 0; i < _animations.size(); i++)
159 do_animation(_animations[i], elapsed_ms);
161 _selector->select(true);
162 FGViewerRPH *pilot_view =
163 (FGViewerRPH *)globals->get_viewmgr()->get_view( 0 );
166 sgMakeTransMat4( sgTRANS, pilot_view->get_view_pos() );
169 sgSetVec3( ownship_up, 0.0, 0.0, 1.0);
172 sgMakeRotMat4( sgROT, -90.0, ownship_up );
175 sgCopyMat4( sgTUX, sgROT );
177 sgCopyMat4( VIEW_ROT, pilot_view->get_VIEW_ROT());
178 if (view_number == 0) {
179 // FIXME: orientation is not applied
180 // correctly when view is not forward
181 sgMakeRotMat4( sgROT, -pilot_view->get_view_offset()
182 * SGD_RADIANS_TO_DEGREES, pilot_view->get_world_up() );
184 /* Warning lame hack from Wilson ahead */
185 /* get the pitch value */
187 sgCopyVec3(rph, pilot_view->get_rph());
188 /* double it to counter the value already in the VIEW_ROT */
189 float pitch = rph[1] * 2;
191 with the values waited by the X coordinate from the offset
192 rotation see sgROT above
195 PunROT[0][0] = SG_ONE;
196 PunROT[0][1] = SG_ZERO;
197 PunROT[0][2] = SG_ZERO;
198 PunROT[0][3] = SG_ZERO;
199 PunROT[1][0] = SG_ZERO;
200 PunROT[1][1] = cos((1 - sgROT[0][0]) * -pitch);
201 PunROT[1][2] = -sin((1 - sgROT[0][0]) * -pitch);
202 PunROT[1][3] = SG_ZERO;
203 PunROT[2][0] = SG_ZERO;
204 PunROT[2][1] = sin((1 - sgROT[0][0]) * -pitch);
205 PunROT[2][2] = cos((1 - sgROT[0][0]) * -pitch);
206 PunROT[2][3] = SG_ZERO;
207 PunROT[3][0] = SG_ZERO;
208 PunROT[3][1] = SG_ZERO;
209 PunROT[3][2] = SG_ZERO;
210 PunROT[3][3] = SG_ONE;
212 sgPostMultMat4( sgTUX, PunROT );
213 sgPostMultMat4( sgTUX, VIEW_ROT );
214 sgPostMultMat4( sgTUX, sgROT );
215 sgPostMultMat4( sgTUX, sgTRANS );
219 sgPostMultMat4( sgTUX, VIEW_ROT );
220 sgPostMultMat4( sgTUX, sgTRANS );
224 sgSetCoord( &tuxpos, sgTUX );
225 _position->setTransform( &tuxpos );
229 FGAircraftModel::Animation
230 FGAircraftModel::read_animation (const string &object_name,
231 const SGPropertyNode * node)
235 // Find the object to be animated
236 ssgEntity * target = find_named_node(_model, object_name);
238 SG_LOG(SG_INPUT, SG_INFO, " Target object is " << object_name);
240 animation.type = Animation::None;
241 SG_LOG(SG_INPUT, SG_ALERT, "Object " << object_name
242 << " not found in model");
246 // Figure out the animation type
247 string type_name = node->getStringValue("type");
248 if (type_name == "spin") {
249 SG_LOG(SG_INPUT, SG_INFO, "Reading spin animation");
250 animation.type = Animation::Spin;
251 } else if (type_name == "rotate") {
252 SG_LOG(SG_INPUT, SG_INFO, "Reading rotate animation");
253 animation.type = Animation::Rotate;
254 } else if (type_name == "none") {
255 SG_LOG(SG_INPUT, SG_INFO, "Reading disabled animation");
256 animation.type = Animation::None;
259 animation.type = Animation::None;
260 SG_LOG(SG_INPUT, SG_ALERT, "Unknown animation type " << type_name);
264 // Splice a transform node into the tree
265 animation.transform = new ssgTransform;
266 int nParents = target->getNumParents();
267 animation.transform->addKid(target);
268 for (int i = 0; i < nParents; i++) {
269 ssgBranch * parent = target->getParent(i);
270 parent->replaceKid(target, animation.transform);
275 fgGetNode(node->getStringValue("property", "/null"), true);
277 animation.position = node->getFloatValue("initial-position", 0);
278 animation.offset = node->getFloatValue("offset", 0);
279 if (node->hasValue("min")) {
280 animation.has_min = true;
281 animation.min = node->getFloatValue("min");
283 animation.has_min = false;
285 if (node->hasValue("max")) {
286 animation.has_max = true;
287 animation.max = node->getFloatValue("max");
289 animation.has_max = false;
291 animation.factor = node->getFloatValue("factor", 1);
293 // Get the center and axis
294 animation.center[0] = node->getFloatValue("center/x-m", 0);
295 animation.center[1] = node->getFloatValue("center/y-m", 0);
296 animation.center[2] = node->getFloatValue("center/z-m", 0);
297 animation.axis[0] = node->getFloatValue("axis/x", 0);
298 animation.axis[1] = node->getFloatValue("axis/y", 0);
299 animation.axis[2] = node->getFloatValue("axis/z", 0);
301 sgNormalizeVec3(animation.axis);
307 FGAircraftModel::do_animation (Animation &animation, long elapsed_ms)
309 switch (animation.type) {
310 case Animation::None:
312 case Animation::Spin:
314 float velocity_rpms = (animation.prop->getDoubleValue()
315 * animation.factor / 60000.0);
316 animation.position += (elapsed_ms * velocity_rpms * 360);
317 animation.setRotation();
320 case Animation::Rotate: {
321 animation.position = ((animation.prop->getFloatValue()
324 if (animation.has_min && animation.position < animation.min)
325 animation.position = animation.min;
326 if (animation.has_max && animation.position > animation.max)
327 animation.position = animation.max;
328 animation.setRotation();
337 * Transform to rotate an object around its local axis
338 * from a relative frame of reference at center -- NHV
341 FGAircraftModel::Animation::setRotation()
343 float temp_angle = -position * SG_DEGREES_TO_RADIANS ;
345 float s = (float) sin ( temp_angle ) ;
346 float c = (float) cos ( temp_angle ) ;
347 float t = SG_ONE - c ;
349 // axis was normalized at load time
350 // hint to the compiler to put these into FP registers
356 matrix[0][0] = t * x * x + c ;
357 matrix[0][1] = t * y * x - s * z ;
358 matrix[0][2] = t * z * x + s * y ;
359 matrix[0][3] = SG_ZERO;
361 matrix[1][0] = t * x * y + s * z ;
362 matrix[1][1] = t * y * y + c ;
363 matrix[1][2] = t * z * y - s * x ;
364 matrix[1][3] = SG_ZERO;
366 matrix[2][0] = t * x * z - s * y ;
367 matrix[2][1] = t * y * z + s * x ;
368 matrix[2][2] = t * z * z + c ;
369 matrix[2][3] = SG_ZERO;
371 // hint to the compiler to put these into FP registers
376 matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
377 matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
378 matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
379 matrix[3][3] = SG_ONE;
381 transform->setTransform(matrix);