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") );
_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;
{
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
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)
).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 )
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;
};
//------------------------------------------------------------------------------
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);
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();
+ }
}
//------------------------------------------------------------------------------
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;
}
//------------------------------------------------------------------------------