]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/material/Effect.cxx
pass SGReaderWriterXMLOptions to effects
[simgear.git] / simgear / scene / material / Effect.cxx
index 0ec20275611edb7a59e3ab09b3fe1c8d669252cd..3ad9316216fd0904e039f9c8caeb79f76092ab2c 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "Effect.hxx"
 #include "EffectBuilder.hxx"
+#include "EffectGeode.hxx"
 #include "Technique.hxx"
 #include "Pass.hxx"
 #include "TextureBuilder.hxx"
 #include <iterator>
 #include <map>
 #include <utility>
+#include <boost/tr1/unordered_map.hpp>
 
+#include <boost/bind.hpp>
 #include <boost/foreach.hpp>
+#include <boost/functional/hash.hpp>
 #include <boost/tuple/tuple.hpp>
 #include <boost/tuple/tuple_comparison.hpp>
 
 #include <osg/AlphaFunc>
 #include <osg/BlendFunc>
 #include <osg/CullFace>
+#include <osg/Depth>
 #include <osg/Drawable>
 #include <osg/Material>
 #include <osg/Math>
@@ -46,6 +51,7 @@
 #include <osg/RenderInfo>
 #include <osg/ShadeModel>
 #include <osg/StateSet>
+#include <osg/Stencil>
 #include <osg/TexEnv>
 #include <osg/Texture1D>
 #include <osg/Texture2D>
@@ -60,6 +66,7 @@
 #include <osgDB/ReadFile>
 #include <osgDB/Registry>
 
+#include <simgear/scene/model/SGReaderWriterXMLOptions.hxx>
 #include <simgear/scene/tgdb/userdata.hxx>
 #include <simgear/scene/util/SGSceneFeatures.hxx>
 #include <simgear/scene/util/StateAttributeFactory.hxx>
@@ -77,11 +84,13 @@ using namespace osgUtil;
 using namespace effect;
 
 Effect::Effect()
+    : _cache(0), _isRealized(false)
 {
 }
 
 Effect::Effect(const Effect& rhs, const CopyOp& copyop)
-    : root(rhs.root), parametersProp(rhs.parametersProp)
+    : root(rhs.root), parametersProp(rhs.parametersProp), _cache(0),
+      _isRealized(rhs._isRealized)
 {
     typedef vector<ref_ptr<Technique> > TechniqueList;
     for (TechniqueList::const_iterator itr = rhs.techniques.begin(),
@@ -131,10 +140,11 @@ void Effect::releaseGLObjects(osg::State* state) const
 
 Effect::~Effect()
 {
+    delete _cache;
 }
 
 void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
-               const osgDB::ReaderWriter::Options* options)
+               const SGReaderWriterXMLOptions* options)
 {
     Pass* pass = new Pass;
     tniq->passes.push_back(pass);
@@ -179,12 +189,12 @@ osg::Vec4f getColor(const SGPropertyNode* prop)
 struct LightingBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options);
+                        const SGReaderWriterXMLOptions* options);
 };
 
 void LightingBuilder::buildAttribute(Effect* effect, Pass* pass,
                                      const SGPropertyNode* prop,
-                                     const osgDB::ReaderWriter::Options* options)
+                                     const SGReaderWriterXMLOptions* options)
 {
     const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
     if (!realProp)
@@ -198,7 +208,7 @@ InstallAttributeBuilder<LightingBuilder> installLighting("lighting");
 struct ShadeModelBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options)
+                        const SGReaderWriterXMLOptions* options)
     {
         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
         if (!realProp)
@@ -220,7 +230,7 @@ InstallAttributeBuilder<ShadeModelBuilder> installShadeModel("shade-model");
 struct CullFaceBuilder : PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options)
