]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/model/animation.cxx
Work around apparent OSG 3.2.0 normal binding bug.
[simgear.git] / simgear / scene / model / animation.cxx
index e84866530a0eceba2a5e3c69580a87701a63b0f1..30cab2e3d244b81efd720a9bbe0e736648200fac 100644 (file)
@@ -12,6 +12,7 @@
 #include <algorithm>
 #include <functional>
 
+#include <OpenThreads/Atomic>
 #include <OpenThreads/Mutex>
 #include <OpenThreads/ReentrantMutex>
 #include <OpenThreads/ScopedLock>
 #include <osg/Geometry>
 #include <osg/LOD>
 #include <osg/Math>
-#include <osg/PolygonMode>
-#include <osg/PolygonOffset>
+#include <osg/Object>
 #include <osg/StateSet>
 #include <osg/Switch>
 #include <osg/TexMat>
 #include <osg/Texture2D>
 #include <osg/Transform>
+#include <osg/Uniform>
 #include <osgDB/ReadFile>
 #include <osgDB/Registry>
 #include <osgDB/Input>
 #include <simgear/math/interpolater.hxx>
 #include <simgear/props/condition.hxx>
 #include <simgear/props/props.hxx>
-#include <simgear/structure/SGBinding.hxx>
 #include <simgear/scene/material/EffectGeode.hxx>
+#include <simgear/scene/material/EffectCullVisitor.hxx>
+#include <simgear/scene/util/DeletionManager.hxx>
+#include <simgear/scene/util/OsgMath.hxx>
 #include <simgear/scene/util/SGNodeMasks.hxx>
 #include <simgear/scene/util/SGSceneUserData.hxx>
 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
+#include <simgear/scene/util/StateAttributeFactory.hxx>
 
 #include "animation.hxx"
 #include "model.hxx"
 #include "SGRotateTransform.hxx"
 #include "SGScaleTransform.hxx"
 #include "SGInteractionAnimation.hxx"
+#include "SGPickAnimation.hxx"
+#include "SGTrackToAnimation.hxx"
+
+#include "ConditionNode.hxx"
 
 using OpenThreads::Mutex;
 using OpenThreads::ReentrantMutex;
 using OpenThreads::ScopedLock;
 
-\f
+using namespace simgear;
 ////////////////////////////////////////////////////////////////////////
 // Static utility functions.
 ////////////////////////////////////////////////////////////////////////
 
-/**
- * Set up the transform matrix for a spin or rotation.
- */
-static void
-set_rotation (osg::Matrix &matrix, double position_deg,
-              const SGVec3d &center, const SGVec3d &axis)
-{
-  double temp_angle = -SGMiscd::deg2rad(position_deg);
-  
-  double s = sin(temp_angle);
-  double c = cos(temp_angle);
-  double t = 1 - c;
-  
-  // axis was normalized at load time 
-  // hint to the compiler to put these into FP registers
-  double x = axis[0];
-  double y = axis[1];
-  double z = axis[2];
-  
-  matrix(0, 0) = t * x * x + c ;
-  matrix(0, 1) = t * y * x - s * z ;
-  matrix(0, 2) = t * z * x + s * y ;
-  matrix(0, 3) = 0;
-  
-  matrix(1, 0) = t * x * y + s * z ;
-  matrix(1, 1) = t * y * y + c ;
-  matrix(1, 2) = t * z * y - s * x ;
-  matrix(1, 3) = 0;
-  
-  matrix(2, 0) = t * x * z - s * y ;
-  matrix(2, 1) = t * y * z + s * x ;
-  matrix(2, 2) = t * z * z + c ;
-  matrix(2, 3) = 0;
-  
-  // hint to the compiler to put these into FP registers
-  x = center[0];
-  y = center[1];
-  z = center[2];
-  
-  matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
-  matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
-  matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
-  matrix(3, 3) = 1;
-}
-
 /**
  * Set up the transform matrix for a translation.
  */
@@ -200,6 +163,10 @@ SGExpressiond*
 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
            const char* unit, double defMin, double defMax)
 {
+  const SGPropertyNode * expression = configNode->getNode( "expression" );
+  if( expression != NULL )
+    return SGReadDoubleExpression( modelRoot, expression->getChild(0) );
+
   SGExpression<double>* value = 0;
 
   std::string inputPropertyName = configNode->getStringValue("property", "");
@@ -239,7 +206,6 @@ read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
   return 0;
 }
 
-\f
 ////////////////////////////////////////////////////////////////////////
 // Animation installer
 ////////////////////////////////////////////////////////////////////////
