]> git.mxchange.org Git - simgear.git/commitdiff
Modified Files:
authorfrohlich <frohlich>
Sun, 3 Dec 2006 16:57:20 +0000 (16:57 +0000)
committerfrohlich <frohlich>
Sun, 3 Dec 2006 16:57:20 +0000 (16:57 +0000)
simgear/scene/model/Makefile.am
simgear/scene/model/animation.cxx
simgear/scene/model/animation.hxx
simgear/scene/model/model.cxx
simgear/scene/model/persparam.cxx
simgear/scene/model/persparam.hxx
simgear/scene/model/shadanim.cxx
Added Files:
simgear/scene/model/SGMaterialAnimation.cxx
simgear/scene/model/SGMaterialAnimation.hxx
Big animation overhaul. Improoves animation correctness.

simgear/scene/model/Makefile.am
simgear/scene/model/SGMaterialAnimation.cxx [new file with mode: 0644]
simgear/scene/model/SGMaterialAnimation.hxx [new file with mode: 0644]
simgear/scene/model/animation.cxx
simgear/scene/model/animation.hxx
simgear/scene/model/model.cxx
simgear/scene/model/persparam.cxx
simgear/scene/model/persparam.hxx
simgear/scene/model/shadanim.cxx

index 70b8449c7e78f519014d781bcd0cd330572f7816..b1ff27345ceb686b17016eca1c8e5898a1bf5ef7 100644 (file)
@@ -11,7 +11,8 @@ include_HEADERS = \
        modellib.hxx \
        persparam.hxx \
        placement.hxx \
-       placementtrans.hxx
+       placementtrans.hxx \
+       SGMaterialAnimation.hxx
 
 libsgmodel_a_SOURCES = \
        animation.cxx \
@@ -21,6 +22,7 @@ libsgmodel_a_SOURCES = \
        persparam.cxx \
        placement.cxx \
        placementtrans.cxx \
-       shadanim.cxx
+       shadanim.cxx \
+       SGMaterialAnimation.cxx
 
 INCLUDES = -I$(top_srcdir)
diff --git a/simgear/scene/model/SGMaterialAnimation.cxx b/simgear/scene/model/SGMaterialAnimation.cxx
new file mode 100644 (file)
index 0000000..f099be0
--- /dev/null
@@ -0,0 +1,492 @@
+// animation.cxx - classes to manage model animation.
+// Written by David Megginson, started 2002.
+//
+// This file is in the Public Domain, and comes with no warranty.
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
+#include "SGMaterialAnimation.hxx"
+
+#include <osg/AlphaFunc>
+#include <osg/Drawable>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/StateSet>
+#include <osgDB/FileNameUtils>
+#include <osgDB/FileUtils>
+#include <osgDB/ReadFile>
+
+#include <simgear/props/condition.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/scene/model/model.hxx>
+
+struct SGMaterialAnimation::ColorSpec {
+  float red, green, blue;
+  float factor;
+  float offset;
+  SGPropertyNode_ptr red_prop;
+  SGPropertyNode_ptr green_prop;
+  SGPropertyNode_ptr blue_prop;
+  SGPropertyNode_ptr factor_prop;
+  SGPropertyNode_ptr offset_prop;
+  SGVec4f v;
+  
+  ColorSpec(const SGPropertyNode* configNode, SGPropertyNode* modelRoot)
+  {
+    red = -1.0;
+    green = -1.0;
+    blue = -1.0;
+    if (!configNode)
+      return;
+    
+    red = configNode->getFloatValue("red", -1.0);
+    green = configNode->getFloatValue("green", -1.0);
+    blue = configNode->getFloatValue("blue", -1.0);
+    factor = configNode->getFloatValue("factor", 1.0);
+    offset = configNode->getFloatValue("offset", 0.0);
+    
+    if (!modelRoot)
+      return;
+    const SGPropertyNode *node;
+    node = configNode->getChild("red-prop");
+    if (node)
+      red_prop = modelRoot->getNode(node->getStringValue(), true);
+    node = configNode->getChild("green-prop");
+    if (node)
+      green_prop = modelRoot->getNode(node->getStringValue(), true);
+    node = configNode->getChild("blue-prop");
+    if (node)
+      blue_prop = modelRoot->getNode(node->getStringValue(), true);
+    node = configNode->getChild("factor-prop");
+    if (node)
+      factor_prop = modelRoot->getNode(node->getStringValue(), true);
+    node = configNode->getChild("offset-prop");
+    if (node)
+      offset_prop = modelRoot->getNode(node->getStringValue(), true);
+  }
+  
+  bool dirty() {
+    return red >= 0 || green >= 0 || blue >= 0;
+  }
+  bool live() {
+    return red_prop || green_prop || blue_prop
+      || factor_prop || offset_prop;
+  }
+  SGVec4f &rgba() {
+    if (red_prop)
+      red = red_prop->getFloatValue();
+    if (green_prop)
+      green = green_prop->getFloatValue();
+    if (blue_prop)
+      blue = blue_prop->getFloatValue();
+    if (factor_prop)
+      factor = factor_prop->getFloatValue();
+    if (offset_prop)
+      offset = offset_prop->getFloatValue();
+    v[0] = SGMiscf::clip(red*factor + offset, 0, 1);
+    v[1] = SGMiscf::clip(green*factor + offset, 0, 1);
+    v[2] = SGMiscf::clip(blue*factor + offset, 0, 1);
+    v[3] = 1;
+    return v;
+  }
+  SGVec4f &initialRgba() {
+    v[0] = SGMiscf::clip(red*factor + offset, 0, 1);
+    v[1] = SGMiscf::clip(green*factor + offset, 0, 1);
+    v[2] = SGMiscf::clip(blue*factor + offset, 0, 1);
+    v[3] = 1;
+    return v;
+  }
+};
+
+
+struct SGMaterialAnimation::PropSpec {
+  float value;
+  float factor;
+  float offset;
+  float min;
+  float max;
+  SGPropertyNode_ptr value_prop;
+  SGPropertyNode_ptr factor_prop;
+  SGPropertyNode_ptr offset_prop;
+  
+  PropSpec(const char* valueName, const char* valuePropName,
+           const SGPropertyNode* configNode, SGPropertyNode* modelRoot)
+  {
+    value = -1;
+    if (!configNode)
+      return;
+    
+    value = configNode->getFloatValue(valueName, -1);
+    factor = configNode->getFloatValue("factor", 1);
+    offset = configNode->getFloatValue("offset", 0);
+    min = configNode->getFloatValue("min", 0);
+    max = configNode->getFloatValue("max", 1);
+    
+    if (!modelRoot)
+      return;
+    const SGPropertyNode *node;
+    node = configNode->getChild(valuePropName);
+    if (node)
+      value_prop = modelRoot->getNode(node->getStringValue(), true);
+    node = configNode->getChild("factor-prop");
+    if (node)
+      factor_prop = modelRoot->getNode(node->getStringValue(), true);
+    node = configNode->getChild("offset-prop");
+    if (node)
+      offset_prop = modelRoot->getNode(node->getStringValue(), true);
+  }
+  bool dirty() { return value >= 0.0; }
+  bool live() { return value_prop || factor_prop || offset_prop; }
+  float getValue()
+  {
+    if (value_prop)
+      value = value_prop->getFloatValue();
+    if (offset_prop)
+      offset = offset_prop->getFloatValue();
+    if (factor_prop)
+      factor = factor_prop->getFloatValue();
+    return SGMiscf::clip(value*factor + offset, min, max);
+  }
+  float getInitialValue()
+  {
+    return SGMiscf::clip(value*factor + offset, min, max);
+  }
+};
+
+class SGMaterialAnimation::MaterialVisitor : public osg::NodeVisitor {
+public:
+  enum {
+    DIFFUSE = 1,
+    AMBIENT = 2,
+    SPECULAR = 4,
+    EMISSION = 8,
+    SHININESS = 16,
+    TRANSPARENCY = 32
+  };
+
+  MaterialVisitor() :
+    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+    _updateMask(0),
+    _ambient(-1, -1, -1, -1),
+    _diffuse(-1, -1, -1, -1),
+    _specular(-1, -1, -1, -1),
+    _emission(-1, -1, -1, -1),
+    _shininess(-1),
+    _alpha(-1)
+  {
+    setVisitorType(osg::NodeVisitor::NODE_VISITOR);
+  }
+
+  void setDiffuse(const SGVec4f& diffuse)
+  {
+    if (diffuse != _diffuse)
+      _diffuse = diffuse;
+    _updateMask |= DIFFUSE;
+  }
+  void setAmbient(const SGVec4f& ambient)
+  {
+    if (ambient != _ambient)
+      _ambient = ambient;
+    _updateMask |= AMBIENT;
+  }
+  void setSpecular(const SGVec4f& specular)
+  {
+    if (specular != _specular)
+      _specular = specular;
+    _updateMask |= SPECULAR;
+  }
+  void setEmission(const SGVec4f& emission)
+  {
+    if (emission != _emission)
+      _emission = emission;
+    _updateMask |= EMISSION;
+  }
+  void setShininess(float shininess)
+  {
+    if (shininess != _shininess)
+      _shininess = shininess;
+    _updateMask |= SHININESS;
+  }
+
+  void setAlpha(float alpha)
+  {
+    if (alpha != _alpha)
+      _alpha = alpha;
+    _updateMask |= TRANSPARENCY;
+  }
+
+  virtual void reset()
+  {
+    _updateMask = 0;
+  }
+  virtual void apply(osg::Node& node)
+  {
+    updateStateSet(node.getStateSet());
+    traverse(node);
+  }
+  virtual void apply(osg::Geode& node)
+  {
+    apply((osg::Node&)node);
+    unsigned nDrawables = node.getNumDrawables();
+    for (unsigned i = 0; i < nDrawables; ++i) {
+      osg::Drawable* drawable = node.getDrawable(i);
+      updateStateSet(drawable->getStateSet());
+
+      if (_updateMask&TRANSPARENCY) {
+        osg::Geometry* geometry = drawable->asGeometry();
+        if (!geometry)
+          continue;
+        osg::Array* array = geometry->getColorArray();
+        if (!array)
+          continue;
+        osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
+        if (!vec4Array)
+          continue;
+
+        // FIXME, according to the colormode in the material
+        // we might incorporate the apropriate color value
+        geometry->dirtyDisplayList();
+        vec4Array->dirty();
+        for (unsigned k = 0; k < vec4Array->size(); ++k) {
+          (*vec4Array)[k][3] = _alpha;
+        }
+      }
+    }
+  }
+  void updateStateSet(osg::StateSet* stateSet)
+  {
+    if (!stateSet)
+      return;
+    osg::StateAttribute* stateAttribute;
+    stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
+    if (!stateAttribute)
+      return;
+    osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
+    if (!material)
+      return;
+    if (_updateMask&AMBIENT)
+      material->setAmbient(osg::Material::FRONT_AND_BACK, _ambient.osg());
+    if (_updateMask&DIFFUSE)
+      material->setDiffuse(osg::Material::FRONT_AND_BACK, _diffuse.osg());
+    if (_updateMask&SPECULAR)
+      material->setSpecular(osg::Material::FRONT_AND_BACK, _specular.osg());
+    if (_updateMask&EMISSION)
+      material->setEmission(osg::Material::FRONT_AND_BACK, _emission.osg());
+    if (_updateMask&SHININESS)
+      material->setShininess(osg::Material::FRONT_AND_BACK, _shininess);
+    if (_updateMask&TRANSPARENCY) {
+      material->setAlpha(osg::Material::FRONT_AND_BACK, _alpha);
+      if (_alpha < 1) {
+        stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
+        stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
+      } else {
+        stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
+      }
+    }
+  }
+private:
+  unsigned _updateMask;
+  SGVec4f _ambient;
+  SGVec4f _diffuse;
+  SGVec4f _specular;
+  SGVec4f _emission;
+  float _shininess;
+  float _alpha;
+};
+
+class SGMaterialAnimation::UpdateCallback : public osg::NodeCallback {
+public:
+  UpdateCallback(const osgDB::FilePathList& texturePathList,
+                 const SGCondition* condition,
+                 const SGPropertyNode* configNode, SGPropertyNode* modelRoot) :
+    _condition(condition),
+    _ambient(configNode->getChild("ambient"), modelRoot),
+    _diffuse(configNode->getChild("diffuse"), modelRoot),
+    _specular(configNode->getChild("specular"), modelRoot),
+    _emission(configNode->getChild("emission"), modelRoot),
+    _shininess("shininess", "shininess-prop",
+               configNode->getChild("shininess"), modelRoot),
+    _transparency("alpha", "alpha-prop",
+                  configNode->getChild("transparency"), modelRoot),
+    _texturePathList(texturePathList)
+  {
+    const SGPropertyNode* node;
+
+    node = configNode->getChild("threshold-prop");
+    if (node)
+      _thresholdProp = modelRoot->getNode(node->getStringValue(), true);
+    node = configNode->getChild("texture-prop");
+    if (node)
+      _textureProp = modelRoot->getNode(node->getStringValue(), true);
+  }
+
+  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+  {
+    if (!_condition || _condition->test()) {
+      if (_textureProp) {
+        std::string textureName = _textureProp->getStringValue();
+        if (_textureName != textureName) {
+          osg::StateSet* stateSet = node->getOrCreateStateSet();
+          while (stateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE)) {
+            stateSet->removeTextureAttribute(0, osg::StateAttribute::TEXTURE);
+          }
+          std::string textureFile;
+          textureFile = osgDB::findFileInPath(textureName, _texturePathList);
+          if (!textureFile.empty()) {
+            osg::Texture2D* texture2D = SGLoadTexture2D(textureFile);
+            if (texture2D) {
+              stateSet->setTextureAttribute(0, texture2D);
+              stateSet->setTextureMode(0, GL_TEXTURE_2D,
+                                       osg::StateAttribute::ON);
+              _textureName = textureName;
+            }
+          }
+        }
+      }
+      if (_thresholdProp) {
+        osg::StateSet* stateSet = node->getOrCreateStateSet();
+        osg::StateAttribute* stateAttribute;
+        stateAttribute = stateSet->getAttribute(osg::StateAttribute::ALPHAFUNC);
+        assert(dynamic_cast<osg::AlphaFunc*>(stateAttribute));
+        osg::AlphaFunc* alphaFunc = static_cast<osg::AlphaFunc*>(stateAttribute);
+        alphaFunc->setReferenceValue(_thresholdProp->getFloatValue());
+      }
+      
+      _visitor.reset();
+      if (_ambient.live())
+        _visitor.setAmbient(_ambient.rgba());
+      if (_diffuse.live())
+        _visitor.setDiffuse(_diffuse.rgba());
+      if (_specular.live())
+        _visitor.setSpecular(_specular.rgba());
+      if (_emission.live())
+        _visitor.setEmission(_emission.rgba());
+      if (_shininess.live())
+        _visitor.setShininess(_shininess.getValue());
+      if (_transparency.live())
+        _visitor.setAlpha(_transparency.getValue());
+      
+      node->accept(_visitor);
+    }
+
+    traverse(node, nv);
+  }
+private:
+  SGSharedPtr<const SGCondition> _condition;
+  SGSharedPtr<const SGPropertyNode> _textureProp;
+  SGSharedPtr<const SGPropertyNode> _thresholdProp;
+  MaterialVisitor _visitor;
+  std::string _textureName;
+  ColorSpec _ambient;
+  ColorSpec _diffuse;
+  ColorSpec _specular;
+  ColorSpec _emission;
+  PropSpec _shininess;
+  PropSpec _transparency;
+  osgDB::FilePathList _texturePathList;
+};
+
+SGMaterialAnimation::SGMaterialAnimation(const SGPropertyNode* configNode,
+                                         SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
+{
+  if (configNode->hasChild("global"))
+    SG_LOG(SG_IO, SG_ALERT, "Using global material animation that can "
+           "no longer work");
+}
+
+osg::Group*
+SGMaterialAnimation::createAnimationGroup(osg::Group& parent)
+{
+  osg::Group* group = new osg::Group;
+  group->setName("material animation group");
+
+  SGPropertyNode* inputRoot = getModelRoot();
+  const SGPropertyNode* node = getConfig()->getChild("property-base");
+  if (node)
+    inputRoot = getModelRoot()->getRootNode()->getNode(node->getStringValue(),
+                                                       true);
+
+  osgDB::FilePathList texturePathList = osgDB::getDataFilePathList();
+
+  if (getConfig()->hasChild("texture")) {
+    std::string textureName = getConfig()->getStringValue("texture");
+    std::string textureFile;
+    textureFile = osgDB::findFileInPath(textureName, texturePathList);
+    if (!textureFile.empty()) {
+      osg::StateSet* stateSet = group->getOrCreateStateSet();
+      osg::Texture2D* texture2D = SGLoadTexture2D(textureFile);
+      if (texture2D) {
+        stateSet->setTextureAttribute(0, texture2D);
+        stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
+        if (texture2D->getImage()->isImageTranslucent()) {
+          stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
+          stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
+        }
+      }
+    }
+  }
+  if (getConfig()->hasChild("threshold-prop") ||
+      getConfig()->hasChild("threshold")) {
+    osg::StateSet* stateSet = group->getOrCreateStateSet();
+    osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
+    alphaFunc->setFunction(osg::AlphaFunc::GREATER);
+    float threshold = getConfig()->getFloatValue("threshold", 0);
+    alphaFunc->setReferenceValue(threshold);
+    stateSet->setAttributeAndModes(alphaFunc);
+  }
+
+  UpdateCallback* updateCallback;
+  updateCallback = new UpdateCallback(texturePathList, getCondition(),
+                                      getConfig(), inputRoot);
+  group->setUpdateCallback(updateCallback);
+  parent.addChild(group);
+  return group;
+}
+
+void
+SGMaterialAnimation::install(osg::Node& node)
+{
+  SGAnimation::install(node);
+  // make sure everything (except the texture attributes)
+  // below is private to our model
+  cloneDrawables(node);
+
+  // Remove all textures if required, they get replaced later on
+  if (getConfig()->hasChild("texture") ||
+      getConfig()->hasChild("texture-prop")) {
+    removeTextureAttribute(node, 0, osg::StateAttribute::TEXTURE);
+    removeTextureMode(node, 0, GL_TEXTURE_2D);
+  }
+  // Remove all nested alphaFuncs
+  if (getConfig()->hasChild("threshold") ||
+      getConfig()->hasChild("threshold-prop"))
+    removeAttribute(node, osg::StateAttribute::ALPHAFUNC);
+
+  ColorSpec ambient(getConfig()->getChild("ambient"), getModelRoot());
+  ColorSpec diffuse(getConfig()->getChild("diffuse"), getModelRoot());
+  ColorSpec specular(getConfig()->getChild("specular"), getModelRoot());
+  ColorSpec emission(getConfig()->getChild("emission"), getModelRoot());
+  PropSpec shininess("shininess", "shininess-prop",
+                     getConfig()->getChild("shininess"), getModelRoot());
+  PropSpec transparency("alpha", "alpha-prop",
+                        getConfig()->getChild("transparency"), getModelRoot());
+
+  MaterialVisitor visitor;
+  if (ambient.dirty())
+    visitor.setAmbient(ambient.initialRgba());
+  if (diffuse.dirty())
+    visitor.setDiffuse(diffuse.initialRgba());
+  if (specular.dirty())
+    visitor.setSpecular(specular.initialRgba());
+  if (emission.dirty())
+    visitor.setEmission(emission.initialRgba());
+  if (shininess.dirty())
+    visitor.setShininess(shininess.getInitialValue());
+  if (transparency.dirty())
+    visitor.setAlpha(transparency.getInitialValue());
+  node.accept(visitor);
+}
+
diff --git a/simgear/scene/model/SGMaterialAnimation.hxx b/simgear/scene/model/SGMaterialAnimation.hxx
new file mode 100644 (file)
index 0000000..808858e
--- /dev/null
@@ -0,0 +1,33 @@
+
+// animation.hxx - classes to manage model animation.
+// Written by David Megginson, started 2002.
+//
+// This file is in the Public Domain, and comes with no warranty.
+
+#ifndef _SG_MATERIALANIMATION_HXX
+#define _SG_MATERIALANIMATION_HXX 1
+
+#ifndef __cplusplus
+# error This library requires C++
+#endif
+
+#include "animation.hxx"
+
+//////////////////////////////////////////////////////////////////////
+// Material animation
+//////////////////////////////////////////////////////////////////////
+
+class SGMaterialAnimation : public SGAnimation {
+public:
+  SGMaterialAnimation(const SGPropertyNode* configNode,
+                      SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
+  virtual void install(osg::Node& node);
+private:
+  struct ColorSpec;
+  struct PropSpec;
+  class MaterialVisitor;
+  class UpdateCallback;
+};
+
+#endif // _SG_MATERIALANIMATION_HXX
index e11a580eb14c8bdb8364c07847c35afcbb997af3..e9aa34337bd2e05d3c2a2b61bff7251f5ff6f1ea 100644 (file)
 #include <math.h>
 
 #include <osg/AlphaFunc>
-#include <osg/AutoTransform>
 #include <osg/Drawable>
 #include <osg/Geode>
 #include <osg/Geometry>
 #include <osg/LOD>
-#include <osg/MatrixTransform>
 #include <osg/StateSet>
 #include <osg/Switch>
 #include <osg/TexMat>
 #include <osg/Texture2D>
+#include <osg/Transform>
 #include <osgDB/ReadFile>
 
 #include <simgear/math/interpolater.hxx>
 #include <simgear/props/condition.hxx>
 #include <simgear/props/props.hxx>
-#include <simgear/math/sg_random.h>
 #include <simgear/scene/util/SGNodeMasks.hxx>
+#include <simgear/scene/util/SGStateAttributeVisitor.hxx>
 
 #include "animation.hxx"
 #include "model.hxx"
 
+#include "SGMaterialAnimation.hxx"
+
 \f
 ////////////////////////////////////////////////////////////////////////
 // Static utility functions.
  */
 static void
 set_rotation (osg::Matrix &matrix, double position_deg,
-              const osg::Vec3 &center, const osg::Vec3 &axis)
+              const SGVec3d &center, const SGVec3d &axis)
 {
float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ;
float s = (float) sin ( temp_angle ) ;
float c = (float) cos ( temp_angle ) ;
float t = SG_ONE - c ;
-
- // axis was normalized at load time 
- // hint to the compiler to put these into FP registers
float x = axis[0];
float y = axis[1];
float 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) = SG_ZERO;
- 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) = SG_ZERO;
- 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) = SG_ZERO;
-
 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) = SG_ONE;
 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.
  */
 static void