+                        const SGReaderWriterXMLOptions* options)
     {
         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
         if (!realProp) {
@@ -245,6 +255,24 @@ struct CullFaceBuilder : PassAttributeBuilder
 
 InstallAttributeBuilder<CullFaceBuilder> installCullFace("cull-face");
 
+struct ColorMaskBuilder : PassAttributeBuilder
+{
+    void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
+                        const SGReaderWriterXMLOptions* options)
+    {
+        const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
+        if (!realProp)
+            return;
+
+        ColorMask *mask = new ColorMask;
+        Vec4 m = getColor(realProp);
+        mask->setMask(m.r(), m.g(), m.b(), m.a());
+        pass->setAttributeAndModes(mask);
+    }    
+};
+
+InstallAttributeBuilder<ColorMaskBuilder> installColorMask("color-mask");
+
 EffectNameValue<StateSet::RenderingHint> renderingHintInit[] =
 {
     { "default", StateSet::DEFAULT_BIN },
@@ -257,7 +285,7 @@ EffectPropertyMap<StateSet::RenderingHint> renderingHints(renderingHintInit);
 struct HintBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options)
+                        const SGReaderWriterXMLOptions* options)
     {
         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
         if (!realProp)
@@ -273,7 +301,7 @@ InstallAttributeBuilder<HintBuilder> installHint("rendering-hint");
 struct RenderBinBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options)
+                        const SGReaderWriterXMLOptions* options)
     {
         if (!isAttributeActive(effect, prop))
             return;
@@ -300,7 +328,7 @@ InstallAttributeBuilder<RenderBinBuilder> installRenderBin("render-bin");
 struct MaterialBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options);
+                        const SGReaderWriterXMLOptions* options);
 };
 
 EffectNameValue<Material::ColorMode> colorModeInit[] =
@@ -316,7 +344,7 @@ EffectPropertyMap<Material::ColorMode> colorModes(colorModeInit);
 
 void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
                                      const SGPropertyNode* prop,
-                                     const osgDB::ReaderWriter::Options* options)
+                                     const SGReaderWriterXMLOptions* options)
 {
     if (!isAttributeActive(effect, prop))
         return;
@@ -386,7 +414,7 @@ EffectPropertyMap<BlendFunc::BlendFuncMode> blendFuncModes(blendFuncModesInit);
 struct BlendBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options)