@@ -367,7 +333,6 @@ SGAnimation::SGAnimation(const SGPropertyNode* configNode,
 {
   _name = configNode->getStringValue("name", "");
   _enableHOT = configNode->getBoolValue("enable-hot", true);
-  _disableShadow = configNode->getBoolValue("disable-shadow", false);
   std::vector<SGPropertyNode_ptr> objectNames =
     configNode->getChildren("object-name");
   for (unsigned i = 0; i < objectNames.size(); ++i)
@@ -376,20 +341,31 @@ SGAnimation::SGAnimation(const SGPropertyNode* configNode,
 
 SGAnimation::~SGAnimation()
 {
-  if (_found)
-    return;
-
-  SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
-         " objects for animation:\n");
-  std::list<std::string>::const_iterator i;
-  for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
-    SG_LOG(SG_IO, SG_ALERT, *i << "\n");
+  if (!_found)
+  {
+      std::list<std::string>::const_iterator i;
+      std::string info;
+      for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
+      {
+          if (!info.empty())
+              info.append(", ");
+          info.append("'");
+          info.append(*i);
+          info.append("'");
+      }
+      if (!info.empty())
+      {
+          SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
+                  " objects for animation: " << info);
+      }
+  }
 }
 
 bool
 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
                      SGPropertyNode* modelRoot,
-                     const osgDB::ReaderWriter::Options* options)
+                     const osgDB::Options* options,
+                     const std::string &path, int i)
 {
   std::string type = configNode->getStringValue("type", "none");
   if (type == "alpha-test") {
@@ -411,7 +387,7 @@ SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
     SGInteractionAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
   } else if (type == "material") {
-    SGMaterialAnimation animInst(configNode, modelRoot, options);
+    SGMaterialAnimation animInst(configNode, modelRoot, options, path);
     animInst.apply(node);
   } else if (type == "noshadow") {
     SGShadowAnimation animInst(configNode, modelRoot);
@@ -419,6 +395,12 @@ SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
   } else if (type == "pick") {
     SGPickAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
+  } else if (type == "knob") {
+    SGKnobAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "slider") {
+    SGSliderAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
   } else if (type == "range") {
     SGRangeAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
@@ -441,9 +423,15 @@ SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
   } else if (type == "timed") {
     SGTimedAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
+  } else if (type == "locked-track") {
+    SGTrackToAnimation animInst(node, configNode, modelRoot);
+    animInst.apply(node);
   } else if (type == "translate") {
     SGTranslateAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
+  } else if (type == "light") {
+    SGLightAnimation animInst(configNode, modelRoot, options, path, i);
+    animInst.apply(node);
   } else if (type == "null" || type == "none" || type.empty()) {
     SGGroupAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
@@ -476,10 +464,6 @@ SGAnimation::install(osg::Node& node)
     node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
   else
     node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
-  if (!_disableShadow)
-    node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
-  else
-    node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
 }
 
 osg::Group*
@@ -548,6 +532,65 @@ SGAnimation::installInGroup(const std::string& name, osg::Group& group,
   }
 }
 
+//------------------------------------------------------------------------------
+SGVec3d SGAnimation::readVec3( const std::string& name,
+                               const std::string& suffix,
+                               const SGVec3d& def ) const
+{
+  SGVec3d vec;
+  vec[0] = _configNode->getDoubleValue(name + "/x" + suffix, def.x());
+  vec[1] = _configNode->getDoubleValue(name + "/y" + suffix, def.y());
+  vec[2] = _configNode->getDoubleValue(name + "/z" + suffix, def.z());
+  return vec;
+}
+
+//------------------------------------------------------------------------------
+// factored out to share with SGKnobAnimation
+void SGAnimation::readRotationCenterAndAxis( SGVec3d& center,
+                                             SGVec3d& axis ) const
+{
+  center = SGVec3d::zeros();
+  if( _configNode->hasValue("axis/x1-m") )
+  {
+    SGVec3d v1 = readVec3("axis", "1-m"), // axis/[xyz]1-m
+            v2 = readVec3("axis", "2-m"); // axis/[xyz]2-m
+    center = 0.5*(v1+v2);
+    axis = v2 - v1;
+  }
+  else
+  {
+    axis = readVec3("axis");
+  }
+  if( 8 * SGLimitsd::min() < norm(axis) )
+    axis = normalize(axis);
+
+  center = readVec3("center", "-m", center);
+}
+
+//------------------------------------------------------------------------------
+SGExpressiond* SGAnimation::readOffsetValue(const char* tag_name) const
+{
+  const SGPropertyNode* node = _configNode->getChild(tag_name);
+  if( !node )
+    return 0;
+
+  SGExpressiond_ref expression;
+  if( !node->nChildren() )
+    expression = new SGConstExpression<double>(node->getDoubleValue());
+  else
+    expression = SGReadDoubleExpression(_modelRoot, node->getChild(0));
+
+  if( !expression )
+    return 0;
+
+  expression = expression->simplify();
+
+  if( expression->isConst() && expression->getValue() == 0 )
+    return 0;
+
+  return expression.release();
+}
+
 void
 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
 {
@@ -602,7 +645,7 @@ SGAnimation::getCondition() const
 }
 
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of null animation
 ////////////////////////////////////////////////////////////////////////
