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).
This commit is contained in:
parent
e53c71543a
commit
0fa23b83e6
@ -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<SGRotateTransform*>(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;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -24,6 +24,12 @@
|
||||
|
||||
#include <simgear/scene/model/animation.hxx>
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
@ -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") {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user