]> git.mxchange.org Git - simgear.git/commitdiff
Effects in models working for transparent materials and chrome animation
authorTim Moore <timoore@redhat.com>
Fri, 16 Oct 2009 10:54:46 +0000 (12:54 +0200)
committerTim Moore <timoore@redhat.com>
Fri, 13 Nov 2009 21:41:11 +0000 (22:41 +0100)
Implementation of animated effect values via the property system.

Add names for TexEnvCombine attributes

20 files changed:
projects/VC7.1/SimGear.vcproj
projects/VC90/SimGear.vcproj
simgear/props/AtomicChangeListener.cxx [new file with mode: 0644]
simgear/props/AtomicChangeListener.hxx [new file with mode: 0644]
simgear/props/ExtendedPropertyAdapter.hxx [new file with mode: 0644]
simgear/props/Makefile.am
simgear/props/props.hxx
simgear/scene/material/Effect.cxx
simgear/scene/material/Effect.hxx
simgear/scene/material/EffectBuilder.cxx
simgear/scene/material/EffectBuilder.hxx
simgear/scene/material/EffectGeode.cxx
simgear/scene/material/EffectGeode.hxx
simgear/scene/material/TextureBuilder.cxx
simgear/scene/material/makeEffect.cxx
simgear/scene/model/SGReaderWriterXML.cxx
simgear/scene/model/model.cxx
simgear/scene/util/Makefile.am
simgear/scene/util/UpdateOnceCallback.cxx [new file with mode: 0644]
simgear/scene/util/UpdateOnceCallback.hxx [new file with mode: 0644]

index 1139bc366ee7baf720d70b2a5812973a957b0e1f..783ac4d05ace81dfda35c470e7f252238c4dba94 100755 (executable)
                <Filter
                        Name="Lib_sgprops"
                        Filter="">
+                       <File
+                               RelativePath="..\..\simgear\props\AtomicChangeListener.cxx">
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\props\AtomicChangeListener.hxx">
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\props\ExtendedPropertyAdapter.hxx">
+                       </File>
                        <File
                                RelativePath="..\..\simgear\props\condition.cxx">
                        </File>
                        <File
                                RelativePath="..\..\simgear\scene\util\StateAttributeFactory.hxx">
                        </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\UpdateOnceCallback.cxx">
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\UpdateOnceCallback.hxx">
+                       </File>
                </Filter>
                <Filter
                        Name="Lib_sgbvh"
index 95d18ebbbf3d54514c03efb69f69ed7c2d87da42..14805d89dfaea63584effba876622508d67e2825 100644 (file)
                <Filter
                        Name="Lib_sgprops"
                        >
+                       <File
+                               RelativePath="..\..\simgear\props\AtomicChangeListener.cxx"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\props\AtomicChangeListener.hxx"
+                               >
+                       </File>
                        <File
                                RelativePath="..\..\simgear\props\condition.cxx"
                                >
                                RelativePath="..\..\simgear\props\condition.hxx"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\simgear\props\ExtendedPropertyAdapter.hxx"
+                               >
+                       </File>
+
                        <File
                                RelativePath="..\..\simgear\props\props.cxx"
                                >
                                RelativePath="..\..\simgear\scene\util\StateAttributeFactory.hxx"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\UpdateOnceCallback.cxx"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\UpdateOnceCallback.hxx"
+                               >
+                       </File>
                </Filter>
                <Filter
                        Name="Lib_sgbvh"