@@ -624,7 +667,7 @@ SGGroupAnimation::createAnimationGroup(osg::Group& parent)
   return group;
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of translate animation
 ////////////////////////////////////////////////////////////////////////
@@ -635,7 +678,9 @@ public:
                  SGExpressiond const* animationValue) :
     _condition(condition),
     _animationValue(animationValue)
-  { }
+  {
+      setName("SGTranslateAnimation::UpdateCallback");
+  }
   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
   {
     if (!_condition || _condition->test()) {
@@ -664,22 +709,7 @@ SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
   else
     _initialValue = 0;
 
-  if (configNode->hasValue("axis/x1-m")) {
-    SGVec3d v1, v2;
-    v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
-    v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
-    v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
-    v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
-    v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
-    v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
-    _axis = v2 - v1;
-  } else {
-    _axis[0] = configNode->getDoubleValue("axis/x", 0);
-    _axis[1] = configNode->getDoubleValue("axis/y", 0);
-    _axis[2] = configNode->getDoubleValue("axis/z", 0);
-  }
-  if (8*SGLimitsd::min() < norm(_axis))
-    _axis = normalize(_axis);
+  _axis = readTranslateAxis(configNode);
 }
 
 osg::Group*
@@ -697,65 +727,201 @@ SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
   return transform;
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of rotate/spin animation
 ////////////////////////////////////////////////////////////////////////
 
-class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
+class SGRotAnimTransform : public SGRotateTransform
+{
 public:
-  UpdateCallback(SGCondition const* condition,
-                 SGExpressiond const* animationValue) :
-    _condition(condition),
-    _animationValue(animationValue)
-  { }
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
-  {
+    SGRotAnimTransform();
+    SGRotAnimTransform(const SGRotAnimTransform&,
+                       const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
+    META_Node(simgear, SGRotAnimTransform);
+    virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
+                                           osg::NodeVisitor* nv) const;
+    virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
+                                           osg::NodeVisitor* nv) const;
+    SGSharedPtr<SGCondition const> _condition;
+    SGSharedPtr<SGExpressiond const> _animationValue;
+    // used when condition is false
+    mutable double _lastAngle;
+};
+
+SGRotAnimTransform::SGRotAnimTransform()
+    : _lastAngle(0.0)
+{
+}
+
+SGRotAnimTransform::SGRotAnimTransform(const SGRotAnimTransform& rhs,
+                                       const osg::CopyOp& copyop)
+    : SGRotateTransform(rhs, copyop), _condition(rhs._condition),
+      _animationValue(rhs._animationValue), _lastAngle(rhs._lastAngle)
+{
+}
+
+bool SGRotAnimTransform::computeLocalToWorldMatrix(osg::Matrix& matrix,
+                                                   osg::NodeVisitor* nv) const
+{
+    double angle = 0.0;
     if (!_condition || _condition->test()) {
-      SGRotateTransform* transform;
-      transform = static_cast<SGRotateTransform*>(node);
-      transform->setAngleDeg(_animationValue->getValue());
+        angle = _animationValue->getValue();
+        _lastAngle = angle;
+    } else {
+        angle = _lastAngle;
     }
-    traverse(node, nv);
-  }
-public:
-  SGSharedPtr<SGCondition const> _condition;
-  SGSharedPtr<SGExpressiond const> _animationValue;
-};
+    double angleRad = SGMiscd::deg2rad(angle);
+    if (_referenceFrame == RELATIVE_RF) {
+        // FIXME optimize
+        osg::Matrix tmp;
+        set_rotation(tmp, angleRad, getCenter(), getAxis());
+        matrix.preMult(tmp);
+    } else {
+        osg::Matrix tmp;
+        SGRotateTransform::set_rotation(tmp, angleRad, getCenter(), getAxis());
+        matrix = tmp;
+    }
+    return true;
+}
+
+bool SGRotAnimTransform::computeWorldToLocalMatrix(osg::Matrix& matrix,
+                                                   osg::NodeVisitor* nv) const
+{
+    double angle = 0.0;
+    if (!_condition || _condition->test()) {
+        angle = _animationValue->getValue();
+        _lastAngle = angle;
+    } else {
+        angle = _lastAngle;
+    }
+    double angleRad = SGMiscd::deg2rad(angle);
+    if (_referenceFrame == RELATIVE_RF) {
+        // FIXME optimize
+        osg::Matrix tmp;
+        set_rotation(tmp, -angleRad, getCenter(), getAxis());
+        matrix.postMult(tmp);
+    } else {
+        osg::Matrix tmp;
+        set_rotation(tmp, -angleRad, getCenter(), getAxis());
+        matrix = tmp;
+    }
+    return true;
+}
 
