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