]> git.mxchange.org Git - simgear.git/commitdiff
Extend locked-track animation to support a slave element.
authorThomas Geymayer <tomgey@gmail.com>
Sun, 28 Apr 2013 21:46:21 +0000 (23:46 +0200)
committerThomas Geymayer <tomgey@gmail.com>
Sun, 28 Apr 2013 21:46:25 +0000 (23:46 +0200)
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
simgear/scene/model/SGTrackToAnimation.hxx
simgear/scene/model/animation.cxx
simgear/scene/model/animation.hxx

index 20f55e11b7ada8dae04ec01c41299e2c4d59f7f8..38a79b7d73d7a455c13ba685ffd11d2e9bbdd4bf 100644 (file)
@@ -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;
 }
 
 //------------------------------------------------------------------------------
index 717d9552025642004b01ad1397b8ff6ff8afe0c5..96445decfde11bcd6331faf7fdf618c1c5f5b50f 100644 (file)
 
 #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;
 };
index 2e1f80fdae499372d625ff400269637e70591724..180b1851f6586c0ab66f93fb06177bfd18712625 100644 (file)
@@ -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") {
index 9716dd21a26d6e949f639a93a48cf86ab16b0697..5afc8c1972d4a56c8338ffdf91022b625e9671fd 100644 (file)
@@ -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);