-class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
+// Cull callback
+class SpinAnimCallback : public osg::NodeCallback {
 public:
-  SpinUpdateCallback(SGCondition const* condition,
-                     SGExpressiond const* animationValue) :
+    SpinAnimCallback(SGCondition const* condition,
+                       SGExpressiond const* animationValue,
+          double initialValue = 0.0) :
     _condition(condition),
     _animationValue(animationValue),
-    _lastTime(-1)
-  { }
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
-  {
-    if (!_condition || _condition->test()) {
-      SGRotateTransform* transform;
-      transform = static_cast<SGRotateTransform*>(node);
-
-      double t = nv->getFrameStamp()->getReferenceTime();
-      double dt = 0;
-      if (0 <= _lastTime)
-        dt = t - _lastTime;
-      _lastTime = t;
-      double velocity_rpms = _animationValue->getValue()/60;
-      double angle = transform->getAngleDeg();
-      angle += dt*velocity_rpms*360;
-      angle -= 360*floor(angle/360);
-      transform->setAngleDeg(angle);
-    }
-    traverse(node, nv);
-  }
+    _initialValue(initialValue)
+    {}
+    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
 public:
-  SGSharedPtr<SGCondition const> _condition;
-  SGSharedPtr<SGExpressiond const> _animationValue;
-  double _lastTime;
+    SGSharedPtr<SGCondition const> _condition;
+    SGSharedPtr<SGExpressiond const> _animationValue;
+    double _initialValue;
+protected:
+    // This cull callback can run in different threads if there is
+    // more than one camera. It is probably safe to overwrite the
+    // reference values in multiple threads, but we'll provide a
+    // threadsafe way to manage those values just to be safe.
+    struct ReferenceValues : public osg::Referenced
+    {
+        ReferenceValues(double t, double rot, double vel)
+            : _time(t), _rotation(rot), _rotVelocity(vel)
+        {
+        }
+        double _time;
+        double _rotation;
+        double _rotVelocity;
+    };
+    OpenThreads::AtomicPtr _referenceValues;
 };
 
+void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
+{
+    using namespace osg;
+    SGRotateTransform* transform = static_cast<SGRotateTransform*>(node);
+    EffectCullVisitor* cv = dynamic_cast<EffectCullVisitor*>(nv);
+    if (!cv)
+        return;
+    if (!_condition || _condition->test()) {
+        double t = nv->getFrameStamp()->getSimulationTime();
+        double rps = _animationValue->getValue() / 60.0;
+        ref_ptr<ReferenceValues>
+            refval(static_cast<ReferenceValues*>(_referenceValues.get()));
+    if (!refval || refval->_rotVelocity != rps) {
+            ref_ptr<ReferenceValues> newref;
+            if (!refval.valid()) {
+                // initialization
+                newref = new ReferenceValues(t, 0.0, rps);
+            } else {
+                double newRot = refval->_rotation + (t - refval->_time) * refval->_rotVelocity;
+                newref = new ReferenceValues(t, newRot, rps);
+            }
+            // increment reference pointer, because it will be stored
+            // naked in _referenceValues.
+            newref->ref();
+            if (_referenceValues.assign(newref, refval)) {
+                if (refval.valid()) {
+                    DeletionManager::instance()->addStaleObject(refval.get());
+                    refval->unref();
+                }
+            } else {
+                // Another thread installed new values before us
+                newref->unref();
+            }
+            // Whatever happened, we can use the reference values just
+            // calculated.
+            refval = newref;
+        }
+        double rotation = refval->_rotation + (t - refval->_time) * rps;
+        double intPart;
+        double rot = modf(rotation, &intPart);
+        double angle = rot * 2.0 * osg::PI;
+        const SGVec3d& sgcenter = transform->getCenter();
+        const SGVec3d& sgaxis = transform->getAxis();
+        Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2])
+            * Matrixd::rotate(angle, sgaxis[0], sgaxis[1], sgaxis[2])
+            * Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2])
+            * *cv->getModelViewMatrix();
+        ref_ptr<RefMatrix> refmat = new RefMatrix(mat);
+        cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame());
+        traverse(transform, nv);
+        cv->popModelViewMatrix();
+    } else {
+        traverse(transform, nv);
+    }
+}
+
+SGVec3d readTranslateAxis(const SGPropertyNode* configNode)
+{
+    SGVec3d axis;
+    
+    if (configNode->hasValue("axis/x1-m")) {
+        SGVec3d v1, v2;
+        v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
+        v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
+        v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
+        v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
+        v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
+        v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
+        axis = v2 - v1;
+    } else {
+        axis[0] = configNode->getDoubleValue("axis/x", 0);
+        axis[1] = configNode->getDoubleValue("axis/y", 0);
+        axis[2] = configNode->getDoubleValue("axis/z", 0);
+    }
+    if (8*SGLimitsd::min() < norm(axis))
+        axis = normalize(axis);
+
+    return axis;
+}
+
 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
                                      SGPropertyNode* modelRoot) :
   SGAnimation(configNode, modelRoot)