diff --git a/simgear/props/AtomicChangeListener.cxx b/simgear/props/AtomicChangeListener.cxx
new file mode 100644 (file)
index 0000000..c2e08ac
--- /dev/null
@@ -0,0 +1,71 @@
+#include "AtomicChangeListener.hxx"
+
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+#include <boost/bind.hpp>
+
+#include <simgear/structure/Singleton.hxx>
+
+namespace simgear
+{
+using namespace std;
+
+MultiChangeListener::MultiChangeListener()
+{
+}
+
+void MultiChangeListener::valueChanged()
+{
+    valueChangedImplementation();
+}
+
+void MultiChangeListener::valueChangedImplementation()
+{
+}
+
+AtomicChangeListener::AtomicChangeListener(std::vector<SGPropertyNode*>& nodes)
+    :  _dirty(false), _valid(true)
+{
+    listenToProperties(nodes.begin(), nodes.end());
+}
+
+void AtomicChangeListener::unregister_property(SGPropertyNode* node)
+{
+    _valid = false;
+    // not necessary, but good hygine
+    vector<SGPropertyNode*>::iterator itr
+        = find(_watched.begin(), _watched.end(), node);
+    if (itr != _watched.end())
+        *itr = 0;
+    MultiChangeListener::unregister_property(node);
+}
+
+void AtomicChangeListener::fireChangeListeners()
+{
+    vector<SGSharedPtr<AtomicChangeListener> >& listeners
+        = ListenerListSingleton::instance()->listeners;
+    for (vector<SGSharedPtr<AtomicChangeListener> >::iterator itr = listeners.begin(),
+             end = listeners.end();
+         itr != end;
+         ++itr) {
+        (*itr)->valuesChanged();
+        (*itr)->_dirty = false;
+    }
+    listeners.clear();
+}
+
+void AtomicChangeListener::valueChangedImplementation()
+{
+    if (!_dirty) {
+        _dirty = true;
+        if (_valid)
+            ListenerListSingleton::instance()->listeners.push_back(this);
+    }
+}
+
+void AtomicChangeListener::valuesChanged()
+{
+}
+}
diff --git a/simgear/props/AtomicChangeListener.hxx b/simgear/props/AtomicChangeListener.hxx
new file mode 100644 (file)
index 0000000..a5ad7f6
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef SIMGEAR_ATOMICCHANGELISTENER_HXX
+#define SIMGEAR_ATOMICCHANGELISTENER_HXX 1
+
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+#include <boost/bind.hpp>
+
+#include <simgear/structure/Singleton.hxx>
+
+#include "props.hxx"
+#include "ExtendedPropertyAdapter.hxx"
+
+namespace simgear
+{
+// Performs an action when one of several nodes changes
+class MultiChangeListener : private SGPropertyChangeListener
+{
+public:
+    MultiChangeListener();
+    template<typename Pitr>
+    void listenToProperties(Pitr propsBegin, Pitr propsEnd)
+    {
+        for (Pitr itr = propsBegin, end = propsEnd; itr != end; ++itr)
+            (*itr)->addChangeListener(this);
+    }
+    void valueChanged();
+    using SGPropertyChangeListener::unregister_property;
+private:
+    virtual void valueChangedImplementation();
+
+};
+
+class AtomicChangeListener : public MultiChangeListener,
+                             public virtual SGReferenced
+{
+public:
+    AtomicChangeListener(std::vector<SGPropertyNode*>& nodes);
+    /**
+     * Lookup / create child nodes from their relative names.
+     */
+    template<typename Itr>
+    AtomicChangeListener(SGPropertyNode* parent, Itr childNamesBegin,
+                         Itr childNamesEnd)
+        : _dirty(false), _valid(true)
+    {
+        using namespace std;
+        for (Itr itr = childNamesBegin, end = childNamesEnd;
+             itr != end;
+             ++itr)
+            _watched.push_back(makeNode(parent, *itr));
+        listenToProperties(_watched.begin(), _watched.end());
+    }
+    bool isDirty() { return _dirty; }
+    bool isValid() { return _valid; }
+    void unregister_property(SGPropertyNode* node);
+    static void fireChangeListeners();
+private:
+    virtual void valueChangedImplementation();
+    virtual void valuesChanged();
+    bool _dirty;
+    bool _valid;
+    struct ListenerListSingleton : public Singleton<ListenerListSingleton>
+    {
+        std::vector<SGSharedPtr<AtomicChangeListener> > listeners;
+    };
+protected:
+    std::vector<SGPropertyNode*> _watched;
+};
+
+template<typename T, typename Func>
+class ExtendedPropListener : public AtomicChangeListener
+{
+public:
+    ExtendedPropListener(std::vector<SGPropertyNode*>& nodes, const Func& func,
+                         bool initial = false)
+        : AtomicChangeListener(nodes), _func(func)
+    {
+        if (initial)
+            valuesChanged();
+
+    }
+    template<typename Itr>
+    ExtendedPropListener(SGPropertyNode* parent, Itr childNamesBegin,
+                         Itr childNamesEnd, const Func& func,
+                         bool initial = false)
+        : AtomicChangeListener(parent, childNamesBegin, childNamesEnd),
+          _func(func)
+    {
+        if (initial)
+            valuesChanged();
+    }
+    virtual void valuesChanged()
+    {
+        ExtendedPropertyAdapter<T, std::vector<SGPropertyNode*> > adaptor(_watched);
+        T val = adaptor();
+        _func(val);
+    }
+private:
+    Func _func;
+};
+
+}
+#endif
diff --git a/simgear/props/ExtendedPropertyAdapter.hxx b/simgear/props/ExtendedPropertyAdapter.hxx
new file mode 100644 (file)
index 0000000..064f407
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef SIMGEAR_EXTENDEDPROPERTYADAPTER_HXX
+#define SIMGEAR_EXTENDEDPROPERTYADAPTER_HXX 1
+
+#include <algorithm>
+
+#include <boost/bind.hpp>
+
+#include <simgear/math/SGMath.hxx>
+#include <simgear/structure/exception.hxx>
+
+#include "props.hxx"
+
+namespace simgear
+{
+
+namespace props
+{
+// This should be in simgear/math/SGVec.hxx and friends
+
+template<typename T> struct NumComponents;
+
+template<> struct NumComponents<SGVec3d>
+{
+    enum { num_components = 3 };
+};
+
+template<> struct NumComponents<SGVec4d>
+{
+    enum { num_components = 4 };
+};
+
+}
+
+template<typename T, typename NodeContainer>
+class ExtendedPropertyAdapter
+{
+public:
+    enum { num_components = props::NumComponents<T>::num_components };
+    ExtendedPropertyAdapter(const NodeContainer& elements)
+        : _elements(elements)
+    {
+    }
+    T operator()() const
+    {
+        T result;
+        if (_elements.size() < num_components)
+            throw sg_exception();
+        for (int i = 0; i < num_components; ++i)
+            result[i] = _elements[i]->getValue<double>();
+        return result;
+    }
+    void set(const T& val)
+    {
+        if (_elements.size() < num_components)
+            throw sg_exception();
+        for (int i = 0; i < num_components; ++i)
+            _elements[i]->setValue(val[i]);
+    }
+private:
+    const NodeContainer& _elements;
+};
+
+template<typename InIterator, typename OutIterator>
+inline void makeChildList(SGPropertyNode* prop, InIterator inBegin,
+                          InIterator inEnd, OutIterator outBegin)
+{
+    std::transform(inBegin, inEnd, outBegin,
+                   boost::bind(static_cast<SGPropertyNode* (SGPropertyNode::*)(const char*, int, bool)>(&SGPropertyNode::getChild), prop, _1, 0, true));
+}
+
+}
+#endif
index 8e726f15a96a822ad73b25c2740923fdd90ab82e..acf5201711ec4dfc6eda70fd44fa4af07335467d 100644 (file)
@@ -5,12 +5,15 @@ lib_LIBRARIES = libsgprops.a
 include_HEADERS = \
        condition.hxx \
        props.hxx \
