]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/model/SGMaterialAnimation.cxx
Work around apparent OSG 3.2.0 normal binding bug.
[simgear.git] / simgear / scene / model / SGMaterialAnimation.cxx
index f099be0cf8e8b370c72a4a21b8166eb38bcf777e..cc5c660c6ae93355779e3f8600e1e6c2791b3f0c 100644 (file)
 #include "SGMaterialAnimation.hxx"
 
 #include <osg/AlphaFunc>
+#include <osg/Array>
 #include <osg/Drawable>
 #include <osg/Geode>
 #include <osg/Geometry>
+#include <osg/Material>
 #include <osg/StateSet>
 #include <osgDB/FileNameUtils>
 #include <osgDB/FileUtils>
 
 #include <simgear/props/condition.hxx>
 #include <simgear/props/props.hxx>
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
+#include <simgear/scene/material/Pass.hxx>
+#include <simgear/scene/material/Technique.hxx>
 #include <simgear/scene/model/model.hxx>
+#include <simgear/scene/model/ConditionNode.hxx>
+#include <simgear/scene/util/OsgMath.hxx>
 
-struct SGMaterialAnimation::ColorSpec {
+using namespace std;
+using namespace simgear;
+
+namespace {
+/**
+ * Get a color from properties.
+ */
+struct ColorSpec {
   float red, green, blue;
   float factor;
   float offset;
@@ -91,6 +106,12 @@ struct SGMaterialAnimation::ColorSpec {
     v[3] = 1;
     return v;
   }
+
+  osg::Vec4 rgbaVec4()
+  {
+    return toOsg(rgba());
+  }
+  
   SGVec4f &initialRgba() {
     v[0] = SGMiscf::clip(red*factor + offset, 0, 1);
     v[1] = SGMiscf::clip(green*factor + offset, 0, 1);
@@ -100,8 +121,10 @@ struct SGMaterialAnimation::ColorSpec {
   }
 };
 
-
-struct SGMaterialAnimation::PropSpec {
+/**
+ * Get a property value from a property.
+ */
+struct PropSpec {
   float value;
   float factor;
   float offset;
@@ -155,162 +178,174 @@ struct SGMaterialAnimation::PropSpec {
   }
 };
 
-class SGMaterialAnimation::MaterialVisitor : public osg::NodeVisitor {
-public:
-  enum {
-    DIFFUSE = 1,
-    AMBIENT = 2,
-    SPECULAR = 4,
-    EMISSION = 8,
-    SHININESS = 16,
-    TRANSPARENCY = 32
-  };
+/**
+ * The possible color properties supplied by a material animation.
+ */
+enum SuppliedColor {
+  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);
-  }
+const unsigned AMBIENT_DIFFUSE = AMBIENT | DIFFUSE;
 
-  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;
-  }
+const int allMaterialColors = (DIFFUSE | AMBIENT | SPECULAR | EMISSION
+                              | SHININESS);
 
-  void setAlpha(float alpha)
+// Visitor for finding default material colors in the animation node's
+// subgraph. This makes some assumptions about the subgraph i.e.,
+// there will be one material and one color value found. This is
+// probably true for ac3d models and most uses of material animations,
+// but will break down if, for example, you animate the transparency
+// of a vertex colored model.
+class MaterialDefaultsVisitor : public osg::NodeVisitor {
+public:
+  MaterialDefaultsVisitor()
+    : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+      ambientDiffuse(-1.0f, -1.0f, -1.0f, -1.0f)
   {
-    if (alpha != _alpha)
-      _alpha = alpha;
-    _updateMask |= TRANSPARENCY;
+    setVisitorType(osg::NodeVisitor::NODE_VISITOR);
   }
 
-  virtual void reset()
-  {
-    _updateMask = 0;
-  }
   virtual void apply(osg::Node& node)
   {
-    updateStateSet(node.getStateSet());
+    maybeGetMaterialValues(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;
+    using namespace simgear;
+    EffectGeode* eg = dynamic_cast<EffectGeode*>(&node);
+    if (eg) {
+      const Effect* effect = eg->getEffect();
+      if (effect)
+        for (vector<osg::ref_ptr<Technique> >::const_iterator itr
+               = effect->techniques.begin(), end = effect->techniques.end();
+             itr != end;
+             ++itr) {
+          const Technique* tniq = itr->get();
+          for (vector<osg::ref_ptr<Pass> >::const_iterator pitr
+                 = tniq->passes.begin(), pend = tniq->passes.end();
+               pitr != pend;
+               ++pitr)
+            maybeGetMaterialValues(pitr->get());
         }
+    } else {
+      maybeGetMaterialValues(node.getStateSet());
+    }
+    int numDrawables = node.getNumDrawables();
+    for (int i = 0; i < numDrawables; i++) {
+      osg::Geometry* geom = dynamic_cast<osg::Geometry*>(node.getDrawable(i));
+      if (!geom || geom->getColorBinding() != osg::Geometry::BIND_OVERALL)
+       continue;
+      maybeGetMaterialValues(geom->getStateSet());
+      osg::Array* colorArray = geom->getColorArray();
+      osg::Vec4Array* colorVec4 = dynamic_cast<osg::Vec4Array*>(colorArray);
+      if (colorVec4) {
+       ambientDiffuse = (*colorVec4)[0];
+       break;
+      }
+      osg::Vec3Array* colorVec3 = dynamic_cast<osg::Vec3Array*>(colorArray);
+      if (colorVec3) {
+       ambientDiffuse = osg::Vec4((*colorVec3)[0], 1.0f);
+       break;
       }
     }
   }
-  void updateStateSet(osg::StateSet* stateSet)
+  
+  void maybeGetMaterialValues(const osg::StateSet* stateSet)
   {
     if (!stateSet)
       return;
-    osg::StateAttribute* stateAttribute;
-    stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
-    if (!stateAttribute)
+    const osg::Material* nodeMat
+      = dynamic_cast<const osg::Material*>(stateSet
+                                           ->getAttribute(osg::StateAttribute
+                                                          ::MATERIAL));
+    if (!nodeMat)
       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);
-      }
-    }
+    material = nodeMat;
   }
-private:
-  unsigned _updateMask;
-  SGVec4f _ambient;
-  SGVec4f _diffuse;
-  SGVec4f _specular;
-  SGVec4f _emission;
-  float _shininess;
-  float _alpha;
+
+  osg::ref_ptr<const osg::Material> material;
+  osg::Vec4 ambientDiffuse;
+};
+
+class MaterialPropertyAdapter
+{
+public:
+    MaterialPropertyAdapter(const SGPropertyNode* configNode,
+                            SGPropertyNode* modelRoot) :
+        _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)
+    {
+        _shininess.max = 128;
+        _isAnimated = (_ambient.live() || _diffuse.live() || _specular.live()
+                       || _emission.live() || _shininess.live()
+                       || _transparency.live());
+    }
+    bool isAnimated() { return _isAnimated; }
+    // This takes a StateSet argument because the rendering bin will
+    // be changed if there is transparency.
+    void setMaterialValues(osg::StateSet* stateSet)
+    {
+        osg::StateAttribute* stateAttribute
+            = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
+        osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
+        if (material) {
+            if (_ambient.live() || _ambient.dirty())
+                material->setAmbient(osg::Material::FRONT_AND_BACK,
+                                     _ambient.rgbaVec4());     
+            if (_diffuse.live() || _diffuse.dirty())
+                material->setDiffuse(osg::Material::FRONT_AND_BACK,
+                                     _diffuse.rgbaVec4());
+            if (_specular.live() || _specular.dirty())
+                material->setSpecular(osg::Material::FRONT_AND_BACK,
+                                      _specular.rgbaVec4());
+            if (_emission.live() || _emission.dirty())
+                material->setEmission(osg::Material::FRONT_AND_BACK,
+                                      _emission.rgbaVec4());
+            if (_shininess.live() || _shininess.dirty())
+                material->setShininess(osg::Material::FRONT_AND_BACK,
+                                       _shininess.getValue());
+            if (_transparency.live() || _transparency.dirty()) {
+                float alpha = _transparency.getValue();
+                material->setAlpha(osg::Material::FRONT_AND_BACK, alpha);
+                if (alpha < 1.0f) {
+                    stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
+                    stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
+                } else {
+                    stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
+                }
+            }
+        }
+    }
+    ColorSpec _ambient;
+    ColorSpec _diffuse;
+    ColorSpec _specular;
+    ColorSpec _emission;
+    PropSpec _shininess;
+    PropSpec _transparency;
+    bool _isAnimated;
+
 };
 
