From 54c4055af3b106371ed57852c4ad5f1dd087badc Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 16 Oct 2009 12:54:46 +0200 Subject: [PATCH] Effects in models working for transparent materials and chrome animation Implementation of animated effect values via the property system. Add names for TexEnvCombine attributes --- projects/VC7.1/SimGear.vcproj | 15 ++ projects/VC90/SimGear.vcproj | 21 ++ simgear/props/AtomicChangeListener.cxx | 71 +++++++ simgear/props/AtomicChangeListener.hxx | 105 ++++++++++ simgear/props/ExtendedPropertyAdapter.hxx | 72 +++++++ simgear/props/Makefile.am | 7 +- simgear/props/props.hxx | 15 +- simgear/scene/material/Effect.cxx | 23 +++ simgear/scene/material/Effect.hxx | 47 ++++- simgear/scene/material/EffectBuilder.cxx | 16 ++ simgear/scene/material/EffectBuilder.hxx | 226 +++++++++++++++++++++- simgear/scene/material/EffectGeode.cxx | 8 + simgear/scene/material/EffectGeode.hxx | 2 +- simgear/scene/material/TextureBuilder.cxx | 204 +++++++++++++++++++ simgear/scene/material/makeEffect.cxx | 2 +- simgear/scene/model/SGReaderWriterXML.cxx | 50 ++++- simgear/scene/model/model.cxx | 4 +- simgear/scene/util/Makefile.am | 4 +- simgear/scene/util/UpdateOnceCallback.cxx | 39 ++++ simgear/scene/util/UpdateOnceCallback.hxx | 44 +++++ 20 files changed, 958 insertions(+), 17 deletions(-) create mode 100644 simgear/props/AtomicChangeListener.cxx create mode 100644 simgear/props/AtomicChangeListener.hxx create mode 100644 simgear/props/ExtendedPropertyAdapter.hxx create mode 100644 simgear/scene/util/UpdateOnceCallback.cxx create mode 100644 simgear/scene/util/UpdateOnceCallback.hxx 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 #include #include #include +#include #include #include #include +#include +#include #include #include #include #include +#include "Effect.hxx" /** * Support classes for parsing effects. */ @@ -153,15 +158,10 @@ EffectPropertyMap::EffectPropertyMap(const EffectNameValue (&attrs)[N]) template bool findAttr(const effect::EffectPropertyMap& 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::BMap::iterator itr = pMap._map.get().find(name); if (itr == pMap._map.end()) { @@ -172,6 +172,27 @@ bool findAttr(const effect::EffectPropertyMap& pMap, } } +template +inline bool findAttr(const effect::EffectPropertyMap& pMap, + const std::string& name, + T& result) +{ + return findAttr(pMap, name.c_str(), result); +} + +template +bool findAttr(const effect::EffectPropertyMap& 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 std::string findName(const effect::EffectPropertyMap& 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 clause from the global property + * tree. + * @return empty if prop doesn't contain a 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 struct OSGBridge; + +template +struct OSGBridge : public OSGBridge +{ +}; + +template<> +struct OSGBridge +{ + typedef SGVec3d sg_type; + static osg::Vec3f getOsgType(const SGVec3d& val) { return toOsg(val); } +}; + +template<> +struct OSGBridge +{ + typedef SGVec3d sg_type; + static osg::Vec3d getOsgType(const SGVec3d& val) { return toOsg(val); } +}; + +template<> +struct OSGBridge +{ + typedef SGVec4d sg_type; + static osg::Vec4f getOsgType(const SGVec4d& val) { return toOsg(val); } +}; + +template<> +struct OSGBridge +{ + typedef SGVec4d sg_type; + static osg::Vec4d getOsgType(const SGVec4d& val) { return toOsg(val); } +}; + +template +struct OSGFunctor : public OSGBridge +{ + OSGFunctor(Obj* obj, void (Obj::*func)(const OSGParam&)) + : _obj(obj), _func(func) {} + void operator()(const typename OSGBridge::sg_type& val) const + { + ((_obj.get())->*_func)(this->getOsgType(val)); + } + osg::ref_ptr_obj; + void (Obj::*_func)(const OSGParam&); +}; + +template +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()); + } + 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 _obj; + setter_type _setter; + std::string* _propName; +}; + +template +class EffectExtendedPropListener : public InitializeWhenAdded, + public Effect::Updater +{ +public: + template + EffectExtendedPropListener(const Func& func, + const std::string& propName, Itr childNamesBegin, + Itr childNamesEnd) + : _func(func) + { + _propName = new std::string(propName); + _childNames = new std::vector(childNamesBegin, + childNamesEnd); + } + virtual ~EffectExtendedPropListener() + { + delete _propName; + delete _childNames; + } + void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot) + { + SGPropertyNode* parent = propRoot->getNode(*_propName, true); + _propListener + = new ExtendedPropListener(parent, _childNames->begin(), + _childNames->end(), + _func, true); + delete _propName; + _propName = 0; + delete _childNames; + _childNames = 0; + } +private: + std::string* _propName; + std::vector* _childNames; + SGSharedPtr > _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 tag to look at the parameters. Again, if there is a + * value there set it directly. Otherwise, the parameter contains its + * own tag referring to a property in the global property tree; + * install a change listener that will set the attribute when the + * property changes. + */ +template +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()); + } else { + std::string propName = getGlobalProperty(prop); + ScalarChangeListener* listener + = new ScalarChangeListener(obj, setter, + propName); + effect->addUpdater(listener); + } +} + +template +void +initFromParameters(Effect* effect, const SGPropertyNode* prop, ObjType* obj, + void (ObjType::*setter)(const OSGParamType&), + NameItrType nameItr) +{ + typedef typename OSGBridge::sg_type sg_type; + const SGPropertyNode* valProp = getEffectPropertyNode(effect, prop); + if (!valProp) + return; + if (valProp->nChildren() == 0) { + (obj->*setter)(OSGBridge + ::getOsgType(valProp->getValue())); + } else { + string listenPropName = getGlobalProperty(valProp); + if (listenPropName.empty()) + return; + typedef OSGFunctor Functor; + Effect::Updater* listener + = new EffectExtendedPropListener + (Functor(obj, setter), listenPropName, nameItr, + nameItr + props::NumComponents::num_components); + effect->addUpdater(listener); + } +} + +extern const char* colorFields[]; +} } #endif diff --git a/simgear/scene/material/EffectGeode.cxx b/simgear/scene/material/EffectGeode.cxx index 949055b5..aff9aebc 100644 --- a/simgear/scene/material/EffectGeode.cxx +++ b/simgear/scene/material/EffectGeode.cxx @@ -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()) diff --git a/simgear/scene/material/EffectGeode.hxx b/simgear/scene/material/EffectGeode.hxx index ad35beb9..93c552a3 100644 --- a/simgear/scene/material/EffectGeode.hxx +++ b/simgear/scene/material/EffectGeode.hxx @@ -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; diff --git a/simgear/scene/material/TextureBuilder.cxx b/simgear/scene/material/TextureBuilder.cxx index 0db435de..d7382e92 100644 --- a/simgear/scene/material/TextureBuilder.cxx +++ b/simgear/scene/material/TextureBuilder.cxx @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -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 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 combineParams(combineParamInit); + +EffectNameValue 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 sourceParams(sourceParamInit); + +EffectNameValue 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 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()); + } + if ((p = getEffectPropertyChild(effect, envProp, "scale-alpha"))) { + result->setScale_Alpha(p->getValue()); + } +#if 0 + if ((p = getEffectPropertyChild(effect, envProp, "constant-color"))) { + SGVec4d color = p->getValue(); + result->setConstantColor(toOsg(color)); + } else if ((p = getEffectPropertyChild(effect, envProp, + "light-direction"))) { + SGVec3d direction = p->getValue(); + result->setConstantColorAsLightDirection(toOsg(direction)); + } +#endif + const SGPropertyNode* colorNode = envProp->getChild("constant-color"); + if (colorNode) + initFromParameters(effect, colorNode, result, + &TexEnvCombine::setConstantColor, colorFields); + return result; +} + +EffectNameValue 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 tgenModes(tgenModeInit); + +EffectNameValue tgenCoordInit[] = +{ + {"s", TexGen::S}, + {"t", TexGen::T}, + {"r", TexGen::R}, + {"q", TexGen::Q} +}; + +EffectPropertyMap 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(); + result->setPlane(coord, toOsg(plane)); + } + } + } + return result; +} + bool makeTextureParameters(SGPropertyNode* paramRoot, const StateSet* ss) { SGPropertyNode* texUnit = makeChild(paramRoot, "texture"); diff --git a/simgear/scene/material/makeEffect.cxx b/simgear/scene/material/makeEffect.cxx index 56bc1636..59d3b2d9 100644 --- a/simgear/scene/material/makeEffect.cxx +++ b/simgear/scene/material/makeEffect.cxx @@ -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) { diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index c19d7e82..a097b809 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -20,6 +20,12 @@ # include #endif +#include +//yuck +#include + +#include + #include #include #include @@ -132,6 +138,45 @@ public: private: osg::ref_ptr 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 modelWithEffects = instantiateEffects(group.get(), effect_nodes, options.get()); group = static_cast(modelWithEffects.get()); } - std::vector 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, diff --git a/simgear/scene/model/model.cxx b/simgear/scene/model/model.cxx index b9d0d89e..1485173f 100644 --- a/simgear/scene/model/model.cxx +++ b/simgear/scene/model/model.cxx @@ -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 _options; }; -void MakeEffectVisitor::apply(osg::Node& node) +void MakeEffectVisitor::apply(osg::Group& node) { SGPropertyNode_ptr savedEffectRoot; const string& nodeName = node.getName(); diff --git a/simgear/scene/util/Makefile.am b/simgear/scene/util/Makefile.am index d8ca92ad..376f72f9 100644 --- a/simgear/scene/util/Makefile.am +++ b/simgear/scene/util/Makefile.am @@ -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 index 00000000..7130b507 --- /dev/null +++ b/simgear/scene/util/UpdateOnceCallback.cxx @@ -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 + +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 index 00000000..04034844 --- /dev/null +++ b/simgear/scene/util/UpdateOnceCallback.hxx @@ -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 + +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