-       props_io.hxx
+       props_io.hxx \
+       AtomicChangeListener.hxx \
+       ExtendedPropertyAdapter.hxx
 
 libsgprops_a_SOURCES = \
        condition.cxx \
        props.cxx \
-       props_io.cxx
+       props_io.cxx \
+       AtomicChangeListener.cxx
 
 noinst_PROGRAMS = props_test
 
index ee2e6b6f25f75da225ac2b21088f7ce5cc5f2222..e32d9a64d0811fcb5837220cb073b6c5f1203737 100644 (file)
@@ -1927,13 +1927,26 @@ inline bool SGPropertyNode::setValue(const T& val,
 }
 
 /**
- * Utility function for creation of a child property node
+ * Utility function for creation of a child property node.
  */
 inline SGPropertyNode* makeChild(SGPropertyNode* parent, const char* name,
                                  int index = 0)
 {
     return parent->getChild(name, index, true);
 }
+
+/**
+ * Utility function for creation of a child property node using a
+ * relative path.
+ */
+namespace simgear
+{
+template<typename StringType>
+inline SGPropertyNode* makeNode(SGPropertyNode* parent, const StringType& name)
+{
+    return parent->getNode(name, true);
+}
+}
 #endif // __PROPS_HXX
 
 // end of props.hxx
index 0ec20275611edb7a59e3ab09b3fe1c8d669252cd..d75a1ac4bb14365e7755ee496e3101a8c910b14a 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "Effect.hxx"
 #include "EffectBuilder.hxx"
+#include "EffectGeode.hxx"
 #include "Technique.hxx"
 #include "Pass.hxx"
 #include "TextureBuilder.hxx"
@@ -856,6 +857,8 @@ 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;
 }
