]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/EffectBuilder.hxx
Effects in models working for transparent materials and chrome animation
[simgear.git] / simgear / scene / material / EffectBuilder.hxx
1 // Copyright (C) 2009  Tim Moore timoore@redhat.com
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Library General Public
5 // License as published by the Free Software Foundation; either
6 // version 2 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Library General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16
17 #ifndef SIMGEAR_EFFECTBUILDER_HXX
18 #define SIMGEAR_EFFECTBUILDER_HXX 1
19
20 #include <algorithm>
21 #include <iterator>
22 #include <map>
23 #include <string>
24 #include <cstring>
25
26 #include <osgDB/Registry>
27
28 #include <boost/bind.hpp>
29 #include <boost/multi_index_container.hpp>
30 #include <boost/multi_index/member.hpp>
31 #include <boost/multi_index/ordered_index.hpp>
32
33 #include <simgear/math/SGMath.hxx>
34 #include <simgear/props/AtomicChangeListener.hxx>
35 #include <simgear/props/props.hxx>
36 #include <simgear/structure/exception.hxx>
37 #include <simgear/structure/SGSharedPtr.hxx>
38 #include <simgear/structure/Singleton.hxx>
39
40 #include "Effect.hxx"
41 /**
42  * Support classes for parsing effects.
43  */
44
45 namespace simgear
46 {
47 class Effect;
48 class Pass;
49
50 /**
51  * Builder that returns an object, probably an OSG object.
52  */
53 template<typename T>
54 class EffectBuilder : public SGReferenced
55 {
56 public:
57     virtual ~EffectBuilder() {}
58     virtual T* build(Effect* effect, const SGPropertyNode*,
59                      const osgDB::ReaderWriter::Options* options) = 0;
60     static T* buildFromType(Effect* effect, const std::string& type,
61                             const SGPropertyNode*props,
62                             const osgDB::ReaderWriter::Options* options)
63     {
64         BuilderMap& builderMap = getMap();
65         typename BuilderMap::iterator iter = builderMap.find(type);
66         if (iter != builderMap.end())
67             return iter->second->build(effect, props, options);
68         else
69             return 0;
70     }
71     struct Registrar;
72     friend struct Registrar;
73     struct Registrar
74     {
75         Registrar(const std::string& type, EffectBuilder* builder)
76         {
77             getMap().insert(std::make_pair(type, builder));
78         }
79     };
80 protected:
81     typedef std::map<std::string, SGSharedPtr<EffectBuilder> > BuilderMap;
82     struct BuilderMapSingleton : public simgear::Singleton<BuilderMapSingleton>
83     {
84         BuilderMap _map;
85     };
86     static BuilderMap& getMap()
87     {
88         return BuilderMapSingleton::instance()->_map;
89     }
90 };
91
92 // Tables of strings and constants. We want to reconstruct the effect
93 // property tree from OSG state sets, so the tables should be bi-directional.
94
95 // two-way map for building StateSets from property descriptions, and
96 // vice versa. Mostly copied from the boost documentation.
97
98 namespace effect
99 {
100 using boost::multi_index_container;
101 using namespace boost::multi_index;
102
103 // tags for accessing both sides of a bidirectional map
104
105 struct from{};
106 struct to{};
107
108 template <typename T>
109 struct EffectNameValue
110 {
111     // Don't use std::pair because we want to use aggregate intialization.
112     const char* first;
113     T second;
114 };
115
116 // The class template bidirectional_map wraps the specification
117 // of a bidirectional map based on multi_index_container.
118
119 template<typename FromType,typename ToType>
120 struct bidirectional_map
121 {
122     typedef std::pair<FromType,ToType> value_type;
123
124     /* A bidirectional map can be simulated as a multi_index_container
125      * of pairs of (FromType,ToType) with two unique indices, one
126      * for each member of the pair.
127      */
128
129     typedef multi_index_container<
130         value_type,
131         indexed_by<
132             ordered_unique<
133                 tag<from>, member<value_type, FromType, &value_type::first> >,
134             ordered_unique<
135                 tag<to>,  member<value_type, ToType, &value_type::second> >
136             >
137         > type;
138 };
139
140 template<typename T>
141 struct EffectPropertyMap
142 {
143     typedef typename bidirectional_map<std::string, T>::type BMap;
144     BMap _map;
145     template<int N>
146     EffectPropertyMap(const EffectNameValue<T> (&attrs)[N]);
147 };
148
149 template<typename T>
150 template<int N>
151 EffectPropertyMap<T>::EffectPropertyMap(const EffectNameValue<T> (&attrs)[N])
152 {
153     for (int i = 0; i < N; ++i)
154         _map.insert(typename BMap::value_type(attrs[i].first, attrs[i].second));
155 }
156
157 }
158
159 template<typename T>
160 bool findAttr(const effect::EffectPropertyMap<T>& pMap,
161               const char* name,
162               T& result)
163 {
164     using namespace effect;
165     typename EffectPropertyMap<T>::BMap::iterator itr
166         = pMap._map.get<from>().find(name);
167     if (itr == pMap._map.end()) {
168         return false;
169     } else {
170         result = itr->second;
171         return true;
172     }
173 }
174
175 template<typename T>
176 inline bool findAttr(const effect::EffectPropertyMap<T>& pMap,
177                      const std::string& name,
178                      T& result)
179 {
180     return findAttr(pMap, name.c_str(), result);
181 }
182
183 template<typename T>
184 bool findAttr(const effect::EffectPropertyMap<T>& pMap,
185               const SGPropertyNode* prop,
186               T& result)
187 {
188     if (!prop)
189         return false;
190     const char* name = prop->getStringValue();
191     if (!name)
192         return false;
193     return findAttr(pMap, name, result);
194 }
195
196 template<typename T>
197 std::string findName(const effect::EffectPropertyMap<T>& pMap, T value)
198 {
199     using namespace effect;
200     std::string result;
201     typename EffectPropertyMap<T>::BMap::template index_iterator<to>::type itr
202         = pMap._map.get<to>().find(value);
203     if (itr != pMap._map.get<to>().end())
204         result = itr->first;
205     return result;
206 }
207
208 template<typename T>
209 std::string findName(const effect::EffectPropertyMap<T>& pMap, GLenum value)
210 {
211     return findName(pMap, static_cast<T>(value));
212 }
213
214 /**
215  * Given a property node from a pass, get its value either from it or
216  * from the effect parameters.
217  */
218
219 const SGPropertyNode* getEffectPropertyNode(Effect* effect,
220                                             const SGPropertyNode* prop);
221 /**
222  * Get a named child property from pass parameters or effect
223  * parameters.
224  */
225 const SGPropertyNode* getEffectPropertyChild(Effect* effect,
226                                              const SGPropertyNode* prop,
227                                              const char* name);
228
229 /**
230  * Get the name of a node mentioned in a <use> clause from the global property
231  * tree.
232  * @return empty if prop doesn't contain a <use> clause; otherwise the
233  * mentioned node name.
234  */
235 std::string getGlobalProperty(const SGPropertyNode* prop);
236
237 class BuilderException : public sg_exception
238 {
239 public:
240     BuilderException();
241     BuilderException(const char* message, const char* origin = 0);
242     BuilderException(const std::string& message, const std::string& = "");
243     virtual ~BuilderException() throw();
244 };
245
246 class PassAttributeBuilder : public SGReferenced
247 {
248 protected:
249     typedef std::map<const std::string, SGSharedPtr<PassAttributeBuilder> >
250     PassAttrMap;
251
252     struct PassAttrMapSingleton : public simgear::Singleton<PassAttrMapSingleton>
253     {
254         PassAttrMap passAttrMap;
255     };
256 public:
257     virtual void buildAttribute(Effect* effect, Pass* pass,
258                                 const SGPropertyNode* prop,
259                                 const osgDB::ReaderWriter::Options* options)
260     = 0;
261     static PassAttributeBuilder* find(const std::string& str)
262     {
263         PassAttrMap::iterator itr
264             = PassAttrMapSingleton::instance()->passAttrMap.find(str);
265         if (itr == PassAttrMapSingleton::instance()->passAttrMap.end())
266             return 0;
267         else
268             return itr->second.ptr();
269     }
270     template<typename T> friend class InstallAttributeBuilder;
271 };
272
273 template<typename T>
274 struct InstallAttributeBuilder
275 {
276     InstallAttributeBuilder(const string& name)
277     {
278         PassAttributeBuilder::PassAttrMapSingleton::instance()
279             ->passAttrMap.insert(make_pair(name, new T));
280     }
281 };
282
283 // The description of an attribute may exist in a pass' XML, but a
284 // derived effect might want to disable the attribute altogether. So,
285 // some attributes have an "active" property; if it exists and is
286 // false, the OSG attribute is not built at all. This is different
287 // from any OSG mode settings that might be around.
288 bool isAttributeActive(Effect* effect, const SGPropertyNode* prop);
289
290 namespace effect
291 {
292 /**
293  * Bridge between types stored in properties and what OSG wants.
294  */
295 template<typename T> struct OSGBridge;
296
297 template<typename T>
298 struct OSGBridge<const T> : public OSGBridge<T>
299 {
300 };
301
302 template<>
303 struct OSGBridge<osg::Vec3f>
304 {
305     typedef SGVec3d sg_type;
306     static osg::Vec3f getOsgType(const SGVec3d& val) { return toOsg(val); }
307 };
308
309 template<>
310 struct OSGBridge<osg::Vec3d>
311 {
312     typedef SGVec3d sg_type;
313     static osg::Vec3d getOsgType(const SGVec3d& val) { return toOsg(val); }
314 };
315
316 template<>
317 struct OSGBridge<osg::Vec4f>
318 {
319     typedef SGVec4d sg_type;
320     static osg::Vec4f getOsgType(const SGVec4d& val) { return toOsg(val); }
321 };
322
323 template<>
324 struct OSGBridge<osg::Vec4d>
325 {
326     typedef SGVec4d sg_type;
327     static osg::Vec4d getOsgType(const SGVec4d& val) { return toOsg(val); }
328 };
329
330 template<typename Obj, typename OSGParam>
331 struct OSGFunctor : public OSGBridge<OSGParam>
332 {
333     OSGFunctor(Obj* obj, void (Obj::*func)(const OSGParam&))
334         : _obj(obj), _func(func) {}
335     void operator()(const typename OSGBridge<OSGParam>::sg_type& val) const
336     {
337         ((_obj.get())->*_func)(this->getOsgType(val));
338     }
339     osg::ref_ptr<Obj>_obj;
340     void (Obj::*_func)(const OSGParam&);
341 };
342
343 template<typename ObjType, typename OSGParamType>
344 class ScalarChangeListener
345     : public SGPropertyChangeListener, public InitializeWhenAdded,
346       public Effect::Updater
347 {
348 public:
349     typedef void (ObjType::*setter_type)(const OSGParamType);
350     ScalarChangeListener(ObjType* obj, setter_type setter,
351                          const std::string& propName)
352         : _obj(obj), _setter(setter)
353     {
354         _propName = new std::string(propName);
355     }
356     virtual ~ScalarChangeListener()
357     {
358         delete _propName;
359         _propName = 0;
360     }
361     void valueChanged(SGPropertyNode* node)
362     {
363         _obj->*setter(node->getValue<OSGParamType>());
364     }
365     void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot)
366     {
367         SGPropertyNode* listenProp = makeNode(propRoot, *_propName);
368         delete _propName;
369         _propName = 0;
370         if (listenProp)
371             listenProp->addChangeListener(this, true);
372     }
373 private:
374     osg::ref_ptr<ObjType> _obj;
375     setter_type _setter;
376     std::string* _propName;
377 };
378
379 template<typename T, typename Func>
380 class EffectExtendedPropListener : public InitializeWhenAdded,
381                                    public Effect::Updater
382 {
383 public:
384     template<typename Itr>
385     EffectExtendedPropListener(const Func& func,
386                                const std::string& propName, Itr childNamesBegin,
387                                Itr childNamesEnd)
388         : _func(func)
389     {
390         _propName = new std::string(propName);
391         _childNames = new std::vector<std::string>(childNamesBegin,
392                                                    childNamesEnd);
393     }
394     virtual ~EffectExtendedPropListener()
395     {
396         delete _propName;
397         delete _childNames;
398     }
399     void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot)
400     {
401         SGPropertyNode* parent = propRoot->getNode(*_propName, true);
402         _propListener
403             = new ExtendedPropListener<T, Func>(parent, _childNames->begin(),
404                                                 _childNames->end(),
405                                                 _func, true);
406         delete _propName;
407         _propName = 0;
408         delete _childNames;
409         _childNames = 0;
410     }
411 private:
412     std::string* _propName;
413     std::vector<std::string>* _childNames;
414     SGSharedPtr<ExtendedPropListener<T, Func> > _propListener;
415     Func _func;
416 };
417
418 /**
419  * Initialize the value and the possible updating of an effect
420  * attribute. If the value is specified directly, set it. Otherwise,
421  * use the <use> tag to look at the parameters. Again, if there is a
422  * value there set it directly. Otherwise, the parameter contains its
423  * own <use> tag referring to a property in the global property tree;
424  * install a change listener that will set the attribute when the
425  * property changes.
426  */
427 template<typename ObjType, typename OSGParamType>
428 void
429 initFromParameters(Effect* effect, const SGPropertyNode* prop, ObjType* obj,
430                    void (ObjType::*setter)(const OSGParamType))
431 {
432     const SGPropertyNode* valProp = getEffectPropertyNode(effect, prop);
433     if (!valProp)
434         return;
435     if (valProp->nChildren() == 0) {
436         obj->*setter(valProp->getValue<OSGParamType>());
437     } else {
438         std::string propName = getGlobalProperty(prop);
439         ScalarChangeListener<ObjType, OSGParamType>* listener
440             = new ScalarChangeListener<ObjType, OSGParamType>(obj, setter,
441                                                               propName);
442         effect->addUpdater(listener);
443     }
444 }
445
446 template<typename ObjType, typename OSGParamType, typename NameItrType>
447 void
448 initFromParameters(Effect* effect, const SGPropertyNode* prop, ObjType* obj,
449                    void (ObjType::*setter)(const OSGParamType&),
450                    NameItrType nameItr)
451 {
452     typedef typename OSGBridge<OSGParamType>::sg_type sg_type;
453     const SGPropertyNode* valProp = getEffectPropertyNode(effect, prop);
454     if (!valProp)
455         return;
456     if (valProp->nChildren() == 0) {
457         (obj->*setter)(OSGBridge<OSGParamType>
458                      ::getOsgType(valProp->getValue<sg_type>()));
459     } else {
460         string listenPropName = getGlobalProperty(valProp);
461         if (listenPropName.empty())
462             return;
463         typedef OSGFunctor<ObjType, OSGParamType> Functor;
464         Effect::Updater* listener
465             =  new EffectExtendedPropListener<sg_type, Functor>
466             (Functor(obj, setter), listenPropName, nameItr,
467              nameItr + props::NumComponents<sg_type>::num_components);
468         effect->addUpdater(listener);
469     }
470 }
471
472 extern const char* colorFields[];
473 }
474 }
475 #endif