@@ -772,52 +938,38 @@ SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
     _initialValue = _animationValue->getValue();
   else
     _initialValue = 0;
-
-  _center = SGVec3d::zeros();
-  if (configNode->hasValue("axis/x1-m")) {
-    SGVec3d v1, v2;
-    v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
-    v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
-    v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
-    v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
-    v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
-    v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
-    _center = 0.5*(v1+v2);
-    _axis = v2 - v1;
-  } else {
-    _axis[0] = configNode->getDoubleValue("axis/x", 0);
-    _axis[1] = configNode->getDoubleValue("axis/y", 0);
-    _axis[2] = configNode->getDoubleValue("axis/z", 0);
-  }
-  if (8*SGLimitsd::min() < norm(_axis))
-    _axis = normalize(_axis);
-
-  _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
-  _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
-  _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
+  
+  readRotationCenterAndAxis(_center, _axis);
 }
 
 osg::Group*
 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
 {
-  SGRotateTransform* transform = new SGRotateTransform;
-  transform->setName("rotate animation");
-  if (_isSpin) {
-    SpinUpdateCallback* uc;
-    uc = new SpinUpdateCallback(_condition, _animationValue);
-    transform->setUpdateCallback(uc);
-  } else if (_animationValue || !_animationValue->isConst()) {
-    UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
-    transform->setUpdateCallback(uc);
-  }
-  transform->setCenter(_center);
-  transform->setAxis(_axis);
-  transform->setAngleDeg(_initialValue);
-  parent.addChild(transform);
-  return transform;
+    if (_isSpin) {
+        SGRotateTransform* transform = new SGRotateTransform;
+        transform->setName("spin rotate animation");
+        SpinAnimCallback* cc;
+        cc = new SpinAnimCallback(_condition, _animationValue, _initialValue);
+        transform->setCullCallback(cc);
+        transform->setCenter(_center);
+        transform->setAxis(_axis);
+        transform->setAngleDeg(_initialValue);
+        parent.addChild(transform);
+        return transform;
+    } else {
+        SGRotAnimTransform* transform = new SGRotAnimTransform;
+        transform->setName("rotate animation");
+        transform->_condition = _condition;
+        transform->_animationValue = _animationValue;
+        transform->_lastAngle = _initialValue;
+        transform->setCenter(_center);
+        transform->setAxis(_axis);
+        parent.addChild(transform);
+        return transform;
+    }
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of scale animation
 ////////////////////////////////////////////////////////////////////////
@@ -831,6 +983,7 @@ public:
     _animationValue[0] = animationValue[0];
     _animationValue[1] = animationValue[1];
     _animationValue[2] = animationValue[2];
+    setName("SGScaleAnimation::UpdateCallback");
   }
   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
   {
@@ -950,7 +1103,7 @@ SGScaleAnimation::createAnimationGroup(osg::Group& parent)
   return transform;
 }
 
-\f
+
 // Don't create a new state state everytime we need GL_NORMALIZE!
 
 namespace
@@ -1096,7 +1249,7 @@ namespace
    &SGDistScaleAnimation::Transform::writeLocalData
    );
 }
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of flash animation
 ////////////////////////////////////////////////////////////////////////
