X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Fmaterial%2FEffectBuilder.hxx;h=be4b03452d035e0e89ff8b592bbf78fae61244f5;hb=a050654b4cac8c4813bddfc6e376730339b678b7;hp=831c9fabe078a60f02662e9540f024a7230b624e;hpb=01a896ef5b5894f1026575c0d750fc6d0b07a394;p=simgear.git diff --git a/simgear/scene/material/EffectBuilder.hxx b/simgear/scene/material/EffectBuilder.hxx index 831c9fab..be4b0345 100644 --- a/simgear/scene/material/EffectBuilder.hxx +++ b/simgear/scene/material/EffectBuilder.hxx @@ -18,17 +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. */ @@ -36,6 +45,7 @@ namespace simgear { class Effect; +class Pass; /** * Builder that returns an object, probably an OSG object. @@ -79,55 +89,134 @@ protected: } }; -// Simple tables of strings and constants. The table intialization -// *must* be in alphabetical order. +// Tables of strings and constants. We want to reconstruct the effect +// property tree from OSG state sets, so the tables should be bi-directional. + +// two-way map for building StateSets from property descriptions, and +// vice versa. Mostly copied from the boost documentation. + +namespace effect +{ +using boost::multi_index_container; +using namespace boost::multi_index; + +// tags for accessing both sides of a bidirectional map + +struct from{}; +struct to{}; + template struct EffectNameValue { // Don't use std::pair because we want to use aggregate intialization. - const char* first; T second; - class Compare - { - private: - static bool compare(const char* lhs, const char* rhs) - { - return std::strcmp(lhs, rhs) < 0; - } - public: - bool operator()(const EffectNameValue& lhs, - const EffectNameValue& rhs) const - { - return compare(lhs.first, rhs.first); - } - bool operator()(const char* lhs, const EffectNameValue& rhs) const - { - return compare(lhs, rhs.first); - } - bool operator()(const EffectNameValue& lhs, const char* rhs) const - { - return compare (lhs.first, rhs); - } - }; }; -template -bool findAttr(const ENV (&attrs)[N], const SGPropertyNode* prop, T& result) +// The class template bidirectional_map wraps the specification +// of a bidirectional map based on multi_index_container. + +template +struct bidirectional_map +{ + typedef std::pair value_type; + + /* A bidirectional map can be simulated as a multi_index_container + * of pairs of (FromType,ToType) with two unique indices, one + * for each member of the pair. + */ + + typedef multi_index_container< + value_type, + indexed_by< + ordered_unique< + tag, member >, + ordered_unique< + tag, member > + > + > type; +}; + +template +struct EffectPropertyMap +{ + typedef typename bidirectional_map::type BMap; + BMap _map; + template + EffectPropertyMap(const EffectNameValue (&attrs)[N]); +}; + +template +template +EffectPropertyMap::EffectPropertyMap(const EffectNameValue (&attrs)[N]) +{ + for (int i = 0; i < N; ++i) + _map.insert(typename BMap::value_type(attrs[i].first, attrs[i].second)); +} + +class BuilderException : public sg_exception +{ +public: + BuilderException(); + BuilderException(const char* message, const char* origin = 0); + BuilderException(const std::string& message, const std::string& = ""); + virtual ~BuilderException() throw(); +}; +} + +template +void findAttr(const effect::EffectPropertyMap& pMap, + const char* name, + T& result) +{ + using namespace effect; + typename EffectPropertyMap::BMap::iterator itr + = pMap._map.get().find(name); + if (itr == pMap._map.end()) { + throw effect::BuilderException(string("findAttr: could not find attribute ") + + string(name)); + } else { + result = itr->second; + } +} + +template +inline void findAttr(const effect::EffectPropertyMap& pMap, + const std::string& name, + T& result) +{ + findAttr(pMap, name.c_str(), result); +} + +template +void findAttr(const effect::EffectPropertyMap& pMap, + const SGPropertyNode* prop, + T& result) { if (!prop) - return false; + throw effect::BuilderException("findAttr: empty property"); const char* name = prop->getStringValue(); if (!name) - return false; - std::pair itrs - = std::equal_range(&attrs[0], &attrs[N], name, typename ENV::Compare()); - if (itrs.first == itrs.second) { - return false; - } else { - result = itrs.first->second; - return true; - } + throw effect::BuilderException("findAttr: no name for lookup"); + findAttr(pMap, name, result); +} + +template +std::string findName(const effect::EffectPropertyMap& pMap, T value) +{ + using namespace effect; + std::string result; + typename EffectPropertyMap::BMap::template index_iterator::type itr + = pMap._map.get().find(value); + if (itr != pMap._map.get().end()) + result = itr->first; + return result; +} + +template +std::string findName(const effect::EffectPropertyMap& pMap, GLenum value) +{ + return findName(pMap, static_cast(value)); } /** @@ -145,13 +234,241 @@ const SGPropertyNode* getEffectPropertyChild(Effect* effect, const SGPropertyNode* prop, const char* name); -class BuilderException : public sg_exception +/** + * 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 PassAttributeBuilder : public SGReferenced { +protected: + typedef std::map > + PassAttrMap; + + struct PassAttrMapSingleton : public simgear::Singleton + { + PassAttrMap passAttrMap; + }; public: - BuilderException(); - BuilderException(const char* message, const char* origin = 0); - BuilderException(const std::string& message, const std::string& = ""); - virtual ~BuilderException() throw(); + virtual void buildAttribute(Effect* effect, Pass* pass, + const SGPropertyNode* prop, + const osgDB::ReaderWriter::Options* options) + = 0; + static PassAttributeBuilder* find(const std::string& str) + { + PassAttrMap::iterator itr + = PassAttrMapSingleton::instance()->passAttrMap.find(str); + if (itr == PassAttrMapSingleton::instance()->passAttrMap.end()) + return 0; + else + return itr->second.ptr(); + } + template friend struct InstallAttributeBuilder; +}; + +template +struct InstallAttributeBuilder +{ + InstallAttributeBuilder(const string& name) + { + PassAttributeBuilder::PassAttrMapSingleton::instance() + ->passAttrMap.insert(make_pair(name, new T)); + } +}; + +// The description of an attribute may exist in a pass' XML, but a +// derived effect might want to disable the attribute altogether. So, +// some attributes have an "active" property; if it exists and is +// 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