-set_translation (osg::Matrix &matrix, double position_m, const osg::Vec3 &axis)
+set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
 {
-  osg::Vec3 xyz = axis * position_m;
+  SGVec3d xyz = axis * position_m;
   matrix.makeIdentity();
   matrix(3, 0) = xyz[0];
   matrix(3, 1) = xyz[1];
   matrix(3, 2) = xyz[2];
 }
 
-/**
- * Set up the transform matrix for a scale operation.
- */
-static void
-set_scale (osg::Matrix &matrix, double x, double y, double z)
-{
-  matrix.makeIdentity();
-  matrix(0, 0) = x;
-  matrix(1, 1) = y;
-  matrix(2, 2) = z;
-}
-
 /**
  * Modify property value by step and scroll settings in texture translations
  */
@@ -140,1338 +129,1148 @@ apply_mods(double property, double step, double scroll)
  * Read an interpolation table from properties.
  */
 static SGInterpTable *
-read_interpolation_table (SGPropertyNode_ptr props)
+read_interpolation_table(const SGPropertyNode* props)
 {
-  SGPropertyNode_ptr table_node = props->getNode("interpolation");
-  if (table_node != 0) {
-    SGInterpTable * table = new SGInterpTable();
-    vector<SGPropertyNode_ptr> entries = table_node->getChildren("entry");
-    for (unsigned int i = 0; i < entries.size(); i++)
-      table->addEntry(entries[i]->getDoubleValue("ind", 0.0),
-                      entries[i]->getDoubleValue("dep", 0.0));
-    return table;
-  } else {
+  const SGPropertyNode* table_node = props->getNode("interpolation");
+  if (!table_node)
     return 0;
-  }
+  return new SGInterpTable(table_node);
 }
 
-
-\f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of SGAnimation
+// Utility value classes
 ////////////////////////////////////////////////////////////////////////
+class SGScaleOffsetValue : public SGDoubleValue {
+public:
+  SGScaleOffsetValue(SGPropertyNode const* propertyNode) :
+    _propertyNode(propertyNode),
+    _scale(1),
+    _offset(0),
+    _min(-SGLimitsd::max()),
+    _max(SGLimitsd::max())
+  { }
+  void setScale(double scale)
+  { _scale = scale; }
+  void setOffset(double offset)
+  { _offset = offset; }
+  void setMin(double min)
+  { _min = min; }
+  void setMax(double max)
+  { _max = max; }
+
+  virtual double getValue() const
+  {
+    double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
+    return std::min(_max, std::max(_min, _offset + _scale*value));
+  }
+private:
+  SGSharedPtr<SGPropertyNode const> _propertyNode;
+  double _scale;
+  double _offset;
+  double _min;
+  double _max;
+};
 
-SGAnimation::SGAnimation (SGPropertyNode_ptr props, osg::Group * branch)
-    : _branch(branch),
-    animation_type(0)
-{
-    _branch->setName(props->getStringValue("name", "Animation"));
-    if ( props->getBoolValue( "enable-hot", true ) ) {
-        _branch->setNodeMask(SG_NODEMASK_TERRAIN_BIT|_branch->getNodeMask());
-    } else {
-        _branch->setNodeMask(~SG_NODEMASK_TERRAIN_BIT&_branch->getNodeMask());
-    }
-}
+class SGPersScaleOffsetValue : public SGDoubleValue {
+public:
+  SGPersScaleOffsetValue(SGPropertyNode const* propertyNode,
+                         SGPropertyNode const* config,
+                         const char* scalename, const char* offsetname,
+                         double defScale = 1, double defOffset = 0) :
+    _propertyNode(propertyNode),
+    _scale(config, scalename, defScale),
+    _offset(config, offsetname, defOffset),
+    _min(-SGLimitsd::max()),
+    _max(SGLimitsd::max())
+  { }
+  void setScale(double scale)
+  { _scale = scale; }
+  void setOffset(double offset)
+  { _offset = offset; }
+  void setMin(double min)
+  { _min = min; }
+  void setMax(double max)
+  { _max = max; }
+
+  virtual double getValue() const
+  {
+    _offset.shuffle();
+    _scale.shuffle();
+    double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
+    return SGMiscd::clip(_offset + _scale*value, _min, _max);
+  }
+private:
+  SGSharedPtr<SGPropertyNode const> _propertyNode;
+  mutable SGPersonalityParameter<double> _scale;
+  mutable SGPersonalityParameter<double> _offset;
+  double _min;
+  double _max;
+};
 
-SGAnimation::~SGAnimation ()
-{
-}
+class SGInterpTableValue : public SGDoubleValue {
+public:
+  SGInterpTableValue(SGPropertyNode const* propertyNode,
+                     SGInterpTable const* interpTable) :
+    _propertyNode(propertyNode),
+    _interpTable(interpTable)
+  { }
+  virtual double getValue() const
+  { return _interpTable->interpolate(_propertyNode->getDoubleValue()); }
+private:
+  SGSharedPtr<SGPropertyNode const> _propertyNode;
+  SGSharedPtr<SGInterpTable const> _interpTable;
+};
 
-void
-SGAnimation::init ()
-{
-}
+class SGTexScaleOffsetValue : public SGDoubleValue {
+public:
+  SGTexScaleOffsetValue(const SGPropertyNode* propertyNode) :
+    _propertyNode(propertyNode),
+    _scale(1),
+    _offset(0),
+    _step(0),
+    _scroll(0),
+    _min(-SGLimitsd::max()),
+    _max(SGLimitsd::max())
+  { }
+  void setScale(double scale)
+  { _scale = scale; }
+  void setOffset(double offset)
+  { _offset = offset; }
+  void setStep(double step)
+  { _step = step; }
+  void setScroll(double scroll)
+  { _scroll = scroll; }
+  void setMin(double min)
+  { _min = min; }
+  void setMax(double max)
+  { _max = max; }
+
+  virtual double getValue() const
+  {
+    double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
+    value = apply_mods(value, _step, _scroll);
+    return SGMiscd::clip(_scale*(_offset + value), _min, _max);
+  }
+private:
+  SGSharedPtr<const SGPropertyNode> _propertyNode;
+  double _scale;
+  double _offset;
+  double _step;
+  double _scroll;
+  double _min;
+  double _max;
+};
 
-void
-SGAnimation::restore()
+class SGTexTableValue : public SGDoubleValue {
+public:
+  SGTexTableValue(const SGPropertyNode* propertyNode,
+                  const SGInterpTable* interpTable) :
+    _propertyNode(propertyNode),
+    _interpTable(interpTable)
+  { }
+  void setStep(double step)
+  { _step = step; }
+  void setScroll(double scroll)
+  { _scroll = scroll; }
+  virtual double getValue() const
+  {
+    double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
+    value = apply_mods(value, _step, _scroll);
+    return _interpTable->interpolate(value);
+  }
+private:
+  SGSharedPtr<SGPropertyNode const> _propertyNode;
+  SGSharedPtr<SGInterpTable const> _interpTable;
+  double _step;
+  double _scroll;
+};
+
+static std::string
+unit_string(const char* value, const char* unit)
 {
+  return std::string(value) + unit;
 }
 
-void
-SGAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
+static SGDoubleValue*
+read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
+           const char* unit, double defMin, double defMax)
 {
-    traverse(node, nv);
+  std::string inputPropertyName;
+  inputPropertyName = configNode->getStringValue("property", "");
+  if (!inputPropertyName.empty()) {
+    SGPropertyNode* inputProperty;
+    inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
+    SGInterpTable* interpTable = read_interpolation_table(configNode);
+    if (interpTable) {
+      SGInterpTableValue* value;
+      value = new SGInterpTableValue(inputProperty, interpTable);
+      return value;
+    } else {
+      std::string offset = unit_string("offset", unit);
+      std::string min = unit_string("min", unit);
+      std::string max = unit_string("max", unit);
+
+      if (configNode->getBoolValue("use-personality", false)) {
+        SGPersScaleOffsetValue* value;
+        value = new SGPersScaleOffsetValue(inputProperty, configNode,
+                                           "factor", offset.c_str());
+        value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
+        value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
+        return value;
+      } else {
+        SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
+        value->setScale(configNode->getDoubleValue("factor", 1));
+        value->setOffset(configNode->getDoubleValue(offset.c_str(), 0));
+        value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
+        value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
+        return value;
+      }
+    }
+  }
+  return 0;
 }
 
 \f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of SGNullAnimation
+// Animation installer
 ////////////////////////////////////////////////////////////////////////
 
-SGNullAnimation::SGNullAnimation (SGPropertyNode_ptr props)
-  : SGAnimation(props, new osg::Group)
-{
-}
-
-SGNullAnimation::~SGNullAnimation ()
-{
-}
+class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
+public:
+  RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
+    _mode(mode)
+  { }
+  virtual void apply(osg::StateSet* stateSet)
+  {
+    if (!stateSet)
+      return;
+    stateSet->removeMode(_mode);
+  }
+private:
+  osg::StateAttribute::GLMode _mode;
+};
 
+class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
+public:
+  RemoveAttributeVisitor(osg::StateAttribute::Type type) :
+    _type(type)
+  { }
+  virtual void apply(osg::StateSet* stateSet)
+  {
+    if (!stateSet)
+      return;
+    while (stateSet->getAttribute(_type)) {
+      stateSet->removeAttribute(_type);
+    }
+  }
+private:
+  osg::StateAttribute::Type _type;
+};
 
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGRangeAnimation
-////////////////////////////////////////////////////////////////////////
+class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
+public:
+  RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
+    _unit(unit),
+    _mode(mode)
+  { }
+  virtual void apply(osg::StateSet* stateSet)
+  {
+    if (!stateSet)
+      return;
+    stateSet->removeTextureMode(_unit, _mode);
+  }
+private:
+  unsigned _unit;
+  osg::StateAttribute::GLMode _mode;
+};
 
-SGRangeAnimation::SGRangeAnimation (SGPropertyNode *prop_root,
-                                    SGPropertyNode_ptr props)
-  : SGAnimation(props, new osg::LOD),
-    _min(0.0), _max(0.0), _min_factor(1.0), _max_factor(1.0),
-    _condition(0)
-{
-    SGPropertyNode_ptr node = props->getChild("condition");
-    if (node != 0)
-       _condition = sgReadCondition(prop_root, node);
+class SGAnimation::RemoveTextureAttributeVisitor :
+  public SGStateAttributeVisitor {
+public:
+  RemoveTextureAttributeVisitor(unsigned unit,
+                                osg::StateAttribute::Type type) :
+    _unit(unit),
+    _type(type)
+  { }
+  virtual void apply(osg::StateSet* stateSet)
+  {
+    if (!stateSet)
+      return;
+    while (stateSet->getTextureAttribute(_unit, _type)) {
+      stateSet->removeTextureAttribute(_unit, _type);
+    }
+  }
+private:
+  unsigned _unit;
+  osg::StateAttribute::Type _type;
+};
 
-    float ranges[2];
+class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
+public:
+  virtual void apply(osg::StateSet* stateSet)
+  {
+    if (!stateSet)
+      return;
+    stateSet->setRenderBinToInherit();
+  }
+};
 
-    node = props->getChild( "min-factor" );
-    if (node != 0) {
-       _min_factor = props->getFloatValue("min-factor", 1.0);
-    }
-    node = props->getChild( "max-factor" );
-    if (node != 0) {
-       _max_factor = props->getFloatValue("max-factor", 1.0);
-    }
-    node = props->getChild( "min-property" );
-    if (node != 0) {
-       _min_prop = (SGPropertyNode *)prop_root->getNode(node->getStringValue(), true);
-       ranges[0] = _min_prop->getFloatValue() * _min_factor;
-    } else {
-       _min = props->getFloatValue("min-m", 0);
-       ranges[0] = _min * _min_factor;
-    }
-    node = props->getChild( "max-property" );
-    if (node != 0) {
-       _max_prop = (SGPropertyNode *)prop_root->getNode(node->getStringValue(), true);
-       ranges[1] = _max_prop->getFloatValue() * _max_factor;
-    } else {
-       _max = props->getFloatValue("max-m", 0);
-       ranges[1] = _max * _max_factor;
+class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
+public:
+  DrawableCloneVisitor() :
+    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
+  {}
+  void apply(osg::Geode& geode)
+  {
+    for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
+      osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
+                         ~osg::CopyOp::DEEP_COPY_TEXTURES);
+      geode.setDrawable(i, copyOp(geode.getDrawable(i)));
     }
-    static_cast<osg::LOD*>(_branch)->setRange(0, ranges[0], ranges[1]);
-}
+  }
+};
 
-SGRangeAnimation::~SGRangeAnimation ()
-{
-}
 
+SGAnimation::SGAnimation(const SGPropertyNode* configNode,
+                                           SGPropertyNode* modelRoot) :
+  osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+  _found(false),
+  _configNode(configNode),
+  _modelRoot(modelRoot)
+{
+  _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)
+    _objectNames.push_back(objectNames[i]->getStringValue());
+}
+
+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");
+}
+
+bool
+SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
+                     SGPropertyNode* modelRoot)
+{
+  std::string type = configNode->getStringValue("type", "none");
+  if (type == "alpha-test") {
+    SGAlphaTestAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "billboard") {
+    SGBillboardAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "blend") {
+    SGBlendAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "dist-scale") {
+    SGDistScaleAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "flash") {
+    SGFlashAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "material") {
+    SGMaterialAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "noshadow") {
+    SGShadowAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "range") {
+    SGRangeAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "rotate" || type == "spin") {
+    SGRotateAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "scale") {
+    SGScaleAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "select") {
+    SGSelectAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "shader") {
+    SGShaderAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "textranslate" || type == "texrotate" ||
+             type == "texmultiple") {
+    SGTexTransformAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "timed") {
+    SGTimedAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "translate") {
+    SGTranslateAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else if (type == "null" || type == "none" || type.empty()) {
+    SGGroupAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
+  } else
+    return false;
+
+  return true;
+}
+  
+  
 void
-SGRangeAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
+SGAnimation::apply(osg::Node* node)
 {
-  float ranges[2];
-  if ( _condition == 0 || _condition->test() ) {
-    if (_min_prop != 0) {
-      ranges[0] = _min_prop->getFloatValue() * _min_factor;
-    } else {
-      ranges[0] = _min * _min_factor;
-    }
-    if (_max_prop != 0) {
-      ranges[1] = _max_prop->getFloatValue() * _max_factor;
-    } else {
-      ranges[1] = _max * _max_factor;
+  // duh what a special case ...
+  if (_objectNames.empty()) {
+    osg::Group* group = node->asGroup();
+    if (group) {
+      osg::ref_ptr<osg::Group> animationGroup;
+      installInGroup(std::string(), *group, animationGroup);
     }
-  } else {
-    ranges[0] = 0.f;
-    ranges[1] = 1000000000.f;
-  }
-  static_cast<osg::LOD*>(_branch)->setRange(0, ranges[0], ranges[1]);
-  traverse(node, nv);
+  } else
+    node->accept(*this);
 }
 
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGBillboardAnimation
-////////////////////////////////////////////////////////////////////////
-
-SGBillboardAnimation::SGBillboardAnimation (SGPropertyNode_ptr props)
-  : SGAnimation(props, new osg::AutoTransform)
+void
+SGAnimation::install(osg::Node& node)
 {
-//OSGFIXME: verify
-  bool spherical = props->getBoolValue("spherical", true);
-  osg::AutoTransform* autoTrans = static_cast<osg::AutoTransform*>(_branch);
-  if (spherical) {
-    autoTrans->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN);
-  } else {
-    autoTrans->setAutoRotateMode(osg::AutoTransform::NO_ROTATION);
-    autoTrans->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
-  }
-  autoTrans->setAutoScaleToScreen(false);
+  _found = true;
+  if (_enableHOT)
+    node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
+  else
+    node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
+  if (!_disableShadow)
+    node.setNodeMask( SG_NODEMASK_SHADOW_BIT | node.getNodeMask());
+  else
+    node.setNodeMask(~SG_NODEMASK_SHADOW_BIT & node.getNodeMask());
 }
 
-SGBillboardAnimation::~SGBillboardAnimation ()
+osg::Group*
+SGAnimation::createAnimationGroup(osg::Group& parent)
 {
+  // default implementation, we do not need a new group
+  // for every animation type. Usually animations that just change
+  // the StateSet of some parts of the model
+  return 0;
 }
 
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGSelectAnimation
-////////////////////////////////////////////////////////////////////////
-
-SGSelectAnimation::SGSelectAnimation( SGPropertyNode *prop_root,
-                                  SGPropertyNode_ptr props )
-  : SGAnimation(props, new osg::Switch),
-    _condition(0)
+void
+SGAnimation::apply(osg::Group& group)
 {
-  SGPropertyNode_ptr node = props->getChild("condition");
-  if (node != 0)
-    _condition = sgReadCondition(prop_root, node);
-}
+  // the trick is to first traverse the children and then
+  // possibly splice in a new group node if required.
+  // Else we end up in a recursive loop where we infinitly insert new
+  // groups in between
+  traverse(group);
 
-SGSelectAnimation::~SGSelectAnimation ()
-{
+  // Note that this algorithm preserves the order of the child objects
+  // like they appear in the object-name tags.
+  // The timed animations require this
+  osg::ref_ptr<osg::Group> animationGroup;
+  std::list<std::string>::const_iterator nameIt;
+  for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
+    installInGroup(*nameIt, group, animationGroup);
 }
 
 void
-SGSelectAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
-{ 
-  if (_condition != 0 && _condition->test())
-    static_cast<osg::Switch*>(_branch)->setAllChildrenOn();
-  else
-    static_cast<osg::Switch*>(_branch)->setAllChildrenOff();
-  traverse(node, nv);
+SGAnimation::installInGroup(const std::string& name, osg::Group& group,
+                            osg::ref_ptr<osg::Group>& animationGroup)
+{
+  int i = group.getNumChildren() - 1;
+  for (; 0 <= i; --i) {
+    osg::Node* child = group.getChild(i);
+    if (name.empty() || child->getName() == name) {
+      // fire the installation of the animation
+      install(*child);
+      
+      // create a group node on demand
+      if (!animationGroup.valid()) {
+        animationGroup = createAnimationGroup(group);
+        // Animation type that does not require a new group,
+        // in this case we can stop and look for the next object
+        if (animationGroup.valid() && !_name.empty())
+          animationGroup->setName(_name);
+      }
+      if (animationGroup.valid()) {
+        animationGroup->addChild(child);
+        group.removeChild(i);
+      }
+    }
+  }
 }
 
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGSpinAnimation
-////////////////////////////////////////////////////////////////////////
-
-SGSpinAnimation::SGSpinAnimation( SGPropertyNode *prop_root,
-                                  SGPropertyNode_ptr props,
-                                  double sim_time_sec )
-  : SGAnimation(props, new osg::MatrixTransform),
-    _use_personality( props->getBoolValue("use-personality",false) ),
-    _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
-    _factor( props, "factor", 1.0 ),
-    _position_deg( props, "starting-position-deg", 0.0 ),
-    _last_time_sec( sim_time_sec ),
-    _condition(0)
+void
+SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
 {
-    SGPropertyNode_ptr node = props->getChild("condition");
-    if (node != 0)
-        _condition = sgReadCondition(prop_root, node);
-
-    _center[0] = 0;
-    _center[1] = 0;
-    _center[2] = 0;
-    if (props->hasValue("axis/x1-m")) {
-        double x1,y1,z1,x2,y2,z2;
-        x1 = props->getFloatValue("axis/x1-m");
-        y1 = props->getFloatValue("axis/y1-m");
-        z1 = props->getFloatValue("axis/z1-m");
-        x2 = props->getFloatValue("axis/x2-m");
-        y2 = props->getFloatValue("axis/y2-m");
-        z2 = props->getFloatValue("axis/z2-m");
-        _center[0] = (x1+x2)/2;
-        _center[1]= (y1+y2)/2;
-        _center[2] = (z1+z2)/2;
-        float vector_length = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1));
-        _axis[0] = (x2-x1)/vector_length;
-        _axis[1] = (y2-y1)/vector_length;
-        _axis[2] = (z2-z1)/vector_length;
-    } else {
-       _axis[0] = props->getFloatValue("axis/x", 0);
-       _axis[1] = props->getFloatValue("axis/y", 0);
-       _axis[2] = props->getFloatValue("axis/z", 0);
-    }
-    if (props->hasValue("center/x-m")) {
-       _center[0] = props->getFloatValue("center/x-m", 0);
-       _center[1] = props->getFloatValue("center/y-m", 0);
-       _center[2] = props->getFloatValue("center/z-m", 0);
-    }
-    
-    _axis.normalize();
-
-    if ( _use_personality ) {
-      _factor.shuffle();
-      _position_deg.shuffle();
-    }
+  RemoveModeVisitor visitor(mode);
+  node.accept(visitor);
 }
 
-SGSpinAnimation::~SGSpinAnimation ()
+void
+SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
 {
+  RemoveAttributeVisitor visitor(type);
+  node.accept(visitor);
 }
 
 void
-SGSpinAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
+SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
+                               osg::StateAttribute::GLMode mode)
 {
-  double sim_time_sec = nv->getFrameStamp()->getReferenceTime();
-  if ( _condition == 0 || _condition->test() ) {
-    double dt;
-    float velocity_rpms;
-    dt = sim_time_sec - _last_time_sec;
-    _last_time_sec = sim_time_sec;
-    
-    velocity_rpms = (_prop->getDoubleValue() * _factor / 60.0);
-    _position_deg += (dt * velocity_rpms * 360);
-    _position_deg -= 360*floor(_position_deg/360);
-
-    osg::Matrix _matrix;
-    set_rotation(_matrix, _position_deg, _center, _axis);
-    static_cast<osg::MatrixTransform*>(_branch)->setMatrix(_matrix);
-  }
-  traverse(node, nv);
+  RemoveTextureModeVisitor visitor(unit, mode);
+  node.accept(visitor);
 }
 
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGTimedAnimation
-////////////////////////////////////////////////////////////////////////
-
-SGTimedAnimation::SGTimedAnimation (SGPropertyNode_ptr props)
-  : SGAnimation(props, new osg::Switch),
-    _use_personality( props->getBoolValue("use-personality",false) ),
-    _duration_sec(props->getDoubleValue("duration-sec", 1.0)),
-    _last_time_sec( 0 ),
-    _total_duration_sec( 0 ),
-    _step( 0 )
-    
+void
+SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
+                                    osg::StateAttribute::Type type)
 {
-    vector<SGPropertyNode_ptr> nodes = props->getChildren( "branch-duration-sec" );
-    size_t nb = nodes.size();
-    for ( size_t i = 0; i < nb; i++ ) {
-        size_t ind = nodes[ i ]->getIndex();
-        while ( ind >= _branch_duration_specs.size() ) {
-            _branch_duration_specs.push_back( DurationSpec( _duration_sec ) );
-        }
-        SGPropertyNode_ptr rNode = nodes[ i ]->getChild("random");
-        if ( rNode == 0 ) {
-            _branch_duration_specs[ ind ] = DurationSpec( nodes[ i ]->getDoubleValue() );
-        } else {
-            _branch_duration_specs[ ind ] = DurationSpec( rNode->getDoubleValue( "min", 0.0 ),
-                                                          rNode->getDoubleValue( "max", 1.0 ) );
-        }
-    }
+  RemoveTextureAttributeVisitor visitor(unit, type);
+  node.accept(visitor);
 }
 
-SGTimedAnimation::~SGTimedAnimation ()
+void
+SGAnimation::setRenderBinToInherit(osg::Node& node)
 {
+  BinToInheritVisitor visitor;
+  node.accept(visitor);
 }
 
 void
-SGTimedAnimation::init()
+SGAnimation::cloneDrawables(osg::Node& node)
 {
-    if ( !_use_personality ) {
-        for ( unsigned i = 0; i < getBranch()->getNumChildren(); i++ ) {
-            double v;
-            if ( i < _branch_duration_specs.size() ) {
-                DurationSpec &sp = _branch_duration_specs[ i ];
-                v = sp._min + sg_random() * ( sp._max - sp._min );
-            } else {
-                v = _duration_sec;
-            }
-            _branch_duration_sec.push_back( v );
-            _total_duration_sec += v;
-        }
-    }
-    // Sanity check : total duration shouldn't equal zero
-    if (_duration_sec < 0.01)
-        _duration_sec = 0.01;
-    if ( _total_duration_sec < 0.01 )
-        _total_duration_sec = 0.01;
-
-    static_cast<osg::Switch*>(getBranch())->setSingleChildOn(_step);
+  DrawableCloneVisitor visitor;
+  node.accept(visitor);
 }
 
-void
-SGTimedAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
+const SGCondition*
+SGAnimation::getCondition() const
 {
-    double sim_time_sec = nv->getFrameStamp()->getReferenceTime();
-    _last_time_sec -= _total_duration_sec*floor((sim_time_sec - _last_time_sec)/_total_duration_sec);
-    double duration = _duration_sec;
-    if ( _step < _branch_duration_sec.size() ) {
-      duration = _branch_duration_sec[ _step ];
-    }
-    if ( ( sim_time_sec - _last_time_sec ) >= duration ) {
-      _last_time_sec += duration;
-      _step += 1;
-      if ( _step >= getBranch()->getNumChildren() )
-        _step = 0;
-      static_cast<osg::Switch*>(getBranch())->setSingleChildOn(_step);
-    }
-    traverse(node, nv);
+  const SGPropertyNode* conditionNode = _configNode->getChild("condition");
+  if (!conditionNode)
+    return 0;
+  return sgReadCondition(_modelRoot, conditionNode);
 }
 
 
 \f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of SGRotateAnimation
+// Implementation of null animation
 ////////////////////////////////////////////////////////////////////////
 
-SGRotateAnimation::SGRotateAnimation( SGPropertyNode *prop_root,
-                                  SGPropertyNode_ptr props )
-  : SGAnimation(props, new osg::MatrixTransform),
-      _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
-      _offset_deg(props->getDoubleValue("offset-deg", 0.0)),
-      _factor(props->getDoubleValue("factor", 1.0)),
-      _table(read_interpolation_table(props)),
-      _has_min(props->hasValue("min-deg")),
-      _min_deg(props->getDoubleValue("min-deg")),
-      _has_max(props->hasValue("max-deg")),
-      _max_deg(props->getDoubleValue("max-deg")),
-      _position_deg(props->getDoubleValue("starting-position-deg", 0)),
-      _condition(0)
-{
-    SGPropertyNode_ptr node = props->getChild("condition");
-    if (node != 0)
-      _condition = sgReadCondition(prop_root, node);
-
-    _center[0] = 0;
-    _center[1] = 0;
-    _center[2] = 0;
-    if (props->hasValue("axis/x") || props->hasValue("axis/y") || props->hasValue("axis/z")) {
-       _axis[0] = props->getFloatValue("axis/x", 0);
-       _axis[1] = props->getFloatValue("axis/y", 0);
-       _axis[2] = props->getFloatValue("axis/z", 0);
-    } else {
-        double x1,y1,z1,x2,y2,z2;
-        x1 = props->getFloatValue("axis/x1-m", 0);
-        y1 = props->getFloatValue("axis/y1-m", 0);
-        z1 = props->getFloatValue("axis/z1-m", 0);
-        x2 = props->getFloatValue("axis/x2-m", 0);
-        y2 = props->getFloatValue("axis/y2-m", 0);
-        z2 = props->getFloatValue("axis/z2-m", 0);
-        _center[0] = (x1+x2)/2;
-        _center[1]= (y1+y2)/2;
-        _center[2] = (z1+z2)/2;
-        float vector_length = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1));
-        _axis[0] = (x2-x1)/vector_length;
-        _axis[1] = (y2-y1)/vector_length;
-        _axis[2] = (z2-z1)/vector_length;
-    }
-    if (props->hasValue("center/x-m") || props->hasValue("center/y-m")
-            || props->hasValue("center/z-m")) {
-        _center[0] = props->getFloatValue("center/x-m", 0);
-        _center[1] = props->getFloatValue("center/y-m", 0);
-        _center[2] = props->getFloatValue("center/z-m", 0);
-    }
-    _axis.normalize();
-}
-
-SGRotateAnimation::~SGRotateAnimation ()
+// Ok, that is to build a subgraph from different other
+// graph nodes. I guess that this stems from the time where modellers
+// could not build hierarchical trees ...
+SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
+                                   SGPropertyNode* modelRoot):
+  SGAnimation(configNode, modelRoot)
 {
 }
 
-void
-SGRotateAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
+osg::Group*
+SGGroupAnimation::createAnimationGroup(osg::Group& parent)
 {
-  if (_condition == 0 || _condition->test()) {
-    if (_table == 0) {
-      _position_deg = _prop->getDoubleValue() * _factor + _offset_deg;
-      if (_has_min && _position_deg < _min_deg)
-        _position_deg = _min_deg;
-      if (_has_max && _position_deg > _max_deg)
-        _position_deg = _max_deg;
-    } else {
-      _position_deg = _table->interpolate(_prop->getDoubleValue());
-    }
-    osg::Matrix _matrix;
-    set_rotation(_matrix, _position_deg, _center, _axis);
-    static_cast<osg::MatrixTransform*>(_branch)->setMatrix(_matrix);
-  }
-  traverse(node, nv);
+  osg::Group* group = new osg::Group;
+  parent.addChild(group);
+  return group;
 }
 
 \f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of SGBlendAnimation
+// Implementation of translate animation
 ////////////////////////////////////////////////////////////////////////
 
-class SGBlendAnimationVisitor : public osg::NodeVisitor {
+class SGTranslateAnimation::Transform : public osg::Transform {
 public:
-  SGBlendAnimationVisitor(float blend) :
-    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
-    _blend(blend)
+  Transform() :
+    _axis(0, 0, 0),
+    _value(0)
+  { setReferenceFrame(RELATIVE_RF); }
+  void setAxis(const SGVec3d& axis)
+  { _axis = axis; dirtyBound(); }
+  void setValue(double value)
+  { _value = value; dirtyBound(); }
+  virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
+                                         osg::NodeVisitor* nv) const 
   {
-    setVisitorType(osg::NodeVisitor::NODE_VISITOR);
+    assert(_referenceFrame == RELATIVE_RF);
+    osg::Matrix tmp;
+    set_translation(tmp, _value, _axis);
+    matrix.preMult(tmp);
+    return true;
   }
-  virtual void apply(osg::Node& node)
+  virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
+                                         osg::NodeVisitor* nv) const
   {
-    updateStateSet(node.getStateSet());
-    traverse(node);
+    assert(_referenceFrame == RELATIVE_RF);
+    osg::Matrix tmp;
+    set_translation(tmp, -_value, _axis);
+    matrix.postMult(tmp);
+    return true;
   }
-  virtual void apply(osg::Geode& node)
+private:
+  SGVec3d _axis;
+  double _value;
+};
+
+class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
+public:
+  UpdateCallback(SGCondition const* condition,
+                 SGDoubleValue const* animationValue) :
+    _condition(condition),
+    _animationValue(animationValue)
+  { }
+  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
   {
-    apply((osg::Node&)node);
-    unsigned nDrawables = node.getNumDrawables();
-    for (unsigned i = 0; i < nDrawables; ++i) {
-      osg::Drawable* drawable = node.getDrawable(i);
-      updateStateSet(drawable->getStateSet());
-      osg::Geometry* geometry = drawable->asGeometry();
-      if (!geometry)
-        continue;
-      osg::Array* array = geometry->getColorArray();
-      if (!array)
-        continue;
-      osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
-      if (!vec4Array)
-        continue;
-      geometry->dirtyDisplayList();
-      vec4Array->dirty();
-      for (unsigned k = 0; k < vec4Array->size(); ++k) {
-        (*vec4Array)[k][3] = _blend;
-      }
+    if (!_condition || _condition->test()) {
+      SGTranslateAnimation::Transform* transform;
+      transform = static_cast<SGTranslateAnimation::Transform*>(node);
+      transform->setValue(_animationValue->getValue());
     }
+    traverse(node, nv);
   }
-  void updateStateSet(osg::StateSet* stateSet)
-  {
-    if (!stateSet)
-      return;
-    osg::StateAttribute* stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
-    if (!stateAttribute)
-      return;
-    osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
-    if (!material)
-      return;
-    material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
-  }
-private:
-  float _blend;
+public:
+  SGSharedPtr<SGCondition const> _condition;
+  SGSharedPtr<SGDoubleValue const> _animationValue;
 };
 
-
-SGBlendAnimation::SGBlendAnimation( SGPropertyNode *prop_root,
-                                        SGPropertyNode_ptr props )
-  : SGAnimation(props, new osg::Group),
-    _use_personality( props->getBoolValue("use-personality",false) ),
-    _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
-    _table(read_interpolation_table(props)),
-    _prev_value(1.0),
-    _offset(props,"offset",0.0),
-    _factor(props,"factor",1.0),
-    _min(props->getDoubleValue("min", 0.0)),
-    _max(props->getDoubleValue("max", 1.0))
+SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
+                                           SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
 {
-  if ( _use_personality ) {
-    _factor.shuffle();
-    _offset.shuffle();
-  }
-}
+  _condition = getCondition();
+  _animationValue = read_value(configNode, modelRoot, "-m",
+                               -SGLimitsd::max(), SGLimitsd::max());
+  _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);
 
-SGBlendAnimation::~SGBlendAnimation ()
-{
+  _initialValue = configNode->getDoubleValue("starting-position-m", 0);
+  _initialValue *= configNode->getDoubleValue("factor", 1);
+  _initialValue += configNode->getDoubleValue("offset-m", 0);
 }
 
-void
-SGBlendAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
+osg::Group*
+SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
 {
-  double _blend;
-
-  if (_table == 0) {
-    _blend = 1.0 - (_prop->getDoubleValue() * _factor + _offset);
-  } else {
-    _blend = _table->interpolate(_prop->getDoubleValue());
+  Transform* transform = new Transform;
+  transform->setName("translate animation");
+  if (_animationValue) {
+    UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
+    transform->setUpdateCallback(uc);
   }
-  if (_blend < _min)
-    _blend = _min;
-  if (_blend > _max)
-    _blend = _max;
-
-  if (_blend != _prev_value) {
-    _prev_value = _blend;
-    SGBlendAnimationVisitor visitor(1-_blend);
-    _branch->accept(visitor);
-  }
-  traverse(node, nv);
-}
-
+  transform->setAxis(_axis);
+  transform->setValue(_initialValue);
+  parent.addChild(transform);
+  return transform;
+}
 
 \f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of SGTranslateAnimation
+// Implementation of rotate/spin animation
 ////////////////////////////////////////////////////////////////////////
 
-SGTranslateAnimation::SGTranslateAnimation( SGPropertyNode *prop_root,
-                                        SGPropertyNode_ptr props )
-  : SGAnimation(props, new osg::MatrixTransform),
-    _use_personality( props->getBoolValue("use-personality",false) ),
-    _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
-    _offset_m( props, "offset-m", 0.0 ),
-    _factor( props, "factor", 1.0 ),
-    _table(read_interpolation_table(props)),
-    _has_min(props->hasValue("min-m")),
-    _min_m(props->getDoubleValue("min-m")),
-    _has_max(props->hasValue("max-m")),
-    _max_m(props->getDoubleValue("max-m")),
-    _position_m(props->getDoubleValue("starting-position-m", 0)),
-    _condition(0)
-{
-  SGPropertyNode_ptr node = props->getChild("condition");
-  if (node != 0)
-    _condition = sgReadCondition(prop_root, node);
-
-  _axis[0] = props->getFloatValue("axis/x", 0);
-  _axis[1] = props->getFloatValue("axis/y", 0);
-  _axis[2] = props->getFloatValue("axis/z", 0);
-  _axis.normalize();
-
-  if ( _use_personality ) {
-    _factor.shuffle();
-    _offset_m.shuffle();
+class SGRotateAnimation::Transform : public osg::Transform {
+public:
+  Transform()
+  { setReferenceFrame(RELATIVE_RF); }
+  void setCenter(const SGVec3d& center)
+  { _center = center; dirtyBound(); }
+  void setAxis(const SGVec3d& axis)
+  { _axis = axis; dirtyBound(); }
+  void setAngle(double angle)
+  { _angle = angle; }
+  double getAngle() const
+  { return _angle; }
+  virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
+                                         osg::NodeVisitor* nv) const 
+  {
+    // This is the fast path, optimize a bit
+    assert(_referenceFrame == RELATIVE_RF);
+    // FIXME optimize
+    osg::Matrix tmp;
+    set_rotation(tmp, _angle, _center, _axis);
+    matrix.preMult(tmp);
+    return true;
+  }
+  virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
+                                         osg::NodeVisitor* nv) const
+  {
+    assert(_referenceFrame == RELATIVE_RF);
+    // FIXME optimize
+    osg::Matrix tmp;
+    set_rotation(tmp, -_angle, _center, _axis);
+    matrix.postMult(tmp);
+    return true;
+  }
+  virtual osg::BoundingSphere computeBound() const
+  {
+    osg::BoundingSphere bs = osg::Group::computeBound();
+    osg::BoundingSphere centerbs(_center.osg(), bs.radius());
+    centerbs.expandBy(bs);
+    return centerbs;
   }
-}
 
-SGTranslateAnimation::~SGTranslateAnimation ()
-{
-}
+private:
+  SGVec3d _center;
+  SGVec3d _axis;
+  double _angle;
+};
 