+                        const SGReaderWriterXMLOptions* options)
     {
         if (!isAttributeActive(effect, prop))
             return;
@@ -467,6 +495,99 @@ struct BlendBuilder : public PassAttributeBuilder
 
 InstallAttributeBuilder<BlendBuilder> installBlend("blend");
 
+
+EffectNameValue<Stencil::Function> stencilFunctionInit[] =
+{
+    {"never", Stencil::NEVER },
+    {"less", Stencil::LESS},
+    {"equal", Stencil::EQUAL},
+    {"less-or-equal", Stencil::LEQUAL},
+    {"greater", Stencil::GREATER},
+    {"not-equal", Stencil::NOTEQUAL},
+    {"greater-or-equal", Stencil::GEQUAL},
+    {"always", Stencil::ALWAYS}
+};
+
+EffectPropertyMap<Stencil::Function> stencilFunction(stencilFunctionInit);
+
+EffectNameValue<Stencil::Operation> stencilOperationInit[] =
+{
+    {"keep", Stencil::KEEP},
+    {"zero", Stencil::ZERO},
+    {"replace", Stencil::REPLACE},
+    {"increase", Stencil::INCR},
+    {"decrease", Stencil::DECR},
+    {"invert", Stencil::INVERT},
+    {"increase-wrap", Stencil::INCR_WRAP},
+    {"decrease-wrap", Stencil::DECR_WRAP}
+};
+
+EffectPropertyMap<Stencil::Operation> stencilOperation(stencilOperationInit);
+
+struct StencilBuilder : public PassAttributeBuilder
+{
+    void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
+                        const SGReaderWriterXMLOptions* options)
+    {
+        if (!isAttributeActive(effect, prop))
+            return;
+
+        const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
+                                                             "mode");
+        if (pmode && !pmode->getValue<bool>()) {
+            pass->setMode(GL_STENCIL, StateAttribute::OFF);
+            return;
+        }
+        const SGPropertyNode* pfunction
+            = getEffectPropertyChild(effect, prop, "function");
+        const SGPropertyNode* pvalue
+            = getEffectPropertyChild(effect, prop, "value");
+        const SGPropertyNode* pmask
+            = getEffectPropertyChild(effect, prop, "mask");
+        const SGPropertyNode* psfail
+            = getEffectPropertyChild(effect, prop, "stencil-fail");
+        const SGPropertyNode* pzfail
+            = getEffectPropertyChild(effect, prop, "z-fail");
+        const SGPropertyNode* ppass
+            = getEffectPropertyChild(effect, prop, "pass");
+
+        Stencil::Function func = Stencil::ALWAYS;  // Always pass
+        int ref = 0;
+        unsigned int mask = ~0u;  // All bits on
+        Stencil::Operation sfailop = Stencil::KEEP;  // Keep the old values as default
+        Stencil::Operation zfailop = Stencil::KEEP;
+        Stencil::Operation passop = Stencil::KEEP;
+
+        ref_ptr<Stencil> stencilFunc = new Stencil;
+
+        if (pfunction)
+            findAttr(stencilFunction, pfunction, func);
+        if (pvalue)
+            ref = pvalue->getIntValue();
+        if (pmask) 
+            mask = pmask->getIntValue();
+
+        if (psfail)
+            findAttr(stencilOperation, psfail, sfailop);
+        if (pzfail)
+            findAttr(stencilOperation, pzfail, zfailop);
+        if (ppass)
+            findAttr(stencilOperation, ppass, passop);
+
+        // Set the stencil operation
+        stencilFunc->setFunction(func, ref, mask);
+
+        // Set the operation, s-fail, s-pass/z-fail, s-pass/z-pass
+        stencilFunc->setOperation(sfailop, zfailop, passop);
+
+        // Add the operation to pass
+        pass->setAttributeAndModes(stencilFunc.get());
+    }
+};
+
+InstallAttributeBuilder<StencilBuilder> installStencil("stencil");
+
+
 EffectNameValue<AlphaFunc::ComparisonFunction> alphaComparisonInit[] =
 {
     {"never", AlphaFunc::NEVER},
@@ -484,7 +605,7 @@ alphaComparison(alphaComparisonInit);
 struct AlphaTestBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options)
+                        const SGReaderWriterXMLOptions* options)
     {
         if (!isAttributeActive(effect, prop))
             return;
@@ -534,10 +655,50 @@ InstallAttributeBuilder<AlphaTestBuilder> installAlphaTest("alpha-test");
 
 InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
 
-typedef map<string, ref_ptr<Program> > ProgramMap;
+// Shader key, used both for shaders with relative and absolute names
+typedef pair<string, Shader::Type> ShaderKey;
+
+struct ProgramKey
+{
+    typedef pair<string, int> AttribKey;
+    osgDB::FilePathList paths;
+    vector<ShaderKey> shaders;
+    vector<AttribKey> attributes;
+    struct EqualTo
+    {
+        bool operator()(const ProgramKey& lhs, const ProgramKey& rhs) const
+        {
+            return (lhs.paths.size() == rhs.paths.size()
+                    && equal(lhs.paths.begin(), lhs.paths.end(),
+                             rhs.paths.begin())
+                    && lhs.shaders.size() == rhs.shaders.size()
+                    && equal (lhs.shaders.begin(), lhs.shaders.end(),
+                              rhs.shaders.begin())
+                    && lhs.attributes.size() == rhs.attributes.size()
+                    && equal(lhs.attributes.begin(), lhs.attributes.end(),
+                             rhs.attributes.begin()));
+        }
+    };
+};
+
+size_t hash_value(const ProgramKey& key)
+{
+    size_t seed = 0;
+    boost::hash_range(seed, key.paths.begin(), key.paths.end());
+    boost::hash_range(seed, key.shaders.begin(), key.shaders.end());
+    boost::hash_range(seed, key.attributes.begin(), key.attributes.end());
+    return seed;
+}
+
+// XXX Should these be protected by a mutex? Probably
+
+typedef tr1::unordered_map<ProgramKey, ref_ptr<Program>,
+                           boost::hash<ProgramKey>, ProgramKey::EqualTo>
+ProgramMap;
 ProgramMap programMap;
 
-typedef map<string, ref_ptr<Shader> > ShaderMap;
+typedef tr1::unordered_map<ShaderKey, ref_ptr<Shader>, boost::hash<ShaderKey> >
+ShaderMap;
 ShaderMap shaderMap;
 
 void reload_shaders()
@@ -545,7 +706,7 @@ void reload_shaders()
     for(ShaderMap::iterator sitr = shaderMap.begin(); sitr != shaderMap.end(); ++sitr)
     {
        Shader *shader = sitr->second.get();
-        string fileName = osgDB::findDataFile(sitr->first);
+        string fileName = osgDB::findDataFile(sitr->first.first);
         if (!fileName.empty()) {
            shader->loadShaderSourceFromFile(fileName);
         }
@@ -555,42 +716,55 @@ void reload_shaders()
 struct ShaderProgramBuilder : PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options);
+                        const SGReaderWriterXMLOptions* options);
 };
 
 void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
                                           const SGPropertyNode* prop,