-class SGMaterialAnimation::UpdateCallback : public osg::NodeCallback {
+class 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)
+    _materialProps(configNode, modelRoot),
+    _texturePathList(texturePathList),
+    _prevState(false)
   {
     const SGPropertyNode* node;
 
@@ -324,12 +359,13 @@ public:
 
   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
   {
-    if (!_condition || _condition->test()) {
+    osg::StateSet* stateSet = node->getStateSet();
+    if (stateSet) {
       if (_textureProp) {
         std::string textureName = _textureProp->getStringValue();
         if (_textureName != textureName) {
-          osg::StateSet* stateSet = node->getOrCreateStateSet();
-          while (stateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE)) {
+          while (stateSet->getTextureAttribute(0,
+                                              osg::StateAttribute::TEXTURE)) {
             stateSet->removeTextureAttribute(0, osg::StateAttribute::TEXTURE);
           }
           std::string textureFile;
@@ -337,7 +373,8 @@ public:
           if (!textureFile.empty()) {
             osg::Texture2D* texture2D = SGLoadTexture2D(textureFile);
             if (texture2D) {
-              stateSet->setTextureAttribute(0, texture2D);
+              stateSet->setTextureAttribute(0, texture2D,
+                                           osg::StateAttribute::OVERRIDE);
               stateSet->setTextureMode(0, GL_TEXTURE_2D,
                                        osg::StateAttribute::ON);
               _textureName = textureName;
@@ -349,52 +386,41 @@ public:
         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);
+        osg::AlphaFunc* alphaFunc
+            = dynamic_cast<osg::AlphaFunc*>(stateAttribute);
+       assert(alphaFunc);
         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);
+      if (_materialProps.isAnimated() || !_prevState)
+          _materialProps.setMaterialValues(stateSet);
+      _prevState = true;
+    } else {
+      _prevState = false;
     }
-
     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;
+  MaterialPropertyAdapter _materialProps;
   osgDB::FilePathList _texturePathList;
+  bool _prevState;
 };
+} // namespace
+
 
 SGMaterialAnimation::SGMaterialAnimation(const SGPropertyNode* configNode,
-                                         SGPropertyNode* modelRoot) :
-  SGAnimation(configNode, modelRoot)
+                                         SGPropertyNode* modelRoot,
+                                         const osgDB::Options*
+                                         options, const string &path) :
+  SGAnimation(configNode, modelRoot),
+  texturePathList(options->getDatabasePathList())
 {
   if (configNode->hasChild("global"))
-    SG_LOG(SG_IO, SG_ALERT, "Using global material animation that can "
-           "no longer work");
+    SG_LOG(SG_IO, SG_ALERT, path <<
+           ": Use of <global> in material animation is "
+           "no longer supported.");
 }
 
 osg::Group*
@@ -406,20 +432,17 @@ SGMaterialAnimation::createAnimationGroup(osg::Group& parent)
   SGPropertyNode* inputRoot = getModelRoot();
   const SGPropertyNode* node = getConfig()->getChild("property-base");
   if (node)
-    inputRoot = getModelRoot()->getRootNode()->getNode(node->getStringValue(),
-                                                       true);
-
-  osgDB::FilePathList texturePathList = osgDB::getDataFilePathList();
-
+    inputRoot = getModelRoot()->getNode(node->getStringValue(), true);
+  osg::StateSet* stateSet = group->getOrCreateStateSet();  
   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->setTextureAttribute(0, texture2D,
+                                     osg::StateAttribute::OVERRIDE);
         stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
         if (texture2D->getImage()->isImageTranslucent()) {
           stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
@@ -430,63 +453,168 @@ SGMaterialAnimation::createAnimationGroup(osg::Group& parent)
   }
   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);
+    stateSet->setAttribute(alphaFunc, osg::StateAttribute::OVERRIDE);
   }
 
