1 // animation.cxx - classes to manage model animation.
2 // Written by David Megginson, started 2002.
4 // This file is in the Public Domain, and comes with no warranty.
7 #include <string.h> // for strcmp()
14 #include <simgear/math/interpolater.hxx>
15 #include <simgear/props/condition.hxx>
16 #include <simgear/props/props.hxx>
18 #include "animation.hxx"
22 ////////////////////////////////////////////////////////////////////////
23 // Static utility functions.
24 ////////////////////////////////////////////////////////////////////////
27 * Set up the transform matrix for a spin or rotation.
30 set_rotation (sgMat4 &matrix, double position_deg,
31 sgVec3 ¢er, sgVec3 &axis)
33 float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
35 float s = (float) sin ( temp_angle ) ;
36 float c = (float) cos ( temp_angle ) ;
37 float t = SG_ONE - c ;
39 // axis was normalized at load time
40 // hint to the compiler to put these into FP registers
45 matrix[0][0] = t * x * x + c ;
46 matrix[0][1] = t * y * x - s * z ;
47 matrix[0][2] = t * z * x + s * y ;
48 matrix[0][3] = SG_ZERO;
50 matrix[1][0] = t * x * y + s * z ;
51 matrix[1][1] = t * y * y + c ;
52 matrix[1][2] = t * z * y - s * x ;
53 matrix[1][3] = SG_ZERO;
55 matrix[2][0] = t * x * z - s * y ;
56 matrix[2][1] = t * y * z + s * x ;
57 matrix[2][2] = t * z * z + c ;
58 matrix[2][3] = SG_ZERO;
60 // hint to the compiler to put these into FP registers
65 matrix[3][0] = x - x*matrix[0][0] - y*matrix[1][0] - z*matrix[2][0];
66 matrix[3][1] = y - x*matrix[0][1] - y*matrix[1][1] - z*matrix[2][1];
67 matrix[3][2] = z - x*matrix[0][2] - y*matrix[1][2] - z*matrix[2][2];
68 matrix[3][3] = SG_ONE;
72 * Set up the transform matrix for a translation.
75 set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis)
78 sgScaleVec3(xyz, axis, position_m);
79 sgMakeTransMat4(matrix, xyz);
83 * Modify property value by step and scroll settings in texture translations
86 apply_mods(double property, double step, double scroll)
91 double scrollval = 0.0;
93 // calculate scroll amount (for odometer like movement)
94 double remainder = step - fmod(fabs(property), step);
95 if (remainder < scroll) {
96 scrollval = (scroll - remainder) / scroll * step;
99 // apply stepping of input value
101 modprop = ((floor(property/step) * step) + scrollval);
103 modprop = ((ceil(property/step) * step) + scrollval);
112 * Read an interpolation table from properties.
114 static SGInterpTable *
115 read_interpolation_table (SGPropertyNode_ptr props)
117 SGPropertyNode_ptr table_node = props->getNode("interpolation");
118 if (table_node != 0) {
119 SGInterpTable * table = new SGInterpTable();
120 vector<SGPropertyNode_ptr> entries = table_node->getChildren("entry");
121 for (unsigned int i = 0; i < entries.size(); i++)
122 table->addEntry(entries[i]->getDoubleValue("ind", 0.0),
123 entries[i]->getDoubleValue("dep", 0.0));
132 ////////////////////////////////////////////////////////////////////////
133 // Implementation of SGAnimation
134 ////////////////////////////////////////////////////////////////////////
136 // Initialize the static data member
137 double SGAnimation::sim_time_sec = 0.0;
139 SGAnimation::SGAnimation (SGPropertyNode_ptr props, ssgBranch * branch)
142 _branch->setName(props->getStringValue("name", 0));
145 SGAnimation::~SGAnimation ()
155 SGAnimation::update()
161 ////////////////////////////////////////////////////////////////////////
162 // Implementation of SGNullAnimation
163 ////////////////////////////////////////////////////////////////////////
165 SGNullAnimation::SGNullAnimation (SGPropertyNode_ptr props)
166 : SGAnimation(props, new ssgBranch)
170 SGNullAnimation::~SGNullAnimation ()
176 ////////////////////////////////////////////////////////////////////////
177 // Implementation of SGRangeAnimation
178 ////////////////////////////////////////////////////////////////////////
180 SGRangeAnimation::SGRangeAnimation (SGPropertyNode_ptr props)
181 : SGAnimation(props, new ssgRangeSelector)
183 float ranges[] = { props->getFloatValue("min-m", 0),
184 props->getFloatValue("max-m", 5000) };
185 ((ssgRangeSelector *)_branch)->setRanges(ranges, 2);
189 SGRangeAnimation::~SGRangeAnimation ()
195 ////////////////////////////////////////////////////////////////////////
196 // Implementation of SGBillboardAnimation
197 ////////////////////////////////////////////////////////////////////////
199 SGBillboardAnimation::SGBillboardAnimation (SGPropertyNode_ptr props)
200 : SGAnimation(props, new ssgCutout(props->getBoolValue("spherical", true)))
204 SGBillboardAnimation::~SGBillboardAnimation ()
210 ////////////////////////////////////////////////////////////////////////
211 // Implementation of SGSelectAnimation
212 ////////////////////////////////////////////////////////////////////////
214 SGSelectAnimation::SGSelectAnimation( SGPropertyNode *prop_root,
215 SGPropertyNode_ptr props )
216 : SGAnimation(props, new ssgSelector),
219 SGPropertyNode_ptr node = props->getChild("condition");
221 _condition = sgReadCondition(prop_root, node);
224 SGSelectAnimation::~SGSelectAnimation ()
230 SGSelectAnimation::update()
232 if (_condition != 0 && _condition->test())
233 ((ssgSelector *)_branch)->select(0xffff);
235 ((ssgSelector *)_branch)->select(0x0000);
240 ////////////////////////////////////////////////////////////////////////
241 // Implementation of SGSpinAnimation
242 ////////////////////////////////////////////////////////////////////////
244 SGSpinAnimation::SGSpinAnimation( SGPropertyNode *prop_root,
245 SGPropertyNode_ptr props,
246 double sim_time_sec )
247 : SGAnimation(props, new ssgTransform),
248 _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
249 _factor(props->getDoubleValue("factor", 1.0)),
250 _position_deg(props->getDoubleValue("starting-position-deg", 0)),
251 _last_time_sec( sim_time_sec )
256 if (props->hasValue("axis/x1-m")) {
257 double x1,y1,z1,x2,y2,z2;
258 x1 = props->getFloatValue("axis/x1-m");
259 y1 = props->getFloatValue("axis/y1-m");
260 z1 = props->getFloatValue("axis/z1-m");
261 x2 = props->getFloatValue("axis/x2-m");
262 y2 = props->getFloatValue("axis/y2-m");
263 z2 = props->getFloatValue("axis/z2-m");
264 _center[0] = (x1+x2)/2;
265 _center[1]= (y1+y2)/2;
266 _center[2] = (z1+z2)/2;
267 float vector_length = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1));
268 _axis[0] = (x2-x1)/vector_length;
269 _axis[1] = (y2-y1)/vector_length;
270 _axis[2] = (z2-z1)/vector_length;
272 _axis[0] = props->getFloatValue("axis/x", 0);
273 _axis[1] = props->getFloatValue("axis/y", 0);
274 _axis[2] = props->getFloatValue("axis/z", 0);
276 if (props->hasValue("center/x-m")) {
277 _center[0] = props->getFloatValue("center/x-m", 0);
278 _center[1] = props->getFloatValue("center/y-m", 0);
279 _center[2] = props->getFloatValue("center/z-m", 0);
281 sgNormalizeVec3(_axis);
284 SGSpinAnimation::~SGSpinAnimation ()
289 SGSpinAnimation::update()
291 double dt = sim_time_sec - _last_time_sec;
292 _last_time_sec = sim_time_sec;
294 float velocity_rpms = (_prop->getDoubleValue() * _factor / 60.0);
295 _position_deg += (dt * velocity_rpms * 360);
296 while (_position_deg < 0)
297 _position_deg += 360.0;
298 while (_position_deg >= 360.0)
299 _position_deg -= 360.0;
300 set_rotation(_matrix, _position_deg, _center, _axis);
301 ((ssgTransform *)_branch)->setTransform(_matrix);
306 ////////////////////////////////////////////////////////////////////////
307 // Implementation of SGTimedAnimation
308 ////////////////////////////////////////////////////////////////////////
310 SGTimedAnimation::SGTimedAnimation (SGPropertyNode_ptr props)
311 : SGAnimation(props, new ssgSelector),
312 _duration_sec(props->getDoubleValue("duration-sec", 1.0)),
318 SGTimedAnimation::~SGTimedAnimation ()
323 SGTimedAnimation::update()
325 if ((sim_time_sec - _last_time_sec) >= _duration_sec) {
326 _last_time_sec = sim_time_sec;
328 if (_step >= getBranch()->getNumKids())
330 ((ssgSelector *)getBranch())->selectStep(_step);
336 ////////////////////////////////////////////////////////////////////////
337 // Implementation of SGRotateAnimation
338 ////////////////////////////////////////////////////////////////////////
340 SGRotateAnimation::SGRotateAnimation( SGPropertyNode *prop_root,
341 SGPropertyNode_ptr props )
342 : SGAnimation(props, new ssgTransform),
343 _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
344 _offset_deg(props->getDoubleValue("offset-deg", 0.0)),
345 _factor(props->getDoubleValue("factor", 1.0)),
346 _table(read_interpolation_table(props)),
347 _has_min(props->hasValue("min-deg")),
348 _min_deg(props->getDoubleValue("min-deg")),
349 _has_max(props->hasValue("max-deg")),
350 _max_deg(props->getDoubleValue("max-deg")),
351 _position_deg(props->getDoubleValue("starting-position-deg", 0))
356 if (props->hasValue("axis/x1-m")) {
357 double x1,y1,z1,x2,y2,z2;
358 x1 = props->getFloatValue("axis/x1-m");
359 y1 = props->getFloatValue("axis/y1-m");
360 z1 = props->getFloatValue("axis/z1-m");
361 x2 = props->getFloatValue("axis/x2-m");
362 y2 = props->getFloatValue("axis/y2-m");
363 z2 = props->getFloatValue("axis/z2-m");
364 _center[0] = (x1+x2)/2;
365 _center[1]= (y1+y2)/2;
366 _center[2] = (z1+z2)/2;
367 float vector_length = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1));
368 _axis[0] = (x2-x1)/vector_length;
369 _axis[1] = (y2-y1)/vector_length;
370 _axis[2] = (z2-z1)/vector_length;
372 _axis[0] = props->getFloatValue("axis/x", 0);
373 _axis[1] = props->getFloatValue("axis/y", 0);
374 _axis[2] = props->getFloatValue("axis/z", 0);
376 if (props->hasValue("center/x-m")) {
377 _center[0] = props->getFloatValue("center/x-m", 0);
378 _center[1] = props->getFloatValue("center/y-m", 0);
379 _center[2] = props->getFloatValue("center/z-m", 0);
381 sgNormalizeVec3(_axis);
384 SGRotateAnimation::~SGRotateAnimation ()
390 SGRotateAnimation::update()
393 _position_deg = _prop->getDoubleValue() * _factor + _offset_deg;
394 if (_has_min && _position_deg < _min_deg)
395 _position_deg = _min_deg;
396 if (_has_max && _position_deg > _max_deg)
397 _position_deg = _max_deg;
399 _position_deg = _table->interpolate(_prop->getDoubleValue());
401 set_rotation(_matrix, _position_deg, _center, _axis);
402 ((ssgTransform *)_branch)->setTransform(_matrix);
407 ////////////////////////////////////////////////////////////////////////
408 // Implementation of SGTranslateAnimation
409 ////////////////////////////////////////////////////////////////////////
411 SGTranslateAnimation::SGTranslateAnimation( SGPropertyNode *prop_root,
412 SGPropertyNode_ptr props )
413 : SGAnimation(props, new ssgTransform),
414 _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
415 _offset_m(props->getDoubleValue("offset-m", 0.0)),
416 _factor(props->getDoubleValue("factor", 1.0)),
417 _table(read_interpolation_table(props)),
418 _has_min(props->hasValue("min-m")),
419 _min_m(props->getDoubleValue("min-m")),
420 _has_max(props->hasValue("max-m")),
421 _max_m(props->getDoubleValue("max-m")),
422 _position_m(props->getDoubleValue("starting-position-m", 0))
424 _axis[0] = props->getFloatValue("axis/x", 0);
425 _axis[1] = props->getFloatValue("axis/y", 0);
426 _axis[2] = props->getFloatValue("axis/z", 0);
427 sgNormalizeVec3(_axis);
430 SGTranslateAnimation::~SGTranslateAnimation ()
436 SGTranslateAnimation::update()
439 _position_m = (_prop->getDoubleValue() + _offset_m) * _factor;
440 if (_has_min && _position_m < _min_m)
441 _position_m = _min_m;
442 if (_has_max && _position_m > _max_m)
443 _position_m = _max_m;
445 _position_m = _table->interpolate(_prop->getDoubleValue());
447 set_translation(_matrix, _position_m, _axis);
448 ((ssgTransform *)_branch)->setTransform(_matrix);
452 ////////////////////////////////////////////////////////////////////////
453 // Implementation of SGTexRotateAnimation
454 ////////////////////////////////////////////////////////////////////////
456 SGTexRotateAnimation::SGTexRotateAnimation( SGPropertyNode *prop_root,
457 SGPropertyNode_ptr props )
458 : SGAnimation(props, new ssgTexTrans),
459 _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
460 _offset_deg(props->getDoubleValue("offset-deg", 0.0)),
461 _factor(props->getDoubleValue("factor", 1.0)),
462 _table(read_interpolation_table(props)),
463 _has_min(props->hasValue("min-deg")),
464 _min_deg(props->getDoubleValue("min-deg")),
465 _has_max(props->hasValue("max-deg")),
466 _max_deg(props->getDoubleValue("max-deg")),
467 _position_deg(props->getDoubleValue("starting-position-deg", 0))
469 _center[0] = props->getFloatValue("center/x", 0);
470 _center[1] = props->getFloatValue("center/y", 0);
471 _center[2] = props->getFloatValue("center/z", 0);
472 _axis[0] = props->getFloatValue("axis/x", 0);
473 _axis[1] = props->getFloatValue("axis/y", 0);
474 _axis[2] = props->getFloatValue("axis/z", 0);
475 sgNormalizeVec3(_axis);
478 SGTexRotateAnimation::~SGTexRotateAnimation ()
484 SGTexRotateAnimation::update()
487 _position_deg = _prop->getDoubleValue() * _factor + _offset_deg;
488 if (_has_min && _position_deg < _min_deg)
489 _position_deg = _min_deg;
490 if (_has_max && _position_deg > _max_deg)
491 _position_deg = _max_deg;
493 _position_deg = _table->interpolate(_prop->getDoubleValue());
495 set_rotation(_matrix, _position_deg, _center, _axis);
496 ((ssgTexTrans *)_branch)->setTransform(_matrix);
500 ////////////////////////////////////////////////////////////////////////
501 // Implementation of SGTexTranslateAnimation
502 ////////////////////////////////////////////////////////////////////////
504 SGTexTranslateAnimation::SGTexTranslateAnimation( SGPropertyNode *prop_root,
505 SGPropertyNode_ptr props )
506 : SGAnimation(props, new ssgTexTrans),
507 _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
508 _offset(props->getDoubleValue("offset", 0.0)),
509 _factor(props->getDoubleValue("factor", 1.0)),
510 _step(props->getDoubleValue("step",0.0)),
511 _scroll(props->getDoubleValue("scroll",0.0)),
512 _table(read_interpolation_table(props)),
513 _has_min(props->hasValue("min")),
514 _min(props->getDoubleValue("min")),
515 _has_max(props->hasValue("max")),
516 _max(props->getDoubleValue("max")),
517 _position(props->getDoubleValue("starting-position", 0))
519 _axis[0] = props->getFloatValue("axis/x", 0);
520 _axis[1] = props->getFloatValue("axis/y", 0);
521 _axis[2] = props->getFloatValue("axis/z", 0);
522 sgNormalizeVec3(_axis);
525 SGTexTranslateAnimation::~SGTexTranslateAnimation ()
531 SGTexTranslateAnimation::update()
534 _position = (apply_mods(_prop->getDoubleValue(), _step, _scroll) + _offset) * _factor;
535 if (_has_min && _position < _min)
537 if (_has_max && _position > _max)
540 _position = _table->interpolate(apply_mods(_prop->getDoubleValue(), _step, _scroll));
542 set_translation(_matrix, _position, _axis);
543 ((ssgTexTrans *)_branch)->setTransform(_matrix);
547 ////////////////////////////////////////////////////////////////////////
548 // Implementation of SGTexMultipleAnimation
549 ////////////////////////////////////////////////////////////////////////
551 SGTexMultipleAnimation::SGTexMultipleAnimation( SGPropertyNode *prop_root,
552 SGPropertyNode_ptr props )
553 : SGAnimation(props, new ssgTexTrans),
554 _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true))
558 vector<SGPropertyNode_ptr> transform_nodes = props->getChildren("transform");
559 _transform = new TexTransform [transform_nodes.size()];
561 for (i = 0; i < transform_nodes.size(); i++) {
562 SGPropertyNode_ptr transform_props = transform_nodes[i];
564 if (!strcmp("textranslate",transform_props->getStringValue("subtype", 0))) {
566 // transform is a translation
567 _transform[i].subtype = 0;
569 _transform[i].prop = (SGPropertyNode *)prop_root->getNode(transform_props->getStringValue("property", "/null"), true);
571 _transform[i].offset = transform_props->getDoubleValue("offset", 0.0);
572 _transform[i].factor = transform_props->getDoubleValue("factor", 1.0);
573 _transform[i].step = transform_props->getDoubleValue("step",0.0);
574 _transform[i].scroll = transform_props->getDoubleValue("scroll",0.0);
575 _transform[i].table = read_interpolation_table(transform_props);
576 _transform[i].has_min = transform_props->hasValue("min");
577 _transform[i].min = transform_props->getDoubleValue("min");
578 _transform[i].has_max = transform_props->hasValue("max");
579 _transform[i].max = transform_props->getDoubleValue("max");
580 _transform[i].position = transform_props->getDoubleValue("starting-position", 0);
582 _transform[i].axis[0] = transform_props->getFloatValue("axis/x", 0);
583 _transform[i].axis[1] = transform_props->getFloatValue("axis/y", 0);
584 _transform[i].axis[2] = transform_props->getFloatValue("axis/z", 0);
585 sgNormalizeVec3(_transform[i].axis);
587 } else if (!strcmp("texrotate",transform_nodes[i]->getStringValue("subtype", 0))) {
589 // transform is a rotation
590 _transform[i].subtype = 1;
592 _transform[i].prop = (SGPropertyNode *)prop_root->getNode(transform_props->getStringValue("property", "/null"), true);
593 _transform[i].offset = transform_props->getDoubleValue("offset-deg", 0.0);
594 _transform[i].factor = transform_props->getDoubleValue("factor", 1.0);
595 _transform[i].table = read_interpolation_table(transform_props);
596 _transform[i].has_min = transform_props->hasValue("min-deg");
597 _transform[i].min = transform_props->getDoubleValue("min-deg");
598 _transform[i].has_max = transform_props->hasValue("max-deg");
599 _transform[i].max = transform_props->getDoubleValue("max-deg");
600 _transform[i].position = transform_props->getDoubleValue("starting-position-deg", 0);
602 _transform[i].center[0] = transform_props->getFloatValue("center/x", 0);
603 _transform[i].center[1] = transform_props->getFloatValue("center/y", 0);
604 _transform[i].center[2] = transform_props->getFloatValue("center/z", 0);
605 _transform[i].axis[0] = transform_props->getFloatValue("axis/x", 0);
606 _transform[i].axis[1] = transform_props->getFloatValue("axis/y", 0);
607 _transform[i].axis[2] = transform_props->getFloatValue("axis/z", 0);
608 sgNormalizeVec3(_transform[i].axis);
614 SGTexMultipleAnimation::~SGTexMultipleAnimation ()
621 SGTexMultipleAnimation::update()
625 sgMakeIdentMat4(tmatrix);
626 for (i = 0; i < _num_transforms; i++) {
628 if(_transform[i].subtype == 0) {
630 // subtype 0 is translation
631 if (_transform[i].table == 0) {
632 _transform[i].position = (apply_mods(_transform[i].prop->getDoubleValue(), _transform[i].step,_transform[i].scroll) + _transform[i].offset) * _transform[i].factor;
633 if (_transform[i].has_min && _transform[i].position < _transform[i].min)
634 _transform[i].position = _transform[i].min;
635 if (_transform[i].has_max && _transform[i].position > _transform[i].max)
636 _transform[i].position = _transform[i].max;
638 _transform[i].position = _transform[i].table->interpolate(apply_mods(_transform[i].prop->getDoubleValue(), _transform[i].step,_transform[i].scroll));
640 set_translation(_transform[i].matrix, _transform[i].position, _transform[i].axis);
641 sgPreMultMat4(tmatrix, _transform[i].matrix);
643 } else if (_transform[i].subtype == 1) {
645 // subtype 1 is rotation
647 if (_transform[i].table == 0) {
648 _transform[i].position = _transform[i].prop->getDoubleValue() * _transform[i].factor + _transform[i].offset;
649 if (_transform[i].has_min && _transform[i].position < _transform[i].min)
650 _transform[i].position = _transform[i].min;
651 if (_transform[i].has_max && _transform[i].position > _transform[i].max)
652 _transform[i].position = _transform[i].max;
654 _transform[i].position = _transform[i].table->interpolate(_transform[i].prop->getDoubleValue());
656 set_rotation(_transform[i].matrix, _transform[i].position, _transform[i].center, _transform[i].axis);
657 sgPreMultMat4(tmatrix, _transform[i].matrix);
660 ((ssgTexTrans *)_branch)->setTransform(tmatrix);
663 // end of animation.cxx