-                                          const osgDB::ReaderWriter::Options*
+                                          const SGReaderWriterXMLOptions*
                                           options)
 {
+    using namespace boost;
     if (!isAttributeActive(effect, prop))
         return;
     PropertyList pVertShaders = prop->getChildren("vertex-shader");
     PropertyList pFragShaders = prop->getChildren("fragment-shader");
-    string programKey;
+    PropertyList pAttributes = prop->getChildren("attribute");
+    ProgramKey prgKey;
     for (PropertyList::iterator itr = pVertShaders.begin(),
              e = pVertShaders.end();
          itr != e;
          ++itr)
-    {
-        programKey += (*itr)->getStringValue();
-        programKey += ";";
-    }
+        prgKey.shaders.push_back(ShaderKey((*itr)->getStringValue(),
+                                           Shader::VERTEX));
     for (PropertyList::iterator itr = pFragShaders.begin(),
              e = pFragShaders.end();
          itr != e;
          ++itr)
-    {
-        programKey += (*itr)->getStringValue();
-        programKey += ";";
+        prgKey.shaders.push_back(ShaderKey((*itr)->getStringValue(),
+                                           Shader::FRAGMENT));
+    for (PropertyList::iterator itr = pAttributes.begin(),
+             e = pAttributes.end();
+         itr != e;
+         ++itr) {
+        const SGPropertyNode* pName = getEffectPropertyChild(effect, *itr,
+                                                             "name");
+        const SGPropertyNode* pIndex = getEffectPropertyChild(effect, *itr,
+                                                              "index");
+        if (!pName || ! pIndex)
+            throw BuilderException("malformed attribute property");
+        prgKey.attributes
+            .push_back(ProgramKey::AttribKey(pName->getStringValue(),
+                                             pIndex->getValue<int>()));
     }
+    if (options)
+        prgKey.paths = options->getDatabasePathList();
     Program* program = 0;
-    ProgramMap::iterator pitr = programMap.find(programKey);
+    ProgramMap::iterator pitr = programMap.find(prgKey);
     if (pitr != programMap.end()) {
         program = pitr->second.get();
     } else {
         program = new Program;
-        program->setName(programKey);
         // Add vertex shaders, then fragment shaders
         PropertyList& pvec = pVertShaders;
         Shader::Type stype = Shader::VERTEX;
@@ -599,24 +773,29 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
                  nameItr != e;
                  ++nameItr) {
                 string shaderName = (*nameItr)->getStringValue();
-                ShaderMap::iterator sitr = shaderMap.find(shaderName);
+                string fileName = osgDB::findDataFile(shaderName, options);
+                if (fileName.empty())
+                    throw BuilderException(string("couldn't find shader ") +
+                                           shaderName);
+                ShaderKey skey(fileName, stype);
+                ShaderMap::iterator sitr = shaderMap.find(skey);
                 if (sitr != shaderMap.end()) {
                     program->addShader(sitr->second.get());
                 } else {
-                    string fileName = osgDB::findDataFile(shaderName, options);
-                    if (!fileName.empty()) {
-                        ref_ptr<Shader> shader = new Shader(stype);
-                        if (shader->loadShaderSourceFromFile(fileName)) {
-                            program->addShader(shader.get());
-                            shaderMap.insert(make_pair(shaderName, shader));
-                        }
+                    ref_ptr<Shader> shader = new Shader(stype);
+                    if (shader->loadShaderSourceFromFile(fileName)) {
+                        program->addShader(shader.get());
+                        shaderMap.insert(ShaderMap::value_type(skey, shader));
                     }
                 }
             }
             pvec = pFragShaders;
             stype = Shader::FRAGMENT;
         }
-        programMap.insert(make_pair(programKey, program));
+        BOOST_FOREACH(const ProgramKey::AttribKey& key, prgKey.attributes) {
+            program->addBindAttribLocation(key.first, key.second);
+        }
+       programMap.insert(ProgramMap::value_type(prgKey, program));
     }
     pass->setAttributeAndModes(program);
 }