-void
-SGTranslateAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
-{
-  if (_condition == 0 || _condition->test()) {
+class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
+public:
+  UpdateCallback(SGCondition const* condition,
+                 SGDoubleValue const* animationValue) :
+    _condition(condition),
+    _animationValue(animationValue)
+  { }
+  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+  {
+    if (!_condition || _condition->test()) {
+      SGRotateAnimation::Transform* transform;
+      transform = static_cast<SGRotateAnimation::Transform*>(node);
+      transform->setAngle(_animationValue->getValue());
+    }
+    traverse(node, nv);
+  }
+public:
+  SGSharedPtr<SGCondition const> _condition;
+  SGSharedPtr<SGDoubleValue const> _animationValue;
+};
 
-    if (_table == 0) {
-      _position_m = (_prop->getDoubleValue() * _factor) + _offset_m;
-      if (_has_min && _position_m < _min_m)
-        _position_m = _min_m;
-      if (_has_max && _position_m > _max_m)
-        _position_m = _max_m;
-    } else {
-      _position_m = _table->interpolate(_prop->getDoubleValue());
+class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
+public:
+  SpinUpdateCallback(SGCondition const* condition,
+                     SGDoubleValue const* animationValue) :
+    _condition(condition),
+    _animationValue(animationValue),
+    _lastTime(-1)
+  { }
+  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+  {
+    if (!_condition || _condition->test()) {
+      SGRotateAnimation::Transform* transform;
+      transform = static_cast<SGRotateAnimation::Transform*>(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->getAngle();
+      angle += dt*velocity_rpms*360;
+      angle -= 360*floor(angle/360);
+      transform->setAngle(angle);
     }
+    traverse(node, nv);
+  }
+public:
+  SGSharedPtr<SGCondition const> _condition;
+  SGSharedPtr<SGDoubleValue const> _animationValue;
+  double _lastTime;
+};
 
-    osg::Matrix _matrix;
-    set_translation(_matrix, _position_m, _axis);
-    static_cast<osg::MatrixTransform*>(_branch)->setMatrix(_matrix);
+SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
+{
+  std::string type = configNode->getStringValue("type", "");
+  _isSpin = (type == "spin");
+
+  _condition = getCondition();
+  _animationValue = read_value(configNode, modelRoot, "-deg",
+                               -SGLimitsd::max(), SGLimitsd::max());
+  _initialValue = configNode->getDoubleValue("starting-position-deg", 0);
+  _initialValue *= configNode->getDoubleValue("factor", 1);
+  _initialValue += configNode->getDoubleValue("offset-deg", 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]);
+}
+
+osg::Group*
+SGRotateAnimation::createAnimationGroup(osg::Group& parent)
+{
+  Transform* transform = new Transform;
+  transform->setName("rotate animation");
+  if (_isSpin) {
+    SpinUpdateCallback* uc;
+    uc = new SpinUpdateCallback(_condition, _animationValue);
+    transform->setUpdateCallback(uc);
+  } else if (_animationValue) {
+    UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
+    transform->setUpdateCallback(uc);
   }
-  traverse(node, nv);
+  transform->setCenter(_center);
+  transform->setAxis(_axis);
+  transform->setAngle(_initialValue);
+  parent.addChild(transform);
+  return transform;
 }
 
-
 \f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of SGScaleAnimation
+// Implementation of scale animation
 ////////////////////////////////////////////////////////////////////////
 
-SGScaleAnimation::SGScaleAnimation( SGPropertyNode *prop_root,
-                                        SGPropertyNode_ptr props )
-  : SGAnimation(props, new osg::MatrixTransform),
-    _use_personality( props->getBoolValue("use-personality",false) ),
-    _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
-    _x_factor(props,"x-factor",1.0),
-    _y_factor(props,"y-factor",1.0),
-    _z_factor(props,"z-factor",1.0),
-    _x_offset(props,"x-offset",1.0),
-    _y_offset(props,"y-offset",1.0),
-    _z_offset(props,"z-offset",1.0),
-    _table(read_interpolation_table(props)),
-    _has_min_x(props->hasValue("x-min")),
-    _has_min_y(props->hasValue("y-min")),
-    _has_min_z(props->hasValue("z-min")),
-    _min_x(props->getDoubleValue("x-min")),
-    _min_y(props->getDoubleValue("y-min")),
-    _min_z(props->getDoubleValue("z-min")),
-    _has_max_x(props->hasValue("x-max")),
-    _has_max_y(props->hasValue("y-max")),
-    _has_max_z(props->hasValue("z-max")),
-    _max_x(props->getDoubleValue("x-max")),
-    _max_y(props->getDoubleValue("y-max")),
-    _max_z(props->getDoubleValue("z-max"))
-{
-  if ( _use_personality ) {
-    _x_factor.shuffle();
-    _x_offset.shuffle();
-    _y_factor.shuffle();
-    _y_offset.shuffle();
-    _z_factor.shuffle();
-    _z_offset.shuffle();
+class SGScaleAnimation::Transform : public osg::Transform {
+public:
+  Transform() :
+    _center(0, 0, 0),
+    _scaleFactor(1, 1, 1),
+    _boundScale(0)
+  {
+    setReferenceFrame(RELATIVE_RF);
   }
-}
-
-SGScaleAnimation::~SGScaleAnimation ()
-{
-}
-
-void
-SGScaleAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
-{
-  if (_table == 0) {
-      _x_scale = _prop->getDoubleValue() * _x_factor + _x_offset;
-    if (_has_min_x && _x_scale < _min_x)
-      _x_scale = _min_x;
-    if (_has_max_x && _x_scale > _max_x)
-      _x_scale = _max_x;
-  } else {
-    _x_scale = _table->interpolate(_prop->getDoubleValue());
+  void setCenter(const SGVec3d& center)
+  {
+    _center = center;
+    dirtyBound();
   }
-
-  if (_table == 0) {
-    _y_scale = _prop->getDoubleValue() * _y_factor + _y_offset;
-    if (_has_min_y && _y_scale < _min_y)
-      _y_scale = _min_y;
-    if (_has_max_y && _y_scale > _max_y)
-      _y_scale = _max_y;
-  } else {
-    _y_scale = _table->interpolate(_prop->getDoubleValue());
+  void setScaleFactor(const SGVec3d& scaleFactor)
+  {
+    if (_boundScale < normI(scaleFactor))
+      dirtyBound();
+    _scaleFactor = scaleFactor;
   }
-
-  if (_table == 0) {
-    _z_scale = _prop->getDoubleValue() * _z_factor + _z_offset;
-    if (_has_min_z && _z_scale < _min_z)
-      _z_scale = _min_z;
-    if (_has_max_z && _z_scale > _max_z)
-      _z_scale = _max_z;
-  } else {
-    _z_scale = _table->interpolate(_prop->getDoubleValue());
+  void setScaleFactor(double scaleFactor)
+  { 
+    if (_boundScale < fabs(scaleFactor))
+      dirtyBound();
+    _scaleFactor = SGVec3d(scaleFactor, scaleFactor, scaleFactor);
   }
-
-  osg::Matrix _matrix;
-  set_scale(_matrix, _x_scale, _y_scale, _z_scale );
-  static_cast<osg::MatrixTransform*>(_branch)->setMatrix(_matrix);
-  traverse(node, nv);
-}
-
-
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGTexRotateAnimation
-////////////////////////////////////////////////////////////////////////
-
-SGTexRotateAnimation::SGTexRotateAnimation( SGPropertyNode *prop_root,
-                                  SGPropertyNode_ptr props )
-    : SGAnimation(props, new osg::Group),
-      _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
-      _offset_deg(props->getDoubleValue("offset-deg", 0.0)),
-      _factor(props->getDoubleValue("factor", 1.0)),
-      _table(read_interpolation_table(props)),
-      _has_min(props->hasValue("min-deg")),
-      _min_deg(props->getDoubleValue("min-deg")),
-      _has_max(props->hasValue("max-deg")),
-      _max_deg(props->getDoubleValue("max-deg")),
-      _position_deg(props->getDoubleValue("starting-position-deg", 0)),
-      _condition(0)
-{
-  SGPropertyNode *node = props->getChild("condition");
-  if (node != 0)
-    _condition = sgReadCondition(prop_root, node);
-
-  _center[0] = props->getFloatValue("center/x", 0);
-  _center[1] = props->getFloatValue("center/y", 0);
-  _center[2] = props->getFloatValue("center/z", 0);
-  _axis[0] = props->getFloatValue("axis/x", 0);
-  _axis[1] = props->getFloatValue("axis/y", 0);
-  _axis[2] = props->getFloatValue("axis/z", 0);
-  _axis.normalize();
-
-  osg::StateSet* stateSet = _branch->getOrCreateStateSet();
-  _texMat = new osg::TexMat;
-  stateSet->setTextureAttribute(0, _texMat.get());
-}
-
-SGTexRotateAnimation::~SGTexRotateAnimation ()
-{
-}
-
-void
-SGTexRotateAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
-{
-  if (!_condition || _condition->test()) {
-    if (_table == 0) {
-      _position_deg = _prop->getDoubleValue() * _factor + _offset_deg;
-      if (_has_min && _position_deg < _min_deg)
-        _position_deg = _min_deg;
-      if (_has_max && _position_deg > _max_deg)
-        _position_deg = _max_deg;
-    } else {
-      _position_deg = _table->interpolate(_prop->getDoubleValue());
-    }
-    osg::Matrix _matrix;
-    set_rotation(_matrix, _position_deg, _center, _axis);
-    _texMat->setMatrix(_matrix);
+  virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
+                                         osg::NodeVisitor* nv) const 
+  {
+    assert(_referenceFrame == RELATIVE_RF);
+    osg::Matrix transform;
+    transform(0,0) = _scaleFactor[0];
+    transform(1,1) = _scaleFactor[1];
+    transform(2,2) = _scaleFactor[2];
+    transform(3,0) = _center[0]*(1 - _scaleFactor[0]);
+    transform(3,1) = _center[1]*(1 - _scaleFactor[1]);
+    transform(3,2) = _center[2]*(1 - _scaleFactor[2]);
+    matrix.preMult(transform);
+    return true;
   }
-  traverse(node, nv);
-}
-
-
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGTexTranslateAnimation
-////////////////////////////////////////////////////////////////////////
-
-SGTexTranslateAnimation::SGTexTranslateAnimation( SGPropertyNode *prop_root,
-                                        SGPropertyNode_ptr props )
-  : SGAnimation(props, new osg::Group),
-      _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)),
-    _offset(props->getDoubleValue("offset", 0.0)),
-    _factor(props->getDoubleValue("factor", 1.0)),
-    _step(props->getDoubleValue("step",0.0)),
-    _scroll(props->getDoubleValue("scroll",0.0)),
-    _table(read_interpolation_table(props)),
-    _has_min(props->hasValue("min")),
-    _min(props->getDoubleValue("min")),
-    _has_max(props->hasValue("max")),
-    _max(props->getDoubleValue("max")),
-    _position(props->getDoubleValue("starting-position", 0)),
-    _condition(0)
-{
-  SGPropertyNode *node = props->getChild("condition");
-  if (node != 0)
-    _condition = sgReadCondition(prop_root, node);
-
-  _axis[0] = props->getFloatValue("axis/x", 0);
-  _axis[1] = props->getFloatValue("axis/y", 0);
-  _axis[2] = props->getFloatValue("axis/z", 0);
-  _axis.normalize();
-
-  osg::StateSet* stateSet = _branch->getOrCreateStateSet();
-  _texMat = new osg::TexMat;
-  stateSet->setTextureAttribute(0, _texMat.get());
-}
-
-SGTexTranslateAnimation::~SGTexTranslateAnimation ()
-{
-}
-
-void
-SGTexTranslateAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
-{
-  if (!_condition || _condition->test()) {
-    if (_table == 0) {
-      _position = (apply_mods(_prop->getDoubleValue(), _step, _scroll) + _offset) * _factor;
-      if (_has_min && _position < _min)
-        _position = _min;
-      if (_has_max && _position > _max)
-        _position = _max;
-    } else {
-      _position = _table->interpolate(apply_mods(_prop->getDoubleValue(), _step, _scroll));
-    }
-    osg::Matrix _matrix;
-    set_translation(_matrix, _position, _axis);
-    _texMat->setMatrix(_matrix);
+  virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
+                                         osg::NodeVisitor* nv) const
+  {
+    assert(_referenceFrame == RELATIVE_RF);
+    if (fabs(_scaleFactor[0]) < SGLimitsd::min())
+      return false;
+    if (fabs(_scaleFactor[1]) < SGLimitsd::min())
+      return false;
+    if (fabs(_scaleFactor[2]) < SGLimitsd::min())
+      return false;
+    SGVec3d rScaleFactor(1/_scaleFactor[0],
+                         1/_scaleFactor[1],
+                         1/_scaleFactor[2]);
+    osg::Matrix transform;
+    transform(0,0) = rScaleFactor[0];
+    transform(1,1) = rScaleFactor[1];
+    transform(2,2) = rScaleFactor[2];
+    transform(3,0) = _center[0]*(1 - rScaleFactor[0]);
+    transform(3,1) = _center[1]*(1 - rScaleFactor[1]);
+    transform(3,2) = _center[2]*(1 - rScaleFactor[2]);
+    matrix.postMult(transform);
+    return true;
+  }
+  virtual osg::BoundingSphere computeBound() const
+  {
+    osg::BoundingSphere bs = osg::Group::computeBound();
+    _boundScale = normI(_scaleFactor);
+    bs.radius() *= _boundScale;
+    return bs;
   }
-  traverse(node, nv);
-}
-
 
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGTexMultipleAnimation
-////////////////////////////////////////////////////////////////////////
+private:
+  SGVec3d _center;
+  SGVec3d _scaleFactor;
+  mutable double _boundScale;
+};
 
-SGTexMultipleAnimation::SGTexMultipleAnimation( SGPropertyNode *prop_root,
-                                        SGPropertyNode_ptr props )
-  : SGAnimation(props, new osg::Group),
-      _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true))
-{
-  unsigned int i;
-  // Load animations
-  vector<SGPropertyNode_ptr> transform_nodes = props->getChildren("transform");
-  _transform = new TexTransform [transform_nodes.size()];
-  _num_transforms = 0;
-  for (i = 0; i < transform_nodes.size(); i++) {
-    SGPropertyNode_ptr transform_props = transform_nodes[i];
-
-    if (!strcmp("textranslate",transform_props->getStringValue("subtype", 0))) {
-
-      // transform is a translation
-      _transform[i].subtype = 0;
-
-      _transform[i].prop = (SGPropertyNode *)prop_root->getNode(transform_props->getStringValue("property", "/null"), true);
-
-      _transform[i].offset = transform_props->getDoubleValue("offset", 0.0);
-      _transform[i].factor = transform_props->getDoubleValue("factor", 1.0);
-      _transform[i].step = transform_props->getDoubleValue("step",0.0);
-      _transform[i].scroll = transform_props->getDoubleValue("scroll",0.0);
-      _transform[i].table = read_interpolation_table(transform_props);
-      _transform[i].has_min = transform_props->hasValue("min");
-      _transform[i].min = transform_props->getDoubleValue("min");
-      _transform[i].has_max = transform_props->hasValue("max");
-      _transform[i].max = transform_props->getDoubleValue("max");
-      _transform[i].position = transform_props->getDoubleValue("starting-position", 0);
-
-      _transform[i].axis[0] = transform_props->getFloatValue("axis/x", 0);
-      _transform[i].axis[1] = transform_props->getFloatValue("axis/y", 0);
-      _transform[i].axis[2] = transform_props->getFloatValue("axis/z", 0);
-      _transform[i].axis.normalize();
-      _num_transforms++;
-    } else if (!strcmp("texrotate",transform_nodes[i]->getStringValue("subtype", 0))) {
-
-      // transform is a rotation
-      _transform[i].subtype = 1;
-
-      _transform[i].prop = (SGPropertyNode *)prop_root->getNode(transform_props->getStringValue("property", "/null"), true);
-      _transform[i].offset = transform_props->getDoubleValue("offset-deg", 0.0);
-      _transform[i].factor = transform_props->getDoubleValue("factor", 1.0);
-      _transform[i].table = read_interpolation_table(transform_props);
-      _transform[i].has_min = transform_props->hasValue("min-deg");
-      _transform[i].min = transform_props->getDoubleValue("min-deg");
-      _transform[i].has_max = transform_props->hasValue("max-deg");
-      _transform[i].max = transform_props->getDoubleValue("max-deg");
-      _transform[i].position = transform_props->getDoubleValue("starting-position-deg", 0);
-
-      _transform[i].center[0] = transform_props->getFloatValue("center/x", 0);
-      _transform[i].center[1] = transform_props->getFloatValue("center/y", 0);
-      _transform[i].center[2] = transform_props->getFloatValue("center/z", 0);
-      _transform[i].axis[0] = transform_props->getFloatValue("axis/x", 0);
-      _transform[i].axis[1] = transform_props->getFloatValue("axis/y", 0);
-      _transform[i].axis[2] = transform_props->getFloatValue("axis/z", 0);
-      _transform[i].axis.normalize();
-      _num_transforms++;
+class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
+public:
+  UpdateCallback(const SGCondition* condition,
+                 SGSharedPtr<const SGDoubleValue> animationValue[3]) :
+    _condition(condition)
+  {
+    _animationValue[0] = animationValue[0];
+    _animationValue[1] = animationValue[1];
+    _animationValue[2] = animationValue[2];
+  }
+  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+  {
+    if (!_condition || _condition->test()) {
+      SGScaleAnimation::Transform* transform;
+      transform = static_cast<SGScaleAnimation::Transform*>(node);
+      SGVec3d scale(_animationValue[0]->getValue(),
+                    _animationValue[1]->getValue(),
+                    _animationValue[2]->getValue());
+      transform->setScaleFactor(scale);
     }
+    traverse(node, nv);
   }
-  osg::StateSet* stateSet = _branch->getOrCreateStateSet();
-  _texMat = new osg::TexMat;
-  stateSet->setTextureAttribute(0, _texMat.get());
-}
-
-SGTexMultipleAnimation::~SGTexMultipleAnimation ()
-{
-   delete [] _transform;
-}
-
-void
-SGTexMultipleAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
-{
-  int i;
-  osg::Matrix tmatrix;
-  tmatrix.makeIdentity();
-  for (i = 0; i < _num_transforms; i++) {
-
-    if(_transform[i].subtype == 0) {
-
-      // subtype 0 is translation
-      if (_transform[i].table == 0) {
-        _transform[i].position = (apply_mods(_transform[i].prop->getDoubleValue(), _transform[i].step,_transform[i].scroll) + _transform[i].offset) * _transform[i].factor;
-        if (_transform[i].has_min && _transform[i].position < _transform[i].min)
-          _transform[i].position = _transform[i].min;
-        if (_transform[i].has_max && _transform[i].position > _transform[i].max)
-          _transform[i].position = _transform[i].max;
-      } else {
-         _transform[i].position = _transform[i].table->interpolate(apply_mods(_transform[i].prop->getDoubleValue(), _transform[i].step,_transform[i].scroll));
-      }
-      osg::Matrix matrix;
-      set_translation(matrix, _transform[i].position, _transform[i].axis);
-      tmatrix = matrix*tmatrix;
-
-    } else if (_transform[i].subtype == 1) {
-
-      // subtype 1 is rotation
-
-      if (_transform[i].table == 0) {
-        _transform[i].position = _transform[i].prop->getDoubleValue() * _transform[i].factor + _transform[i].offset;
-        if (_transform[i].has_min && _transform[i].position < _transform[i].min)
-         _transform[i].position = _transform[i].min;
-       if (_transform[i].has_max && _transform[i].position > _transform[i].max)
-         _transform[i].position = _transform[i].max;
-     } else {
-        _transform[i].position = _transform[i].table->interpolate(_transform[i].prop->getDoubleValue());
-      }
+public:
+  SGSharedPtr<SGCondition const> _condition;
+  SGSharedPtr<SGDoubleValue const> _animationValue[3];
+};
 
-      osg::Matrix matrix;
-      set_rotation(matrix, _transform[i].position, _transform[i].center, _transform[i].axis);
-      tmatrix = matrix*tmatrix;
+SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
+                                   SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
+{
+  _condition = getCondition();
+
+  // default offset/factor for all directions
+  double offset = configNode->getDoubleValue("offset", 1);
+  double factor = configNode->getDoubleValue("factor", 1);
+
+  std::string inputPropertyName;
+  inputPropertyName = configNode->getStringValue("property", "");
+  if (!inputPropertyName.empty()) {
+    SGPropertyNode* inputProperty;
+    inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
+    SGInterpTable* interpTable = read_interpolation_table(configNode);
+    if (interpTable) {
+      SGInterpTableValue* value;
+      value = new SGInterpTableValue(inputProperty, interpTable);
+      _animationValue[0] = value;
+      _animationValue[1] = value;
+      _animationValue[2] = value;
+    } else if (configNode->getBoolValue("use-personality", false)) {
+      SGPersScaleOffsetValue* value;
+      value = new SGPersScaleOffsetValue(inputProperty, configNode,
+                                         "x-factor", "x-offset",
+                                         factor, offset);
+      value->setMin(configNode->getDoubleValue("x-min", 0));
+      value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
+      _animationValue[0] = value;
+      value = new SGPersScaleOffsetValue(inputProperty, configNode,
+                                         "y-factor", "y-offset",
+                                         factor, offset);
+      value->setMin(configNode->getDoubleValue("y-min", 0));
+      value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
+      _animationValue[1] = value;
+      value = new SGPersScaleOffsetValue(inputProperty, configNode,
+                                         "z-factor", "z-offset",
+                                         factor, offset);
+      value->setMin(configNode->getDoubleValue("z-min", 0));
+      value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
+      _animationValue[2] = value;
+    } else {
+      SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
+      value->setScale(configNode->getDoubleValue("x-factor", factor));
+      value->setOffset(configNode->getDoubleValue("x-offset", offset));
+      value->setMin(configNode->getDoubleValue("x-min", 0));
+      value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
+      _animationValue[0] = value;
+      value = new SGScaleOffsetValue(inputProperty);
+      value->setScale(configNode->getDoubleValue("y-factor", factor));
+      value->setOffset(configNode->getDoubleValue("y-offset", offset));
+      value->setMin(configNode->getDoubleValue("y-min", 0));
+      value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
+      _animationValue[1] = value;
+      value = new SGScaleOffsetValue(inputProperty);
+      value->setScale(configNode->getDoubleValue("z-factor", factor));
+      value->setOffset(configNode->getDoubleValue("z-offset", offset));
+      value->setMin(configNode->getDoubleValue("z-min", 0));
+      value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
+      _animationValue[2] = value;
     }
   }
-  _texMat->setMatrix(tmatrix);
-  traverse(node, nv);
+  _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
+  _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
+  _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
+  _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
+  _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
+  _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
+  _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
+  _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
+  _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
+  _center[0] = configNode->getDoubleValue("center/x-m", 0);
+  _center[1] = configNode->getDoubleValue("center/y-m", 0);
+  _center[2] = configNode->getDoubleValue("center/z-m", 0);
+}
+
+osg::Group*
+SGScaleAnimation::createAnimationGroup(osg::Group& parent)
+{
+  Transform* transform = new Transform;
+  transform->setName("scale animation");
+  transform->setCenter(_center);
+  transform->setScaleFactor(_initialValue);
+  UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
+  transform->setUpdateCallback(uc);
+  parent.addChild(transform);
+  return transform;
 }
 
-
 \f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of SGAlphaTestAnimation
+// Implementation of dist scale animation
 ////////////////////////////////////////////////////////////////////////
 
-SGAlphaTestAnimation::SGAlphaTestAnimation(SGPropertyNode_ptr props)
-  : SGAnimation(props, new osg::Group)
-{
-  _alpha_clamp = props->getFloatValue("alpha-factor", 0.0);
-}
-
-SGAlphaTestAnimation::~SGAlphaTestAnimation ()
-{
-}
-
-void SGAlphaTestAnimation::init()
-{
-  osg::StateSet* stateSet = _branch->getOrCreateStateSet();
-  osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
-  alphaFunc->setFunction(osg::AlphaFunc::GREATER);
-  alphaFunc->setReferenceValue(_alpha_clamp);
-  stateSet->setAttribute(alphaFunc);
-  stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
-  stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
-  stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGMaterialAnimation
-////////////////////////////////////////////////////////////////////////
-
-SGMaterialAnimation::SGMaterialAnimation( SGPropertyNode *prop_root,
-        SGPropertyNode_ptr props, const SGPath &texture_path)
-    : SGAnimation(props, new osg::Group),
-    _last_condition(false),
-    _prop_root(prop_root),
-    _prop_base(""),
-    _texture_base(texture_path),
-    _read(0),
-    _update(0),
-    _global(props->getBoolValue("global", false))
-{
-    SGPropertyNode_ptr n;
-    n = props->getChild("condition");
-    _condition = n ? sgReadCondition(_prop_root, n) : 0;
-    n = props->getChild("property-base");
-    if (n) {
-        _prop_base = n->getStringValue();
-        if (!_prop_base.empty() && _prop_base.end()[-1] != '/')
-            _prop_base += '/';
-    }
-
-    initColorGroup(props->getChild("diffuse"), &_diff, DIFFUSE);
-    initColorGroup(props->getChild("ambient"), &_amb, AMBIENT);
-    initColorGroup(props->getChild("emission"), &_emis, EMISSION);
-    initColorGroup(props->getChild("specular"), &_spec, SPECULAR);
-
-    _shi = props->getFloatValue("shininess", -1.0);
-    if (_shi >= 0.0)
-        _update |= SHININESS;
-
-    SGPropertyNode_ptr group = props->getChild("transparency");
-    if (group) {
-        _trans.value = group->getFloatValue("alpha", -1.0);
-        _trans.factor = group->getFloatValue("factor", 1.0);
-        _trans.offset = group->getFloatValue("offset", 0.0);
-        _trans.min = group->getFloatValue("min", 0.0);
-        if (_trans.min < 0.0)
-            _trans.min = 0.0;
-        _trans.max = group->getFloatValue("max", 1.0);
-        if (_trans.max > 1.0)
-            _trans.max = 1.0;
-        if (_trans.dirty())
-            _update |= TRANSPARENCY;
-
-        n = group->getChild("alpha-prop");
-        _trans.value_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-        n = group->getChild("factor-prop");
-        _trans.factor_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-        n = group->getChild("offset-prop");
-        _trans.offset_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-        if (_trans.live())
-            _read |= TRANSPARENCY;
-    }
-
-    _thresh = props->getFloatValue("threshold", -1.0);
-    if (_thresh >= 0.0)
-        _update |= THRESHOLD;
-
-    string _texture_str = props->getStringValue("texture", "");
-    if (!_texture_str.empty()) {
-        _texture = _texture_base;
-        _texture.append(_texture_str);
-        _texture2D = SGLoadTexture2D(_texture);
-        _update |= TEXTURE;
-    }
-
-    n = props->getChild("shininess-prop");
-    _shi_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-    n = props->getChild("threshold-prop");
-    _thresh_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-    n = props->getChild("texture-prop");
-    _tex_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-
-    _static_update = _update;
-
-    _alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0);
-}
-
-SGMaterialAnimation::~SGMaterialAnimation()
-{
-}
-
-void SGMaterialAnimation::initColorGroup(SGPropertyNode_ptr group, ColorSpec *col, int flag)
-{
-    if (!group)
-        return;
-
-    col->red = group->getFloatValue("red", -1.0);
-    col->green = group->getFloatValue("green", -1.0);
-    col->blue = group->getFloatValue("blue", -1.0);
-    col->factor = group->getFloatValue("factor", 1.0);
-    col->offset = group->getFloatValue("offset", 0.0);
-    if (col->dirty())
-        _update |= flag;
-
-    SGPropertyNode *n;
-    n = group->getChild("red-prop");
-    col->red_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-    n = group->getChild("green-prop");
-    col->green_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-    n = group->getChild("blue-prop");
-    col->blue_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-    n = group->getChild("factor-prop");
-    col->factor_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-    n = group->getChild("offset-prop");
-    col->offset_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0;
-    if (col->live())
-        _read |= flag;
-}
-
-void SGMaterialAnimation::init()
-{
-    if (!_global)
-        cloneMaterials(_branch);
-
-    // OSGFIXME
-    osg::StateSet* stateSet = _branch->getOrCreateStateSet();
-    if (_update & THRESHOLD) {
-      stateSet->setAttribute(_alphaFunc.get(), osg::StateAttribute::OVERRIDE);
-      stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
-    }
-    if ((_update & TEXTURE) && _texture2D.valid()) {
-      stateSet->setTextureAttribute(0, _texture2D.get(), osg::StateAttribute::OVERRIDE);
-      stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
-    }
-}
-
-void
-SGMaterialAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
-{
-    if (_condition) {
-        bool cond = _condition->test();
-        if (cond && !_last_condition)
-            _update |= _static_update;
-
-        _last_condition = cond;
-        if (!cond) {
-            traverse(node, nv);
-            return;
-        }
-    }
-
-    if (_read & DIFFUSE)
-        updateColorGroup(&_diff, DIFFUSE);
-    if (_read & AMBIENT)
-        updateColorGroup(&_amb, AMBIENT);
-    if (_read & EMISSION)
-        updateColorGroup(&_emis, EMISSION);
-    if (_read & SPECULAR)
-        updateColorGroup(&_spec, SPECULAR);
-
-    float f;
-    if (_shi_prop) {
-        f = _shi;
-        _shi = _shi_prop->getFloatValue();
-        if (_shi != f)
-            _update |= SHININESS;
-    }
-    if (_read & TRANSPARENCY) {
-        PropSpec tmp = _trans;
-        if (_trans.value_prop)
-            _trans.value = _trans.value_prop->getFloatValue();
-        if (_trans.factor_prop)
-            _trans.factor = _trans.factor_prop->getFloatValue();
-        if (_trans.offset_prop)
-            _trans.offset = _trans.offset_prop->getFloatValue();
-        if (_trans != tmp)
-            _update |= TRANSPARENCY;
-    }
-    if (_thresh_prop) {
-        f = _thresh;
-        _thresh = _thresh_prop->getFloatValue();
-        if (_thresh != f)
-            _update |= THRESHOLD;
-    }
-    if (_tex_prop) {
-        string t = _tex_prop->getStringValue();
-        if (!t.empty() && t != _texture_str) {
-            _texture_str = t;
-            _texture = _texture_base;
-            _texture.append(t);
-            _update |= TEXTURE;
-        }
-    }
-    if (_update) {
-        setMaterialBranch(_branch);
-        _update = 0;
-    }
-    traverse(node, nv);
-}
-
-void SGMaterialAnimation::updateColorGroup(ColorSpec *col, int flag)
-{
-    ColorSpec tmp = *col;
-    if (col->red_prop)
-        col->red = col->red_prop->getFloatValue();
-    if (col->green_prop)
-        col->green = col->green_prop->getFloatValue();
-    if (col->blue_prop)
-        col->blue = col->blue_prop->getFloatValue();
-    if (col->factor_prop)
-        col->factor = col->factor_prop->getFloatValue();
-    if (col->offset_prop)
-        col->offset = col->offset_prop->getFloatValue();
-    if (*col != tmp)
-        _update |= flag;
-}
-
-class SGMaterialAnimationCloneVisitor : public osg::NodeVisitor {
+class SGDistScaleAnimation::Transform : public osg::Transform {
 public:
-  SGMaterialAnimationCloneVisitor() :
-    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
+  Transform(const SGPropertyNode* configNode)
   {
-    setVisitorType(osg::NodeVisitor::NODE_VISITOR);
+    setName(configNode->getStringValue("name", "dist scale animation"));
+    setReferenceFrame(RELATIVE_RF);
+    getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
+    _factor = configNode->getFloatValue("factor", 1);
+    _offset = configNode->getFloatValue("offset", 0);
+    _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
+    _max_v = configNode->getFloatValue("max", SGLimitsf::max());
+    _table = read_interpolation_table(configNode);
+    _center[0] = configNode->getFloatValue("center/x-m", 0);
+    _center[1] = configNode->getFloatValue("center/y-m", 0);
+    _center[2] = configNode->getFloatValue("center/z-m", 0);
   }
-  virtual void apply(osg::Node& node)
+  virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
+                                         osg::NodeVisitor* nv) const 
   {
-    traverse(node);
-    osg::StateSet* stateSet = node.getStateSet();
-    if (!stateSet)
-      return;
-    if (1 < stateSet->referenceCount()) {
-      osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_STATESETS);
-      osg::Object* object = stateSet->clone(copyOp);
-      stateSet = static_cast<osg::StateSet*>(object);
-      node.setStateSet(stateSet);
-    }
-    cloneMaterial(stateSet);
+    osg::Matrix transform;
+    double scale_factor = computeScaleFactor(nv);
+    transform(0,0) = scale_factor;
+    transform(1,1) = scale_factor;
+    transform(2,2) = scale_factor;
+    transform(3,0) = _center[0]*(1 - scale_factor);
+    transform(3,1) = _center[1]*(1 - scale_factor);
+    transform(3,2) = _center[2]*(1 - scale_factor);
+    matrix.preMult(transform);
+    return true;
   }
-  virtual void apply(osg::Geode& node)
+  
+  virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
+                                         osg::NodeVisitor* nv) const
   {
-    apply((osg::Node&)node);
-    traverse(node);
-    unsigned nDrawables = node.getNumDrawables();
-    for (unsigned i = 0; i < nDrawables; ++i) {
-      osg::Drawable* drawable = node.getDrawable(i);
-      osg::StateSet* stateSet = drawable->getStateSet();
-      if (!stateSet)
-        continue;
-      if (1 < stateSet->referenceCount()) {
-        osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_STATESETS);
-        osg::Object* object = stateSet->clone(copyOp);
-        stateSet = static_cast<osg::StateSet*>(object);
-        drawable->setStateSet(stateSet);
-      }
-      cloneMaterial(stateSet);
-    }
+    double scale_factor = computeScaleFactor(nv);
+    if (fabs(scale_factor) <= SGLimits<double>::min())
+      return false;
+    osg::Matrix transform;
+    double rScaleFactor = 1/scale_factor;
+    transform(0,0) = rScaleFactor;
+    transform(1,1) = rScaleFactor;
+    transform(2,2) = rScaleFactor;
+    transform(3,0) = _center[0]*(1 - rScaleFactor);
+    transform(3,1) = _center[1]*(1 - rScaleFactor);
+    transform(3,2) = _center[2]*(1 - rScaleFactor);
+    matrix.postMult(transform);
+    return true;
   }
-  void cloneMaterial(osg::StateSet* stateSet)
+
+private:
+  double computeScaleFactor(osg::NodeVisitor* nv) const
   {
-    osg::StateAttribute* stateAttr;
-    stateAttr = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
-    if (!stateAttr)
-      return;
-    osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_STATEATTRIBUTES);
-    osg::Object* object = stateAttr->clone(copyOp);
-    osg::Material* material = static_cast<osg::Material*>(object);
-    materialList.push_back(material);
-    while (stateSet->getAttribute(osg::StateAttribute::MATERIAL)) {
-      stateSet->removeAttribute(osg::StateAttribute::MATERIAL);
+    if (!nv)
+      return 1;
+
+    double scale_factor = (_center.osg() - nv->getEyePoint()).length();
+    if (_table == 0) {
+      scale_factor = _factor * scale_factor + _offset;
+    } else {
+      scale_factor = _table->interpolate( scale_factor );
     }
-    stateSet->setAttribute(material);
+    if (scale_factor < _min_v)
+      scale_factor = _min_v;
+    if (scale_factor > _max_v)
+      scale_factor = _max_v;
+
+    return scale_factor;
   }
-  std::vector<osg::ref_ptr<osg::Material> > materialList;
+
+  SGSharedPtr<SGInterpTable> _table;
+  SGVec3d _center;
+  double _min_v;
+  double _max_v;
+  double _factor;
+  double _offset;
 };
 
-void SGMaterialAnimation::cloneMaterials(osg::Group *b)
+
+SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
+                                           SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
 {
-  SGMaterialAnimationCloneVisitor cloneVisitor;
-  b->accept(cloneVisitor);
-  _materialList.swap(cloneVisitor.materialList);
 }
 
-void SGMaterialAnimation::setMaterialBranch(osg::Group *b)
+osg::Group*
+SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
 {
-  std::vector<osg::ref_ptr<osg::Material> >::iterator i;
-  for (i = _materialList.begin(); i != _materialList.end(); ++i) {
-    osg::Material* material = i->get();
-    if (_update & DIFFUSE) {
-      osg::Vec4 v = _diff.rgba();
-      float alpha = material->getDiffuse(osg::Material::FRONT_AND_BACK)[3];
-      material->setColorMode(osg::Material::DIFFUSE);
-      material->setDiffuse(osg::Material::FRONT_AND_BACK,
-                           osg::Vec4(v[0], v[1], v[2], alpha));
-    }
-    if (_update & AMBIENT) {
-      material->setColorMode(osg::Material::AMBIENT);
-      material->setDiffuse(osg::Material::FRONT_AND_BACK, _amb.rgba());
-    }
-    if (_update & EMISSION)
-      material->setEmission(osg::Material::FRONT_AND_BACK, _emis.rgba());
-    if (_update & SPECULAR)
-      material->setSpecular(osg::Material::FRONT_AND_BACK, _spec.rgba());
-    if (_update & SHININESS)
-      material->setShininess(osg::Material::FRONT_AND_BACK,
-                             clamp(_shi, 0.0, 128.0));
-    if (_update & THRESHOLD)
-        _alphaFunc->setReferenceValue(clamp(_thresh));
-  }
-
-  if (_update & TRANSPARENCY) {
-    float trans = _trans.value * _trans.factor + _trans.offset;
-    trans = trans < _trans.min ? _trans.min : trans > _trans.max ? _trans.max : trans;
-    SGBlendAnimationVisitor visitor(trans);
-    _branch->accept(visitor);
-  }
-  if (_update & TEXTURE) {
-    if (!_texture2D) {
-      _texture2D = new osg::Texture2D;
-      osg::StateSet* stateSet = _branch->getOrCreateStateSet();
-      stateSet->setTextureAttribute(0, _texture2D.get(), osg::StateAttribute::OVERRIDE);
-      stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
-    }
-    osg::Image* image = osgDB::readImageFile(_texture.str());
-    if (image) {
-      _texture2D->setImage(image);
-      if (image->isImageTranslucent()) {
-        osg::StateSet* stateSet = _branch->getOrCreateStateSet();
-        stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
-        stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
-        stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
-      }
-    }
-  }
+  Transform* transform = new Transform(getConfig());
+  parent.addChild(transform);
+  return transform;
 }
 
-
 \f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of SGFlashAnimation
+// Implementation of flash animation
 ////////////////////////////////////////////////////////////////////////
-class SGFlashAnimationTransform : public osg::Transform {
+
+class SGFlashAnimation::Transform : public osg::Transform {
 public:
-  SGFlashAnimationTransform(SGPropertyNode* props)
+  Transform(const SGPropertyNode* configNode)
   {
+    setReferenceFrame(RELATIVE_RF);
+    setName(configNode->getStringValue("name", "flash animation"));
     getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
 
-    _axis[0] = props->getFloatValue("axis/x", 0);
-    _axis[1] = props->getFloatValue("axis/y", 0);
-    _axis[2] = props->getFloatValue("axis/z", 1);
+    _axis[0] = configNode->getFloatValue("axis/x", 0);
+    _axis[1] = configNode->getFloatValue("axis/y", 0);
+    _axis[2] = configNode->getFloatValue("axis/z", 1);
     _axis.normalize();
     
-    _center[0] = props->getFloatValue("center/x-m", 0);
-    _center[1] = props->getFloatValue("center/y-m", 0);
-    _center[2] = props->getFloatValue("center/z-m", 0);
+    _center[0] = configNode->getFloatValue("center/x-m", 0);
+    _center[1] = configNode->getFloatValue("center/y-m", 0);
+    _center[2] = configNode->getFloatValue("center/z-m", 0);
     
-    _offset = props->getFloatValue("offset", 0.0);
-    _factor = props->getFloatValue("factor", 1.0);
-    _power = props->getFloatValue("power", 1.0);
-    _two_sides = props->getBoolValue("two-sides", false);
+    _offset = configNode->getFloatValue("offset", 0);
+    _factor = configNode->getFloatValue("factor", 1);
+    _power = configNode->getFloatValue("power", 1);
+    _two_sides = configNode->getBoolValue("two-sides", false);
     
-    _min_v = props->getFloatValue("min", 0.0);
-    _max_v = props->getFloatValue("max", 1.0);
+    _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
+    _max_v = configNode->getFloatValue("max", 1);
   }
-
   virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
                                          osg::NodeVisitor* nv) const 
   {
-    double scale_factor = computeScaleFactor(nv);
     osg::Matrix transform;
+    double scale_factor = computeScaleFactor(nv);
     transform(0,0) = scale_factor;
     transform(1,1) = scale_factor;
     transform(2,2) = scale_factor;
-    transform(3,0) = _center[0] * ( 1 - scale_factor );
-    transform(3,1) = _center[1] * ( 1 - scale_factor );
-    transform(3,2) = _center[2] * ( 1 - scale_factor );
-    if (_referenceFrame == RELATIVE_RF)
-      matrix.preMult(transform);
-    else
-      matrix = transform;
-
+    transform(3,0) = _center[0]*(1 - scale_factor);
+    transform(3,1) = _center[1]*(1 - scale_factor);
+    transform(3,2) = _center[2]*(1 - scale_factor);
+    matrix.preMult(transform);
     return true;
   }
   
@@ -1479,23 +1278,21 @@ public:
                                          osg::NodeVisitor* nv) const
   {
     double scale_factor = computeScaleFactor(nv);
-    if (fabs(scale_factor) <= std::numeric_limits<double>::min())
+    if (fabs(scale_factor) <= SGLimits<double>::min())
       return false;
     osg::Matrix transform;
     double rScaleFactor = 1/scale_factor;
     transform(0,0) = rScaleFactor;
     transform(1,1) = rScaleFactor;
     transform(2,2) = rScaleFactor;
-    transform(3,0) = rScaleFactor*_center[0] * ( scale_factor - 1 );
-    transform(3,1) = rScaleFactor*_center[1] * ( scale_factor - 1 );
-    transform(3,2) = rScaleFactor*_center[2] * ( scale_factor - 1 );
-    if (_referenceFrame == RELATIVE_RF)
-      matrix.postMult(transform);
-    else
-      matrix = transform;
+    transform(3,0) = _center[0]*(1 - rScaleFactor);
+    transform(3,1) = _center[1]*(1 - rScaleFactor);
+    transform(3,2) = _center[2]*(1 - rScaleFactor);
+    matrix.postMult(transform);
     return true;
   }
 
+private:
   double computeScaleFactor(osg::NodeVisitor* nv) const
   {
     if (!nv)
@@ -1519,161 +1316,750 @@ public:
     return scale_factor;
   }
 
+  virtual osg::BoundingSphere computeBound() const
+  {
+    // avoid being culled away by small feature culling
+    osg::BoundingSphere bs = osg::Group::computeBound();
+    bs.radius() *= _max_v;
+    return bs;
+  }
+
 private:
-  osg::Vec3 _axis, _center;
+  osg::Vec3 _center;
+  osg::Vec3 _axis;
   double _power, _factor, _offset, _min_v, _max_v;
   bool _two_sides;
 };
 
-SGFlashAnimation::SGFlashAnimation(SGPropertyNode_ptr props)
-  : SGAnimation( props, new SGFlashAnimationTransform(props) )
+
+SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
+                                   SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
 {
 }
 
-SGFlashAnimation::~SGFlashAnimation()
+osg::Group*
+SGFlashAnimation::createAnimationGroup(osg::Group& parent)
 {
+  Transform* transform = new Transform(getConfig());
+  parent.addChild(transform);
+  return transform;
 }
 
-
 \f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of SGDistScaleAnimation
+// Implementation of flash animation
 ////////////////////////////////////////////////////////////////////////
-class SGDistScaleTransform : public osg::Transform {
-public:
-  SGDistScaleTransform(SGPropertyNode* props)
-  {
-    getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
 
-    _factor = props->getFloatValue("factor", 1.0);
-    _offset = props->getFloatValue("offset", 0.0);
-    _min_v = props->getFloatValue("min", 0.0);
-    _max_v = props->getFloatValue("max", 1.0);
-    _has_min = props->hasValue("min");
-    _has_max = props->hasValue("max");
-    _table = read_interpolation_table(props);
-    _center[0] = props->getFloatValue("center/x-m", 0);
-    _center[1] = props->getFloatValue("center/y-m", 0);
-    _center[2] = props->getFloatValue("center/z-m", 0);
-  }
-  ~SGDistScaleTransform()
+class SGBillboardAnimation::Transform : public osg::Transform {
+public:
+  Transform(const SGPropertyNode* configNode) :
+    _spherical(configNode->getBoolValue("spherical", true))
   {
+    setReferenceFrame(RELATIVE_RF);
+    setName(configNode->getStringValue("name", "billboard animation"));
   }
-
   virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
                                          osg::NodeVisitor* nv) const 
   {
-    osg::Matrix transform;
-    double scale_factor = computeScaleFactor(nv);
-    transform(0,0) = scale_factor;
-    transform(1,1) = scale_factor;
-    transform(2,2) = scale_factor;
-    transform(3,0) = _center[0] * ( 1 - scale_factor );
-    transform(3,1) = _center[1] * ( 1 - scale_factor );
-    transform(3,2) = _center[2] * ( 1 - scale_factor );
-    if (_referenceFrame == RELATIVE_RF)
-      matrix.preMult(transform);
-    else
-      matrix = transform;
+    // More or less taken from plibs ssgCutout
+    if (_spherical) {
+      matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
+      matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
+      matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
+    } else {
+      osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
+      osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
+      osg::Vec3 yAxis = zAxis^xAxis;
+      
+      xAxis.normalize();
+      yAxis.normalize();
+      zAxis.normalize();
+      
+      matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
+      matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
+      matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
+    }
     return true;
   }
   
   virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
                                          osg::NodeVisitor* nv) const
   {
-    double scale_factor = computeScaleFactor(nv);
-    if (fabs(scale_factor) <= std::numeric_limits<double>::min())
-      return false;
-    osg::Matrix transform;
-    double rScaleFactor = 1/scale_factor;
-    transform(0,0) = rScaleFactor;
-    transform(1,1) = rScaleFactor;
-    transform(2,2) = rScaleFactor;
-    transform(3,0) = rScaleFactor*_center[0] * ( scale_factor - 1 );
-    transform(3,1) = rScaleFactor*_center[1] * ( scale_factor - 1 );
-    transform(3,2) = rScaleFactor*_center[2] * ( scale_factor - 1 );
-    if (_referenceFrame == RELATIVE_RF)
-      matrix.postMult(transform);
-    else
-      matrix = transform;
-    return true;
+    // Hmm, don't yet know how to get that back ...
+    return false;
   }
 
-  double computeScaleFactor(osg::NodeVisitor* nv) const
-  {
-    if (!nv)
-      return 1;
+private:
+  bool _spherical;
+};
 
-    osg::Vec3 localEyeToCenter = _center - nv->getEyePoint();
-    double scale_factor = localEyeToCenter.length();
-    if (_table == 0) {
-      scale_factor = _factor * scale_factor + _offset;
-      if ( _has_min && scale_factor < _min_v )
-        scale_factor = _min_v;
-      if ( _has_max && scale_factor > _max_v )
-        scale_factor = _max_v;
+
+SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
+                                           SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
+{
+}
+
+osg::Group*
+SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
+{
+  Transform* transform = new Transform(getConfig());
+  parent.addChild(transform);
+  return transform;
+}
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of a range animation
+////////////////////////////////////////////////////////////////////////
+
+class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
+public:
+  UpdateCallback(const SGCondition* condition,
+                 const SGDoubleValue* minAnimationValue,
+                 const SGDoubleValue* maxAnimationValue,
+                 double minValue, double maxValue) :
+    _condition(condition),
+    _minAnimationValue(minAnimationValue),
+    _maxAnimationValue(maxAnimationValue),
+    _minStaticValue(minValue),
+    _maxStaticValue(maxValue)
+  {}
+  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+  {
+    osg::LOD* lod = static_cast<osg::LOD*>(node);
+    if (!_condition || _condition->test()) {
+      double minRange;
+      if (_minAnimationValue)
+        minRange = _minAnimationValue->getValue();
+      else
+        minRange = _minStaticValue;
+      double maxRange;
+      if (_maxAnimationValue)
+        maxRange = _maxAnimationValue->getValue();
+      else
+        maxRange = _maxStaticValue;
+      lod->setRange(0, minRange, maxRange);
     } else {
-      scale_factor = _table->interpolate( scale_factor );
+      lod->setRange(0, 0, SGLimitsf::max());
     }
+    traverse(node, nv);
+  }
 
-    return scale_factor;
+private:
+  SGSharedPtr<const SGCondition> _condition;
+  SGSharedPtr<const SGDoubleValue> _minAnimationValue;
+  SGSharedPtr<const SGDoubleValue> _maxAnimationValue;
+  double _minStaticValue;
+  double _maxStaticValue;
+};
+
+SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
+                                   SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
+{
+  _condition = getCondition();
+
+  std::string inputPropertyName;
+  inputPropertyName = configNode->getStringValue("min-property", "");
+  if (!inputPropertyName.empty()) {
+    SGPropertyNode* inputProperty;
+    inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
+    SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
+    value->setScale(configNode->getDoubleValue("min-factor", 1));
+    _minAnimationValue = value;
+  }
+  inputPropertyName = configNode->getStringValue("max-property", "");
+  if (!inputPropertyName.empty()) {
+    SGPropertyNode* inputProperty;
+    inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
+    SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
+    value->setScale(configNode->getDoubleValue("max-factor", 1));
+    _maxAnimationValue = value;
   }
 
+  _initialValue[0] = configNode->getDoubleValue("min-m", 0);
+  _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
+  _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
+  _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
+}
+
+osg::Group*
+SGRangeAnimation::createAnimationGroup(osg::Group& parent)
+{
+  osg::Group* group = new osg::Group;
+  group->setName("range animation group");
+
+  osg::LOD* lod = new osg::LOD;
+  lod->setName("range animation node");
+  parent.addChild(lod);
+
+  lod->addChild(group, _initialValue[0], _initialValue[1]);
+  lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
+  lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
+  if (_minAnimationValue || _maxAnimationValue || _condition) {
+    UpdateCallback* uc;
+    uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
+                            _initialValue[0], _initialValue[1]);
+    lod->setUpdateCallback(uc);
+  }
+  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:
-  osg::Vec3 _center;
-  float _factor, _offset, _min_v, _max_v;
-  bool _has_min, _has_max;
-  SGSharedPtr<SGInterpTable> _table;
+  SGSharedPtr<SGCondition const> _condition;
 };
 