@@ -1252,7 +1405,7 @@ namespace
    &SGFlashAnimation::Transform::writeLocalData
    );
 }
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of billboard animation
 ////////////////////////////////////////////////////////////////////////
@@ -1337,7 +1490,7 @@ namespace
    &SGBillboardAnimation::Transform::writeLocalData
    );
 }
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of a range animation
 ////////////////////////////////////////////////////////////////////////
@@ -1353,7 +1506,9 @@ public:
     _maxAnimationValue(maxAnimationValue),
     _minStaticValue(minValue),
     _maxStaticValue(maxValue)
-  {}
+  {
+      setName("SGRangeAnimation::UpdateCallback");
+  }
   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
   {
     osg::LOD* lod = static_cast<osg::LOD*>(node);
@@ -1440,30 +1595,11 @@ SGRangeAnimation::createAnimationGroup(osg::Group& parent)
   return group;
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of a select animation
 ////////////////////////////////////////////////////////////////////////
 
-class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
-public:
-  UpdateCallback(const SGCondition* condition) :
-    _condition(condition)
-  {}
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
-  {
-    osg::Switch* sw = static_cast<osg::Switch*>(node);
-    if (_condition->test())
-      sw->setAllChildrenOn();
-    else
-      sw->setAllChildrenOff();
-    traverse(node, nv);
-  }
-
-private:
-  SGSharedPtr<SGCondition const> _condition;
-};
-
 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
                                      SGPropertyNode* modelRoot) :
   SGAnimation(configNode, modelRoot)
@@ -1479,16 +1615,17 @@ SGSelectAnimation::createAnimationGroup(osg::Group& parent)
   // when the animation installer returns
   if (!condition)
     return new osg::Group;
-
-  osg::Switch* sw = new osg::Switch;
-  sw->setName("select animation node");
-  sw->setUpdateCallback(new UpdateCallback(condition));
-  parent.addChild(sw);
-  return sw;
+  simgear::ConditionNode* cn = new simgear::ConditionNode;
+  cn->setName("select animation node");
+  cn->setCondition(condition.ptr());
+  osg::Group* grp = new osg::Group;
+  cn->addChild(grp);
+  parent.addChild(cn);
+  return grp;
 }
 
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of alpha test animation
 ////////////////////////////////////////////////////////////////////////
@@ -1558,7 +1695,7 @@ SGAlphaTestAnimation::install(osg::Node& node)
   }
 }
 
-\f
+
 //////////////////////////////////////////////////////////////////////
 // Blend animation installer
 //////////////////////////////////////////////////////////////////////
@@ -1627,7 +1764,9 @@ public:
   UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
     _prev_value(-1),
     _animationValue(v)
-  { }
+  {
+      setName("SGBlendAnimation::UpdateCallback");
+  }
   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
   {
     double blend = _animationValue->getValue();
@@ -1675,7 +1814,7 @@ SGBlendAnimation::install(osg::Node& node)
   node.accept(visitor);
 }
 
-\f
+
 //////////////////////////////////////////////////////////////////////
 // Timed animation installer
 //////////////////////////////////////////////////////////////////////
@@ -1706,6 +1845,7 @@ public:
                                        rNode->getDoubleValue( "max", 1));
       }
     }