-  UpdateCallback* updateCallback;
-  updateCallback = new UpdateCallback(texturePathList, getCondition(),
-                                      getConfig(), inputRoot);
-  group->setUpdateCallback(updateCallback);
-  parent.addChild(group);
-  return group;
+  unsigned suppliedColors = 0;
+  if (getConfig()->hasChild("ambient"))
+    suppliedColors |= AMBIENT;
+  if (getConfig()->hasChild("diffuse"))
+    suppliedColors |= DIFFUSE;
+  if (getConfig()->hasChild("specular"))
+    suppliedColors |= SPECULAR;
+  if (getConfig()->hasChild("emission"))
+    suppliedColors |= EMISSION;
+  if (getConfig()->hasChild("shininess")
+      || getConfig()->hasChild("shininess-prop"))
+    suppliedColors |= SHININESS;
+  if (getConfig()->hasChild("transparency"))
+    suppliedColors |= TRANSPARENCY;
+  osg::Material* mat = 0;
+  if (suppliedColors != 0) {
+    if (defaultMaterial.valid()) {
+      mat = defaultMaterial.get();
+
+    } else {
+      mat = new osg::Material;
+      mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
+    }
+    mat->setDataVariance(osg::Object::DYNAMIC);
+    unsigned defaultColorModeMask = 0;
+    mat->setUpdateCallback(0); // Just to make sure.
+    // XXX This should probably go away, as ac3d models always have a
+    // DIFFUSE color mode.
+    switch (mat->getColorMode()) {
+    case osg::Material::OFF:
+      defaultColorModeMask = 0;
+      break;
+    case osg::Material::AMBIENT:
+      defaultColorModeMask = AMBIENT;
+      break;
+    case osg::Material::DIFFUSE:
+      defaultColorModeMask = DIFFUSE;
+      break;
+    case osg::Material::AMBIENT_AND_DIFFUSE:
+      defaultColorModeMask = AMBIENT | DIFFUSE;
+      break;
+    case osg::Material::SPECULAR:
+      defaultColorModeMask = SPECULAR;
+      break;
+    case osg::Material::EMISSION:
+      defaultColorModeMask = EMISSION;
+      break;
+    }
+    // Copy the color found by traversing geometry into the material
+    // in case we need to specify it (e.g., transparency) and it is
+    // not specified by the animation.
+    if (defaultAmbientDiffuse.x() >= 0) {
+      if (defaultColorModeMask & AMBIENT)
+        mat->setAmbient(osg::Material::FRONT_AND_BACK, defaultAmbientDiffuse);
+      if (defaultColorModeMask & DIFFUSE)
+        mat->setDiffuse(osg::Material::FRONT_AND_BACK, defaultAmbientDiffuse);
+    }
+    // Compute which colors in the animation override colors set via
+    // colorMode / glColor, and set the colorMode for the animation's
+    // material accordingly. 
+    if (suppliedColors & TRANSPARENCY) {
+      // All colors will be affected by the material. Hope all the
+      // defaults are fine, if needed.
+      mat->setColorMode(osg::Material::OFF);
+    } else if ((suppliedColors & defaultColorModeMask) != 0) {
+      // First deal with the complicated AMBIENT/DIFFUSE case.
+      if ((defaultColorModeMask & AMBIENT_DIFFUSE) != 0) {
+        // glColor can supply colors not specified by the animation.
+        unsigned matColorModeMask = ((~suppliedColors & defaultColorModeMask)
+                                     & AMBIENT_DIFFUSE);
+        if ((matColorModeMask & DIFFUSE) != 0)
+          mat->setColorMode(osg::Material::DIFFUSE);
+        else if ((matColorModeMask & AMBIENT) != 0)
+          mat->setColorMode(osg::Material::AMBIENT);
+        else
+          mat->setColorMode(osg::Material::OFF);
+      } else {
+        // The animation overrides the glColor color.
+        mat->setColorMode(osg::Material::OFF);
+      }
+    } else {
+      // No overlap between the animation and color mode, so leave
+      // the color mode alone.
+    }
+    stateSet->setAttribute(mat,(osg::StateAttribute::ON
+                                | osg::StateAttribute::OVERRIDE));
+  }
+  bool matAnimated = false;
+  if (mat) {
+    MaterialPropertyAdapter adapter(getConfig(), inputRoot);
+    adapter.setMaterialValues(stateSet);
+    matAnimated = adapter.isAnimated();
+  }
+  if (matAnimated || getConfig()->hasChild("texture-prop")
+      || getConfig()->hasChild("threshold-prop") || getCondition()) {
+    stateSet->setDataVariance(osg::Object::DYNAMIC);
+    group->setUpdateCallback(new UpdateCallback(texturePathList,
+                                                getConfig(), inputRoot));
+  } else {
+    stateSet->setDataVariance(osg::Object::STATIC);
+  }
+  if (getCondition()) {
+    ConditionNode* cn = new ConditionNode;
+    cn->setCondition(getCondition());
+    osg::Group* modelGroup = new osg::Group;
+    group->addChild(modelGroup);
+    cn->addChild(group);
+    cn->addChild(modelGroup);
+    parent.addChild(cn);
+    return modelGroup;
+  } else {
+    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);
+    MaterialDefaultsVisitor defaultsVisitor;
+    node.accept(defaultsVisitor);
+    if (defaultsVisitor.material.valid()) {
+      defaultMaterial
+       = static_cast<osg::Material*>(defaultsVisitor.material->clone(osg::CopyOp::SHALLOW_COPY));
+    }
+    defaultAmbientDiffuse = defaultsVisitor.ambientDiffuse;
+}
+
+const char* colorNames[] =
+{
+    "ambient",
+    "diffuse",
+    "specular",
+    "emission"
+};
 
-  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());
+// Build an effect which mimics the material color mode in a
+// shader. The OpenGL material values will be overridden by the
+// material animation's material.
+//
+// This is a hack to get the effect to respect the values set in the
+// material, set up by the animation, which overrides the values in
+// the effect's material attributes. Things will be different when
+// material animations are implemented purely by manipulating effects.
 
-  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);
+SGPropertyNode_ptr
+SGMaterialAnimation::makeEffectProperties(const SGPropertyNode* animProp)
+{
+    SGPropertyNode_ptr eRoot = new SGPropertyNode;
+    SGPropertyNode* inherit = makeNode(eRoot, "inherits-from");
+    if (animProp->hasChild("diffuse") || animProp->hasChild("transparency"))
+        inherit->setStringValue("Effects/material-off");
+    else
+        inherit->setStringValue("Effects/material-diffuse");
+    return eRoot;
 }
-