-SGDistScaleAnimation::SGDistScaleAnimation(SGPropertyNode_ptr props)
-  : SGAnimation( props, new SGDistScaleTransform(props) )
+SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
+                                     SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
 {
 }
 
-SGDistScaleAnimation::~SGDistScaleAnimation()
+osg::Group*
+SGSelectAnimation::createAnimationGroup(osg::Group& parent)
 {
+  // if no condition given, this is a noop.
+  SGSharedPtr<SGCondition const> condition = getCondition();
+  // trick, gets deleted with all its 'animated' children
+  // 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;
 }
 
+
+\f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of SGShadowAnimation
+// Implementation of alpha test animation
 ////////////////////////////////////////////////////////////////////////
 
-SGShadowAnimation::SGShadowAnimation ( SGPropertyNode *prop_root,
-                                       SGPropertyNode_ptr props )
-  : SGAnimation(props, new osg::Group),
-    _condition(0),
-    _condition_value(true)
+SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
+                                           SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
+{
+}
+
+void
+SGAlphaTestAnimation::install(osg::Node& node)
 {
-    animation_type = 1;
-    SGPropertyNode_ptr node = props->getChild("condition");
-    if (node != 0) {
-        _condition = sgReadCondition(prop_root, node);
-        _condition_value = false;
+  SGAnimation::install(node);
+  
+  cloneDrawables(node);
+  removeMode(node, GL_ALPHA_TEST);
+  removeAttribute(node, osg::StateAttribute::ALPHAFUNC);
+
+  osg::StateSet* stateSet = node.getOrCreateStateSet();
+  osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
+  alphaFunc->setFunction(osg::AlphaFunc::GREATER);
+  float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
+  alphaFunc->setReferenceValue(alphaClamp);
+  stateSet->setAttribute(alphaFunc);
+  stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
+}
+
+
+\f
+//////////////////////////////////////////////////////////////////////
+// Blend animation installer
+//////////////////////////////////////////////////////////////////////
+
+class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
+public:
+  BlendVisitor(float blend) :
+    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+    _blend(blend)
+  { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
+  virtual void apply(osg::Node& node)
+  {
+    updateStateSet(node.getStateSet());
+    traverse(node);
+  }
+  virtual void apply(osg::Geode& node)
+  {
+    apply((osg::Node&)node);
+    unsigned nDrawables = node.getNumDrawables();
+    for (unsigned i = 0; i < nDrawables; ++i) {
+      osg::Drawable* drawable = node.getDrawable(i);
+      updateStateSet(drawable->getStateSet());
+      osg::Geometry* geometry = drawable->asGeometry();
+      if (!geometry)
+        continue;
+      osg::Array* array = geometry->getColorArray();
+      if (!array)
+        continue;
+      osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
+      if (!vec4Array)
+        continue;
+      geometry->dirtyDisplayList();
+      vec4Array->dirty();
+      for (unsigned k = 0; k < vec4Array->size(); ++k) {
+        (*vec4Array)[k][3] = _blend;
+      }
+    }
+  }
+  void updateStateSet(osg::StateSet* stateSet)
+  {
+    if (!stateSet)
+      return;
+    osg::StateAttribute* stateAttribute;
+    stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
+    if (!stateAttribute)
+      return;
+    osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
+    if (!material)
+      return;
+    material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
+    if (_blend < 1) {
+      stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
+      stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
+    } else {
+      stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
+    }
+  }
+private:
+  float _blend;
+};
+
+class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
+public:
+  UpdateCallback(const SGPropertyNode* configNode, const SGDoubleValue* v) :
+    _prev_value(-1),
+    _animationValue(v)
+  { }
+  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+  {
+    double blend = _animationValue->getValue();
+    if (blend != _prev_value) {
+      _prev_value = blend;
+      BlendVisitor visitor(1-blend);
+      node->accept(visitor);
     }
+    traverse(node, nv);
+  }
+public:
+  double _prev_value;
+  SGSharedPtr<SGDoubleValue const> _animationValue;
+};
+
+
+SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
+                                   SGPropertyNode* modelRoot)
+  : SGAnimation(configNode, modelRoot),
+    _animationValue(read_value(configNode, modelRoot, "", 0, 1))
+{
 }
 
