From 0fa23b83e66f02b43a979f2ef9c8b73a359e98a1 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Sun, 28 Apr 2013 23:46:21 +0200 Subject: [PATCH] Extend locked-track animation to support a slave element. This allows tracking elements while at the same time changing the rotation of both animated elements to fill the available distance to the target element. This allows for example animating gear scissors or other connected objects like gear doors and their actuators/arms (possibly connected to the gear strut). --- simgear/scene/model/SGTrackToAnimation.cxx | 130 ++++++++++++++++++--- simgear/scene/model/SGTrackToAnimation.hxx | 9 +- simgear/scene/model/animation.cxx | 2 +- simgear/scene/model/animation.hxx | 2 +- 4 files changed, 123 insertions(+), 20 deletions(-) diff --git a/simgear/scene/model/SGTrackToAnimation.cxx b/simgear/scene/model/SGTrackToAnimation.cxx index 20f55e11..38a79b7d 100644 --- a/simgear/scene/model/SGTrackToAnimation.cxx +++ b/simgear/scene/model/SGTrackToAnimation.cxx @@ -94,18 +94,40 @@ class FindGroupVisitor: osg::Group *_group; }; +/** + * Get angle of a triangle given by three side lengths + * + * @return Angle enclosed by @c side1 and @c side2 + */ +double angleFromSSS(double side1, double side2, double opposite) +{ + return std::acos + ( + ( SGMiscd::pow<2>(side1) + + SGMiscd::pow<2>(side2) + - SGMiscd::pow<2>(opposite) + ) / (2 * side1 * side2) + ); +} + //------------------------------------------------------------------------------ class SGTrackToAnimation::UpdateCallback: public osg::NodeCallback { public: UpdateCallback( osg::Group* target, - const SGTrackToAnimation* anim ): - _target(target) + const SGTrackToAnimation* anim, + SGRotateTransform* slave_tf ): + _target(target), + _slave_tf(slave_tf), + _root_length(0), + _slave_length(0), + _root_initial_angle(0) { setName("SGTrackToAnimation::UpdateCallback"); _node_center = toOsg( anim->readVec3("center", "-m") ); + _slave_center = toOsg( anim->readVec3("slave-center", "-m") ); _target_center = toOsg( anim->readVec3("target-center", "-m") ); _lock_axis = toOsg( anim->readVec3("lock-axis") ); _track_axis = toOsg( anim->readVec3("track-axis") ); @@ -116,6 +138,24 @@ class SGTrackToAnimation::UpdateCallback: _lock_axis.set(0, 1, 0); } + if( _slave_center != osg::Vec3() ) + { + _root_length = (_slave_center - _node_center).length(); + _slave_length = (_target_center - _slave_center).length(); + double dist = (_target_center - _node_center).length(); + + _root_initial_angle = angleFromSSS(_root_length, dist, _slave_length); + + // If no rotation should be applied to the slave element it is looking + // in the same direction then the root node. Inside the triangle given + // by the root length, slave length and distance from the root node to + // the target node, this equals an angle of 180 degrees. + _slave_initial_angle = angleFromSSS(_root_length, _slave_length, dist) + - SGMiscd::pi(); + + _track_axis = _target_center - _node_center; + } + for(;;) { float proj = _lock_axis * _track_axis; @@ -146,6 +186,9 @@ class SGTrackToAnimation::UpdateCallback: { SGRotateTransform* tf = static_cast(node); + // We need to wait with this initialization steps until the first update + // as this allows us to be sure all animations have already been installed + // and are therefore also accounted for calculating the animation. if( _target ) { // Get path to animated node and calculated simplified paths to the @@ -172,6 +215,12 @@ class SGTrackToAnimation::UpdateCallback: tf->setCenter( toSG(_node_center) ); tf->setAxis( toSG(_lock_axis) ); + + if( _slave_tf ) + { + _slave_tf->setCenter( toSG(_slave_center) ); + _slave_tf->setAxis( toSG(_lock_axis) ); + } } osg::Vec3 target_pos = ( osg::computeLocalToWorld(_target_path) @@ -179,6 +228,24 @@ class SGTrackToAnimation::UpdateCallback: ).preMult(_target_center), dir = target_pos - _node_center; + double offset = -_root_initial_angle; + if( _root_length > 0 ) + { + double dist = dir.length(), + slave_angle = -_slave_initial_angle; + if( dist < _root_length + _slave_length ) + { + offset += angleFromSSS(_root_length, dist, _slave_length); + + if( _slave_tf ) + slave_angle += angleFromSSS(_root_length, _slave_length, dist) + - SGMiscd::pi(); + } + + if( _slave_tf ) + _slave_tf->setAngleRad(slave_angle); + } + // Ensure direction is perpendicular to lock axis float proj = _lock_axis * dir; if( proj != 0.0 ) @@ -186,19 +253,26 @@ class SGTrackToAnimation::UpdateCallback: float x = dir * _track_axis, y = dir * _up_axis; - tf->setAngleRad( std::atan2(y, x) ); + tf->setAngleRad( std::atan2(y, x) + offset ); traverse(node, nv); } protected: - osg::Vec3 _node_center, - _target_center, - _lock_axis, - _track_axis, - _up_axis; - osg::Group *_target; - osg::NodePath _node_path, - _target_path; + + osg::Vec3 _node_center, + _slave_center, + _target_center, + _lock_axis, + _track_axis, + _up_axis; + osg::Group *_target; + SGRotateTransform *_slave_tf; + osg::NodePath _node_path, + _target_path; + double _root_length, + _slave_length, + _root_initial_angle, + _slave_initial_angle; }; //------------------------------------------------------------------------------ @@ -206,7 +280,8 @@ SGTrackToAnimation::SGTrackToAnimation( osg::Node* node, const SGPropertyNode* configNode, SGPropertyNode* modelRoot ): SGAnimation(configNode, modelRoot), - _target_group(0) + _target_group(0), + _slave_group(0) { std::string target = configNode->getStringValue("target-name"); FindGroupVisitor target_finder(target); @@ -214,6 +289,14 @@ SGTrackToAnimation::SGTrackToAnimation( osg::Node* node, if( !(_target_group = target_finder.getGroup()) ) log(SG_ALERT, "target not found: '" + target + '\''); + + std::string slave = configNode->getStringValue("slave-name"); + if( !slave.empty() ) + { + FindGroupVisitor slave_finder(slave); + node->accept(slave_finder); + _slave_group = slave_finder.getGroup(); + } } //------------------------------------------------------------------------------ @@ -222,11 +305,24 @@ osg::Group* SGTrackToAnimation::createAnimationGroup(osg::Group& parent) if( !_target_group ) return 0; - SGRotateTransform* transform = new SGRotateTransform; - transform->setName("locked-track animation"); - transform->setUpdateCallback(new UpdateCallback(_target_group, this)); - parent.addChild(transform); - return transform; + SGRotateTransform* slave_tf = 0; + if( _slave_group ) + { + slave_tf = new SGRotateTransform; + slave_tf->setName("locked-track slave animation"); + + osg::Group* parent = _slave_group->getParent(0); + slave_tf->addChild(_slave_group); + parent->removeChild(_slave_group); + parent->addChild(slave_tf); + } + + SGRotateTransform* tf = new SGRotateTransform; + tf->setName("locked-track animation"); + tf->setUpdateCallback(new UpdateCallback(_target_group, this, slave_tf)); + parent.addChild(tf); + + return tf; } //------------------------------------------------------------------------------ diff --git a/simgear/scene/model/SGTrackToAnimation.hxx b/simgear/scene/model/SGTrackToAnimation.hxx index 717d9552..96445dec 100644 --- a/simgear/scene/model/SGTrackToAnimation.hxx +++ b/simgear/scene/model/SGTrackToAnimation.hxx @@ -24,6 +24,12 @@ #include +/** + * Animation to let an object always track another object. An optional second + * slave object can be specified which is rotate to always fit the space between + * the root object and the target object. This can be used to eg. create a gear + * scissor animation. + */ class SGTrackToAnimation: public SGAnimation { @@ -37,7 +43,8 @@ class SGTrackToAnimation: protected: class UpdateCallback; - osg::Group *_target_group; + osg::Group *_target_group, + *_slave_group; void log(sgDebugPriority p, const std::string& msg) const; }; diff --git a/simgear/scene/model/animation.cxx b/simgear/scene/model/animation.cxx index 2e1f80fd..180b1851 100644 --- a/simgear/scene/model/animation.cxx +++ b/simgear/scene/model/animation.cxx @@ -423,7 +423,7 @@ SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode, } else if (type == "timed") { SGTimedAnimation animInst(configNode, modelRoot); animInst.apply(node); - } else if (type == "track-to" || type == "locked-track") { + } else if (type == "locked-track") { SGTrackToAnimation animInst(node, configNode, modelRoot); animInst.apply(node); } else if (type == "translate") { diff --git a/simgear/scene/model/animation.hxx b/simgear/scene/model/animation.hxx index 9716dd21..5afc8c19 100644 --- a/simgear/scene/model/animation.hxx +++ b/simgear/scene/model/animation.hxx @@ -70,7 +70,7 @@ protected: */ SGVec3d readVec3( const std::string& name, const std::string& suffix = "", - const SGVec3d& def = SGVec3d() ) const; + const SGVec3d& def = SGVec3d::zeros() ) const; void readRotationCenterAndAxis(SGVec3d& center, SGVec3d& axis) const; void removeMode(osg::Node& node, osg::StateAttribute::GLMode mode); -- 2.39.5