diff --git a/projects/VC7.1/SimGear.vcproj b/projects/VC7.1/SimGear.vcproj
index 1139bc36..783ac4d0 100755
--- a/projects/VC7.1/SimGear.vcproj
+++ b/projects/VC7.1/SimGear.vcproj
@@ -643,6 +643,15 @@
+
+
+
+
+
+
@@ -1249,6 +1258,12 @@
+
+
+
+
+
+
+
+
@@ -991,6 +999,11 @@
RelativePath="..\..\simgear\props\condition.hxx"
>
+
+
+
@@ -1809,6 +1822,14 @@
RelativePath="..\..\simgear\scene\util\StateAttributeFactory.hxx"
>
+
+
+
+
+#include
+#include
+
+#include
+
+#include
+
+namespace simgear
+{
+using namespace std;
+
+MultiChangeListener::MultiChangeListener()
+{
+}
+
+void MultiChangeListener::valueChanged()
+{
+ valueChangedImplementation();
+}
+
+void MultiChangeListener::valueChangedImplementation()
+{
+}
+
+AtomicChangeListener::AtomicChangeListener(std::vector& nodes)
+ : _dirty(false), _valid(true)
+{
+ listenToProperties(nodes.begin(), nodes.end());
+}
+
+void AtomicChangeListener::unregister_property(SGPropertyNode* node)
+{
+ _valid = false;
+ // not necessary, but good hygine
+ vector::iterator itr
+ = find(_watched.begin(), _watched.end(), node);
+ if (itr != _watched.end())
+ *itr = 0;
+ MultiChangeListener::unregister_property(node);
+}
+
+void AtomicChangeListener::fireChangeListeners()
+{
+ vector >& listeners
+ = ListenerListSingleton::instance()->listeners;
+ for (vector >::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
index 00000000..a5ad7f6b
--- /dev/null
+++ b/simgear/props/AtomicChangeListener.hxx
@@ -0,0 +1,105 @@
+#ifndef SIMGEAR_ATOMICCHANGELISTENER_HXX
+#define SIMGEAR_ATOMICCHANGELISTENER_HXX 1
+
+#include
+#include
+#include
+
+#include
+
+#include
+
+#include "props.hxx"
+#include "ExtendedPropertyAdapter.hxx"
+
+namespace simgear
+{
+// Performs an action when one of several nodes changes
+class MultiChangeListener : private SGPropertyChangeListener
+{
+public:
+ MultiChangeListener();
+ template
+ 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& nodes);
+ /**
+ * Lookup / create child nodes from their relative names.
+ */
+ template
+ 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
+ {
+ std::vector > listeners;
+ };
+protected:
+ std::vector _watched;
+};
+
+template
+class ExtendedPropListener : public AtomicChangeListener
+{
+public:
+ ExtendedPropListener(std::vector& nodes, const Func& func,
+ bool initial = false)
+ : AtomicChangeListener(nodes), _func(func)
+ {
+ if (initial)
+ valuesChanged();
+
+ }
+ template
+ 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 > 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
index 00000000..064f4074
--- /dev/null
+++ b/simgear/props/ExtendedPropertyAdapter.hxx
@@ -0,0 +1,72 @@
+#ifndef SIMGEAR_EXTENDEDPROPERTYADAPTER_HXX
+#define SIMGEAR_EXTENDEDPROPERTYADAPTER_HXX 1
+
+#include
+
+#include
+
+#include
+#include
+
+#include "props.hxx"
+
+namespace simgear
+{
+
+namespace props
+{
+// This should be in simgear/math/SGVec.hxx and friends
+
+template struct NumComponents;
+
+template<> struct NumComponents
+{
+ enum { num_components = 3 };
+};
+
+template<> struct NumComponents
+{
+ enum { num_components = 4 };
+};
+
+}
+
+template
+class ExtendedPropertyAdapter
+{
+public:
+ enum { num_components = props::NumComponents::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();
+ 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
+inline void makeChildList(SGPropertyNode* prop, InIterator inBegin,
+ InIterator inEnd, OutIterator outBegin)
+{
+ std::transform(inBegin, inEnd, outBegin,
+ boost::bind(static_cast(&SGPropertyNode::getChild), prop, _1, 0, true));
+}
+
+}
+#endif
diff --git a/simgear/props/Makefile.am b/simgear/props/Makefile.am
index 8e726f15..acf52017 100644
--- a/simgear/props/Makefile.am
+++ b/simgear/props/Makefile.am
@@ -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
diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx
index ee2e6b6f..e32d9a64 100644
--- a/simgear/props/props.hxx
+++ b/simgear/props/props.hxx
@@ -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
+inline SGPropertyNode* makeNode(SGPropertyNode* parent, const StringType& name)
+{
+ return parent->getNode(name, true);
+}
+}
#endif // __PROPS_HXX
// end of props.hxx
diff --git a/simgear/scene/material/Effect.cxx b/simgear/scene/material/Effect.cxx
index 0ec20275..d75a1ac4 100644
--- a/simgear/scene/material/Effect.cxx
+++ b/simgear/scene/material/Effect.cxx
@@ -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(node);
+ if (!eg)
+ return;
+ Effect* effect = eg->getEffect();
+ if (!effect)
+ return;
+ SGPropertyNode* root = getPropertyRoot();
+ for (vector >::iterator itr = effect->_extraData.begin(),
+ end = effect->_extraData.end();
+ itr != end;
+ ++itr) {
+ InitializeWhenAdded* adder
+ = dynamic_cast(itr->ptr());
+ if (adder)
+ adder->initOnAdd(effect, root);
+ }
+}
+
bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
{
const Effect& effect = static_cast(obj);
diff --git a/simgear/scene/material/Effect.hxx b/simgear/scene/material/Effect.hxx
index 594d02eb..c3436c43 100644
--- a/simgear/scene/material/Effect.hxx
+++ b/simgear/scene/material/Effect.hxx
@@ -24,6 +24,7 @@
#include
#include
+#include
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 > _extraData;
~Effect();
};
Effect* makeEffect(const std::string& name,
diff --git a/simgear/scene/material/EffectBuilder.cxx b/simgear/scene/material/EffectBuilder.cxx
index a485f110..14a28061 100644
--- a/simgear/scene/material/EffectBuilder.cxx
+++ b/simgear/scene/material/EffectBuilder.cxx
@@ -2,6 +2,8 @@
# include
#endif
+#include
+
#include
#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();
}
+namespace effect
+{
+const char* colorFields[] = {"red", "green", "blue", "alpha"};
+}
}
diff --git a/simgear/scene/material/EffectBuilder.hxx b/simgear/scene/material/EffectBuilder.hxx
index 4b2ebad3..69e926ae 100644
--- a/simgear/scene/material/EffectBuilder.hxx
+++ b/simgear/scene/material/EffectBuilder.hxx
@@ -18,21 +18,26 @@
#define SIMGEAR_EFFECTBUILDER_HXX 1
#include
+#include
#include