-SGShadowAnimation::~SGShadowAnimation ()
+osg::Group*
+SGBlendAnimation::createAnimationGroup(osg::Group& parent)
 {
+  if (!_animationValue)
+    return 0;
+
+  osg::Group* group = new osg::Switch;
+  group->setName("blend animation node");
+  group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
+  parent.addChild(group);
+  return group;
 }
 
 void
-SGShadowAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
+SGBlendAnimation::install(osg::Node& node)
 {
-    if (_condition)
-        _condition_value = _condition->test();
+  SGAnimation::install(node);
+  // make sure we do not change common geometries,
+  // that also creates new display lists for these subgeometries.
+  cloneDrawables(node);
+}
+
+\f
+//////////////////////////////////////////////////////////////////////
+// Timed animation installer
+//////////////////////////////////////////////////////////////////////
+
+
+
+class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
+public:
+  UpdateCallback(const SGPropertyNode* configNode) :
+    _current_index(0),
+    _reminder(0),
+    _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
+    _last_time_sec(SGLimitsd::max()),
+    _use_personality(configNode->getBoolValue("use-personality", false))
+  {
+    std::vector<SGSharedPtr<SGPropertyNode> > nodes;
+    nodes = configNode->getChildren("branch-duration-sec");
+    for (size_t i = 0; i < nodes.size(); ++i) {
+      unsigned ind = nodes[ i ]->getIndex();
+      while ( ind >= _durations.size() ) {
+        _durations.push_back(DurationSpec(_duration_sec));
+      }
+      SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
+      if ( rNode == 0 ) {
+        _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
+      } else {
+        _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
+                                       rNode->getDoubleValue( "max", 1));
+      }
+    }
+  }
+  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+  {
+    assert(dynamic_cast<osg::Switch*>(node));
+    osg::Switch* sw = static_cast<osg::Switch*>(node);
+
+    unsigned nChildren = sw->getNumChildren();
+
+    // blow up the durations vector to the required size
+    while (_durations.size() < nChildren) {
+      _durations.push_back(_duration_sec);
+    }
+    // make sure the current index is an duration that really exists
+    _current_index = _current_index % nChildren;
 
-    if ( _condition_value ) {
-        _branch->setNodeMask(SG_NODEMASK_SHADOW_BIT|_branch->getNodeMask());
+    // update the time and compute the current systems time value
+    double t = nv->getFrameStamp()->getReferenceTime();
+    if (_last_time_sec == SGLimitsd::max()) {
+      _last_time_sec = t;
     } else {
-        _branch->setNodeMask(~SG_NODEMASK_SHADOW_BIT&_branch->getNodeMask());
+      double dt = t - _last_time_sec;
+      if (_use_personality)
+        dt *= 1 + 0.2*(0.5 - sg_random());
+      _reminder += dt;
+      _last_time_sec = t;
+    }
+
+    double currentDuration = _durations[_current_index].get();
+    while (currentDuration < _reminder) {
+      _reminder -= currentDuration;
+      _current_index = (_current_index + 1) % nChildren;
+      currentDuration = _durations[_current_index].get();
     }
+
+    sw->setSingleChildOn(_current_index);
+
+    traverse(node, nv);
+  }
+
+private:
+  struct DurationSpec {
+    DurationSpec(double t) :
+      minTime(SGMiscd::max(0.01, t)),
+      maxTime(SGMiscd::max(0.01, t))
+    {}
+    DurationSpec(double t0, double t1) :
+      minTime(SGMiscd::max(0.01, t0)),
+      maxTime(SGMiscd::max(0.01, t1))
+    {}
+    double get() const
+    { return minTime + sg_random()*(maxTime - minTime); }
+    double minTime;
+    double maxTime;
+  };
+  std::vector<DurationSpec> _durations;
+  unsigned _current_index;
+  double _reminder;
+  double _duration_sec;
+  double _last_time_sec;
+  bool _use_personality;
+};
+
+
+SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
+                                   SGPropertyNode* modelRoot)
+  : SGAnimation(configNode, modelRoot)
+{
+}
+
+osg::Group*
+SGTimedAnimation::createAnimationGroup(osg::Group& parent)
+{
+  osg::Switch* sw = new osg::Switch;
+  sw->setName("timed animation node");
+  sw->setUpdateCallback(new UpdateCallback(getConfig()));
+  parent.addChild(sw);
+  return sw;
+}
+
+\f
+////////////////////////////////////////////////////////////////////////
+// dynamically switch on/off shadows
+////////////////////////////////////////////////////////////////////////
+
+class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
+public:
+  UpdateCallback(const SGCondition* condition) :
+    _condition(condition)
+  {}
+  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+  {
+    if (_condition->test())
+      node->setNodeMask( SG_NODEMASK_SHADOW_BIT | node->getNodeMask());
+    else
+      node->setNodeMask(~SG_NODEMASK_SHADOW_BIT & node->getNodeMask());
     traverse(node, nv);
+  }
+
+private:
+  SGSharedPtr<const SGCondition> _condition;
+};
+
+SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
+                                     SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
+{
+}
+
+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));
+  parent.addChild(group);
+  return group;
+}
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of SGTexTransformAnimation
+////////////////////////////////////////////////////////////////////////
+
+class SGTexTransformAnimation::Transform : public SGReferenced {
+public:
+  Transform() :
+    _value(0)
+  {}
+  virtual ~Transform()
+  { }
+  void setValue(double value)
+  { _value = value; }
+  virtual bool transform(osg::Matrix&) = 0;
+protected:
+  double _value;
+};
+
+class SGTexTransformAnimation::Translation :
+  public SGTexTransformAnimation::Transform {
+public:
+  Translation(const SGVec3d& axis) :
+    _axis(axis)
+  { }
+  void setValue(double value)
+  { _value = value; }
+  virtual bool transform(osg::Matrix& matrix)
+  {
+    osg::Matrix tmp;
+    set_translation(tmp, _value, _axis);
+    matrix.preMult(tmp);
+  }
+private:
+  SGVec3d _axis;
+};
+
+class SGTexTransformAnimation::Rotation :
+  public SGTexTransformAnimation::Transform {
+public:
+  Rotation(const SGVec3d& axis, const SGVec3d& center) :
+    _axis(axis),
+    _center(center)
+  { }
+  virtual bool transform(osg::Matrix& matrix)
+  {
+    osg::Matrix tmp;
+    set_rotation(tmp, _value, _center, _axis);
+    matrix.preMult(tmp);
+  }
+private:
+  SGVec3d _axis;
+  SGVec3d _center;
+};
+
+class SGTexTransformAnimation::UpdateCallback :
+  public osg::StateAttribute::Callback {
+public:
+  UpdateCallback(const SGCondition* condition) :
+    _condition(condition)
+  { }
+  virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
+  {
+    if (!_condition || _condition->test()) {
+      TransformList::const_iterator i;
+      for (i = _transforms.begin(); i != _transforms.end(); ++i)
+        i->transform->setValue(i->value->getValue());
+    }
+    assert(dynamic_cast<osg::TexMat*>(sa));
+    osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
+    texMat->getMatrix().makeIdentity();
+    TransformList::const_iterator i;
+    for (i = _transforms.begin(); i != _transforms.end(); ++i)
+      i->transform->transform(texMat->getMatrix());
+  }
+  void appendTransform(Transform* transform, SGDoubleValue* value)
+  {
+    Entry entry = { transform, value };
+    transform->transform(_matrix);
+    _transforms.push_back(entry);
+  }
+
+private:
+  struct Entry {
+    SGSharedPtr<Transform> transform;
+    SGSharedPtr<const SGDoubleValue> value;
+  };
+  typedef std::vector<Entry> TransformList;
+  TransformList _transforms;
+  SGSharedPtr<const SGCondition> _condition;
+  osg::Matrix _matrix;
+};
+
+SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
+                                                 SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
+{
+}
+
+osg::Group*
+SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
+{
+  osg::Group* group = new osg::Group;
+  group->setName("texture transform group");
+  osg::StateSet* stateSet = group->getOrCreateStateSet();
+  osg::TexMat* texMat = new osg::TexMat;
+  UpdateCallback* updateCallback = new UpdateCallback(getCondition());
+  // interpret the configs ...
+  std::string type = getType();
+
+  if (type == "textranslate") {
+    appendTexTranslate(getConfig(), updateCallback);
+  } else if (type == "texrotate") {
+    appendTexRotate(getConfig(), updateCallback);
+  } else if (type == "texmultiple") {
+    std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
+    transformConfigs = getConfig()->getChildren("transform");
+    for (unsigned i = 0; i < transformConfigs.size(); ++i) {
+      std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
+      if (subtype == "textranslate")
+        appendTexTranslate(transformConfigs[i], updateCallback);
+      else if (subtype == "texrotate")
+        appendTexRotate(transformConfigs[i], updateCallback);
+      else
+        SG_LOG(SG_INPUT, SG_ALERT,
+               "Ignoring unknown texture transform subtype");
+    }
+  } else {
+    SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
+  }
+
+  texMat->setUpdateCallback(updateCallback);
+  stateSet->setTextureAttribute(0, texMat);
+  parent.addChild(group);
+  return group;
 }
 
-bool SGShadowAnimation::get_condition_value(void) {
-    return _condition_value;
+void
+SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
+                                            UpdateCallback* updateCallback)
+{
+  std::string propertyName = config->getStringValue("property", "/null");
+  SGPropertyNode* inputNode;
+  inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
+
+  SGDoubleValue* animationValue;
+  SGInterpTable* table = read_interpolation_table(config);
+  if (table) {
+    SGTexTableValue* value;
+    value = new SGTexTableValue(inputNode, table);
+    value->setStep(config->getDoubleValue("step", 0));
+    value->setScroll(config->getDoubleValue("scroll", 0));
+    animationValue = value;
+  } else {
+    SGTexScaleOffsetValue* value;
+    value = new SGTexScaleOffsetValue(inputNode);
+    value->setScale(config->getDoubleValue("factor", 1));
+    value->setOffset(config->getDoubleValue("offset", 0));
+    value->setStep(config->getDoubleValue("step", 0));
+    value->setScroll(config->getDoubleValue("scroll", 0));
+    value->setMin(config->getDoubleValue("min", -SGLimitsd::max()));
+    value->setMax(config->getDoubleValue("max", SGLimitsd::max()));
+    animationValue = value;
+  }
+  SGVec3d axis(getConfig()->getDoubleValue("axis/x", 0),
+               getConfig()->getDoubleValue("axis/y", 0),
+               getConfig()->getDoubleValue("axis/z", 0));
+  Translation* translation;
+  translation = new Translation(normalize(axis));
+  translation->setValue(config->getDoubleValue("starting-position", 0));
+  updateCallback->appendTransform(translation, animationValue);
+}
+
+void
+SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
+                                         UpdateCallback* updateCallback)
+{
+  std::string propertyName = config->getStringValue("property", "/null");
+  SGPropertyNode* inputNode;
+  inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
+
+  SGDoubleValue* animationValue;
+  SGInterpTable* table = read_interpolation_table(config);
+  if (table) {
+    SGTexTableValue* value;
+    value = new SGTexTableValue(inputNode, table);
+    value->setStep(config->getDoubleValue("step", 0));
+    value->setScroll(config->getDoubleValue("scroll", 0));
+    animationValue = value;
+  } else {
+    SGTexScaleOffsetValue* value;
+    value = new SGTexScaleOffsetValue(inputNode);
+    value->setScale(config->getDoubleValue("factor", 1));
+    value->setOffset(config->getDoubleValue("offset-deg", 0));
+    value->setStep(config->getDoubleValue("step", 0));
+    value->setScroll(config->getDoubleValue("scroll", 0));
+    value->setMin(config->getDoubleValue("min-deg", -SGLimitsd::max()));
+    value->setMax(config->getDoubleValue("max-deg", SGLimitsd::max()));
+    animationValue = value;
+  }
+  SGVec3d axis(getConfig()->getDoubleValue("axis/x", 0),
+               getConfig()->getDoubleValue("axis/y", 0),
+               getConfig()->getDoubleValue("axis/z", 0));
+  SGVec3d center(getConfig()->getDoubleValue("center/x", 0),
+                 getConfig()->getDoubleValue("center/y", 0),
+                 getConfig()->getDoubleValue("center/z", 0));
+  Rotation* rotation;
+  rotation = new Rotation(normalize(axis), center);
+  rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
+  updateCallback->appendTransform(rotation, animationValue);
 }
 
-// end of animation.cxx
index 4e83468b7b73dec3a565ba25162c3a050a9d40db..f23b692347d41f162633af21adddc2d30eb6c4e3 100644 (file)
 #include <simgear/props/props.hxx>
 #include <simgear/misc/sg_path.hxx>
 
+#include <simgear/math/interpolater.hxx>
 #include <simgear/scene/model/persparam.hxx>
 #include <simgear/scene/util/SGNodeMasks.hxx>
 
+
 SG_USING_STD(vector);
 SG_USING_STD(map);
 
@@ -52,554 +54,310 @@ class SGCondition;
 
 \f
 //////////////////////////////////////////////////////////////////////
-// Animation classes
+// Helper classes, FIXME: factor out
 //////////////////////////////////////////////////////////////////////
 
-/**
- * Abstract base class for all animations.
- */
-class SGAnimation :  public osg::NodeCallback
-{
+class SGDoubleValue : public SGReferenced {
 public:
-  SGAnimation (SGPropertyNode_ptr props, osg::Group * branch);
-
-  virtual ~SGAnimation ();
-
-  /**
-   * Get the SSG branch holding the animation.
-   */
-  virtual osg::Group * getBranch () { return _branch; }
-
-  /**
-   * Initialize the animation, after children have been added.
-   */
-  virtual void init ();
+  virtual ~SGDoubleValue() {}
+  virtual double getValue() const = 0;
+};
 
-  /**
-   * Update the animation.
-   */
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+\f
+//////////////////////////////////////////////////////////////////////
+// Base class for animation installers
+//////////////////////////////////////////////////////////////////////
 
-  /**
-   * Restore the state after the animation.
-   */
-  virtual void restore();
+class SGAnimation : protected osg::NodeVisitor {
+public:
+  SGAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot);
+  virtual ~SGAnimation();
 
-  int get_animation_type(void) { return animation_type; }
+  static bool animate(osg::Node* node, const SGPropertyNode* configNode,
+                      SGPropertyNode* modelRoot);
 
 protected:
+  void apply(osg::Node* node);
 
-  osg::Group* _branch;
+  virtual void install(osg::Node& node);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 
-  int animation_type;
-};
+  virtual void apply(osg::Group& group);
 
+  void removeMode(osg::Node& node, osg::StateAttribute::GLMode mode);
+  void removeAttribute(osg::Node& node, osg::StateAttribute::Type type);
+  void removeTextureMode(osg::Node& node, unsigned unit,
+                         osg::StateAttribute::GLMode mode);
+  void removeTextureAttribute(osg::Node& node, unsigned unit,
+                              osg::StateAttribute::Type type);
+  void setRenderBinToInherit(osg::Node& node);
+  void cloneDrawables(osg::Node& node);
 
-/**
- * A no-op animation.
- */
-class SGNullAnimation : public SGAnimation
-{
-public:
-  SGNullAnimation (SGPropertyNode_ptr props);
-  virtual ~SGNullAnimation ();
-};
+  std::string getType() const
+  { return std::string(_configNode->getStringValue("type", "")); }
 
+  const SGPropertyNode* getConfig() const
+  { return _configNode; }
+  SGPropertyNode* getModelRoot() const
+  { return _modelRoot; }
+
+  const SGCondition* getCondition() const;
 