@@ -872,6 +875,26 @@ bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
     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_writeLocalData(const Object& obj, osgDB::Output& fw)
 {
     const Effect& effect = static_cast<const Effect&>(obj);
index 594d02ebeb7580fe7715e5d470c158cdc784502c..c3436c432f6eb2453b377206f17556ed4cf143e8 100644 (file)
@@ -24,6 +24,7 @@
 #include <osgDB/ReaderWriter>
 
 #include <simgear/props/props.hxx>
+#include <simgear/scene/util/UpdateOnceCallback.hxx>
 
 namespace osg
 {
@@ -40,6 +41,32 @@ class CullVisitor;
 namespace simgear
 {
 class Technique;
+class Effect;
+
+/**
+ * Object to be initialized at some point after an effect -- and its
+ * containing effect geode -- are hooked into the scene graph. Some
+ * things, like manipulations of the global property tree, are are
+ * only safe in the update process.
+ */
+
+class InitializeWhenAdded
+{
+public:
+    InitializeWhenAdded() : _initialized(false) {};
+    virtual ~InitializeWhenAdded() {};
+    void initOnAdd(Effect* effect, SGPropertyNode* propRoot)
+    {
+        if (!_initialized) {
+            initOnAddImpl(effect, propRoot);
+            _initialized = true;
+        }
+    }
+    bool getInitialized() const { return _initialized; }
+private:
+    virtual void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot) = 0;
+    bool _initialized;
+};
 
 class Effect : public osg::Object
 {
@@ -56,12 +83,30 @@ public:
     Technique* chooseTechnique(osg::RenderInfo* renderInfo);
     virtual void resizeGLObjectBuffers(unsigned int maxSize);
     virtual void releaseGLObjects(osg::State* state = 0) const;
-    /*
+    /**
      * Build the techniques from the effect properties.
      */
     bool realizeTechniques(const osgDB::ReaderWriter::Options* options = 0);
+    /**
+     * Updaters that should be derefed when the effect is
+     * deleted. Updaters arrange to be run by listening on properties
+     * or something.
+     */
+    struct Updater : public virtual SGReferenced
+    {
+        virtual ~Updater() {}
+    };
+    void addUpdater(Updater* data) { _extraData.push_back(data); }
+    // Callback that is added to the effect geode to initialize the
+    // effect.
+    friend struct InitializeCallback;
+    struct InitializeCallback : public UpdateOnceCallback
+    {
+        void doUpdate(osg::Node* node, osg::NodeVisitor* nv);
+    };
     
 protected:
+    std::vector<SGSharedPtr<Updater> > _extraData;
     ~Effect();
 };
 Effect* makeEffect(const std::string& name,
index a485f110ed27261bba4d9498960c4070f647caa4..14a280615d87269263850bcd08e3d87ebdfe6c78 100644 (file)
@@ -2,6 +2,8 @@
 #  include <simgear_config.h>
 #endif
 
+#include <simgear/scene/tgdb/userdata.hxx>
+
 #include <simgear/math/SGMath.hxx>
 
 #include "EffectBuilder.hxx"
@@ -39,6 +41,16 @@ const SGPropertyNode* getEffectPropertyChild(Effect* effect,
         return getEffectPropertyNode(effect, child);
 }
 
+string getGlobalProperty(const SGPropertyNode* prop)
+{
+    if (!prop)
+        return string();
+    const SGPropertyNode* useProp = prop->getChild("use");
+    if (!useProp)
+        return string();
+    return useProp->getStringValue();
+}
+
 BuilderException::BuilderException()
 {
 }
@@ -66,4 +78,8 @@ bool isAttributeActive(Effect* effect, const SGPropertyNode* prop)
     return !activeProp || activeProp->getValue<bool>();
 }
 
+namespace effect
+{
+const char* colorFields[] = {"red", "green", "blue", "alpha"};
+}
 }
index 4b2ebad3eb029791deceb23f1f43b672e3bd31ca..69e926aed989ef53d58600e0840919da9dafd9a1 100644 (file)
 #define SIMGEAR_EFFECTBUILDER_HXX 1
 
 #include <algorithm>
+#include <iterator>
 #include <map>
 #include <string>
 #include <cstring>
 
 #include <osgDB/Registry>
 
+#include <boost/bind.hpp>
 #include <boost/multi_index_container.hpp>
 #include <boost/multi_index/member.hpp>
 #include <boost/multi_index/ordered_index.hpp>
 
+#include <simgear/math/SGMath.hxx>
+#include <simgear/props/AtomicChangeListener.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/structure/exception.hxx>
 #include <simgear/structure/SGSharedPtr.hxx>
 #include <simgear/structure/Singleton.hxx>
 
+#include "Effect.hxx"
 /**
  * Support classes for parsing effects.
  */
@@ -153,15 +158,10 @@ EffectPropertyMap<T>::EffectPropertyMap(const EffectNameValue<T> (&attrs)[N])
 
 template<typename T>
 bool findAttr(const effect::EffectPropertyMap<T>& pMap,
-              const SGPropertyNode* prop,
+              const char* name,
               T& result)
 {
     using namespace effect;
-    if (!prop)
-        return false;
-    const char* name = prop->getStringValue();
-    if (!name)
-        return false;
     typename EffectPropertyMap<T>::BMap::iterator itr
         = pMap._map.get<from>().find(name);
     if (itr == pMap._map.end()) {
@@ -172,6 +172,27 @@ bool findAttr(const effect::EffectPropertyMap<T>& pMap,
     }
 }
 
+template<typename T>
+inline bool findAttr(const effect::EffectPropertyMap<T>& pMap,
+                     const std::string& name,
+                     T& result)
+{
+    return findAttr(pMap, name.c_str(), result);
+}
+
+template<typename T>
+bool findAttr(const effect::EffectPropertyMap<T>& pMap,
+              const SGPropertyNode* prop,
+              T& result)
+{
+    if (!prop)
+        return false;
+    const char* name = prop->getStringValue();
+    if (!name)
+        return false;
+    return findAttr(pMap, name, result);
+}
+
 template<typename T>
 std::string findName(const effect::EffectPropertyMap<T>& pMap, T value)
 {
@@ -205,6 +226,14 @@ const SGPropertyNode* getEffectPropertyChild(Effect* effect,
                                              const SGPropertyNode* prop,
                                              const char* name);
 
+/**
+ * Get the name of a node mentioned in a <use> clause from the global property
+ * tree.
+ * @return empty if prop doesn't contain a <use> clause; otherwise the
+ * mentioned node name.
+ */
+std::string getGlobalProperty(const SGPropertyNode* prop);
+
 class BuilderException : public sg_exception
 {
 public:
@@ -257,5 +286,190 @@ struct InstallAttributeBuilder
 // false, the OSG attribute is not built at all. This is different
 // from any OSG mode settings that might be around.
 bool isAttributeActive(Effect* effect, const SGPropertyNode* prop);
+
+namespace effect
+{
+/**
+ * Bridge between types stored in properties and what OSG wants.
+ */
+template<typename T> struct OSGBridge;
+
+template<typename T>
+struct OSGBridge<const T> : public OSGBridge<T>
+{
+};
+
+template<>
+struct OSGBridge<osg::Vec3f>
+{
+    typedef SGVec3d sg_type;
+    static osg::Vec3f getOsgType(const SGVec3d& val) { return toOsg(val); }
+};
+
+template<>
+struct OSGBridge<osg::Vec3d>
+{
+    typedef SGVec3d sg_type;
+    static osg::Vec3d getOsgType(const SGVec3d& val) { return toOsg(val); }
+};
+
+template<>
+struct OSGBridge<osg::Vec4f>
+{
+    typedef SGVec4d sg_type;
+    static osg::Vec4f getOsgType(const SGVec4d& val) { return toOsg(val); }
+};
+
+template<>
+struct OSGBridge<osg::Vec4d>
+{
+    typedef SGVec4d sg_type;
+    static osg::Vec4d getOsgType(const SGVec4d& val) { return toOsg(val); }
+};
+
+template<typename Obj, typename OSGParam>
+struct OSGFunctor : public OSGBridge<OSGParam>
+{
+    OSGFunctor(Obj* obj, void (Obj::*func)(const OSGParam&))
+        : _obj(obj), _func(func) {}
+    void operator()(const typename OSGBridge<OSGParam>::sg_type& val) const
+    {
+        ((_obj.get())->*_func)(this->getOsgType(val));
+    }
+    osg::ref_ptr<Obj>_obj;
+    void (Obj::*_func)(const OSGParam&);
+};
+
+template<typename ObjType, typename OSGParamType>
+class ScalarChangeListener
+    : public SGPropertyChangeListener, public InitializeWhenAdded,
+      public Effect::Updater
+{
+public:
+    typedef void (ObjType::*setter_type)(const OSGParamType);
+    ScalarChangeListener(ObjType* obj, setter_type setter,
+                         const std::string& propName)
+        : _obj(obj), _setter(setter)
+    {
+        _propName = new std::string(propName);
+    }
+    virtual ~ScalarChangeListener()
+    {
+        delete _propName;
+        _propName = 0;
+    }
+    void valueChanged(SGPropertyNode* node)
+    {
+        _obj->*setter(node->getValue<OSGParamType>());
+    }
+    void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot)
+    {
+        SGPropertyNode* listenProp = makeNode(propRoot, *_propName);
+        delete _propName;
+        _propName = 0;
+        if (listenProp)
+            listenProp->addChangeListener(this, true);
+    }
+private:
+    osg::ref_ptr<ObjType> _obj;
+    setter_type _setter;
+    std::string* _propName;
+};
+
+template<typename T, typename Func>
+class EffectExtendedPropListener : public InitializeWhenAdded,
+                                   public Effect::Updater
+{
+public:
+    template<typename Itr>
+    EffectExtendedPropListener(const Func& func,
+                               const std::string& propName, Itr childNamesBegin,
+                               Itr childNamesEnd)
+        : _func(func)
+    {
+        _propName = new std::string(propName);
+        _childNames = new std::vector<std::string>(childNamesBegin,
+                                                   childNamesEnd);
+    }
+    virtual ~EffectExtendedPropListener()
+    {
+        delete _propName;
+        delete _childNames;
+    }
+    void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot)
+    {
+        SGPropertyNode* parent = propRoot->getNode(*_propName, true);
+        _propListener
+            = new ExtendedPropListener<T, Func>(parent, _childNames->begin(),
+                                                _childNames->end(),
+                                                _func, true);
+        delete _propName;
+        _propName = 0;
+        delete _childNames;
+        _childNames = 0;
+    }
+private:
+    std::string* _propName;
+    std::vector<std::string>* _childNames;
+    SGSharedPtr<ExtendedPropListener<T, Func> > _propListener;
+    Func _func;
+};
+
+/**
+ * Initialize the value and the possible updating of an effect
+ * attribute. If the value is specified directly, set it. Otherwise,
+ * use the <use> tag to look at the parameters. Again, if there is a
+ * value there set it directly. Otherwise, the parameter contains its
+ * own <use> tag referring to a property in the global property tree;
+ * install a change listener that will set the attribute when the
+ * property changes.
+ */
+template<typename ObjType, typename OSGParamType>
+void
+initFromParameters(Effect* effect, const SGPropertyNode* prop, ObjType* obj,
+                   void (ObjType::*setter)(const OSGParamType))
+{
+    const SGPropertyNode* valProp = getEffectPropertyNode(effect, prop);
+    if (!valProp)
+        return;
+    if (valProp->nChildren() == 0) {
+        obj->*setter(valProp->getValue<OSGParamType>());
+    } else {
+        std::string propName = getGlobalProperty(prop);
+        ScalarChangeListener<ObjType, OSGParamType>* listener
+            = new ScalarChangeListener<ObjType, OSGParamType>(obj, setter,
+                                                              propName);
+        effect->addUpdater(listener);
+    }
+}
+
+template<typename ObjType, typename OSGParamType, typename NameItrType>
+void
+initFromParameters(Effect* effect, const SGPropertyNode* prop, ObjType* obj,
+                   void (ObjType::*setter)(const OSGParamType&),
+                   NameItrType nameItr)
+{
+    typedef typename OSGBridge<OSGParamType>::sg_type sg_type;
+    const SGPropertyNode* valProp = getEffectPropertyNode(effect, prop);
+    if (!valProp)
+        return;
+    if (valProp->nChildren() == 0) {
+        (obj->*setter)(OSGBridge<OSGParamType>
+                     ::getOsgType(valProp->getValue<sg_type>()));
+    } else {
+        string listenPropName = getGlobalProperty(valProp);
+        if (listenPropName.empty())
+            return;
+        typedef OSGFunctor<ObjType, OSGParamType> Functor;
+        Effect::Updater* listener
+            =  new EffectExtendedPropListener<sg_type, Functor>
+            (Functor(obj, setter), listenPropName, nameItr,
+             nameItr + props::NumComponents<sg_type>::num_components);
+        effect->addUpdater(listener);
+    }
+}
+
+extern const char* colorFields[];
+}
 }
 #endif
index 949055b58f5e61ec3a0efb4ce78fd5aff03ae80c..aff9aebc4469c077f69952db4010199d68a49380 100644 (file)
@@ -44,6 +44,14 @@ EffectGeode::EffectGeode(const EffectGeode& rhs, const osg::CopyOp& copyop) :
 {
 }
 
+void EffectGeode::setEffect(Effect* effect)
+{
+    _effect = effect;
+    if (!_effect)
+        return;
+    addUpdateCallback(new Effect::InitializeCallback);
+}
+
 void EffectGeode::resizeGLObjectBuffers(unsigned int maxSize)
 {
     if (_effect.valid())
index ad35beb95685e483fb12615f4ec010c4b0f7d463..93c552a37558b194bd7d70d6182bed8af202136b 100644 (file)
@@ -31,7 +31,7 @@ public:
                 const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
     META_Node(simgear,EffectGeode);
     Effect* getEffect() const { return _effect.get(); }
-    void setEffect(Effect* effect) { _effect = effect; }
+    void setEffect(Effect* effect);
     virtual void resizeGLObjectBuffers(unsigned int maxSize);
     virtual void releaseGLObjects(osg::State* = 0) const;
     typedef DrawableList::iterator DrawablesIterator;
index 0db435de0835e04e4d491d7c2797ca9d49c424ce..d7382e9219ad969d1a1c14d09d8de3b01eed8152 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <osg/TexEnv>
 #include <osg/TexEnvCombine>
+#include <osg/TexGen>
 #include <osg/Texture1D>
 #include <osg/Texture2D>
 #include <osg/Texture3D>
@@ -48,6 +49,10 @@ using namespace osg;
 
 using namespace effect;
 
+TexEnvCombine* buildTexEnvCombine(Effect* effect,
+                                  const SGPropertyNode* envProp);
+TexGen* buildTexGen(Effect* Effect, const SGPropertyNode* tgenProp);
+
 // Hack to force inclusion of TextureBuilder.cxx in library
 osg::Texture* TextureBuilder::buildFromType(Effect* effect, const string& type,
                                             const SGPropertyNode*props,
@@ -134,6 +139,14 @@ void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
         if (env)
             pass->setTextureAttributeAndModes(unit, env);
     }
+    const SGPropertyNode* combineProp = prop->getChild("texenv-combine");
+    TexEnvCombine* combiner = 0;
+    if (combineProp && ((combiner = buildTexEnvCombine(effect, combineProp))))
+        pass->setTextureAttributeAndModes(unit, combiner);
+    const SGPropertyNode* tgenProp = prop->getChild("texgen");
+    TexGen* tgen = 0;
+    if (tgenProp && (tgen = buildTexGen(effect, tgenProp)))
+        pass->setTextureAttributeAndModes(unit, tgen);
 }
 
 // InstallAttributeBuilder call is in Effect.cxx to force this file to
@@ -366,6 +379,197 @@ namespace
 TextureBuilder::Registrar installNoise("noise", new NoiseBuilder);
 }
 