+    setName("SGTimedAnimation::UpdateCallback");
   }
   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
   {
@@ -1722,7 +1862,7 @@ public:
     _current_index = _current_index % nChildren;
 
     // update the time and compute the current systems time value
-    double t = nv->getFrameStamp()->getReferenceTime();
+    double t = nv->getFrameStamp()->getSimulationTime();
     if (_last_time_sec == SGLimitsd::max()) {
       _last_time_sec = t;
     } else {
@@ -1785,7 +1925,7 @@ SGTimedAnimation::createAnimationGroup(osg::Group& parent)
   return sw;
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // dynamically switch on/off shadows
 ////////////////////////////////////////////////////////////////////////
@@ -1794,7 +1934,9 @@ class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
 public:
   UpdateCallback(const SGCondition* condition) :
     _condition(condition)
-  {}
+  {
+      setName("SGShadowAnimation::UpdateCallback");
+  }
   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
   {
     if (_condition->test())
@@ -1818,17 +1960,18 @@ osg::Group*
 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
 {
   SGSharedPtr<SGCondition const> condition = getCondition();
-  if (!condition)
-    return 0;
 
   osg::Group* group = new osg::Group;
   group->setName("shadow animation");
-  group->setUpdateCallback(new UpdateCallback(condition));
+  if (condition)
+    group->setUpdateCallback(new UpdateCallback(condition));
+  else
+    group->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & group->getNodeMask());
   parent.addChild(group);
   return group;
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of SGTexTransformAnimation
 ////////////////////////////////////////////////////////////////////////
@@ -1873,7 +2016,8 @@ public:
   virtual void transform(osg::Matrix& matrix)
   {
     osg::Matrix tmp;
-    set_rotation(tmp, _value, _center, _axis);
+    SGRotateTransform::set_rotation(tmp, SGMiscd::deg2rad(_value), _center,
+                                    _axis);
     matrix.preMult(tmp);
   }
 private:
@@ -1886,7 +2030,9 @@ class SGTexTransformAnimation::UpdateCallback :
 public:
   UpdateCallback(const SGCondition* condition) :
     _condition(condition)
-  { }
+  {
+      setName("SGTexTransformAnimation::UpdateCallback");
+  }
   virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
   {
     if (!_condition || _condition->test()) {
@@ -2063,275 +2209,3 @@ SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
   updateCallback->appendTransform(rotation, value);
 }
 
-
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGPickAnimation
-////////////////////////////////////////////////////////////////////////
-
-class SGPickAnimation::PickCallback : public SGPickCallback {
-public:
-  PickCallback(const SGPropertyNode* configNode,
-               SGPropertyNode* modelRoot) :
-    _repeatable(configNode->getBoolValue("repeatable", false)),
-    _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
-  {
-    SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
-    std::vector<SGPropertyNode_ptr> bindings;
-
-    bindings = configNode->getChildren("button");
-    for (unsigned int i = 0; i < bindings.size(); ++i) {
-      _buttons.push_back( bindings[i]->getIntValue() );
-    }
-    bindings = configNode->getChildren("binding");
-    for (unsigned int i = 0; i < bindings.size(); ++i) {
-      _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
-    }
-
-    const SGPropertyNode* upNode = configNode->getChild("mod-up");
-    if (!upNode)
-      return;
-    bindings = upNode->getChildren("binding");
-    for (unsigned int i = 0; i < bindings.size(); ++i) {
-      _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
-    }
-  }
-  virtual bool buttonPressed(int button, const Info&)
-  {
-    bool found = false;
-    for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); it++ ) {
-      if( *it == button ) {
-        found = true;
-        break;
-      }
-    }
-    if (!found )
-      return false;
-    SGBindingList::const_iterator i;
-    for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
-      (*i)->fire();
-    _repeatTime = -_repeatInterval;    // anti-bobble: delay start of repeat
-    return true;
-  }
-  virtual void buttonReleased(void)
-  {
-    SGBindingList::const_iterator i;
-    for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
-      (*i)->fire();
-  }
-  virtual void update(double dt)
-  {
-    if (!_repeatable)
-      return;
-
-    _repeatTime += dt;
-    while (_repeatInterval < _repeatTime) {
-      _repeatTime -= _repeatInterval;
-      SGBindingList::const_iterator i;
-      for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
-        (*i)->fire();
-    }
-  }
-private:
-  SGBindingList _bindingsDown;
-  SGBindingList _bindingsUp;
-  std::vector<int> _buttons;
-  bool _repeatable;
-  double _repeatInterval;
-  double _repeatTime;
-};
-
-class VncVisitor : public osg::NodeVisitor {
- public:
-  VncVisitor(double x, double y, int mask) :
-    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
-    _texX(x), _texY(y), _mask(mask), _done(false)
-  {
-    SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
-      << x << "," << y << " mask " << mask);
-  }
-
-  virtual void apply(osg::Node &node)
-  {
-    // Some nodes have state sets attached
-    touchStateSet(node.getStateSet());
-    if (!_done)
-      traverse(node);
-    if (_done) return;
-    // See whether we are a geode worth exploring
-    osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
-    if (!g) return;
-    // Go find all its drawables
-    int i = g->getNumDrawables();
-    while (--i >= 0) {
-      osg::Drawable *d = g->getDrawable(i);
-      if (d) touchDrawable(*d);
-    }
-    // Out of optimism, do the same for EffectGeode
-    simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
-    if (!eg) return;
-    for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
-         di != eg->drawablesEnd(); di++) {
-      touchDrawable(**di);
-    }
-    // Now see whether the EffectGeode has an Effect
-    simgear::Effect *e = eg->getEffect();
-    if (e) {
-      touchStateSet(e->getDefaultStateSet());
-    }
-  }
-
-  inline void touchDrawable(osg::Drawable &d)
-  {
-    osg::StateSet *ss = d.getStateSet();
-    touchStateSet(ss);
-  }
-
-  void touchStateSet(osg::StateSet *ss)
-  {
-    if (!ss) return;
-    osg::StateAttribute *sa = ss->getTextureAttribute(0,
-      osg::StateAttribute::TEXTURE);
-    if (!sa) return;
-    osg::Texture *t = sa->asTexture();
-    if (!t) return;
-    osg::Image *img = t->getImage(0);
-    if (!img) return;
-    if (!_done) {
-      int pixX = _texX * img->s();
-      int pixY = _texY * img->t();
-      _done = img->sendPointerEvent(pixX, pixY, _mask);
-      SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
-        << " to coord " << pixX << "," << pixY);
-    }
-  }
-
-  inline bool wasSuccessful()
-  {
-    return _done;
-  }
-
- private:
-  double _texX, _texY;
-  int _mask;
-  bool _done;
-};
-
-
-class SGPickAnimation::VncCallback : public SGPickCallback {
-public:
-  VncCallback(const SGPropertyNode* configNode,
-               SGPropertyNode* modelRoot,
-               osg::Group *node)
-      : _node(node)
-  {
-    SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
-    const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
-    SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
-    for (int c =0; c < 3; c++) {
-      const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
-      *cornercoords[c] = SGVec3d(
-        cornerNode->getDoubleValue("x"),
-        cornerNode->getDoubleValue("y"),
-        cornerNode->getDoubleValue("z"));
-    }
-    _toRight -= _topLeft;
-    _toDown -= _topLeft;
-    _squaredRight = dot(_toRight, _toRight);
-    _squaredDown = dot(_toDown, _toDown);
-  }
-
-  virtual bool buttonPressed(int button, const Info& info)
-  {
-    SGVec3d loc(info.local);
-    SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
-    loc -= _topLeft;
-    _x = dot(loc, _toRight) / _squaredRight;
-    _y = dot(loc, _toDown) / _squaredDown;
-    if (_x<0) _x = 0; else if (_x > 1) _x = 1;
-    if (_y<0) _y = 0; else if (_y > 1) _y = 1;
-    VncVisitor vv(_x, _y, 1 << button);
-    _node->accept(vv);
-    return vv.wasSuccessful();
-
-  }
-  virtual void buttonReleased(void)
-  {
-    SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
-    VncVisitor vv(_x, _y, 0);
-    _node->accept(vv);
-  }
-  virtual void update(double dt)
-  {
-  }
-private:
-  double _x, _y;
-  osg::ref_ptr<osg::Group> _node;
-  SGVec3d _topLeft, _toRight, _toDown;
-  double _squaredRight, _squaredDown;
-};
-
-SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
-                                 SGPropertyNode* modelRoot) :
-  SGAnimation(configNode, modelRoot)
-{
-}
-
-osg::Group*
-SGPickAnimation::createAnimationGroup(osg::Group& parent)
-{
-  osg::Group* commonGroup = new osg::Group;
-
-  // Contains the normal geometry that is interactive
-  osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
-  normalGroup->setName("pick normal group");
-  normalGroup->addChild(commonGroup);
-
-  // Used to render the geometry with just yellow edges
-  osg::Group* highlightGroup = new osg::Group;
-  highlightGroup->setName("pick highlight group");
-  highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
-  highlightGroup->addChild(commonGroup);
-  SGSceneUserData* ud;
-  ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
-
-  // add actions that become macro and command invocations
-  std::vector<SGPropertyNode_ptr> actions;
-  actions = getConfig()->getChildren("action");
-  for (unsigned int i = 0; i < actions.size(); ++i)
-    ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
-  // Look for the VNC sessions that want raw mouse input
-  actions = getConfig()->getChildren("vncaction");
-  for (unsigned int i = 0; i < actions.size(); ++i)
-    ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
-      &parent));
-
-  // prepare a state set that paints the edges of this object yellow
-  osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
-  stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
-
-  osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
-  polygonOffset->setFactor(-1);
-  polygonOffset->setUnits(-1);
-  stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
-  stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
-
-  osg::PolygonMode* polygonMode = new osg::PolygonMode;
-  polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
-                       osg::PolygonMode::LINE);
-  stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
-  
-  osg::Material* material = new osg::Material;
-  material->setColorMode(osg::Material::OFF);
-  material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
-  material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
-  material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
-  material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
-  stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
-
-  // Only add normal geometry if configured
-  if (getConfig()->getBoolValue("visible", true))
-    parent.addChild(normalGroup.get());
-  parent.addChild(highlightGroup);
-
-  return commonGroup;
-}