-/**
- * A range, or level-of-detail (LOD) animation.
- */
-class SGRangeAnimation : public SGAnimation
-{
-public:
-  SGRangeAnimation (SGPropertyNode *prop_root,
-                    SGPropertyNode_ptr props);
-  virtual ~SGRangeAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
 private:
-  SGPropertyNode_ptr _min_prop;
-  SGPropertyNode_ptr _max_prop;
-  float _min;
-  float _max;
-  float _min_factor;
-  float _max_factor;
-  SGSharedPtr<SGCondition> _condition;
+  void installInGroup(const std::string& name, osg::Group& group,
+                      osg::ref_ptr<osg::Group>& animationGroup);
+
+  class RemoveModeVisitor;
+  class RemoveAttributeVisitor;
+  class RemoveTextureModeVisitor;
+  class RemoveTextureAttributeVisitor;
+  class BinToInheritVisitor;
+  class DrawableCloneVisitor;
+
+  bool _found;
+  std::string _name;
+  SGSharedPtr<SGPropertyNode const> _configNode;
+  SGPropertyNode* _modelRoot;
+  std::list<std::string> _objectNames;
+  bool _enableHOT;
+  bool _disableShadow;
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// Null animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * Animation to turn and face the screen.
- */
-class SGBillboardAnimation : public SGAnimation
-{
+class SGGroupAnimation : public SGAnimation {
 public:
-  SGBillboardAnimation (SGPropertyNode_ptr props);
-  virtual ~SGBillboardAnimation ();
+  SGGroupAnimation(const SGPropertyNode*, SGPropertyNode*);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// Translate animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * Animation to select alternative versions of the same object.
- */
-class SGSelectAnimation : public SGAnimation
-{
+class SGTranslateAnimation : public SGAnimation {
 public:
-  SGSelectAnimation( SGPropertyNode *prop_root,
-                   SGPropertyNode_ptr props );
-  virtual ~SGSelectAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+  SGTranslateAnimation(const SGPropertyNode* configNode,
+                       SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-  SGSharedPtr<SGCondition> _condition;
+  class UpdateCallback;
+  class Transform;
+  SGSharedPtr<const SGCondition> _condition;
+  SGSharedPtr<const SGDoubleValue> _animationValue;
+  SGVec3d _axis;
+  double _initialValue;
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// Rotate/Spin animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * Animation to spin an object around a center point.
- *
- * This animation rotates at a specific velocity.
- */
-class SGSpinAnimation : public SGAnimation
-{
+class SGRotateAnimation : public SGAnimation {
 public:
-  SGSpinAnimation( SGPropertyNode *prop_root,
-                 SGPropertyNode_ptr props,
-                 double sim_time_sec );
-  virtual ~SGSpinAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+  SGRotateAnimation(const SGPropertyNode* configNode,
+                    SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-  bool _use_personality;
-  SGPropertyNode_ptr _prop;
-  SGPersonalityParameter<double> _factor;
-  SGPersonalityParameter<double> _position_deg;
-  double _last_time_sec;
-  osg::Vec3 _center;
-  osg::Vec3 _axis;
-  SGSharedPtr<SGCondition> _condition;
+  class UpdateCallback;
+  class SpinUpdateCallback;
+  class Transform;
+  SGSharedPtr<const SGCondition> _condition;
+  SGSharedPtr<const SGDoubleValue> _animationValue;
+  SGVec3d _axis;
+  SGVec3d _center;
+  double _initialValue;
+  bool _isSpin;
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// Scale animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * Animation to draw objects for a specific amount of time each.
- */
-class SGTimedAnimation : public SGAnimation
-{
+class SGScaleAnimation : public SGAnimation {
 public:
-    SGTimedAnimation (SGPropertyNode_ptr props);
-    virtual ~SGTimedAnimation ();
-    virtual void init();
-    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+  SGScaleAnimation(const SGPropertyNode* configNode,
+                   SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-    bool _use_personality;
-    double _duration_sec;
-    double _last_time_sec;
-    double _total_duration_sec;
-    unsigned _step;
-    struct DurationSpec {
-        DurationSpec( double m = 0.0 ) : _min(m), _max(m) {}
-        DurationSpec( double m1, double m2 ) : _min(m1), _max(m2) {}
-        double _min, _max;
-    };
-    vector<DurationSpec> _branch_duration_specs;
-    vector<double> _branch_duration_sec;
+  class UpdateCallback;
+  class Transform;
+  SGSharedPtr<const SGCondition> _condition;
+  SGSharedPtr<const SGDoubleValue> _animationValue[3];
+  SGVec3d _initialValue;
+  SGVec3d _center;
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// dist scale animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * Animation to rotate an object around a center point.
- *
- * This animation rotates to a specific position.
- */
-class SGRotateAnimation : public SGAnimation
-{
+class SGDistScaleAnimation : public SGAnimation {
 public:
-  SGRotateAnimation( SGPropertyNode *prop_root, SGPropertyNode_ptr props );
-  virtual ~SGRotateAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+  SGDistScaleAnimation(const SGPropertyNode* configNode,
+                       SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-  SGPropertyNode_ptr _prop;
-  double _offset_deg;
-  double _factor;
-  SGSharedPtr<SGInterpTable> _table;
-  bool _has_min;
-  double _min_deg;
-  bool _has_max;
-  double _max_deg;
-  double _position_deg;
-  osg::Vec3 _center;
-  osg::Vec3 _axis;
-  SGSharedPtr<SGCondition> _condition;
+  class Transform;
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// dist scale animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * Animation to slide along an axis.
- */
-class SGTranslateAnimation : public SGAnimation
-{
+class SGFlashAnimation : public SGAnimation {
 public:
-  SGTranslateAnimation( SGPropertyNode *prop_root,
-                      SGPropertyNode_ptr props );
-  virtual ~SGTranslateAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+  SGFlashAnimation(const SGPropertyNode* configNode,
+                   SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-  bool _use_personality;
-  SGPropertyNode_ptr _prop;
-  SGPersonalityParameter<double> _offset_m;
-  SGPersonalityParameter<double> _factor;
-  SGSharedPtr<SGInterpTable> _table;
-  bool _has_min;
-  double _min_m;
-  bool _has_max;
-  double _max_m;
-  double _position_m;
-  osg::Vec3 _axis;
-  SGSharedPtr<SGCondition> _condition;
+  class Transform;
 };
 
-/**
- * Animation to blend an object.
- */
-class SGBlendAnimation : public SGAnimation
-{
-public:
-  SGBlendAnimation( SGPropertyNode *prop_root,
-                      SGPropertyNode_ptr props );
-  virtual ~SGBlendAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
-private:
-  bool _use_personality;
-  SGPropertyNode_ptr _prop;
-  SGSharedPtr<SGInterpTable> _table;
-  double _prev_value;
-  SGPersonalityParameter<double> _offset;
-  SGPersonalityParameter<double> _factor;
-  double _min;
-  double _max;
-};
+\f
+//////////////////////////////////////////////////////////////////////
+// dist scale animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * Animation to scale an object.
- */
-class SGScaleAnimation : public SGAnimation
-{
+class SGBillboardAnimation : public SGAnimation {
 public:
-  SGScaleAnimation( SGPropertyNode *prop_root,
-                        SGPropertyNode_ptr props );
-  virtual ~SGScaleAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+  SGBillboardAnimation(const SGPropertyNode* configNode,
+                       SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-  bool _use_personality;
-  SGPropertyNode_ptr _prop;
-  SGPersonalityParameter<double> _x_factor;
-  SGPersonalityParameter<double> _y_factor;
-  SGPersonalityParameter<double> _z_factor;
-  SGPersonalityParameter<double> _x_offset;
-  SGPersonalityParameter<double> _y_offset;
-  SGPersonalityParameter<double> _z_offset;
-  SGSharedPtr<SGInterpTable> _table;
-  bool _has_min_x;
-  bool _has_min_y;
-  bool _has_min_z;
-  double _min_x;
-  double _min_y;
-  double _min_z;
-  bool _has_max_x;
-  bool _has_max_y;
-  bool _has_max_z;
-  double _max_x;
-  double _max_y;
-  double _max_z;
-  double _x_scale;
-  double _y_scale;
-  double _z_scale;
+  class Transform;
 };
 
-/**
- * Animation to rotate texture mappings around a center point.
- *
- * This animation rotates to a specific position.
- */
-class SGTexRotateAnimation : public SGAnimation
-{
+\f
+//////////////////////////////////////////////////////////////////////
+// Range animation installer
+//////////////////////////////////////////////////////////////////////
+
+class SGRangeAnimation : public SGAnimation {
 public:
-  SGTexRotateAnimation( SGPropertyNode *prop_root, SGPropertyNode_ptr props );
-  virtual ~SGTexRotateAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+  SGRangeAnimation(const SGPropertyNode* configNode,
+                   SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-  SGPropertyNode_ptr _prop;
-  double _offset_deg;
-  double _factor;
-  SGSharedPtr<SGInterpTable> _table;
-  bool _has_min;
-  double _min_deg;
-  bool _has_max;
-  double _max_deg;
-  double _position_deg;
-  osg::Vec3 _center;
-  osg::Vec3 _axis;
-  SGSharedPtr<SGCondition> _condition;
-  osg::ref_ptr<osg::TexMat> _texMat;
+  class UpdateCallback;
+  SGSharedPtr<const SGCondition> _condition;
+  SGSharedPtr<const SGDoubleValue> _minAnimationValue;
+  SGSharedPtr<const SGDoubleValue> _maxAnimationValue;
+  SGVec2d _initialValue;
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// Select animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * Animation to slide texture mappings along an axis.
- */
-class SGTexTranslateAnimation : public SGAnimation
-{
+class SGSelectAnimation : public SGAnimation {
 public:
-  SGTexTranslateAnimation( SGPropertyNode *prop_root,
-                      SGPropertyNode_ptr props );
-  virtual ~SGTexTranslateAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+  SGSelectAnimation(const SGPropertyNode* configNode,
+                    SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-  SGPropertyNode_ptr _prop;
-  double _offset;
-  double _factor;
-  double _step;
-  double _scroll;
-  SGSharedPtr<SGInterpTable> _table;
-  bool _has_min;
-  double _min;
-  bool _has_max;
-  double _max;
-  double _position;
-  osg::Vec3 _axis;
-  SGSharedPtr<SGCondition> _condition;
-  osg::ref_ptr<osg::TexMat> _texMat;
+  class UpdateCallback;
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// Alpha test animation installer
+//////////////////////////////////////////////////////////////////////
 
-
-/**
- * Classes for handling multiple types of Texture translations on one object
- */
-
-class SGTexMultipleAnimation : public SGAnimation
-{
+class SGAlphaTestAnimation : public SGAnimation {
 public:
-  SGTexMultipleAnimation( SGPropertyNode *prop_root,
-                      SGPropertyNode_ptr props );
-  virtual ~SGTexMultipleAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
-private:
-  class TexTransform
-    {
-    public:
-    SGPropertyNode_ptr prop;
-    int subtype; //  0=translation, 1=rotation
-    double offset;
-    double factor;
-    double step;
-    double scroll;
-    SGSharedPtr<SGInterpTable> table;
-    bool has_min;
-    double min;
-    bool has_max;
-    double max;
-    double position;
-    osg::Vec3 center;
-    osg::Vec3 axis;
-  };
-  SGPropertyNode_ptr _prop;
-  TexTransform* _transform;
-  int _num_transforms;
-  osg::ref_ptr<osg::TexMat> _texMat;
+  SGAlphaTestAnimation(const SGPropertyNode* configNode,
+                       SGPropertyNode* modelRoot);
+  virtual void install(osg::Node& node);
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// Blend animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * An "animation" to enable the alpha test 
- */
-class SGAlphaTestAnimation : public SGAnimation
-{
+class SGBlendAnimation : public SGAnimation {
 public:
-  SGAlphaTestAnimation(SGPropertyNode_ptr props);
-  virtual ~SGAlphaTestAnimation ();
-  virtual void init();
+  SGBlendAnimation(const SGPropertyNode* configNode,
+                   SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
+  virtual void install(osg::Node& node);
 private:
-  float _alpha_clamp;
+  class BlendVisitor;
+  class UpdateCallback;
+  SGSharedPtr<SGDoubleValue> _animationValue;
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// Timed animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * An "animation" to modify material properties
- */
-class SGMaterialAnimation : public SGAnimation
-{
+class SGTimedAnimation : public SGAnimation {
 public:
-    SGMaterialAnimation(SGPropertyNode *prop_root, SGPropertyNode_ptr props,
-            const SGPath &texpath);
-    virtual ~SGMaterialAnimation();
-    virtual void init();
-    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+  SGTimedAnimation(const SGPropertyNode* configNode,
+                   SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-    enum {
-        DIFFUSE = 1,
-        AMBIENT = 2,
-        SPECULAR = 4,
-        EMISSION = 8,
-        SHININESS = 16,
-        TRANSPARENCY = 32,
-        THRESHOLD = 64,
-        TEXTURE = 128,
-    };
-    struct ColorSpec {
-        float red, green, blue;
-        float factor;
-        float offset;
-        SGPropertyNode_ptr red_prop;
-        SGPropertyNode_ptr green_prop;
-        SGPropertyNode_ptr blue_prop;
-        SGPropertyNode_ptr factor_prop;
-        SGPropertyNode_ptr offset_prop;
-        osg::Vec4 v;
-        inline bool dirty() {
-            return red >= 0.0 || green >= 0.0 || blue >= 0.0;
-        }
-        inline bool live() {
-            return red_prop || green_prop || blue_prop
-                    || factor_prop || offset_prop;
-        }
-        inline bool operator!=(ColorSpec& a) {
-            return red != a.red || green != a.green || blue != a.blue
-                    || factor != a.factor || offset != a.offset;
-        }
-        osg::Vec4 &rgba() {
-            v[0] = clamp(red * factor + offset);
-            v[1] = clamp(green * factor + offset);
-            v[2] = clamp(blue * factor + offset);
-            v[3] = 1.0;
-            return v;
-        }
-        inline float clamp(float val) {
-            return val < 0.0 ? 0.0 : val > 1.0 ? 1.0 : val;
-        }
-    };
-    struct PropSpec {
-        float value;
-        float factor;
-        float offset;
-        float min;
-        float max;
-        SGPropertyNode_ptr value_prop;
-        SGPropertyNode_ptr factor_prop;
-        SGPropertyNode_ptr offset_prop;
-        inline bool dirty() { return value >= 0.0; }
-        inline bool live() { return value_prop || factor_prop || offset_prop; }
-        inline bool operator!=(PropSpec& a) {
-            return value != a.value || factor != a.factor || offset != a.offset;
-        }
-    };
-    SGSharedPtr<SGCondition> _condition;
-    bool _last_condition;
-    SGPropertyNode_ptr _prop_root;
-    string _prop_base;
-    SGPath _texture_base;
-    SGPath _texture;
-    string _texture_str;
-    unsigned _read;
-    unsigned _update;
-    unsigned _static_update;
-    bool _global;
-    ColorSpec _diff;
-    ColorSpec _amb;
-    ColorSpec _emis;
-    ColorSpec _spec;
-    float _shi;
-    PropSpec _trans;
-    float _thresh;     // alpha_clamp (see man glAlphaFunc)
-    string _tex;
-    string _tmpstr;
-    SGPropertyNode_ptr _shi_prop;
-    SGPropertyNode_ptr _thresh_prop;
-    SGPropertyNode_ptr _tex_prop;
-    std::vector<osg::ref_ptr<osg::Material> > _materialList;
-    osg::ref_ptr<osg::AlphaFunc> _alphaFunc;
-    osg::ref_ptr<osg::Texture2D> _texture2D;
-
-    void cloneMaterials(osg::Group *b);
-    void setMaterialBranch(osg::Group *b);
-    void initColorGroup(SGPropertyNode_ptr, ColorSpec *, int flag);
-    void updateColorGroup(ColorSpec *, int flag);
-    inline float clamp(float val, float min = 0.0, float max = 1.0) {
-        return val < min ? min : val > max ? max : val;
-    }
-    const char *path(const char *rel) {
-        return (_tmpstr = _prop_base + rel).c_str();
-    }
+  class UpdateCallback;
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// Shadow animation installer
+//////////////////////////////////////////////////////////////////////
 
-/**
- * An "animation" that compute a scale according to 
- * the angle between an axis and the view direction
- */
-class SGFlashAnimation : public SGAnimation
-{
+class SGShadowAnimation : public SGAnimation {
 public:
-  SGFlashAnimation(SGPropertyNode_ptr props);
-  virtual ~SGFlashAnimation ();
+  SGShadowAnimation(const SGPropertyNode* configNode,
+                    SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
+private:
+  class UpdateCallback;
 };
 
+\f
+//////////////////////////////////////////////////////////////////////
+// TextureTransform animation
+//////////////////////////////////////////////////////////////////////
 
-/**
- * An animation that compute a scale according to 
- * the distance from a point and the viewer
- */
-class SGDistScaleAnimation : public SGAnimation
-{
-public:
-  SGDistScaleAnimation(SGPropertyNode_ptr props);
-  virtual ~SGDistScaleAnimation ();
-};
-
-/**
- * An animation to tell wich objects don't cast shadows.
- */
-class SGShadowAnimation : public SGAnimation
-{
+class SGTexTransformAnimation : public SGAnimation {
 public:
-  SGShadowAnimation ( SGPropertyNode *prop_root,
-                   SGPropertyNode_ptr props );
-  virtual ~SGShadowAnimation ();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
-  bool get_condition_value(void);
+  SGTexTransformAnimation(const SGPropertyNode* configNode,
+                          SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-  SGSharedPtr<SGCondition> _condition;
-  bool _condition_value;
+  class Transform;
+  class Translation;
+  class Rotation;
+  class UpdateCallback;
+  void appendTexTranslate(const SGPropertyNode* config,
+                          UpdateCallback* updateCallback);
+  void appendTexRotate(const SGPropertyNode* config,
+                       UpdateCallback* updateCallback);
 };
 
-/**
-+ * An "animation" that replace fixed opengl pipeline by shaders
-+ */
-class SGShaderAnimation : public SGAnimation
-{
+\f
+//////////////////////////////////////////////////////////////////////
+// Shader animation
+//////////////////////////////////////////////////////////////////////
+
+class SGShaderAnimation : public SGAnimation {
 public:
-  SGShaderAnimation ( SGPropertyNode *prop_root,
-                   SGPropertyNode_ptr props );
-  virtual ~SGShaderAnimation ();
-  virtual void init();
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
-  bool get_condition_value(void);
+  SGShaderAnimation(const SGPropertyNode* configNode,
+                    SGPropertyNode* modelRoot);
+  virtual osg::Group* createAnimationGroup(osg::Group& parent);
 private:
-  SGSharedPtr<SGCondition> _condition;
-  bool _condition_value;
-  int _shader_type;
-  float _param_1;
-  osg::Vec4 _param_color;
-public:
-  bool _depth_test;
-  float _factor;
-  SGPropertyNode_ptr _factor_prop;
-  float _speed;
-  float totalTime;
-  SGPropertyNode_ptr _speed_prop;
-  osg::ref_ptr<osg::Texture2D> _effectTexture;
-  unsigned char *_textureData;
-  GLint _texWidth, _texHeight;
-  osg::Vec4 _envColor;
+  class UpdateCallback;
 };
 
-
 #endif // _SG_ANIMATION_HXX
index 8be60e92795fd14e60992a29f1e1389f66bdf8ce..f1d6144488006d6250932d32556ab217c878d2a5 100644 (file)
@@ -19,6 +19,7 @@
 #include <osgDB/FileNameUtils>
 #include <osgDB/FileUtils>
 #include <osgDB/ReadFile>
+#include <osgDB/WriteFile>
 #include <osgDB/Registry>
 #include <osgDB/SharedStateManager>
 #include <osgUtil/Optimizer>
@@ -65,9 +66,11 @@ public:
   SGTextureUpdateVisitor(const osgDB::FilePathList& pathList) :
     mPathList(pathList)
   { }
-  osg::Texture2D* textureReplace(int unit, osg::StateSet::RefAttributePair& refAttr)
+  osg::Texture2D* textureReplace(int unit,
+                                 osg::StateSet::RefAttributePair& refAttr)
   {
-    osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(refAttr.first.get());
+    osg::Texture2D* texture;
+    texture = dynamic_cast<osg::Texture2D*>(refAttr.first.get());
     if (!texture)
       return 0;
     
@@ -113,7 +116,8 @@ public:
         if (texture) {
           stateSet->removeTextureAttribute(unit, i->second.first.get());
           stateSet->setTextureAttribute(unit, texture, i->second.second);
-          stateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON);
+          stateSet->setTextureMode(unit, GL_TEXTURE_2D,
+                                   osg::StateAttribute::ON);
         }
         ++i;
       }
@@ -132,7 +136,8 @@ public:
 
   virtual void apply(int, osg::StateSet::RefAttributePair& refAttr)
   {
-    osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(refAttr.first.get());
+    osg::Texture2D* texture;
+    texture = dynamic_cast<osg::Texture2D*>(refAttr.first.get());
     if (!texture)
       return;
     
@@ -157,7 +162,8 @@ class SGAcMaterialCrippleVisitor : public SGStateAttributeVisitor {
 public:
   virtual void apply(osg::StateSet::RefAttributePair& refAttr)
   {
-    osg::Material* material = dynamic_cast<osg::Material*>(refAttr.first.get());
+    osg::Material* material;
+    material = dynamic_cast<osg::Material*>(refAttr.first.get());
     if (!material)
       return;
     material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
@@ -168,7 +174,8 @@ class SGReadFileCallback :
   public osgDB::Registry::ReadFileCallback {
 public:
   virtual osgDB::ReaderWriter::ReadResult
-  readImage(const std::string& fileName, const osgDB::ReaderWriter::Options* opt)
+  readImage(const std::string& fileName,
+            const osgDB::ReaderWriter::Options* opt)
   {
     std::string absFileName = osgDB::findDataFile(fileName);
     if (!osgDB::fileExists(absFileName)) {
@@ -178,7 +185,8 @@ public:
     }
 
     osgDB::Registry* registry = osgDB::Registry::instance();
-    osgDB::ReaderWriter::ReadResult res = registry->readImageImplementation(absFileName, opt);
+    osgDB::ReaderWriter::ReadResult res;
+    res = registry->readImageImplementation(absFileName, opt);
     if (res.loadedFromCache())
       SG_LOG(SG_IO, SG_INFO, "Returning cached image \""
              << res.getImage()->getFileName() << "\"");
@@ -190,7 +198,8 @@ public:
   }
 
   virtual osgDB::ReaderWriter::ReadResult
-  readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* opt)
+  readNode(const std::string& fileName,
+           const osgDB::ReaderWriter::Options* opt)
   {
     std::string absFileName = osgDB::findDataFile(fileName);
     if (!osgDB::fileExists(absFileName)) {
@@ -234,19 +243,23 @@ public:
         osgUtil::Optimizer optimizer;
         unsigned opts = osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS;
         optimizer.optimize(root.get(), opts);
+
+        // strip away unneeded groups
+        if (root->getNumChildren() == 1 && root->getName().empty()) {
+          res = osgDB::ReaderWriter::ReadResult(root->getChild(0));
+        } else
+          res = osgDB::ReaderWriter::ReadResult(root.get());
         
         // Ok, this step is questionable.
         // It is there to have the same visual appearance of ac objects for the
         // first cut. Osg's ac3d loader will correctly set materials from the
         // ac file. But the old plib loader used GL_AMBIENT_AND_DIFFUSE for the
         // materials that in effect igored the ambient part specified in the
-        // file. We emulate that for the first cut here by changing all ac models
-        // here. But in the long term we should use the unchanged model and fix
-        // the input files instead ...
+        // file. We emulate that for the first cut here by changing all
+        // ac models here. But in the long term we should use the
+        // unchanged model and fix the input files instead ...
         SGAcMaterialCrippleVisitor matCriple;
-        root->accept(matCriple);
-        
-        res = osgDB::ReaderWriter::ReadResult(root.get());
+        res.getNode()->accept(matCriple);
       }
       
       osgUtil::Optimizer optimizer;
@@ -261,6 +274,7 @@ public:
       // opts |= osgUtil::Optimizer::CHECK_GEOMETRY;
       // opts |= osgUtil::Optimizer::SPATIALIZE_GROUPS;
       // opts |= osgUtil::Optimizer::COPY_SHARED_NODES;
+      opts |= osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS;
       if (needTristrip)
         opts |= osgUtil::Optimizer::TRISTRIP_GEOMETRY;
       // opts |= osgUtil::Optimizer::TESSELATE_GEOMETRY;
@@ -271,7 +285,8 @@ public:
       registry->getSharedStateManager()->share(res.getNode());
       
       // OSGFIXME: guard that with a flag
-      // OSGFIXME: in the long term it is unclear if we have an OpenGL context here...
+      // OSGFIXME: in the long term it is unclear if we have an OpenGL
+      // context here...
       osg::Texture::Extensions* e = osg::Texture::getExtensions(0, true);
       if (e->isTextureCompressionARBSupported()) {
         SGTexCompressionVisitor texComp(osg::Texture::USE_ARB_COMPRESSION);
@@ -284,10 +299,11 @@ public:
 
     // Add an extra reference to the model stored in the database.
     // That it to avoid expiring the object from the cache even if it is still
-    // in use. Note that the object cache will think that a model is unused if the
-    // reference count is 1. If we clone all structural nodes here we need that extra
-    // reference to the original object
-    SGDatabaseReference* databaseReference = new SGDatabaseReference(res.getNode());
+    // in use. Note that the object cache will think that a model is unused
+    // if the reference count is 1. If we clone all structural nodes here
+    // we need that extra reference to the original object
+    SGDatabaseReference* databaseReference;
+    databaseReference = new SGDatabaseReference(res.getNode());
     osg::CopyOp::CopyFlags flags = osg::CopyOp::DEEP_COPY_ALL;
     flags &= ~osg::CopyOp::DEEP_COPY_TEXTURES;
     flags &= ~osg::CopyOp::DEEP_COPY_IMAGES;
@@ -383,157 +399,9 @@ public:
   }
 
 private:
-  SGCondition* mCondition;
+  SGSharedPtr<SGCondition> mCondition;
 };
 
-/**
- * Locate a named node in a branch.
- */
-class NodeFinder : public osg::NodeVisitor {
-public:
-  NodeFinder(const std::string& nameToFind) :
-    osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR,
-                     osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
-    mName(nameToFind),
-    mNode(0)
-  { }
-  virtual void apply(osg::Node& node)
-  {
-    if (mNode)
-      return;
-    if (mName == node.getName()) {
-      mNode = &node;
-      return;
-    }
-    traverse(node);
-  }
-
-  osg::Node* getNode() const
-  { return mNode; }
-private:
-  std::string mName;
-  osg::Node* mNode;
-};
-
-/**
- * Splice a branch in between all child nodes and their parents.
- */
-static void
-splice_branch(osg::Group* group, osg::Node* child)
-{
-  osg::Node::ParentList parents = child->getParents();
-  group->addChild(child);
-  osg::Node::ParentList::iterator i;
-  for (i = parents.begin(); i != parents.end(); ++i)
-    (*i)->replaceChild(child, group);
-}
-
-void
-sgMakeAnimation( osg::Node * model,
-                 const char * name,
-                 vector<SGPropertyNode_ptr> &name_nodes,
-                 SGPropertyNode *prop_root,
-                 SGPropertyNode_ptr node,
-                 double sim_time_sec,
-                 SGPath &texture_path,
-                 set<osg::Node*> &ignore_branches )
-{
-  bool ignore = false;
-  SGAnimation * animation = 0;
-  const char * type = node->getStringValue("type", "none");
-  if (!strcmp("none", type)) {
-    animation = new SGNullAnimation(node);
-  } else if (!strcmp("range", type)) {
-    animation = new SGRangeAnimation(prop_root, node);
-  } else if (!strcmp("billboard", type)) {
-    animation = new SGBillboardAnimation(node);
-  } else if (!strcmp("select", type)) {
-    animation = new SGSelectAnimation(prop_root, node);
-  } else if (!strcmp("spin", type)) {
-    animation = new SGSpinAnimation(prop_root, node, sim_time_sec );
-  } else if (!strcmp("timed", type)) {
-    animation = new SGTimedAnimation(node);
-  } else if (!strcmp("rotate", type)) {
-    animation = new SGRotateAnimation(prop_root, node);
-  } else if (!strcmp("translate", type)) {
-    animation = new SGTranslateAnimation(prop_root, node);
-  } else if (!strcmp("scale", type)) {
-    animation = new SGScaleAnimation(prop_root, node);
-  } else if (!strcmp("texrotate", type)) {
-    animation = new SGTexRotateAnimation(prop_root, node);
-  } else if (!strcmp("textranslate", type)) {
-    animation = new SGTexTranslateAnimation(prop_root, node);
-  } else if (!strcmp("texmultiple", type)) {
-    animation = new SGTexMultipleAnimation(prop_root, node);
-  } else if (!strcmp("blend", type)) {
-    animation = new SGBlendAnimation(prop_root, node);
-    ignore = true;
-  } else if (!strcmp("alpha-test", type)) {
-    animation = new SGAlphaTestAnimation(node);
-  } else if (!strcmp("material", type)) {
-    animation = new SGMaterialAnimation(prop_root, node, texture_path);
-  } else if (!strcmp("flash", type)) {
-    animation = new SGFlashAnimation(node);
-  } else if (!strcmp("dist-scale", type)) {
-    animation = new SGDistScaleAnimation(node);
-  } else if (!strcmp("noshadow", type)) {
-    animation = new SGShadowAnimation(prop_root, node);
-  } else if (!strcmp("shader", type)) {
-    animation = new SGShaderAnimation(prop_root, node);
-  } else {
-    animation = new SGNullAnimation(node);
-    SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
-  }
-
-  if (name != 0)
-      animation->setName(name);
-
-  osg::Node * object = 0;
-  if (!name_nodes.empty()) {
-    const char * name = name_nodes[0]->getStringValue();
-    NodeFinder nodeFinder(name);
-    model->accept(nodeFinder);
-    object = nodeFinder.getNode();
-    if (object == 0) {
-      SG_LOG(SG_INPUT, SG_ALERT, "Object " << name << " not found");
-      delete animation;
-      animation = 0;
-    }
-  } else {
-    object = model;
-  }
-
-  if ( animation == 0 )
-     return;
-
-  osg::Group* branch = animation->getBranch();
-  splice_branch(branch, object);
-
-  for (unsigned int i = 1; i < name_nodes.size(); i++) {
-      const char * name = name_nodes[i]->getStringValue();
-      NodeFinder nodeFinder(name);
-      model->accept(nodeFinder);
-      object = nodeFinder.getNode();
-      if (object == 0) {
-          SG_LOG(SG_INPUT, SG_ALERT, "Object " << name << " not found");
-          delete animation;
-          animation = 0;
-      } else {
-          osg::Group* oldParent = object->getParent(0);
-          branch->addChild(object);
-          oldParent->removeChild(object);
-      }
-  }
-
-  if ( animation != 0 ) {
-    animation->init();
-    branch->setUpdateCallback(animation);
-    if ( ignore ) {
-      ignore_branches.insert( branch );
-    }
-  }
-}
-
 \f
 ////////////////////////////////////////////////////////////////////////
 // Global functions.
@@ -546,10 +414,10 @@ sgLoad3DModel( const string &fg_root, const string &path,
                SGModelData *data,
                const SGPath& externalTexturePath )
 {
-  osg::Switch* model = 0;
+  osg::Node* model = 0;
   SGPropertyNode props;
 
-                                // Load the 3D aircraft object itself
+  // Load the 3D aircraft object itself
   SGPath modelpath = path, texturepath = path;
   if ( !ulIsAbsolutePathName( path.c_str() ) ) {
     SGPath tmp = fg_root;
@@ -557,7 +425,7 @@ sgLoad3DModel( const string &fg_root, const string &path,
     modelpath = texturepath = tmp;
   }
 
-                                // Check for an XML wrapper
+  // Check for an XML wrapper
   if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") {
     readProperties(modelpath.str(), &props);
     if (props.hasValue("/path")) {
@@ -576,25 +444,23 @@ sgLoad3DModel( const string &fg_root, const string &path,
   osgDB::FilePathList pathList = osgDB::getDataFilePathList();
   osgDB::Registry::instance()->initFilePathLists();
 
-                                // Assume that textures are in
-                                // the same location as the XML file.
+  // Assume that textures are in
+  // the same location as the XML file.
   if (model == 0) {
     if (texturepath.extension() != "")
           texturepath = texturepath.dir();
 
     osgDB::Registry::instance()->getDataFilePathList().push_front(texturepath.str());
 
-    osg::Node* node = osgDB::readNodeFile(modelpath.str());
-    if (node == 0)
+    model = osgDB::readNodeFile(modelpath.str());
+    if (model == 0)
       throw sg_io_exception("Failed to load 3D model", 
                             sg_location(modelpath.str()));
-    model = new osg::Switch;
-    model->addChild(node, true);
   }
 
   osgDB::Registry::instance()->getDataFilePathList().push_front(externalTexturePath.str());
 
-                                // Set up the alignment node
+  // Set up the alignment node
   osg::MatrixTransform* alignmainmodel = new osg::MatrixTransform;
   alignmainmodel->addChild(model);
   osg::Matrix res_matrix;
@@ -612,30 +478,28 @@ sgLoad3DModel( const string &fg_root, const string &path,
                      props.getFloatValue("/offsets/z-m", 0.0));
   alignmainmodel->setMatrix(res_matrix*tmat);
 
-  unsigned int i;
-
-                                // Load sub-models
+  // Load sub-models
   vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
-  for (i = 0; i < model_nodes.size(); i++) {
+  for (unsigned i = 0; i < model_nodes.size(); i++) {
     SGPropertyNode_ptr node = model_nodes[i];
     osg::MatrixTransform* align = new osg::MatrixTransform;
     res_matrix.makeIdentity();
     res_matrix.makeRotate(
-      node->getFloatValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
+      node->getDoubleValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
       osg::Vec3(0, 0, 1),
-      node->getFloatValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
+      node->getDoubleValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
       osg::Vec3(1, 0, 0),
-      node->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
+      node->getDoubleValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
       osg::Vec3(0, 1, 0));
     
     tmat.makeIdentity();
-    tmat.makeTranslate(node->getFloatValue("offsets/x-m", 0.0),
-                       node->getFloatValue("offsets/y-m", 0.0),
-                       node->getFloatValue("offsets/z-m", 0.0));
+    tmat.makeTranslate(node->getDoubleValue("offsets/x-m", 0),
+                       node->getDoubleValue("offsets/y-m", 0),
+                       node->getDoubleValue("offsets/z-m", 0));
     align->setMatrix(res_matrix*tmat);
 
     osg::Node* kid;
-    const char * submodel = node->getStringValue("path");
+    const char* submodel = node->getStringValue("path");
     try {
       kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel );
 
@@ -646,22 +510,28 @@ sgLoad3DModel( const string &fg_root, const string &path,
     align->addChild(kid);
 
     align->setName(node->getStringValue("name", ""));
-    model->addChild(align);
 
     SGPropertyNode *cond = node->getNode("condition", false);
-    if (cond)
-      model->setUpdateCallback(new SGSwitchUpdateCallback(sgReadCondition(prop_root, cond)));
+    if (cond) {
+      osg::Switch* sw = new osg::Switch;
+      sw->setUpdateCallback(new SGSwitchUpdateCallback(sgReadCondition(prop_root, cond)));
+      alignmainmodel->addChild(sw);
+      sw->addChild(align);
+      sw->setName("submodel condition switch");
+    } else {
+      alignmainmodel->addChild(align);
+    }
   }
 
   if ( load_panel ) {
-                                // Load panels
+    // Load panels
     vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
-    for (i = 0; i < panel_nodes.size(); i++) {
+    for (unsigned i = 0; i < panel_nodes.size(); i++) {
         SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
         osg::Node * panel = load_panel(panel_nodes[i]);
         if (panel_nodes[i]->hasValue("name"))
             panel->setName((char *)panel_nodes[i]->getStringValue("name"));
-        model->addChild(panel);
+        alignmainmodel->addChild(panel);
     }
   }
 
@@ -669,20 +539,22 @@ sgLoad3DModel( const string &fg_root, const string &path,
     alignmainmodel->setUserData(data);
     data->modelLoaded(path, &props, alignmainmodel);
   }
-                                // Load animations
-  set<osg::Node*> ignore_branches;
-  vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
-  for (i = 0; i < animation_nodes.size(); i++) {
-    const char * name = animation_nodes[i]->getStringValue("name", 0);
-    vector<SGPropertyNode_ptr> name_nodes =
-      animation_nodes[i]->getChildren("object-name");
-    sgMakeAnimation( model, name, name_nodes, prop_root, animation_nodes[i],
-                     sim_time_sec, texturepath, ignore_branches);
-  }
+
+  std::vector<SGPropertyNode_ptr> animation_nodes;
+  animation_nodes = props.getChildren("animation");
+  for (unsigned i = 0; i < animation_nodes.size(); ++i)
+    /// OSGFIXME: duh, why not only model?????
+    SGAnimation::animate(alignmainmodel, animation_nodes[i], prop_root);
 
   // restore old path list
   osgDB::setDataFilePathList(pathList);
 
+  if (props.hasChild("debug-outfile")) {
+    std::string outputfile = props.getStringValue("debug-outfile",
+                                                  "debug-model.osg");
+    osgDB::writeNodeFile(*alignmainmodel, outputfile);
+  }
+
   return alignmainmodel;
 }
 
index 858963e328bb0bbf6c6c4ae1fbb7d61a3db78daa..e855152c7ed01900cab84de5c8f1c9594486c016 100755 (executable)
@@ -6,7 +6,7 @@
 #include "persparam.hxx"
 
 template <> double
-SGPersonalityParameter<double>::getNodeValue( SGPropertyNode *props,
+SGPersonalityParameter<double>::getNodeValue( const SGPropertyNode *props,
                                               const char *name,
                                               double defval ) const
 {
index 776e0c9c7bbb45f4889c7a497d5b5cbb098de909..e8b571fcf133f02b9c6c695a7a2e615552f16d23 100755 (executable)
 template <class T>
 class SGPersonalityParameter {
 public:
-  SGPersonalityParameter( SGPropertyNode *props, const char *name, T defval )
+  SGPersonalityParameter(const SGPropertyNode *props, const char *name, T defval )
     : _var( defval ), _min( defval ), _max( defval ) {
-    SGPropertyNode_ptr node = props->getNode( name );
+    const SGPropertyNode* node = props->getNode( name );
     if ( node != 0 ) {
-      SGPropertyNode_ptr rand_n = node->getNode( "random" );
+      const SGPropertyNode* rand_n = node->getNode( "random" );
       if ( rand_n != 0 ) {
         _min = getNodeValue( rand_n, "min", (T)0 );
         _max = getNodeValue( rand_n, "max", (T)1 );
@@ -30,7 +30,7 @@ public:
   SGPersonalityParameter<T> &operator-=( T v ) { _var -= v; return *this; }
   T shuffle() { return ( _var = _min + sg_random() * ( _max - _min ) ); }
   T value() const { return _var; }
-  T getNodeValue( SGPropertyNode *props, const char *name, T defval ) const;
+  T getNodeValue(const SGPropertyNode *props, const char *name, T defval ) const;
   operator T() const { return _var; }
 
 private:
@@ -40,7 +40,7 @@ private:
 };
 
 template <> double
-SGPersonalityParameter<double>::getNodeValue( SGPropertyNode *props,
+SGPersonalityParameter<double>::getNodeValue( const SGPropertyNode *props,
                                               const char *name,
                                               double defval ) const;
 
index ab76f23fa7253aaefb59e677131058f436eeba7d..cffae0ff97a3d7ecc8643db429dadbdbe286a519 100644 (file)
 #  include <simgear_config.h>
 #endif
 
-#include <plib/ul.h>
+#include <osg/Group>
+#include <osg/Program>
+#include <osg/Shader>
+#include <osg/StateSet>
+#include <osg/TextureCubeMap>
+#include <osg/TexEnvCombine>
+#include <osg/TexGen>
+#include <osg/Texture1D>
+#include <osgUtil/HighlightMapGenerator>
+
+#include <simgear/scene/util/SGUpdateVisitor.hxx>
+#include <simgear/threads/SGThread.hxx>
+#include <simgear/threads/SGGuard.hxx>
 
 #include <simgear/props/condition.hxx>
 #include <simgear/props/props.hxx>
-#include <simgear/screen/extensions.hxx>
 
 #include <simgear/debug/logstream.hxx>
 
     </animation>
 
 */
+
+
+class SGMapGenCallback :
+  public osg::StateAttribute::Callback {
+public:
+  virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
+  {
+    SGUpdateVisitor* updateVisitor = dynamic_cast<SGUpdateVisitor*>(nv);
+    if (!updateVisitor)
+      return;
+
+    if (distSqr(_lastLightDirection, updateVisitor->getLightDirection()) < 1e-4
+        && distSqr(_lastLightColor, updateVisitor->getAmbientLight()) < 1e-4)
+      return;
+
+    _lastLightDirection = updateVisitor->getLightDirection();
+    _lastLightColor = updateVisitor->getAmbientLight();
+
+    osg::TextureCubeMap *tcm = static_cast<osg::TextureCubeMap*>(sa);
+
+    // FIXME: need an update or callback ...
+    // generate the six highlight map images (light direction = [1, 1, -1])
+    osg::ref_ptr<osgUtil::HighlightMapGenerator> mapgen;
+    mapgen = new osgUtil::HighlightMapGenerator(_lastLightDirection.osg(),
+                                                _lastLightColor.osg(), 5);
+    mapgen->generateMap();
+
+    // assign the six images to the texture object
+    tcm->setImage(osg::TextureCubeMap::POSITIVE_X,
+                  mapgen->getImage(osg::TextureCubeMap::POSITIVE_X));
+    tcm->setImage(osg::TextureCubeMap::NEGATIVE_X,
+                  mapgen->getImage(osg::TextureCubeMap::NEGATIVE_X));
+    tcm->setImage(osg::TextureCubeMap::POSITIVE_Y,
+                  mapgen->getImage(osg::TextureCubeMap::POSITIVE_Y));
+    tcm->setImage(osg::TextureCubeMap::NEGATIVE_Y,
+                  mapgen->getImage(osg::TextureCubeMap::NEGATIVE_Y));
+    tcm->setImage(osg::TextureCubeMap::POSITIVE_Z,
+                  mapgen->getImage(osg::TextureCubeMap::POSITIVE_Z));
+    tcm->setImage(osg::TextureCubeMap::NEGATIVE_Z,
+                  mapgen->getImage(osg::TextureCubeMap::NEGATIVE_Z));
+  }
+private:
+  SGVec3f _lastLightDirection;
+  SGVec4f _lastLightColor;
+};
+
+static osg::TextureCubeMap*
+getOrCreateTextureCubeMap()
+{
+  static osg::TextureCubeMap* textureCubeMap = 0;
+  if (textureCubeMap)
+    return textureCubeMap;
+
+  static SGMutex mutex;
+  SGGuard<SGMutex> locker(mutex);
+  if (textureCubeMap)
+    return textureCubeMap;
+
+  // create and setup the texture object
+  textureCubeMap = new osg::TextureCubeMap;
+
+  textureCubeMap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
+  textureCubeMap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
+  textureCubeMap->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP);
+  textureCubeMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
+  textureCubeMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);    
+  
+  textureCubeMap->setUpdateCallback(new SGMapGenCallback);
+
+  return textureCubeMap;
+}
+
+static void create_specular_highlights(osg::Node *node)
+{
+  osg::StateSet *ss = node->getOrCreateStateSet();
+  
+  // create and setup the texture object
+  osg::TextureCubeMap *tcm = getOrCreateTextureCubeMap();
+  
+  // enable texturing, replacing any textures in the subgraphs
+  ss->setTextureAttributeAndModes(0, tcm, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
+  
+  // texture coordinate generation
+  osg::TexGen *tg = new osg::TexGen;
+  tg->setMode(osg::TexGen::REFLECTION_MAP);
+  ss->setTextureAttributeAndModes(0, tg, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
+  
+  // use TexEnvCombine to add the highlights to the original lighting
+  osg::TexEnvCombine *te = new osg::TexEnvCombine;
+  te->setCombine_RGB(osg::TexEnvCombine::ADD);
+  te->setSource0_RGB(osg::TexEnvCombine::TEXTURE);
+  te->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
+  te->setSource1_RGB(osg::TexEnvCombine::PRIMARY_COLOR);
+  te->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
+  ss->setTextureAttributeAndModes(0, te, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
+}
+
+
+SGShaderAnimation::SGShaderAnimation(const SGPropertyNode* configNode,
+                                     SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
+{
+}
+
+osg::Group*
+SGShaderAnimation::createAnimationGroup(osg::Group& parent)
+{
+  osg::Group* group = new osg::Group;
+  group->setName("shader animation");
+  parent.addChild(group);
+
+  std::string shader_name = getConfig()->getStringValue("shader", "");
+//   if( shader_name == "fresnel" || shader_name == "reflection" )
+//     _shader_type = 1;
+//   else if( shader_name == "heat-haze" )
+//     _shader_type = 2;
+//   else
+  if( shader_name == "chrome")
+    create_specular_highlights(group);
+
+  return group;
+}
+
+#if 0
 // static Shader *shFresnel=NULL;
 // static GLuint texFresnel = 0;
 
@@ -621,3 +756,4 @@ SGShaderAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
   // that the rest of cullbacks and the scene graph are traversed.
   traverse(node, nv);
 }
+#endif