@@ -637,7 +816,7 @@ EffectPropertyMap<Uniform::Type> uniformTypes(uniformTypesInit);
 struct UniformBuilder :public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options)
+                        const SGReaderWriterXMLOptions* options)
     {
         if (!isAttributeActive(effect, prop))
             return;
@@ -711,7 +890,7 @@ InstallAttributeBuilder<UniformBuilder> installUniform("uniform");
 struct NameBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options)
+                        const SGReaderWriterXMLOptions* options)
     {
         // name can't use <use>
         string name = prop->getStringValue();
@@ -733,7 +912,7 @@ EffectPropertyMap<PolygonMode::Mode> polygonModeModes(polygonModeModesInit);
 struct PolygonModeBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options)
+                        const SGReaderWriterXMLOptions* options)
     {
         if (!isAttributeActive(effect, prop))
             return;
@@ -757,8 +936,89 @@ struct PolygonModeBuilder : public PassAttributeBuilder
 };
 
 InstallAttributeBuilder<PolygonModeBuilder> installPolygonMode("polygon-mode");
+
+struct VertexProgramTwoSideBuilder : public PassAttributeBuilder
+{
+    void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
+                        const SGReaderWriterXMLOptions* options)
+    {
+        const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
+        if (!realProp)
+            return;
+        pass->setMode(GL_VERTEX_PROGRAM_TWO_SIDE,
+                      (realProp->getValue<bool>()
+                       ? StateAttribute::ON : StateAttribute::OFF));
+    }
+};
+
+InstallAttributeBuilder<VertexProgramTwoSideBuilder>
+installTwoSide("vertex-program-two-side");
+
+struct VertexProgramPointSizeBuilder : public PassAttributeBuilder
+{
+    void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
+                        const SGReaderWriterXMLOptions* options)
+    {
+        const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
+        if (!realProp)
+            return;
+        pass->setMode(GL_VERTEX_PROGRAM_POINT_SIZE,
+                      (realProp->getValue<bool>()
+                       ? StateAttribute::ON : StateAttribute::OFF));
+    }
+};
+
+InstallAttributeBuilder<VertexProgramPointSizeBuilder>
+installPointSize("vertex-program-point-size");
+
+EffectNameValue<Depth::Function> depthFunctionInit[] =
+{
+    {"never", Depth::NEVER},
+    {"less", Depth::LESS},
+    {"equal", Depth::EQUAL},
+    {"lequal", Depth::LEQUAL},
+    {"greater", Depth::GREATER},
+    {"notequal", Depth::NOTEQUAL},
+    {"gequal", Depth::GEQUAL},
+    {"always", Depth::ALWAYS}
+};
+EffectPropertyMap<Depth::Function> depthFunction(depthFunctionInit);
+
+struct DepthBuilder : public PassAttributeBuilder
+{
+    void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
+                        const SGReaderWriterXMLOptions* options)
+    {
+        if (!isAttributeActive(effect, prop))
+            return;
+        ref_ptr<Depth> depth = new Depth;
+        const SGPropertyNode* pfunc
+            = getEffectPropertyChild(effect, prop, "function");
+        if (pfunc) {
+            Depth::Function func = Depth::LESS;
+            findAttr(depthFunction, pfunc, func);
+            depth->setFunction(func);
+        }
+        const SGPropertyNode* pnear
+            = getEffectPropertyChild(effect, prop, "near");
+        if (pnear)
+            depth->setZNear(pnear->getValue<double>());
+        const SGPropertyNode* pfar
+            = getEffectPropertyChild(effect, prop, "far");
+        if (pfar)
+            depth->setZFar(pnear->getValue<double>());
+        const SGPropertyNode* pmask
+            = getEffectPropertyChild(effect, prop, "write-mask");
+        if (pmask)
+            depth->setWriteMask(pmask->getValue<bool>());
+        pass->setAttribute(depth.get());
+    }
+};
+
+InstallAttributeBuilder<DepthBuilder> installDepth("depth");
+
 void buildTechnique(Effect* effect, const SGPropertyNode* prop,
-                    const osgDB::ReaderWriter::Options* options)
+                    const SGReaderWriterXMLOptions* options)
 {
     Technique* tniq = new Technique;
     effect->techniques.push_back(tniq);
@@ -856,22 +1116,68 @@ bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
     } else {
         makeChild(blendNode, "active")->setValue(false);
     }
