]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/EffectBuilder.hxx
be4b03452d035e0e89ff8b592bbf78fae61244f5
[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 class BuilderException : public sg_exception
158 {
159 public:
160     BuilderException();
161     BuilderException(const char* message, const char* origin = 0);
162     BuilderException(const std::string& message, const std::string& = "");
163     virtual ~BuilderException() throw();
164 };
165 }
166
167 template<typename T>
168 void findAttr(const effect::EffectPropertyMap<T>& pMap,
169               const char* name,
170               T& result)
171 {
172     using namespace effect;
173     typename EffectPropertyMap<T>::BMap::iterator itr
174         = pMap._map.get<from>().find(name);
175     if (itr == pMap._map.end()) {
176         throw effect::BuilderException(string("findAttr: could not find attribute ")
177                                + string(name));
178     } else {
179         result = itr->second;
180     }
181 }
182
183 template<typename T>
184 inline void findAttr(const effect::EffectPropertyMap<T>& pMap,
185                      const std::string& name,
186                      T& result)
187 {
188     findAttr(pMap, name.c_str(), result);
189 }
190
191 template<typename T>
192 void findAttr(const effect::EffectPropertyMap<T>& pMap,
193               const SGPropertyNode* prop,
194               T& result)
195 {
196     if (!prop)
197         throw effect::BuilderException("findAttr: empty property");
198     const char* name = prop->getStringValue();
199     if (!name)
200         throw effect::BuilderException("findAttr: no name for lookup");
201     findAttr(pMap, name, result);
202 }
203
204 template<typename T>
205 std::string findName(const effect::EffectPropertyMap<T>& pMap, T value)
206 {
207     using namespace effect;
208     std::string result;
209     typename EffectPropertyMap<T>::BMap::template index_iterator<to>::type itr
210         = pMap._map.get<to>().find(value);
211     if (itr != pMap._map.get<to>().end())
212         result = itr->first;
213     return result;
214 }
215
216 template<typename T>
217 std::string findName(const effect::EffectPropertyMap<T>& pMap, GLenum value)
218 {
219     return findName(pMap, static_cast<T>(value));
220 }
221
222 /**
223  * Given a property node from a pass, get its value either from it or
224  * from the effect parameters.
225  */
226
227 const SGPropertyNode* getEffectPropertyNode(Effect* effect,
228                                             const SGPropertyNode* prop);
229 /**
230  * Get a named child property from pass parameters or effect
231  * parameters.
232  */
233 const SGPropertyNode* getEffectPropertyChild(Effect* effect,
234                                              const SGPropertyNode* prop,
235                                              const char* name);
236
237 /**
238  * Get the name of a node mentioned in a <use> clause from the global property
239  * tree.
240  * @return empty if prop doesn't contain a <use> clause; otherwise the
241  * mentioned node name.
242  */
243 std::string getGlobalProperty(const SGPropertyNode* prop);
244
245 class PassAttributeBuilder : public SGReferenced
246 {
247 protected:
248     typedef std::map<const std::string, SGSharedPtr<PassAttributeBuilder> >
249     PassAttrMap;
250
251     struct PassAttrMapSingleton : public simgear::Singleton<PassAttrMapSingleton>
252     {
253         PassAttrMap passAttrMap;
254     };
255 public:
256     virtual void buildAttribute(Effect* effect, Pass* pass,
257                                 const SGPropertyNode* prop,
258                                 const osgDB::ReaderWriter::Options* options)
259     = 0;
260     static PassAttributeBuilder* find(const std::string& str)
261     {
262         PassAttrMap::iterator itr
263             = PassAttrMapSingleton::instance()->passAttrMap.find(str);
264         if (itr == PassAttrMapSingleton::instance()->passAttrMap.end())
265             return 0;
266         else
267             return itr->second.ptr();
268     }
269     template<typename T> friend struct InstallAttributeBuilder;
270 };
271
272 template<typename T>
273 struct InstallAttributeBuilder
274 {
275     InstallAttributeBuilder(const string& name)
276     {
277         PassAttributeBuilder::PassAttrMapSingleton::instance()
278             ->passAttrMap.insert(make_pair(name, new T));
279     }
280 };
281
282 // The description of an attribute may exist in a pass' XML, but a
283 // derived effect might want to disable the attribute altogether. So,
284 // some attributes have an "active" property; if it exists and is
285 // false, the OSG attribute is not built at all. This is different
286 // from any OSG mode settings that might be around.
287 bool isAttributeActive(Effect* effect, const SGPropertyNode* prop);
288
289 namespace effect
290 {
291 /**
292  * Bridge between types stored in properties and what OSG wants.
293  */
294 template<typename T> struct OSGBridge;
295
296 template<typename T>
297 struct OSGBridge<const T> : public OSGBridge<T>
298 {
299 };
300
301 template<>
302 struct OSGBridge<osg::Vec3f>
303 {
304     typedef SGVec3d sg_type;
305     static osg::Vec3f getOsgType(const SGVec3d& val) { return toOsg(val); }
306 };
307
308 template<>
309 struct OSGBridge<osg::Vec3d>
310 {
311     typedef SGVec3d sg_type;
312     static osg::Vec3d getOsgType(const SGVec3d& val) { return toOsg(val); }
313 };
314
315 template<>
316 struct OSGBridge<osg::Vec4f>
317 {
318     typedef SGVec4d sg_type;
319     static osg::Vec4f getOsgType(const SGVec4d& val) { return toOsg(val); }
320 };
321
322 template<>
323 struct OSGBridge<osg::Vec4d>
324 {
325     typedef SGVec4d sg_type;
326     static osg::Vec4d getOsgType(const SGVec4d& val) { return toOsg(val); }
327 };
328
329 template<typename Obj, typename OSGParam>
330 struct OSGFunctor : public OSGBridge<OSGParam>
331 {
332     OSGFunctor(Obj* obj, void (Obj::*func)(const OSGParam&))
333         : _obj(obj), _func(func) {}
334     void operator()(const typename OSGBridge<OSGParam>::sg_type& val) const
335     {
336         ((_obj.get())->*_func)(this->getOsgType(val));
337     }
338     osg::ref_ptr<Obj>_obj;
339     void (Obj::*_func)(const OSGParam&);
340 };
341
342 template<typename ObjType, typename OSGParamType>
343 class ScalarChangeListener
344     : public SGPropertyChangeListener, public InitializeWhenAdded,
345       public Effect::Updater
346 {
347 public:
348     typedef void (ObjType::*setter_type)(const OSGParamType);
349     ScalarChangeListener(ObjType* obj, setter_type setter,
350                          const std::string& propName)
351         : _obj(obj), _setter(setter)
352     {
353         _propName = new std::string(propName);
354     }
355     virtual ~ScalarChangeListener()
356     {
357         delete _propName;
358         _propName = 0;
359     }
360     void valueChanged(SGPropertyNode* node)
361     {
362         _obj->*setter(node->getValue<OSGParamType>());
363     }
364     void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot)
365     {
366         SGPropertyNode* listenProp = makeNode(propRoot, *_propName);
367         delete _propName;
368         _propName = 0;
369         if (listenProp)
370             listenProp->addChangeListener(this, true);
371     }
372 private:
373     osg::ref_ptr<ObjType> _obj;
374     setter_type _setter;
375     std::string* _propName;
376 };
377
378 template<typename T, typename Func>
379 class EffectExtendedPropListener : public InitializeWhenAdded,
380                                    public Effect::Updater
381 {
382 public:
383     template<typename Itr>
384     EffectExtendedPropListener(const Func& func,
385                                const std::string& propName, Itr childNamesBegin,
386                                Itr childNamesEnd)
387         : _func(func)
388     {
389         _propName = new std::string(propName);
390         _childNames = new std::vector<std::string>(childNamesBegin,
391                                                    childNamesEnd);
392     }
393     virtual ~EffectExtendedPropListener()
394     {
395         delete _propName;
396         delete _childNames;
397     }
398     void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot)
399     {
400         SGPropertyNode* parent = propRoot->getNode(*_propName, true);
401         _propListener
402             = new ExtendedPropListener<T, Func>(parent, _childNames->begin(),
403                                                 _childNames->end(),
404                                                 _func, true);
405         delete _propName;
406         _propName = 0;
407         delete _childNames;
408         _childNames = 0;
409     }
410 private:
411     std::string* _propName;
412     std::vector<std::string>* _childNames;
413     SGSharedPtr<ExtendedPropListener<T, Func> > _propListener;
414     Func _func;
415 };
416
417 /**
418  * Initialize the value and the possible updating of an effect
419  * attribute. If the value is specified directly, set it. Otherwise,
420  * use the <use> tag to look at the parameters. Again, if there is a
421  * value there set it directly. Otherwise, the parameter contains its
422  * own <use> tag referring to a property in the global property tree;
423  * install a change listener that will set the attribute when the
424  * property changes.
425  */
426 template<typename ObjType, typename OSGParamType>
427 void
428 initFromParameters(Effect* effect, const SGPropertyNode* prop, ObjType* obj,
429                    void (ObjType::*setter)(const OSGParamType))
430 {
431     const SGPropertyNode* valProp = getEffectPropertyNode(effect, prop);
432     if (!valProp)
433         return;
434     if (valProp->nChildren() == 0) {
435         obj->*setter(valProp->getValue<OSGParamType>());
436     } else {
437         std::string propName = getGlobalProperty(prop);
438         ScalarChangeListener<ObjType, OSGParamType>* listener
439             = new ScalarChangeListener<ObjType, OSGParamType>(obj, setter,
440                                                               propName);
441         effect->addUpdater(listener);
442     }
443 }
444
445 template<typename ObjType, typename OSGParamType, typename NameItrType>
446 void
447 initFromParameters(Effect* effect, const SGPropertyNode* prop, ObjType* obj,
448                    void (ObjType::*setter)(const OSGParamType&),
449                    NameItrType nameItr)
450 {
451     typedef typename OSGBridge<OSGParamType>::sg_type sg_type;
452     const SGPropertyNode* valProp = getEffectPropertyNode(effect, prop);
453     if (!valProp)
454         return;
455     if (valProp->nChildren() == 0) {
456         (obj->*setter)(OSGBridge<OSGParamType>
457                      ::getOsgType(valProp->getValue<sg_type>()));
458     } else {
459         string listenPropName = getGlobalProperty(valProp);
460         if (listenPropName.empty())
461             return;
462         typedef OSGFunctor<ObjType, OSGParamType> Functor;
463         Effect::Updater* listener
464             =  new EffectExtendedPropListener<sg_type, Functor>
465             (Functor(obj, setter), listenPropName, nameItr,
466              nameItr + props::NumComponents<sg_type>::num_components);
467         effect->addUpdater(listener);
468     }
469 }
470
471 extern const char* colorFields[];
472 }
473 }
474 #endif