]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/Effect.cxx
a94267fc6439d9e3071cb66ed0bec874be02a10f
[simgear.git] / simgear / scene / material / Effect.cxx
1 // Copyright (C) 2008 - 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 #ifdef HAVE_CONFIG_H
18 #  include <simgear_config.h>
19 #endif
20
21 #include "Effect.hxx"
22 #include "EffectBuilder.hxx"
23 #include "Technique.hxx"
24 #include "Pass.hxx"
25 #include "TextureBuilder.hxx"
26
27 #include <algorithm>
28 #include <functional>
29 #include <iterator>
30 #include <map>
31 #include <utility>
32
33 #include <boost/foreach.hpp>
34 #include <boost/lexical_cast.hpp>
35 #include <boost/tuple/tuple.hpp>
36 #include <boost/tuple/tuple_comparison.hpp>
37
38 #include <osg/AlphaFunc>
39 #include <osg/BlendFunc>
40 #include <osg/CullFace>
41 #include <osg/Drawable>
42 #include <osg/Material>
43 #include <osg/Math>
44 #include <osg/PolygonMode>
45 #include <osg/Program>
46 #include <osg/Referenced>
47 #include <osg/RenderInfo>
48 #include <osg/ShadeModel>
49 #include <osg/StateSet>
50 #include <osg/TexEnv>
51 #include <osg/Texture1D>
52 #include <osg/Texture2D>
53 #include <osg/Texture3D>
54 #include <osg/TextureRectangle>
55 #include <osg/Uniform>
56 #include <osg/Vec4d>
57 #include <osgUtil/CullVisitor>
58 #include <osgDB/FileUtils>
59 #include <osgDB/Input>
60 #include <osgDB/ParameterOutput>
61 #include <osgDB/ReadFile>
62 #include <osgDB/Registry>
63
64 #include <simgear/scene/tgdb/userdata.hxx>
65 #include <simgear/scene/util/SGSceneFeatures.hxx>
66 #include <simgear/scene/util/StateAttributeFactory.hxx>
67 #include <simgear/structure/OSGUtils.hxx>
68 #include <simgear/structure/SGExpression.hxx>
69
70
71
72 namespace simgear
73 {
74 using namespace std;
75 using namespace osg;
76 using namespace osgUtil;
77
78 using namespace effect;
79
80 Effect::Effect()
81 {
82 }
83
84 Effect::Effect(const Effect& rhs, const CopyOp& copyop)
85     : root(rhs.root), parametersProp(rhs.parametersProp)
86 {
87     typedef vector<ref_ptr<Technique> > TechniqueList;
88     for (TechniqueList::const_iterator itr = rhs.techniques.begin(),
89              end = rhs.techniques.end();
90          itr != end;
91          ++itr)
92         techniques.push_back(static_cast<Technique*>(copyop(itr->get())));
93 }
94
95 // Assume that the last technique is always valid.
96 StateSet* Effect::getDefaultStateSet()
97 {
98     Technique* tniq = techniques.back().get();
99     if (!tniq)
100         return 0;
101     Pass* pass = tniq->passes.front().get();
102     return pass;
103 }
104
105 // There should always be a valid technique in an effect.
106
107 Technique* Effect::chooseTechnique(RenderInfo* info)
108 {
109     BOOST_FOREACH(ref_ptr<Technique>& technique, techniques)
110     {
111         if (technique->valid(info) == Technique::VALID)
112             return technique.get();
113     }
114     return 0;
115 }
116
117 void Effect::resizeGLObjectBuffers(unsigned int maxSize)
118 {
119     BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
120     {
121         technique->resizeGLObjectBuffers(maxSize);
122     }
123 }
124
125 void Effect::releaseGLObjects(osg::State* state) const
126 {
127     BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
128     {
129         technique->releaseGLObjects(state);
130     }
131 }
132
133 Effect::~Effect()
134 {
135 }
136
137 class PassAttributeBuilder : public Referenced
138 {
139 public:
140     virtual void buildAttribute(Effect* effect, Pass* pass,
141                                 const SGPropertyNode* prop,
142                                 const osgDB::ReaderWriter::Options* options)
143     = 0;
144 };
145
146 typedef map<const string, ref_ptr<PassAttributeBuilder> > PassAttrMap;
147 PassAttrMap passAttrMap;
148
149 template<typename T>
150 struct InstallAttributeBuilder
151 {
152     InstallAttributeBuilder(const string& name)
153     {
154         passAttrMap.insert(make_pair(name, new T));
155     }
156 };
157
158 void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
159                const osgDB::ReaderWriter::Options* options)
160 {
161     Pass* pass = new Pass;
162     tniq->passes.push_back(pass);
163     for (int i = 0; i < prop->nChildren(); ++i) {
164         const SGPropertyNode* attrProp = prop->getChild(i);
165         PassAttrMap::iterator itr = passAttrMap.find(attrProp->getName());
166         if (itr != passAttrMap.end())
167             itr->second->buildAttribute(effect, pass, attrProp, options);
168         else
169             SG_LOG(SG_INPUT, SG_ALERT,
170                    "skipping unknown pass attribute " << attrProp->getName());
171     }
172 }
173
174 osg::Vec4f getColor(const SGPropertyNode* prop)
175 {
176     if (prop->nChildren() == 0) {
177         if (prop->getType() == props::VEC4D) {
178             return osg::Vec4f(toOsg(prop->getValue<SGVec4d>()));
179         } else if (prop->getType() == props::VEC3D) {
180             return osg::Vec4f(toOsg(prop->getValue<SGVec3d>()), 1.0f);
181         } else {
182             SG_LOG(SG_INPUT, SG_ALERT,
183                    "invalid color property " << prop->getName() << " "
184                    << prop->getStringValue());
185             return osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
186         }
187     } else {
188         osg::Vec4f result;
189         static const char* colors[] = {"r", "g", "b"};
190         for (int i = 0; i < 3; ++i) {
191             const SGPropertyNode* componentProp = prop->getChild(colors[i]);
192             result[i] = componentProp ? componentProp->getValue<float>() : 0.0f;
193         }
194         const SGPropertyNode* alphaProp = prop->getChild("a");
195         result[3] = alphaProp ? alphaProp->getValue<float>() : 1.0f;
196         return result;
197     }
198 }
199
200 // The description of an attribute may exist in a pass' XML, but a
201 // derived effect might want to disable the attribute altogether. So,
202 // some attributes have an "active" property; if it exists and is
203 // false, the OSG attribute is not built at all. This is different
204 // from any OSG mode settings that might be around.
205
206 bool isAttributeActive(Effect* effect, const SGPropertyNode* prop)
207 {
208     const SGPropertyNode* activeProp
209         = getEffectPropertyChild(effect, prop, "active");
210     return !activeProp || activeProp->getValue<bool>();
211 }
212
213 struct LightingBuilder : public PassAttributeBuilder
214 {
215     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
216                         const osgDB::ReaderWriter::Options* options);
217 };
218
219 void LightingBuilder::buildAttribute(Effect* effect, Pass* pass,
220                                      const SGPropertyNode* prop,
221                                      const osgDB::ReaderWriter::Options* options)
222 {
223     const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
224     if (!realProp)
225         return;
226     pass->setMode(GL_LIGHTING, (realProp->getValue<bool>() ? StateAttribute::ON
227                                 : StateAttribute::OFF));
228 }
229
230 InstallAttributeBuilder<LightingBuilder> installLighting("lighting");
231
232 struct ShadeModelBuilder : public PassAttributeBuilder
233 {
234     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
235                         const osgDB::ReaderWriter::Options* options)
236     {
237         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
238         if (!realProp)
239             return;
240         StateAttributeFactory *attrFact = StateAttributeFactory::instance();
241         string propVal = realProp->getStringValue();
242         if (propVal == "flat")
243             pass->setAttribute(attrFact->getFlatShadeModel());
244         else if (propVal == "smooth")
245             pass->setAttribute(attrFact->getSmoothShadeModel());
246         else
247             SG_LOG(SG_INPUT, SG_ALERT,
248                    "invalid shade model property " << propVal);
249     }
250 };
251
252 InstallAttributeBuilder<ShadeModelBuilder> installShadeModel("shade-model");
253
254 struct CullFaceBuilder : PassAttributeBuilder
255 {
256     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
257                         const osgDB::ReaderWriter::Options* options)
258     {
259         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
260         if (!realProp) {
261             pass->setMode(GL_CULL_FACE, StateAttribute::OFF);
262             return;
263         }
264         StateAttributeFactory *attrFact = StateAttributeFactory::instance();
265         string propVal = realProp->getStringValue();
266         if (propVal == "front")
267             pass->setAttributeAndModes(attrFact->getCullFaceFront());
268         else if (propVal == "back")
269             pass->setAttributeAndModes(attrFact->getCullFaceBack());
270         else if (propVal == "front-back")
271             pass->setAttributeAndModes(new CullFace(CullFace::FRONT_AND_BACK));
272         else if (propVal == "off")
273             pass->setMode(GL_CULL_FACE, StateAttribute::OFF);
274         else
275             SG_LOG(SG_INPUT, SG_ALERT,
276                    "invalid cull face property " << propVal);            
277     }    
278 };
279
280 InstallAttributeBuilder<CullFaceBuilder> installCullFace("cull-face");
281
282 EffectNameValue<StateSet::RenderingHint> renderingHintInit[] =
283 {
284     { "default", StateSet::DEFAULT_BIN },
285     { "opaque", StateSet::OPAQUE_BIN },
286     { "transparent", StateSet::TRANSPARENT_BIN }
287 };
288
289 EffectPropertyMap<StateSet::RenderingHint> renderingHints(renderingHintInit);
290
291 struct HintBuilder : public PassAttributeBuilder
292 {
293     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
294                         const osgDB::ReaderWriter::Options* options)
295     {
296         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
297         if (!realProp)
298             return;
299         StateSet::RenderingHint renderingHint = StateSet::DEFAULT_BIN;
300         findAttr(renderingHints, realProp, renderingHint);
301         pass->setRenderingHint(renderingHint);
302     }    
303 };
304
305 InstallAttributeBuilder<HintBuilder> installHint("rendering-hint");
306
307 struct RenderBinBuilder : public PassAttributeBuilder
308 {
309     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
310                         const osgDB::ReaderWriter::Options* options)
311     {
312         if (!isAttributeActive(effect, prop))
313             return;
314         const SGPropertyNode* binProp = prop->getChild("bin-number");
315         binProp = getEffectPropertyNode(effect, binProp);
316         const SGPropertyNode* nameProp = prop->getChild("bin-name");
317         nameProp = getEffectPropertyNode(effect, nameProp);
318         if (binProp && nameProp) {
319             pass->setRenderBinDetails(binProp->getIntValue(),
320                                       nameProp->getStringValue());
321         } else {
322             if (!binProp)
323                 SG_LOG(SG_INPUT, SG_ALERT,
324                        "No render bin number specified in render bin section");
325             if (!nameProp)
326                 SG_LOG(SG_INPUT, SG_ALERT,
327                        "No render bin name specified in render bin section");
328         }
329     }
330 };
331
332 InstallAttributeBuilder<RenderBinBuilder> installRenderBin("render-bin");
333
334 struct MaterialBuilder : public PassAttributeBuilder
335 {
336     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
337                         const osgDB::ReaderWriter::Options* options);
338 };
339
340 EffectNameValue<Material::ColorMode> colorModeInit[] =
341 {
342     { "ambient", Material::AMBIENT },
343     { "ambient-and-diffuse", Material::AMBIENT_AND_DIFFUSE },
344     { "diffuse", Material::DIFFUSE },
345     { "emissive", Material::EMISSION },
346     { "specular", Material::SPECULAR },
347     { "off", Material::OFF }
348 };
349 EffectPropertyMap<Material::ColorMode> colorModes(colorModeInit);
350
351 void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
352                                      const SGPropertyNode* prop,
353                                      const osgDB::ReaderWriter::Options* options)
354 {
355     if (!isAttributeActive(effect, prop))
356         return;
357     Material* mat = new Material;
358     const SGPropertyNode* color = 0;
359     if ((color = getEffectPropertyChild(effect, prop, "ambient")))
360         mat->setAmbient(Material::FRONT_AND_BACK, getColor(color));
361     if ((color = getEffectPropertyChild(effect, prop, "ambient-front")))
362         mat->setAmbient(Material::FRONT, getColor(color));
363     if ((color = getEffectPropertyChild(effect, prop, "ambient-back")))
364         mat->setAmbient(Material::BACK, getColor(color));
365     if ((color = getEffectPropertyChild(effect, prop, "diffuse")))
366         mat->setDiffuse(Material::FRONT_AND_BACK, getColor(color));
367     if ((color = getEffectPropertyChild(effect, prop, "diffuse-front")))
368         mat->setDiffuse(Material::FRONT, getColor(color));
369     if ((color = getEffectPropertyChild(effect, prop, "diffuse-back")))
370         mat->setDiffuse(Material::BACK, getColor(color));
371     if ((color = getEffectPropertyChild(effect, prop, "specular")))
372         mat->setSpecular(Material::FRONT_AND_BACK, getColor(color));
373     if ((color = getEffectPropertyChild(effect, prop, "specular-front")))
374         mat->setSpecular(Material::FRONT, getColor(color));
375     if ((color = getEffectPropertyChild(effect, prop, "specular-back")))
376         mat->setSpecular(Material::BACK, getColor(color));
377     if ((color = getEffectPropertyChild(effect, prop, "emissive")))
378         mat->setEmission(Material::FRONT_AND_BACK, getColor(color));
379     if ((color = getEffectPropertyChild(effect, prop, "emissive-front")))
380         mat->setEmission(Material::FRONT, getColor(color));        
381     if ((color = getEffectPropertyChild(effect, prop, "emissive-back")))
382         mat->setEmission(Material::BACK, getColor(color));        
383     const SGPropertyNode* shininess = 0;
384     mat->setShininess(Material::FRONT_AND_BACK, 0.0f);
385     if ((shininess = getEffectPropertyChild(effect, prop, "shininess")))
386         mat->setShininess(Material::FRONT_AND_BACK, shininess->getFloatValue());
387     if ((shininess = getEffectPropertyChild(effect, prop, "shininess-front")))
388         mat->setShininess(Material::FRONT, shininess->getFloatValue());
389     if ((shininess = getEffectPropertyChild(effect, prop, "shininess-back")))
390         mat->setShininess(Material::BACK, shininess->getFloatValue());
391     Material::ColorMode colorMode = Material::OFF;
392     findAttr(colorModes, getEffectPropertyChild(effect, prop, "color-mode"),
393              colorMode);
394     mat->setColorMode(colorMode);
395     pass->setAttribute(mat);
396 }
397
398 InstallAttributeBuilder<MaterialBuilder> installMaterial("material");
399
400 EffectNameValue<BlendFunc::BlendFuncMode> blendFuncModesInit[] =
401 {
402     {"dst-alpha", BlendFunc::DST_ALPHA},
403     {"dst-color", BlendFunc::DST_COLOR},
404     {"one", BlendFunc::ONE},
405     {"one-minus-dst-alpha", BlendFunc::ONE_MINUS_DST_ALPHA},
406     {"one-minus-dst-color", BlendFunc::ONE_MINUS_DST_COLOR},
407     {"one-minus-src-alpha", BlendFunc::ONE_MINUS_SRC_ALPHA},
408     {"one-minus-src-color", BlendFunc::ONE_MINUS_SRC_COLOR},
409     {"src-alpha", BlendFunc::SRC_ALPHA},
410     {"src-alpha-saturate", BlendFunc::SRC_ALPHA_SATURATE},
411     {"src-color", BlendFunc::SRC_COLOR},
412     {"constant-color", BlendFunc::CONSTANT_COLOR},
413     {"one-minus-constant-color", BlendFunc::ONE_MINUS_CONSTANT_COLOR},
414     {"constant-alpha", BlendFunc::CONSTANT_ALPHA},
415     {"one-minus-constant-alpha", BlendFunc::ONE_MINUS_CONSTANT_ALPHA},
416     {"zero", BlendFunc::ZERO}
417 };
418 EffectPropertyMap<BlendFunc::BlendFuncMode> blendFuncModes(blendFuncModesInit);
419
420 struct BlendBuilder : public PassAttributeBuilder
421 {
422     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
423                         const osgDB::ReaderWriter::Options* options)
424     {
425         if (!isAttributeActive(effect, prop))
426             return;
427         // XXX Compatibility with early <blend> syntax; should go away
428         // before a release
429         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
430         if (!realProp)
431             return;
432         if (realProp->nChildren() == 0) {
433             pass->setMode(GL_BLEND, (realProp->getBoolValue()
434                                      ? StateAttribute::ON
435                                      : StateAttribute::OFF));
436             return;
437         }
438
439         const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
440                                                              "mode");
441         // XXX When dynamic parameters are supported, this code should
442         // create the blend function even if the mode is off.
443         if (pmode && !pmode->getValue<bool>()) {
444             pass->setMode(GL_BLEND, StateAttribute::OFF);
445             return;
446         }
447         const SGPropertyNode* psource
448             = getEffectPropertyChild(effect, prop, "source");
449         const SGPropertyNode* pdestination
450             = getEffectPropertyChild(effect, prop, "destination");
451         const SGPropertyNode* psourceRGB
452             = getEffectPropertyChild(effect, prop, "source-rgb");
453         const SGPropertyNode* psourceAlpha
454             = getEffectPropertyChild(effect, prop, "source-alpha");
455         const SGPropertyNode* pdestRGB
456             = getEffectPropertyChild(effect, prop, "destination-rgb");
457         const SGPropertyNode* pdestAlpha
458             = getEffectPropertyChild(effect, prop, "destination-alpha");
459         BlendFunc::BlendFuncMode sourceMode = BlendFunc::ONE;
460         BlendFunc::BlendFuncMode destMode = BlendFunc::ZERO;
461         if (psource)
462             findAttr(blendFuncModes, psource, sourceMode);
463         if (pdestination)
464             findAttr(blendFuncModes, pdestination, destMode);
465         if (psource && pdestination
466             && !(psourceRGB || psourceAlpha || pdestRGB || pdestAlpha)
467             && sourceMode == BlendFunc::SRC_ALPHA
468             && destMode == BlendFunc::ONE_MINUS_SRC_ALPHA) {
469             pass->setAttributeAndModes(StateAttributeFactory::instance()
470                                        ->getStandardBlendFunc());
471             return;
472         }
473         BlendFunc* blendFunc = new BlendFunc;
474         if (psource)
475             blendFunc->setSource(sourceMode);
476         if (pdestination)
477             blendFunc->setDestination(destMode);
478         if (psourceRGB) {
479             BlendFunc::BlendFuncMode sourceRGBMode;
480             findAttr(blendFuncModes, psourceRGB, sourceRGBMode);
481             blendFunc->setSourceRGB(sourceRGBMode);
482         }
483         if (pdestRGB) {
484             BlendFunc::BlendFuncMode destRGBMode;
485             findAttr(blendFuncModes, pdestRGB, destRGBMode);
486             blendFunc->setDestinationRGB(destRGBMode);
487         }
488         if (psourceAlpha) {
489             BlendFunc::BlendFuncMode sourceAlphaMode;
490             findAttr(blendFuncModes, psourceAlpha, sourceAlphaMode);
491             blendFunc->setSourceAlpha(sourceAlphaMode);
492         }
493         if (pdestAlpha) {
494             BlendFunc::BlendFuncMode destAlphaMode;
495             findAttr(blendFuncModes, pdestAlpha, destAlphaMode);
496             blendFunc->setDestinationAlpha(destAlphaMode);
497         }
498         pass->setAttributeAndModes(blendFunc);
499     }
500 };
501
502 InstallAttributeBuilder<BlendBuilder> installBlend("blend");
503
504 EffectNameValue<AlphaFunc::ComparisonFunction> alphaComparisonInit[] =
505 {
506     {"never", AlphaFunc::NEVER},
507     {"less", AlphaFunc::LESS},
508     {"equal", AlphaFunc::EQUAL},
509     {"lequal", AlphaFunc::LEQUAL},
510     {"greater", AlphaFunc::GREATER},
511     {"notequal", AlphaFunc::NOTEQUAL},
512     {"gequal", AlphaFunc::GEQUAL},
513     {"always", AlphaFunc::ALWAYS}
514 };
515 EffectPropertyMap<AlphaFunc::ComparisonFunction>
516 alphaComparison(alphaComparisonInit);
517
518 struct AlphaTestBuilder : public PassAttributeBuilder
519 {
520     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
521                         const osgDB::ReaderWriter::Options* options)
522     {
523         if (!isAttributeActive(effect, prop))
524             return;
525         // XXX Compatibility with early <alpha-test> syntax; should go away
526         // before a release
527         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
528         if (!realProp)
529             return;
530         if (realProp->nChildren() == 0) {
531             pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
532                                      ? StateAttribute::ON
533                                      : StateAttribute::OFF));
534             return;
535         }
536
537         const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
538                                                              "mode");
539         // XXX When dynamic parameters are supported, this code should
540         // create the blend function even if the mode is off.
541         if (pmode && !pmode->getValue<bool>()) {
542             pass->setMode(GL_ALPHA_TEST, StateAttribute::OFF);
543             return;
544         }
545         const SGPropertyNode* pComp = getEffectPropertyChild(effect, prop,
546                                                              "comparison");
547         const SGPropertyNode* pRef = getEffectPropertyChild(effect, prop,
548                                                              "reference");
549         AlphaFunc::ComparisonFunction func = AlphaFunc::ALWAYS;
550         float refValue = 1.0f;
551         if (pComp)
552             findAttr(alphaComparison, pComp, func);
553         if (pRef)
554             refValue = pRef->getValue<float>();
555         if (func == AlphaFunc::GREATER && osg::equivalent(refValue, 1.0f)) {
556             pass->setAttributeAndModes(StateAttributeFactory::instance()
557                                        ->getStandardAlphaFunc());
558         } else {
559             AlphaFunc* alphaFunc = new AlphaFunc;
560             alphaFunc->setFunction(func);
561             alphaFunc->setReferenceValue(refValue);
562             pass->setAttributeAndModes(alphaFunc);
563         }
564     }
565 };
566
567 InstallAttributeBuilder<AlphaTestBuilder> installAlphaTest("alpha-test");
568
569 EffectNameValue<TexEnv::Mode> texEnvModesInit[] =
570 {
571     {"add", TexEnv::ADD},
572     {"blend", TexEnv::BLEND},
573     {"decal", TexEnv::DECAL},
574     {"modulate", TexEnv::MODULATE},
575     {"replace", TexEnv::REPLACE}
576 };
577 EffectPropertyMap<TexEnv::Mode> texEnvModes(texEnvModesInit);
578
579 TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
580 {
581     const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
582                                                             "mode");
583     const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
584                                                              "color");
585     if (!modeProp)
586         return 0;
587     TexEnv::Mode mode = TexEnv::MODULATE;
588     findAttr(texEnvModes, modeProp, mode);
589     if (mode == TexEnv::MODULATE) {
590         return StateAttributeFactory::instance()->getStandardTexEnv();
591     }
592     TexEnv* env = new TexEnv(mode);
593     if (colorProp)
594         env->setColor(toOsg(colorProp->getValue<SGVec4d>()));
595     return env;
596  }
597
598
599 struct TextureUnitBuilder : PassAttributeBuilder
600 {
601     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
602                         const osgDB::ReaderWriter::Options* options);
603 };
604
605 void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
606                                         const SGPropertyNode* prop,
607                                         const osgDB::ReaderWriter::Options* options)
608 {
609     if (!isAttributeActive(effect, prop))
610         return;
611     // Decode the texture unit
612     int unit = 0;
613     const SGPropertyNode* pUnit = prop->getChild("unit");
614     if (pUnit) {
615         unit = pUnit->getValue<int>();
616     } else {
617         const SGPropertyNode* pName = prop->getChild("name");
618         if (pName)
619             try {
620                 unit = boost::lexical_cast<int>(pName->getStringValue());
621             } catch (boost::bad_lexical_cast& lex) {
622                 SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit "
623                        << lex.what());
624             }
625     }
626     const SGPropertyNode* pType = getEffectPropertyChild(effect, prop, "type");
627     string type;
628     if (!pType)
629         type = "2d";
630     else
631         type = pType->getStringValue();
632     Texture* texture = 0;
633     try {
634         texture = TextureBuilder::buildFromType(effect, type, prop,
635                                                 options);
636     }
637     catch (BuilderException& e) {
638         SG_LOG(SG_INPUT, SG_ALERT, "No image file for texture, using white ");
639         texture = StateAttributeFactory::instance()->getWhiteTexture();
640     }
641     pass->setTextureAttributeAndModes(unit, texture);
642     const SGPropertyNode* envProp = prop->getChild("environment");
643     if (envProp) {
644         TexEnv* env = buildTexEnv(effect, envProp);
645         if (env)
646             pass->setTextureAttributeAndModes(unit, env);
647     }
648 }
649
650
651
652 InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
653
654 typedef map<string, ref_ptr<Program> > ProgramMap;
655 ProgramMap programMap;
656
657 typedef map<string, ref_ptr<Shader> > ShaderMap;
658 ShaderMap shaderMap;
659
660 void reload_shaders()
661 {
662     for(ShaderMap::iterator sitr = shaderMap.begin(); sitr != shaderMap.end(); ++sitr)
663     {
664         Shader *shader = sitr->second.get();
665         string fileName = osgDB::findDataFile(sitr->first);
666         if (!fileName.empty()) {
667             shader->loadShaderSourceFromFile(fileName);
668         }
669     }
670 }
671
672 struct ShaderProgramBuilder : PassAttributeBuilder
673 {
674     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
675                         const osgDB::ReaderWriter::Options* options);
676 };
677
678 void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
679                                           const SGPropertyNode* prop,
680                                           const osgDB::ReaderWriter::Options*
681                                           options)
682 {
683     if (!isAttributeActive(effect, prop))
684         return;
685     PropertyList pVertShaders = prop->getChildren("vertex-shader");
686     PropertyList pFragShaders = prop->getChildren("fragment-shader");
687     string programKey;
688     for (PropertyList::iterator itr = pVertShaders.begin(),
689              e = pVertShaders.end();
690          itr != e;
691          ++itr)
692     {
693         programKey += (*itr)->getStringValue();
694         programKey += ";";
695     }
696     for (PropertyList::iterator itr = pFragShaders.begin(),
697              e = pFragShaders.end();
698          itr != e;
699          ++itr)
700     {
701         programKey += (*itr)->getStringValue();
702         programKey += ";";
703     }
704     Program* program = 0;
705     ProgramMap::iterator pitr = programMap.find(programKey);
706     if (pitr != programMap.end()) {
707         program = pitr->second.get();
708     } else {
709         program = new Program;
710         program->setName(programKey);
711         // Add vertex shaders, then fragment shaders
712         PropertyList& pvec = pVertShaders;
713         Shader::Type stype = Shader::VERTEX;
714         for (int i = 0; i < 2; ++i) {
715             for (PropertyList::iterator nameItr = pvec.begin(), e = pvec.end();
716                  nameItr != e;
717                  ++nameItr) {
718                 string shaderName = (*nameItr)->getStringValue();
719                 ShaderMap::iterator sitr = shaderMap.find(shaderName);
720                 if (sitr != shaderMap.end()) {
721                     program->addShader(sitr->second.get());
722                 } else {
723                     string fileName = osgDB::findDataFile(shaderName, options);
724                     if (!fileName.empty()) {
725                         ref_ptr<Shader> shader = new Shader(stype);
726                         if (shader->loadShaderSourceFromFile(fileName)) {
727                             program->addShader(shader.get());
728                             shaderMap.insert(make_pair(shaderName, shader));
729                         }
730                     }
731                 }
732             }
733             pvec = pFragShaders;
734             stype = Shader::FRAGMENT;
735         }
736         programMap.insert(make_pair(programKey, program));
737     }
738     pass->setAttributeAndModes(program);
739 }
740
741 InstallAttributeBuilder<ShaderProgramBuilder> installShaderProgram("program");
742
743 EffectNameValue<Uniform::Type> uniformTypesInit[] =
744 {
745     {"float", Uniform::FLOAT},
746     {"float-vec3", Uniform::FLOAT_VEC3},
747     {"float-vec4", Uniform::FLOAT_VEC4},
748     {"sampler-1d", Uniform::SAMPLER_1D},
749     {"sampler-2d", Uniform::SAMPLER_2D},
750     {"sampler-3d", Uniform::SAMPLER_3D}
751 };
752 EffectPropertyMap<Uniform::Type> uniformTypes(uniformTypesInit);
753
754 struct UniformBuilder :public PassAttributeBuilder
755 {
756     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
757                         const osgDB::ReaderWriter::Options* options)
758     {
759         if (!isAttributeActive(effect, prop))
760             return;
761         const SGPropertyNode* nameProp = prop->getChild("name");
762         const SGPropertyNode* typeProp = prop->getChild("type");
763         const SGPropertyNode* valProp
764             = getEffectPropertyChild(effect, prop, "value");
765         string name;
766         Uniform::Type uniformType = Uniform::FLOAT;
767         if (nameProp) {
768             name = nameProp->getStringValue();
769         } else {
770             SG_LOG(SG_INPUT, SG_ALERT, "No name for uniform property ");
771             return;
772         }
773         if (!valProp) {
774             SG_LOG(SG_INPUT, SG_ALERT, "No value for uniform property "
775                    << name);
776             return;
777         }
778         if (!typeProp) {
779             props::Type propType = valProp->getType();
780             switch (propType) {
781             case props::FLOAT:
782             case props::DOUBLE:
783                 break;          // default float type;
784             case props::VEC3D:
785                 uniformType = Uniform::FLOAT_VEC3;
786                 break;
787             case props::VEC4D:
788                 uniformType = Uniform::FLOAT_VEC4;
789                 break;
790             default:
791                 SG_LOG(SG_INPUT, SG_ALERT, "Can't deduce type of uniform "
792                        << name);
793                 return;
794             }
795         } else {
796             findAttr(uniformTypes, typeProp, uniformType);
797         }
798         ref_ptr<Uniform> uniform = new Uniform;
799         uniform->setName(name);
800         uniform->setType(uniformType);
801         switch (uniformType) {
802         case Uniform::FLOAT:
803             uniform->set(valProp->getValue<float>());
804             break;
805         case Uniform::FLOAT_VEC3:
806             uniform->set(toOsg(valProp->getValue<SGVec3d>()));
807             break;
808         case Uniform::FLOAT_VEC4:
809             uniform->set(toOsg(valProp->getValue<SGVec4d>()));
810             break;
811         case Uniform::SAMPLER_1D:
812         case Uniform::SAMPLER_2D:
813         case Uniform::SAMPLER_3D:
814             uniform->set(valProp->getValue<int>());
815             break;
816         default: // avoid compiler warning
817             break;
818         }
819         pass->addUniform(uniform.get());
820     }
821 };
822
823 InstallAttributeBuilder<UniformBuilder> installUniform("uniform");
824
825 // Not sure what to do with "name". At one point I wanted to use it to
826 // order the passes, but I do support render bin and stuff too...
827
828 struct NameBuilder : public PassAttributeBuilder
829 {
830     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
831                         const osgDB::ReaderWriter::Options* options)
832     {
833         // name can't use <use>
834         string name = prop->getStringValue();
835         if (!name.empty())
836             pass->setName(name);
837     }
838 };
839
840 InstallAttributeBuilder<NameBuilder> installName("name");
841
842 EffectNameValue<PolygonMode::Mode> polygonModeModesInit[] =
843 {
844     {"fill", PolygonMode::FILL},
845     {"line", PolygonMode::LINE},
846     {"point", PolygonMode::POINT}
847 };
848 EffectPropertyMap<PolygonMode::Mode> polygonModeModes(polygonModeModesInit);
849
850 struct PolygonModeBuilder : public PassAttributeBuilder
851 {
852     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
853                         const osgDB::ReaderWriter::Options* options)
854     {
855         if (!isAttributeActive(effect, prop))
856             return;
857         const SGPropertyNode* frontProp
858             = getEffectPropertyChild(effect, prop, "front");
859         const SGPropertyNode* backProp
860             = getEffectPropertyChild(effect, prop, "back");
861         ref_ptr<PolygonMode> pmode = new PolygonMode;
862         PolygonMode::Mode frontMode = PolygonMode::FILL;
863         PolygonMode::Mode backMode = PolygonMode::FILL;
864         if (frontProp) {
865             findAttr(polygonModeModes, frontProp, frontMode);
866             pmode->setMode(PolygonMode::FRONT, frontMode);
867         }
868         if (backProp) {
869             findAttr(polygonModeModes, backProp, backMode);
870             pmode->setMode(PolygonMode::BACK, backMode);
871         }
872         pass->setAttribute(pmode.get());
873     }
874 };
875
876 InstallAttributeBuilder<PolygonModeBuilder> installPolygonMode("polygon-mode");
877 void buildTechnique(Effect* effect, const SGPropertyNode* prop,
878                     const osgDB::ReaderWriter::Options* options)
879 {
880     Technique* tniq = new Technique;
881     effect->techniques.push_back(tniq);
882     const SGPropertyNode* predProp = prop->getChild("predicate");
883     if (!predProp) {
884         tniq->setAlwaysValid(true);
885     } else {
886         try {
887             TechniquePredParser parser;
888             parser.setTechnique(tniq);
889             expression::BindingLayout& layout = parser.getBindingLayout();
890             /*int contextLoc = */layout.addBinding("__contextId", expression::INT);
891             SGExpressionb* validExp
892                 = dynamic_cast<SGExpressionb*>(parser.read(predProp
893                                                            ->getChild(0)));
894             if (validExp)
895                 tniq->setValidExpression(validExp, layout);
896             else
897                 throw expression::ParseError("technique predicate is not a boolean expression");
898         }
899         catch (expression::ParseError& except)
900         {
901             SG_LOG(SG_INPUT, SG_ALERT,
902                    "parsing technique predicate " << except.getMessage());
903             tniq->setAlwaysValid(false);
904         }
905     }
906     PropertyList passProps = prop->getChildren("pass");
907     for (PropertyList::iterator itr = passProps.begin(), e = passProps.end();
908          itr != e;
909          ++itr) {
910         buildPass(effect, tniq, itr->ptr(), options);
911     }
912 }
913
914 // Specifically for .ac files...
915 bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
916 {
917     SGPropertyNode* paramRoot = makeChild(effectRoot, "parameters");
918     SGPropertyNode* matNode = paramRoot->getChild("material", 0, true);
919     Vec4f ambVal, difVal, specVal, emisVal;
920     float shininess = 0.0f;
921     const Material* mat = getStateAttribute<Material>(ss);
922     if (mat) {
923         ambVal = mat->getAmbient(Material::FRONT_AND_BACK);
924         difVal = mat->getDiffuse(Material::FRONT_AND_BACK);
925         specVal = mat->getSpecular(Material::FRONT_AND_BACK);
926         emisVal = mat->getEmission(Material::FRONT_AND_BACK);
927         shininess = mat->getShininess(Material::FRONT_AND_BACK);
928         makeChild(matNode, "active")->setValue(true);
929         makeChild(matNode, "ambient")->setValue(toVec4d(toSG(ambVal)));
930         makeChild(matNode, "diffuse")->setValue(toVec4d(toSG(difVal)));
931         makeChild(matNode, "specular")->setValue(toVec4d(toSG(specVal)));
932         makeChild(matNode, "emissive")->setValue(toVec4d(toSG(emisVal)));
933         makeChild(matNode, "shininess")->setValue(shininess);
934         matNode->getChild("color-mode", 0, true)->setStringValue("diffuse");
935     } else {
936         makeChild(matNode, "active")->setValue(false);
937     }
938     const ShadeModel* sm = getStateAttribute<ShadeModel>(ss);
939     string shadeModelString("smooth");
940     if (sm) {
941         ShadeModel::Mode smMode = sm->getMode();
942         if (smMode == ShadeModel::FLAT)
943             shadeModelString = "flat";
944     }
945     makeChild(paramRoot, "shade-model")->setStringValue(shadeModelString);
946     string cullFaceString("off");
947     const CullFace* cullFace = getStateAttribute<CullFace>(ss);
948     if (cullFace) {
949         switch (cullFace->getMode()) {
950         case CullFace::FRONT:
951             cullFaceString = "front";
952             break;
953         case CullFace::BACK:
954             cullFaceString = "back";
955             break;
956         case CullFace::FRONT_AND_BACK:
957             cullFaceString = "front-back";
958             break;
959         default:
960             break;
961         }
962     }
963     makeChild(paramRoot, "cull-face")->setStringValue(cullFaceString);
964     const BlendFunc* blendFunc = getStateAttribute<BlendFunc>(ss);
965     SGPropertyNode* blendNode = makeChild(paramRoot, "blend");
966     if (blendFunc) {
967         string sourceMode = findName(blendFuncModes, blendFunc->getSource());
968         string destMode = findName(blendFuncModes, blendFunc->getDestination());
969         makeChild(blendNode, "active")->setValue(true);
970         makeChild(blendNode, "source")->setStringValue(sourceMode);
971         makeChild(blendNode, "destination")->setStringValue(destMode);
972         makeChild(blendNode, "mode")->setValue(true);
973     } else {
974         makeChild(blendNode, "active")->setValue(false);
975     }
976     makeTextureParameters(paramRoot, ss);
977     return true;
978 }
979
980 // Walk the techniques property tree, building techniques and
981 // passes.
982 bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
983 {
984     PropertyList tniqList = root->getChildren("technique");
985     for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
986          itr != e;
987          ++itr)
988         buildTechnique(this, *itr, options);
989     return true;
990 }
991
992 bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
993 {
994     const Effect& effect = static_cast<const Effect&>(obj);
995
996     fw.indent() << "techniques " << effect.techniques.size() << "\n";
997     BOOST_FOREACH(const ref_ptr<Technique>& technique, effect.techniques) {
998         fw.writeObject(*technique);
999     }
1000     return true;
1001 }
1002
1003 namespace
1004 {
1005 osgDB::RegisterDotOsgWrapperProxy effectProxy
1006 (
1007     new Effect,
1008     "simgear::Effect",
1009     "Object simgear::Effect",
1010     0,
1011     &Effect_writeLocalData
1012     );
1013 }
1014
1015 // Property expressions for technique predicates
1016 class PropertyExpression : public SGExpression<bool>
1017 {
1018 public:
1019     PropertyExpression(SGPropertyNode* pnode) : _pnode(pnode) {}
1020     
1021     void eval(bool& value, const expression::Binding*) const
1022     {
1023         value = _pnode->getValue<bool>();
1024     }
1025 protected:
1026     SGPropertyNode_ptr _pnode;
1027 };
1028
1029 class EffectPropertyListener : public SGPropertyChangeListener
1030 {
1031 public:
1032     EffectPropertyListener(Technique* tniq) : _tniq(tniq) {}
1033     
1034     void valueChanged(SGPropertyNode* node)
1035     {
1036         _tniq->refreshValidity();
1037     }
1038 protected:
1039     osg::ref_ptr<Technique> _tniq;
1040 };
1041
1042 Expression* propertyExpressionParser(const SGPropertyNode* exp,
1043                                      expression::Parser* parser)
1044 {
1045     SGPropertyNode_ptr pnode = getPropertyRoot()->getNode(exp->getStringValue(),
1046                                                           true);
1047     PropertyExpression* pexp = new PropertyExpression(pnode);
1048     TechniquePredParser* predParser
1049         = dynamic_cast<TechniquePredParser*>(parser);
1050     if (predParser)
1051         pnode->addChangeListener(new EffectPropertyListener(predParser
1052                                                             ->getTechnique()));
1053     return pexp;
1054 }
1055
1056 expression::ExpParserRegistrar propertyRegistrar("property",
1057                                                  propertyExpressionParser);
1058
1059 }