+    string renderingHint = findName(renderingHints, ss->getRenderingHint());
+    makeChild(paramRoot, "rendering-hint")->setStringValue(renderingHint);
     makeTextureParameters(paramRoot, ss);
     return true;
 }
 
 // Walk the techniques property tree, building techniques and
 // passes.
-bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
+bool Effect::realizeTechniques(const SGReaderWriterXMLOptions* options)
 {
+    if (_isRealized)
+        return true;
     PropertyList tniqList = root->getChildren("technique");
     for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
          itr != e;
          ++itr)
         buildTechnique(this, *itr, options);
+    _isRealized = true;
     return true;
 }
 
+void Effect::InitializeCallback::doUpdate(osg::Node* node, osg::NodeVisitor* nv)
+{
+    EffectGeode* eg = dynamic_cast<EffectGeode*>(node);
+    if (!eg)
+        return;
+    Effect* effect = eg->getEffect();
+    if (!effect)
+        return;
+    SGPropertyNode* root = getPropertyRoot();
+    for (vector<SGSharedPtr<Updater> >::iterator itr = effect->_extraData.begin(),
+             end = effect->_extraData.end();
+         itr != end;
+         ++itr) {
+        InitializeWhenAdded* adder
+            = dynamic_cast<InitializeWhenAdded*>(itr->ptr());
+        if (adder)
+            adder->initOnAdd(effect, root);
+    }
+}
+
+bool Effect::Key::EqualTo::operator()(const Effect::Key& lhs,
+                                      const Effect::Key& rhs) const
+{
+    if (lhs.paths.size() != rhs.paths.size()
+        || !equal(lhs.paths.begin(), lhs.paths.end(), rhs.paths.begin()))
+        return false;
+    if (lhs.unmerged.valid() && rhs.unmerged.valid())
+        return props::Compare()(lhs.unmerged, rhs.unmerged);
+    else
+        return lhs.unmerged == rhs.unmerged;
+}
+
+size_t hash_value(const Effect::Key& key)
+{
+    size_t seed = 0;
+    if (key.unmerged.valid())
+        boost::hash_combine(seed, *key.unmerged);
+    boost::hash_range(seed, key.paths.begin(), key.paths.end());
+    return seed;
+}
+
 bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
 {
     const Effect& effect = static_cast<const Effect&>(obj);