+EffectNameValue<TexEnvCombine::CombineParam> combineParamInit[] =
+{
+    {"replace", TexEnvCombine::REPLACE},
+    {"modulate", TexEnvCombine::MODULATE},
+    {"add", TexEnvCombine::ADD},
+    {"add-signed", TexEnvCombine::ADD_SIGNED},
+    {"interpolate", TexEnvCombine::INTERPOLATE},
+    {"subtract", TexEnvCombine::SUBTRACT},
+    {"dot3-rgb", TexEnvCombine::DOT3_RGB},
+    {"dot3-rgba", TexEnvCombine::DOT3_RGBA}
+};
+
+EffectPropertyMap<TexEnvCombine::CombineParam> combineParams(combineParamInit);
+
+EffectNameValue<TexEnvCombine::SourceParam> sourceParamInit[] =
+{
+    {"constant", TexEnvCombine::CONSTANT},
+    {"primary_color", TexEnvCombine::PRIMARY_COLOR},
+    {"previous", TexEnvCombine::PREVIOUS},
+    {"texture", TexEnvCombine::TEXTURE},
+    {"texture0", TexEnvCombine::TEXTURE0},
+    {"texture1", TexEnvCombine::TEXTURE1},
+    {"texture2", TexEnvCombine::TEXTURE2},
+    {"texture3", TexEnvCombine::TEXTURE3},
+    {"texture4", TexEnvCombine::TEXTURE4},
+    {"texture5", TexEnvCombine::TEXTURE5},
+    {"texture6", TexEnvCombine::TEXTURE6},
+    {"texture7", TexEnvCombine::TEXTURE7}
+};
+
+EffectPropertyMap<TexEnvCombine::SourceParam> sourceParams(sourceParamInit);
+
+EffectNameValue<TexEnvCombine::OperandParam> opParamInit[] =
+{
+    {"src_color", TexEnvCombine::SRC_COLOR},
+    {"one_minus_src_color", TexEnvCombine::ONE_MINUS_SRC_COLOR},
+    {"src_alpha", TexEnvCombine::SRC_ALPHA},
+    {"one_minus_src_alpha", TexEnvCombine::ONE_MINUS_SRC_ALPHA}
+};
+
+EffectPropertyMap<TexEnvCombine::OperandParam> operandParams(opParamInit);
+
+TexEnvCombine* buildTexEnvCombine(Effect* effect, const SGPropertyNode* envProp)
+{
+    if (!isAttributeActive(effect, envProp))
+        return 0;
+    TexEnvCombine* result = new TexEnvCombine;
+    const SGPropertyNode* p = 0;
+    if ((p = getEffectPropertyChild(effect, envProp, "combine-rgb"))) {
+        TexEnvCombine::CombineParam crgb = TexEnvCombine::MODULATE;
+        findAttr(combineParams, p, crgb);
+        result->setCombine_RGB(crgb);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "combine-alpha"))) {
+        TexEnvCombine::CombineParam calpha = TexEnvCombine::MODULATE;
+        findAttr(combineParams, p, calpha);
+        result->setCombine_RGB(calpha);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source0-rgb"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
+        findAttr(sourceParams, p, source);
+        result->setSource0_RGB(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source1-rgb"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
+        findAttr(sourceParams, p, source);
+        result->setSource1_RGB(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source2-rgb"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
+        findAttr(sourceParams, p, source);
+        result->setSource2_RGB(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source0-alpha"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
+        findAttr(sourceParams, p, source);
+        result->setSource0_Alpha(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source1-alpha"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
+        findAttr(sourceParams, p, source);
+        result->setSource1_Alpha(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source2-alpha"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
+        findAttr(sourceParams, p, source);
+        result->setSource2_Alpha(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand0-rgb"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
+        findAttr(operandParams, p, op);
+        result->setOperand0_RGB(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand1-rgb"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
+        findAttr(operandParams, p, op);
+        result->setOperand1_RGB(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand2-rgb"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
+        findAttr(operandParams, p, op);
+        result->setOperand2_RGB(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand0-alpha"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
+        findAttr(operandParams, p, op);
+        result->setOperand0_Alpha(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand1-alpha"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
+        findAttr(operandParams, p, op);
+        result->setOperand1_Alpha(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand2-alpha"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
+        findAttr(operandParams, p, op);
+        result->setOperand2_Alpha(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "scale-rgb"))) {
+        result->setScale_RGB(p->getValue<float>());
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "scale-alpha"))) {
+        result->setScale_Alpha(p->getValue<float>());
+    }
+#if 0
+    if ((p = getEffectPropertyChild(effect, envProp, "constant-color"))) {
+        SGVec4d color = p->getValue<SGVec4d>();
+        result->setConstantColor(toOsg(color));
+    } else if ((p = getEffectPropertyChild(effect, envProp,
+                                           "light-direction"))) {
+        SGVec3d direction = p->getValue<SGVec3d>();
+        result->setConstantColorAsLightDirection(toOsg(direction));
+    }
+#endif
+    const SGPropertyNode* colorNode = envProp->getChild("constant-color");
+    if (colorNode)
+        initFromParameters(effect, colorNode, result,
+                           &TexEnvCombine::setConstantColor, colorFields);
+    return result;
+}
+
+EffectNameValue<TexGen::Mode> tgenModeInit[] =
+{
+    { "object-linear", TexGen::OBJECT_LINEAR},
+    { "eye-linear", TexGen::EYE_LINEAR},
+    { "sphere-map", TexGen::SPHERE_MAP},
+    { "normal-map", TexGen::NORMAL_MAP},
+    { "reflection-map", TexGen::REFLECTION_MAP}
+};
+
+EffectPropertyMap<TexGen::Mode> tgenModes(tgenModeInit);
+
+EffectNameValue<TexGen::Coord> tgenCoordInit[] =
+{
+    {"s", TexGen::S},
+    {"t", TexGen::T},
+    {"r", TexGen::R},
+    {"q", TexGen::Q}
+};
+
+EffectPropertyMap<TexGen::Coord> tgenCoords(tgenCoordInit);
+
+TexGen* buildTexGen(Effect* effect, const SGPropertyNode* tgenProp)
+{
+    if (!isAttributeActive(effect, tgenProp))
+        return 0;
+    TexGen* result = new TexGen;
+    const SGPropertyNode* p = 0;
+    TexGen::Mode mode = TexGen::OBJECT_LINEAR;
+    if (findAttr(tgenModes, getEffectPropertyChild(effect, tgenProp, "mode"),
+                 mode))
+        result->setMode(mode);
+    const SGPropertyNode* planesNode = tgenProp->getChild("planes");
+    if (planesNode) {
+        for (int i = 0; i < planesNode->nChildren(); ++i) {
+            const SGPropertyNode* planeNode = planesNode->getChild(i);
+            TexGen::Coord coord;
+            if (!findAttr(tgenCoords, planeNode->getName(), coord)) {
+                SG_LOG(SG_INPUT, SG_ALERT, "Unknown TexGen plane "
+                       << planeNode->getName());
+            } else {
+                const SGPropertyNode* realNode
+                    = getEffectPropertyNode(effect, planeNode);
+                SGVec4d plane = realNode->getValue<SGVec4d>();
+                result->setPlane(coord, toOsg(plane));
+            }
+        }
+    }
+    return result;
+}
+
 bool makeTextureParameters(SGPropertyNode* paramRoot, const StateSet* ss)
 {
     SGPropertyNode* texUnit = makeChild(paramRoot, "texture");
index 56bc1636e45d0becece7564f63f573c05b551fa2..59d3b2d9b4c5e3e2f5c2943cc5953c821383287c 100644 (file)
@@ -170,7 +170,7 @@ Effect* makeEffect(SGPropertyNode* prop,
     const SGPropertyNode* inheritProp = prop->getChild("inherits-from");
     Effect* parent = 0;
     if (inheritProp) {
-        parent = makeEffect(inheritProp->getStringValue(), realizeTechniques,
+        parent = makeEffect(inheritProp->getStringValue(), false,
                             options);
         if(parent)
         {
index c19d7e82e7dc690364b2126839e5500b299c888d..a097b809741160c81183044c7ef9151405a90639 100644 (file)
 #  include <simgear_config.h>
 #endif
 
+#include <algorithm>
+//yuck
+#include <cstring>
+
+#include <boost/bind.hpp>
+
 #include <osg/Geode>
 #include <osg/MatrixTransform>
 #include <osgDB/WriteFile>
@@ -132,6 +138,45 @@ public:
 private:
     osg::ref_ptr<osg::Referenced> mReferenced;
 };
+
+void makeEffectAnimations(PropertyList& animation_nodes,
+                          PropertyList& effect_nodes)
+{
+    for (PropertyList::iterator itr = animation_nodes.begin();
+         itr != animation_nodes.end();
+         ++itr) {
+        SGPropertyNode* animProp = itr->ptr();
+        SGPropertyNode* typeProp = animProp->getChild("type");
+        if (!typeProp || strcmp(typeProp->getStringValue(), "shader"))
+            continue;
+        SGPropertyNode* shaderProp = animProp->getChild("shader");
+        if (!shaderProp || strcmp(shaderProp->getStringValue(), "chrome"))
+            continue;
+        *itr = 0;
+        SGPropertyNode* textureProp = animProp->getChild("texture");
+        if (!textureProp)
+            continue;
+        SGPropertyNode_ptr effectProp = new SGPropertyNode();
+        makeChild(effectProp.ptr(), "inherits-from")
+            ->setValue("Effects/chrome");
+        SGPropertyNode* paramsProp = makeChild(effectProp.get(), "parameters");
+        makeChild(paramsProp, "chrome-texture")
+            ->setValue(textureProp->getStringValue());
+        PropertyList objectNameNodes = animProp->getChildren("object-name");
+        for (PropertyList::iterator objItr = objectNameNodes.begin(),
+                 end = objectNameNodes.end();
+             objItr != end;
+            ++objItr)
+            effectProp->addChild("object-name")
+                ->setStringValue((*objItr)->getStringValue());
+        effect_nodes.push_back(effectProp);
+    }
+    animation_nodes.erase(remove_if(animation_nodes.begin(),
+                                    animation_nodes.end(),
+                                    !boost::bind(&SGPropertyNode_ptr::valid,
+                                                 _1)),
+                          animation_nodes.end());
+}
 }
 
 static osg::Node *
@@ -368,13 +413,14 @@ sgLoad3DModel_internal(const string &path,
                         options.get()));
     }
     PropertyList effect_nodes = props->getChildren("effect");
+    PropertyList animation_nodes = props->getChildren("animation");
+    // Some material animations (eventually all) are actually effects.
+    makeEffectAnimations(animation_nodes, effect_nodes);
     {
         ref_ptr<Node> modelWithEffects
             = instantiateEffects(group.get(), effect_nodes, options.get());
         group = static_cast<Group*>(modelWithEffects.get());
     }
-    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(group.get(), animation_nodes[i], prop_root,
index b9d0d89e502574a07696a283a1e0f80b218d8311..1485173f0c86d22d95973fab9e6ba0eeb8efe565 100644 (file)
@@ -212,7 +212,7 @@ public:
         : _options(options)
     {
     }
-    virtual void apply(osg::Node& node);
+    virtual void apply(osg::Group& node);
     virtual void apply(osg::Geode& geode);
     EffectMap& getEffectMap() { return _effectMap; }
     const EffectMap& getEffectMap() const { return _effectMap; }
@@ -227,7 +227,7 @@ protected:
     osg::ref_ptr<const osgDB::ReaderWriter::Options> _options;
 };
 
-void MakeEffectVisitor::apply(osg::Node& node)
+void MakeEffectVisitor::apply(osg::Group& node)
 {
     SGPropertyNode_ptr savedEffectRoot;
     const string& nodeName = node.getName();
index d8ca92ad1ec9630341a194375d6d356002c1ae17..376f72f93cc846e19069daa1af4d869dd308019e 100644 (file)
@@ -21,6 +21,7 @@ include_HEADERS = \
        RenderConstants.hxx \
        SplicingVisitor.hxx \
        StateAttributeFactory.hxx \
+       UpdateOnceCallback.hxx \
        VectorArrayAdapter.hxx
 
 
@@ -35,6 +36,7 @@ libsgutil_a_SOURCES = \
        PrimitiveUtils.cxx \
        SplicingVisitor.cxx \
        StateAttributeFactory.cxx \
-       QuadTreeBuilder.cxx
+       QuadTreeBuilder.cxx \
+       UpdateOnceCallback.cxx
 
 INCLUDES = -I$(top_srcdir)
diff --git a/simgear/scene/util/UpdateOnceCallback.cxx b/simgear/scene/util/UpdateOnceCallback.cxx
new file mode 100644 (file)
index 0000000..7130b50
--- /dev/null
@@ -0,0 +1,39 @@
+// UpdateOnceCallback.hxx
+//
+// Copyright (C) 2009  Tim Moore timoore@redhat.com
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA  02111-1307, USA.
+
+#include "UpdateOnceCallback.hxx"
+
+#include <osg/Node>
+
+namespace simgear
+{
+using namespace osg;
+
+void UpdateOnceCallback::operator()(Node* node, NodeVisitor* nv)
+{
+    doUpdate(node, nv);
+    node->removeUpdateCallback(this);
+    // The callback could be deleted now.
+}
+
+void UpdateOnceCallback::doUpdate(Node* node, NodeVisitor* nv)
+{
+    traverse(node, nv);
+}
+}
diff --git a/simgear/scene/util/UpdateOnceCallback.hxx b/simgear/scene/util/UpdateOnceCallback.hxx
new file mode 100644 (file)
index 0000000..0403484
--- /dev/null
@@ -0,0 +1,44 @@
+// UpdateOnceCallback.hxx
+//
+// Copyright (C) 2009  Tim Moore timoore@redhat.com
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA  02111-1307, USA.
+
+#ifndef SIMGEAR_UPDATEONCECALLBACK_HXX
+#define SIMGEAR_UPDATEONCECALLBACK_HXX 1
+#include <osg/NodeCallback>
+
+namespace simgear
+{
+class UpdateOnceCallback : public osg::NodeCallback
+{
+public:
+    UpdateOnceCallback() {}
+    UpdateOnceCallback(const UpdateOnceCallback& nc, const osg::CopyOp& copyop)
+        : osg::NodeCallback(nc, copyop)
+    {
+    }
+
+    META_Object(simgear,UpdateOnceCallback);
+
+    virtual void doUpdate(osg::Node* node, osg::NodeVisitor* nv);
+    /**
+     * Do not override; use doUpdate instead!
+     */
+    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+};
+}
+#endif