]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/Effect.cxx
Work around apparent OSG 3.2.0 normal binding bug.
[simgear.git] / simgear / scene / material / Effect.cxx
1 // Copyright (C) 2008 - 2010  Tim Moore timoore33@gmail.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 "EffectGeode.hxx"
24 #include "Technique.hxx"
25 #include "Pass.hxx"
26 #include "TextureBuilder.hxx"
27 #include "parseBlendFunc.hxx"
28
29 #include <algorithm>
30 #include <functional>
31 #include <iterator>
32 #include <map>
33 #include <utility>
34 #include <boost/tr1/unordered_map.hpp>
35
36 #include <boost/bind.hpp>
37 #include <boost/foreach.hpp>
38 #include <boost/functional/hash.hpp>
39 #include <boost/tuple/tuple.hpp>
40 #include <boost/tuple/tuple_comparison.hpp>
41
42 #include <osg/AlphaFunc>
43 #include <osg/BlendFunc>
44 #include <osg/CullFace>
45 #include <osg/Depth>
46 #include <osg/Drawable>
47 #include <osg/Material>
48 #include <osg/Math>
49 #include <osg/PolygonMode>
50 #include <osg/PolygonOffset>
51 #include <osg/Program>
52 #include <osg/Referenced>
53 #include <osg/RenderInfo>
54 #include <osg/ShadeModel>
55 #include <osg/StateSet>
56 #include <osg/Stencil>
57 #include <osg/TexEnv>
58 #include <osg/Texture1D>
59 #include <osg/Texture2D>
60 #include <osg/Texture3D>
61 #include <osg/TextureRectangle>
62 #include <osg/Uniform>
63 #include <osg/Vec4d>
64 #include <osgUtil/CullVisitor>
65 #include <osgDB/FileUtils>
66 #include <osgDB/Input>
67 #include <osgDB/ParameterOutput>
68 #include <osgDB/ReadFile>
69 #include <osgDB/Registry>
70
71 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
72 #include <simgear/scene/tgdb/userdata.hxx>
73 #include <simgear/scene/util/OsgMath.hxx>
74 #include <simgear/scene/util/SGSceneFeatures.hxx>
75 #include <simgear/scene/util/StateAttributeFactory.hxx>
76 #include <simgear/structure/OSGUtils.hxx>
77 #include <simgear/structure/SGExpression.hxx>
78 #include <simgear/props/vectorPropTemplates.hxx>
79
80
81 namespace simgear
82 {
83 using namespace std;
84 using namespace osg;
85 using namespace osgUtil;
86
87 using namespace effect;
88
89 Effect::Effect()
90     : _cache(0), _isRealized(false)
91 {
92 }
93
94 Effect::Effect(const Effect& rhs, const CopyOp& copyop)
95     : osg::Object(rhs,copyop), root(rhs.root), parametersProp(rhs.parametersProp), _cache(0),
96       _isRealized(rhs._isRealized)
97 {
98     typedef vector<ref_ptr<Technique> > TechniqueList;
99     for (TechniqueList::const_iterator itr = rhs.techniques.begin(),
100              end = rhs.techniques.end();
101          itr != end;
102          ++itr)
103         techniques.push_back(static_cast<Technique*>(copyop(itr->get())));
104
105     generator = rhs.generator;
106 }
107
108 // Assume that the last technique is always valid.
109 StateSet* Effect::getDefaultStateSet()
110 {
111     Technique* tniq = techniques.back().get();
112     if (!tniq)
113         return 0;
114     Pass* pass = tniq->passes.front().get();
115     return pass;
116 }
117
118 int Effect::getGenerator(Effect::Generator what) const
119 {
120     std::map<Generator,int>::const_iterator it = generator.find(what);
121     if(it == generator.end()) return -1;
122     else return it->second;
123 }
124
125 // There should always be a valid technique in an effect.
126
127 Technique* Effect::chooseTechnique(RenderInfo* info)
128 {
129     BOOST_FOREACH(ref_ptr<Technique>& technique, techniques)
130     {
131         if (technique->valid(info) == Technique::VALID)
132             return technique.get();
133     }
134     return 0;
135 }
136
137 void Effect::resizeGLObjectBuffers(unsigned int maxSize)
138 {
139     BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
140     {
141         technique->resizeGLObjectBuffers(maxSize);
142     }
143 }
144
145 void Effect::releaseGLObjects(osg::State* state) const
146 {
147     BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
148     {
149         technique->releaseGLObjects(state);
150     }
151 }
152
153 Effect::~Effect()
154 {
155     delete _cache;
156 }
157
158 void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
159                const SGReaderWriterOptions* 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         PassAttributeBuilder* builder
166             = PassAttributeBuilder::find(attrProp->getNameString());
167         if (builder)
168             builder->buildAttribute(effect, pass, attrProp, options);
169         else
170             SG_LOG(SG_INPUT, SG_ALERT,
171                    "skipping unknown pass attribute " << attrProp->getName());
172     }
173 }
174
175 // Default names for vector property components
176 const char* vec3Names[] = {"x", "y", "z"};
177 const char* vec4Names[] = {"x", "y", "z", "w"};
178
179 osg::Vec4f getColor(const SGPropertyNode* prop)
180 {
181     if (prop->nChildren() == 0) {
182         if (prop->getType() == props::VEC4D) {
183             return osg::Vec4f(toOsg(prop->getValue<SGVec4d>()));
184         } else if (prop->getType() == props::VEC3D) {
185             return osg::Vec4f(toOsg(prop->getValue<SGVec3d>()), 1.0f);
186         } else {
187             SG_LOG(SG_INPUT, SG_ALERT,
188                    "invalid color property " << prop->getName() << " "
189                    << prop->getStringValue());
190             return osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
191         }
192     } else {
193         osg::Vec4f result;
194         static const char* colors[] = {"r", "g", "b"};
195         for (int i = 0; i < 3; ++i) {
196             const SGPropertyNode* componentProp = prop->getChild(colors[i]);
197             result[i] = componentProp ? componentProp->getValue<float>() : 0.0f;
198         }
199         const SGPropertyNode* alphaProp = prop->getChild("a");
200         result[3] = alphaProp ? alphaProp->getValue<float>() : 1.0f;
201         return result;
202     }
203 }
204
205 struct LightingBuilder : public PassAttributeBuilder
206 {
207     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
208                         const SGReaderWriterOptions* options);
209 };
210
211 void LightingBuilder::buildAttribute(Effect* effect, Pass* pass,
212                                      const SGPropertyNode* prop,
213                                      const SGReaderWriterOptions* options)
214 {
215     const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
216     if (!realProp)
217         return;
218     pass->setMode(GL_LIGHTING, (realProp->getValue<bool>() ? StateAttribute::ON
219                                 : StateAttribute::OFF));
220 }
221
222 InstallAttributeBuilder<LightingBuilder> installLighting("lighting");
223
224 struct ShadeModelBuilder : public PassAttributeBuilder
225 {
226     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
227                         const SGReaderWriterOptions* options)
228     {
229         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
230         if (!realProp)
231             return;
232         StateAttributeFactory *attrFact = StateAttributeFactory::instance();
233         string propVal = realProp->getStringValue();
234         if (propVal == "flat")
235             pass->setAttribute(attrFact->getFlatShadeModel());
236         else if (propVal == "smooth")
237             pass->setAttribute(attrFact->getSmoothShadeModel());
238         else
239             SG_LOG(SG_INPUT, SG_ALERT,
240                    "invalid shade model property " << propVal);
241     }
242 };
243
244 InstallAttributeBuilder<ShadeModelBuilder> installShadeModel("shade-model");
245
246 struct CullFaceBuilder : PassAttributeBuilder
247 {
248     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
249                         const SGReaderWriterOptions* options)
250     {
251         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
252         if (!realProp) {
253             pass->setMode(GL_CULL_FACE, StateAttribute::OFF);
254             return;
255         }
256         StateAttributeFactory *attrFact = StateAttributeFactory::instance();
257         string propVal = realProp->getStringValue();
258         if (propVal == "front")
259             pass->setAttributeAndModes(attrFact->getCullFaceFront());
260         else if (propVal == "back")
261             pass->setAttributeAndModes(attrFact->getCullFaceBack());
262         else if (propVal == "front-back")
263             pass->setAttributeAndModes(new CullFace(CullFace::FRONT_AND_BACK));
264         else if (propVal == "off")
265             pass->setMode(GL_CULL_FACE, StateAttribute::OFF);
266         else
267             SG_LOG(SG_INPUT, SG_ALERT,
268                    "invalid cull face property " << propVal);            
269     }    
270 };
271
272 InstallAttributeBuilder<CullFaceBuilder> installCullFace("cull-face");
273
274 struct ColorMaskBuilder : PassAttributeBuilder
275 {
276     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
277                         const SGReaderWriterOptions* options)
278     {
279         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
280         if (!realProp)
281             return;
282
283         ColorMask *mask = new ColorMask;
284         Vec4 m = getColor(realProp);
285         mask->setMask(m.r() > 0.0, m.g() > 0.0, m.b() > 0.0, m.a() > 0.0);
286         pass->setAttributeAndModes(mask);
287     }    
288 };
289
290 InstallAttributeBuilder<ColorMaskBuilder> installColorMask("color-mask");
291
292 EffectNameValue<StateSet::RenderingHint> renderingHintInit[] =
293 {
294     { "default", StateSet::DEFAULT_BIN },
295     { "opaque", StateSet::OPAQUE_BIN },
296     { "transparent", StateSet::TRANSPARENT_BIN }
297 };
298
299 EffectPropertyMap<StateSet::RenderingHint> renderingHints(renderingHintInit);
300
301 struct HintBuilder : public PassAttributeBuilder
302 {
303     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
304                         const SGReaderWriterOptions* options)
305     {
306         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
307         if (!realProp)
308             return;
309         StateSet::RenderingHint renderingHint = StateSet::DEFAULT_BIN;
310         findAttr(renderingHints, realProp, renderingHint);
311         pass->setRenderingHint(renderingHint);
312     }    
313 };
314
315 InstallAttributeBuilder<HintBuilder> installHint("rendering-hint");
316
317 struct RenderBinBuilder : public PassAttributeBuilder
318 {
319     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
320                         const SGReaderWriterOptions* options)
321     {
322         if (!isAttributeActive(effect, prop))
323             return;
324         const SGPropertyNode* binProp = prop->getChild("bin-number");
325         binProp = getEffectPropertyNode(effect, binProp);
326         const SGPropertyNode* nameProp = prop->getChild("bin-name");
327         nameProp = getEffectPropertyNode(effect, nameProp);
328         if (binProp && nameProp) {
329             pass->setRenderBinDetails(binProp->getIntValue(),
330                                       nameProp->getStringValue());
331         } else {
332             if (!binProp)
333                 SG_LOG(SG_INPUT, SG_ALERT,
334                        "No render bin number specified in render bin section");
335             if (!nameProp)
336                 SG_LOG(SG_INPUT, SG_ALERT,
337                        "No render bin name specified in render bin section");
338         }
339     }
340 };
341
342 InstallAttributeBuilder<RenderBinBuilder> installRenderBin("render-bin");
343
344 struct MaterialBuilder : public PassAttributeBuilder
345 {
346     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
347                         const SGReaderWriterOptions* options);
348 };
349
350 EffectNameValue<Material::ColorMode> colorModeInit[] =
351 {
352     { "ambient", Material::AMBIENT },
353     { "ambient-and-diffuse", Material::AMBIENT_AND_DIFFUSE },
354     { "diffuse", Material::DIFFUSE },
355     { "emissive", Material::EMISSION },
356     { "specular", Material::SPECULAR },
357     { "off", Material::OFF }
358 };
359 EffectPropertyMap<Material::ColorMode> colorModes(colorModeInit);
360
361 void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
362                                      const SGPropertyNode* prop,
363                                      const SGReaderWriterOptions* options)
364 {
365     if (!isAttributeActive(effect, prop))
366         return;
367     Material* mat = new Material;
368     const SGPropertyNode* color = 0;
369     if ((color = getEffectPropertyChild(effect, prop, "ambient")))
370         mat->setAmbient(Material::FRONT_AND_BACK, getColor(color));
371     if ((color = getEffectPropertyChild(effect, prop, "ambient-front")))
372         mat->setAmbient(Material::FRONT, getColor(color));
373     if ((color = getEffectPropertyChild(effect, prop, "ambient-back")))
374         mat->setAmbient(Material::BACK, getColor(color));
375     if ((color = getEffectPropertyChild(effect, prop, "diffuse")))
376         mat->setDiffuse(Material::FRONT_AND_BACK, getColor(color));
377     if ((color = getEffectPropertyChild(effect, prop, "diffuse-front")))
378         mat->setDiffuse(Material::FRONT, getColor(color));
379     if ((color = getEffectPropertyChild(effect, prop, "diffuse-back")))
380         mat->setDiffuse(Material::BACK, getColor(color));
381     if ((color = getEffectPropertyChild(effect, prop, "specular")))
382         mat->setSpecular(Material::FRONT_AND_BACK, getColor(color));
383     if ((color = getEffectPropertyChild(effect, prop, "specular-front")))
384         mat->setSpecular(Material::FRONT, getColor(color));
385     if ((color = getEffectPropertyChild(effect, prop, "specular-back")))
386         mat->setSpecular(Material::BACK, getColor(color));
387     if ((color = getEffectPropertyChild(effect, prop, "emissive")))
388         mat->setEmission(Material::FRONT_AND_BACK, getColor(color));
389     if ((color = getEffectPropertyChild(effect, prop, "emissive-front")))
390         mat->setEmission(Material::FRONT, getColor(color));        
391     if ((color = getEffectPropertyChild(effect, prop, "emissive-back")))
392         mat->setEmission(Material::BACK, getColor(color));        
393     const SGPropertyNode* shininess = 0;
394     mat->setShininess(Material::FRONT_AND_BACK, 0.0f);
395     if ((shininess = getEffectPropertyChild(effect, prop, "shininess")))
396         mat->setShininess(Material::FRONT_AND_BACK, shininess->getFloatValue());
397     if ((shininess = getEffectPropertyChild(effect, prop, "shininess-front")))
398         mat->setShininess(Material::FRONT, shininess->getFloatValue());
399     if ((shininess = getEffectPropertyChild(effect, prop, "shininess-back")))
400         mat->setShininess(Material::BACK, shininess->getFloatValue());
401     Material::ColorMode colorMode = Material::OFF;
402     findAttr(colorModes, getEffectPropertyChild(effect, prop, "color-mode"),
403              colorMode);
404     mat->setColorMode(colorMode);
405     pass->setAttribute(mat);
406 }
407
408 InstallAttributeBuilder<MaterialBuilder> installMaterial("material");
409
410 EffectNameValue<BlendFunc::BlendFuncMode> blendFuncModesInit[] =
411 {
412     {"dst-alpha", BlendFunc::DST_ALPHA},
413     {"dst-color", BlendFunc::DST_COLOR},
414     {"one", BlendFunc::ONE},
415     {"one-minus-dst-alpha", BlendFunc::ONE_MINUS_DST_ALPHA},
416     {"one-minus-dst-color", BlendFunc::ONE_MINUS_DST_COLOR},
417     {"one-minus-src-alpha", BlendFunc::ONE_MINUS_SRC_ALPHA},
418     {"one-minus-src-color", BlendFunc::ONE_MINUS_SRC_COLOR},
419     {"src-alpha", BlendFunc::SRC_ALPHA},
420     {"src-alpha-saturate", BlendFunc::SRC_ALPHA_SATURATE},
421     {"src-color", BlendFunc::SRC_COLOR},
422     {"constant-color", BlendFunc::CONSTANT_COLOR},
423     {"one-minus-constant-color", BlendFunc::ONE_MINUS_CONSTANT_COLOR},
424     {"constant-alpha", BlendFunc::CONSTANT_ALPHA},
425     {"one-minus-constant-alpha", BlendFunc::ONE_MINUS_CONSTANT_ALPHA},
426     {"zero", BlendFunc::ZERO}
427 };
428 EffectPropertyMap<BlendFunc::BlendFuncMode> blendFuncModes(blendFuncModesInit);
429
430 struct BlendBuilder : public PassAttributeBuilder
431 {
432     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
433                         const SGReaderWriterOptions* options)
434     {
435         if (!isAttributeActive(effect, prop))
436             return;
437         // XXX Compatibility with early <blend> syntax; should go away
438         // before a release
439         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
440         if (!realProp)
441             return;
442         if (realProp->nChildren() == 0) {
443             pass->setMode(GL_BLEND, (realProp->getBoolValue()
444                                      ? StateAttribute::ON
445                                      : StateAttribute::OFF));
446             return;
447         }
448
449         const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
450                                                              "mode");
451         // XXX When dynamic parameters are supported, this code should
452         // create the blend function even if the mode is off.
453         if (pmode && !pmode->getValue<bool>()) {
454             pass->setMode(GL_BLEND, StateAttribute::OFF);
455             return;
456         }
457
458         parseBlendFunc(
459           pass,
460           getEffectPropertyChild(effect, prop, "source"),
461           getEffectPropertyChild(effect, prop, "destination"),
462           getEffectPropertyChild(effect, prop, "source-rgb"),
463           getEffectPropertyChild(effect, prop, "destination-rgb"),
464           getEffectPropertyChild(effect, prop, "source-alpha"),
465           getEffectPropertyChild(effect, prop, "destination-alpha")
466         );
467     }
468 };
469
470 InstallAttributeBuilder<BlendBuilder> installBlend("blend");
471
472
473 EffectNameValue<Stencil::Function> stencilFunctionInit[] =
474 {
475     {"never", Stencil::NEVER },
476     {"less", Stencil::LESS},
477     {"equal", Stencil::EQUAL},
478     {"less-or-equal", Stencil::LEQUAL},
479     {"greater", Stencil::GREATER},
480     {"not-equal", Stencil::NOTEQUAL},
481     {"greater-or-equal", Stencil::GEQUAL},
482     {"always", Stencil::ALWAYS}
483 };
484
485 EffectPropertyMap<Stencil::Function> stencilFunction(stencilFunctionInit);
486
487 EffectNameValue<Stencil::Operation> stencilOperationInit[] =
488 {
489     {"keep", Stencil::KEEP},
490     {"zero", Stencil::ZERO},
491     {"replace", Stencil::REPLACE},
492     {"increase", Stencil::INCR},
493     {"decrease", Stencil::DECR},
494     {"invert", Stencil::INVERT},
495     {"increase-wrap", Stencil::INCR_WRAP},
496     {"decrease-wrap", Stencil::DECR_WRAP}
497 };
498
499 EffectPropertyMap<Stencil::Operation> stencilOperation(stencilOperationInit);
500
501 struct StencilBuilder : public PassAttributeBuilder
502 {
503     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
504                         const SGReaderWriterOptions* options)
505     {
506         if (!isAttributeActive(effect, prop))
507             return;
508
509         const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
510                                                              "mode");
511         if (pmode && !pmode->getValue<bool>()) {
512             pass->setMode(GL_STENCIL, StateAttribute::OFF);
513             return;
514         }
515         const SGPropertyNode* pfunction
516             = getEffectPropertyChild(effect, prop, "function");
517         const SGPropertyNode* pvalue
518             = getEffectPropertyChild(effect, prop, "value");
519         const SGPropertyNode* pmask
520             = getEffectPropertyChild(effect, prop, "mask");
521         const SGPropertyNode* psfail
522             = getEffectPropertyChild(effect, prop, "stencil-fail");
523         const SGPropertyNode* pzfail
524             = getEffectPropertyChild(effect, prop, "z-fail");
525         const SGPropertyNode* ppass
526             = getEffectPropertyChild(effect, prop, "pass");
527
528         Stencil::Function func = Stencil::ALWAYS;  // Always pass
529         int ref = 0;
530         unsigned int mask = ~0u;  // All bits on
531         Stencil::Operation sfailop = Stencil::KEEP;  // Keep the old values as default
532         Stencil::Operation zfailop = Stencil::KEEP;
533         Stencil::Operation passop = Stencil::KEEP;
534
535         ref_ptr<Stencil> stencilFunc = new Stencil;
536
537         if (pfunction)
538             findAttr(stencilFunction, pfunction, func);
539         if (pvalue)
540             ref = pvalue->getIntValue();
541         if (pmask) 
542             mask = pmask->getIntValue();
543
544         if (psfail)
545             findAttr(stencilOperation, psfail, sfailop);
546         if (pzfail)
547             findAttr(stencilOperation, pzfail, zfailop);
548         if (ppass)
549             findAttr(stencilOperation, ppass, passop);
550
551         // Set the stencil operation
552         stencilFunc->setFunction(func, ref, mask);
553
554         // Set the operation, s-fail, s-pass/z-fail, s-pass/z-pass
555         stencilFunc->setOperation(sfailop, zfailop, passop);
556
557         // Add the operation to pass
558         pass->setAttributeAndModes(stencilFunc.get());
559     }
560 };
561
562 InstallAttributeBuilder<StencilBuilder> installStencil("stencil");
563
564 struct AlphaToCoverageBuilder : public PassAttributeBuilder
565 {
566     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
567                         const SGReaderWriterOptions* options);
568 };
569
570 #ifndef GL_SAMPLE_ALPHA_TO_COVERAGE_ARB
571 #define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E
572 #endif
573
574 void AlphaToCoverageBuilder::buildAttribute(Effect* effect, Pass* pass,
575                                      const SGPropertyNode* prop,
576                                      const SGReaderWriterOptions* options)
577 {
578     const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
579     if (!realProp)
580         return;
581     pass->setMode(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB, (realProp->getValue<bool>() ? 
582                                     StateAttribute::ON : StateAttribute::OFF));
583 }
584
585 InstallAttributeBuilder<AlphaToCoverageBuilder> installAlphaToCoverage("alpha-to-coverage");
586
587 EffectNameValue<AlphaFunc::ComparisonFunction> alphaComparisonInit[] =
588 {
589     {"never", AlphaFunc::NEVER},
590     {"less", AlphaFunc::LESS},
591     {"equal", AlphaFunc::EQUAL},
592     {"lequal", AlphaFunc::LEQUAL},
593     {"greater", AlphaFunc::GREATER},
594     {"notequal", AlphaFunc::NOTEQUAL},
595     {"gequal", AlphaFunc::GEQUAL},
596     {"always", AlphaFunc::ALWAYS}
597 };
598 EffectPropertyMap<AlphaFunc::ComparisonFunction>
599 alphaComparison(alphaComparisonInit);
600
601 struct AlphaTestBuilder : public PassAttributeBuilder
602 {
603     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
604                         const SGReaderWriterOptions* options)
605     {
606         if (!isAttributeActive(effect, prop))
607             return;
608         // XXX Compatibility with early <alpha-test> syntax; should go away
609         // before a release
610         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
611         if (!realProp)
612             return;
613         if (realProp->nChildren() == 0) {
614             pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
615                                      ? StateAttribute::ON
616                                      : StateAttribute::OFF));
617             return;
618         }
619
620         const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
621                                                              "mode");
622         // XXX When dynamic parameters are supported, this code should
623         // create the blend function even if the mode is off.
624         if (pmode && !pmode->getValue<bool>()) {
625             pass->setMode(GL_ALPHA_TEST, StateAttribute::OFF);
626             return;
627         }
628         const SGPropertyNode* pComp = getEffectPropertyChild(effect, prop,
629                                                              "comparison");
630         const SGPropertyNode* pRef = getEffectPropertyChild(effect, prop,
631                                                              "reference");
632
633         AlphaFunc::ComparisonFunction func = AlphaFunc::ALWAYS;
634         float refValue = 1.0f;
635         if (pComp)
636             findAttr(alphaComparison, pComp, func);
637         if (pRef)
638             refValue = pRef->getValue<float>();
639         if (func == AlphaFunc::GREATER && osg::equivalent(refValue, 1.0f)) {
640             pass->setAttributeAndModes(StateAttributeFactory::instance()
641                                        ->getStandardAlphaFunc());
642         } else {
643             AlphaFunc* alphaFunc = new AlphaFunc;
644             alphaFunc->setFunction(func);
645             alphaFunc->setReferenceValue(refValue);
646             pass->setAttributeAndModes(alphaFunc);
647         }
648     }
649 };
650
651 InstallAttributeBuilder<AlphaTestBuilder> installAlphaTest("alpha-test");
652
653 InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
654
655 // Shader key, used both for shaders with relative and absolute names
656 typedef pair<string, int> ShaderKey;
657
658 inline ShaderKey makeShaderKey(SGPropertyNode_ptr& ptr, int shaderType)
659 {
660     return ShaderKey(ptr->getStringValue(), shaderType);
661 }
662
663 struct ProgramKey
664 {
665     typedef pair<string, int> AttribKey;
666     osgDB::FilePathList paths;
667     vector<ShaderKey> shaders;
668     vector<AttribKey> attributes;
669     struct EqualTo
670     {
671         bool operator()(const ProgramKey& lhs, const ProgramKey& rhs) const
672         {
673             return (lhs.paths.size() == rhs.paths.size()
674                     && equal(lhs.paths.begin(), lhs.paths.end(),
675                              rhs.paths.begin())
676                     && lhs.shaders.size() == rhs.shaders.size()
677                     && equal (lhs.shaders.begin(), lhs.shaders.end(),
678                               rhs.shaders.begin())
679                     && lhs.attributes.size() == rhs.attributes.size()
680                     && equal(lhs.attributes.begin(), lhs.attributes.end(),
681                              rhs.attributes.begin()));
682         }
683     };
684 };
685
686 size_t hash_value(const ProgramKey& key)
687 {
688     size_t seed = 0;
689     boost::hash_range(seed, key.paths.begin(), key.paths.end());
690     boost::hash_range(seed, key.shaders.begin(), key.shaders.end());
691     boost::hash_range(seed, key.attributes.begin(), key.attributes.end());
692     return seed;
693 }
694
695 // XXX Should these be protected by a mutex? Probably
696
697 typedef tr1::unordered_map<ProgramKey, ref_ptr<Program>,
698                            boost::hash<ProgramKey>, ProgramKey::EqualTo>
699 ProgramMap;
700 ProgramMap programMap;
701 ProgramMap resolvedProgramMap;  // map with resolved shader file names
702
703 typedef tr1::unordered_map<ShaderKey, ref_ptr<Shader>, boost::hash<ShaderKey> >
704 ShaderMap;
705 ShaderMap shaderMap;
706
707 void reload_shaders()
708 {
709     for(ShaderMap::iterator sitr = shaderMap.begin(); sitr != shaderMap.end(); ++sitr)
710     {
711         Shader *shader = sitr->second.get();
712         string fileName = SGModelLib::findDataFile(sitr->first.first);
713         if (!fileName.empty()) {
714             shader->loadShaderSourceFromFile(fileName);
715         }
716     }
717 }
718
719 struct ShaderProgramBuilder : PassAttributeBuilder
720 {
721     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
722                         const SGReaderWriterOptions* options);
723 };
724
725
726 EffectNameValue<GLint> geometryInputTypeInit[] =
727 {
728     {"points", GL_POINTS},
729     {"lines", GL_LINES},
730     {"lines-adjacency", GL_LINES_ADJACENCY_EXT},
731     {"triangles", GL_TRIANGLES},
732     {"triangles-adjacency", GL_TRIANGLES_ADJACENCY_EXT},
733 };
734 EffectPropertyMap<GLint>
735 geometryInputType(geometryInputTypeInit);
736
737
738 EffectNameValue<GLint> geometryOutputTypeInit[] =
739 {
740     {"points", GL_POINTS},
741     {"line-strip", GL_LINE_STRIP},
742     {"triangle-strip", GL_TRIANGLE_STRIP}
743 };
744 EffectPropertyMap<GLint>
745 geometryOutputType(geometryOutputTypeInit);
746
747 void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
748                                           const SGPropertyNode* prop,
749                                           const SGReaderWriterOptions*
750                                           options)
751 {
752     using namespace boost;
753     if (!isAttributeActive(effect, prop))
754         return;
755     PropertyList pVertShaders = prop->getChildren("vertex-shader");
756     PropertyList pGeomShaders = prop->getChildren("geometry-shader");
757     PropertyList pFragShaders = prop->getChildren("fragment-shader");
758     PropertyList pAttributes = prop->getChildren("attribute");
759     ProgramKey prgKey;
760     std::back_insert_iterator<vector<ShaderKey> > inserter(prgKey.shaders);
761     transform(pVertShaders.begin(), pVertShaders.end(), inserter,
762               boost::bind(makeShaderKey, _1, Shader::VERTEX));
763     transform(pGeomShaders.begin(), pGeomShaders.end(), inserter,
764               boost::bind(makeShaderKey, _1, Shader::GEOMETRY));
765     transform(pFragShaders.begin(), pFragShaders.end(), inserter,
766               boost::bind(makeShaderKey, _1, Shader::FRAGMENT));
767     for (PropertyList::iterator itr = pAttributes.begin(),
768              e = pAttributes.end();
769          itr != e;
770          ++itr) {
771         const SGPropertyNode* pName = getEffectPropertyChild(effect, *itr,
772                                                              "name");
773         const SGPropertyNode* pIndex = getEffectPropertyChild(effect, *itr,
774                                                               "index");
775         if (!pName || ! pIndex)
776             throw BuilderException("malformed attribute property");
777         prgKey.attributes
778             .push_back(ProgramKey::AttribKey(pName->getStringValue(),
779                                              pIndex->getValue<int>()));
780     }
781     if (options)
782         prgKey.paths = options->getDatabasePathList();
783     Program* program = 0;
784     ProgramMap::iterator pitr = programMap.find(prgKey);
785     if (pitr != programMap.end()) {
786         program = pitr->second.get();
787         pass->setAttributeAndModes(program);
788         return;
789     }
790     // The program wasn't in the map using the load path passed in with
791     // the options, but it might have already been loaded using a
792     // different load path i.e., its shaders were found in the fg data
793     // directory. So, resolve the shaders' file names and look in the
794     // resolvedProgramMap for a program using those shaders.
795     ProgramKey resolvedKey;
796     resolvedKey.attributes = prgKey.attributes;
797     BOOST_FOREACH(const ShaderKey& shaderKey, prgKey.shaders)
798     {
799         const string& shaderName = shaderKey.first;
800         Shader::Type stype = (Shader::Type)shaderKey.second;
801         string fileName = SGModelLib::findDataFile(shaderName, options);
802         if (fileName.empty())
803             throw BuilderException(string("couldn't find shader ") +
804                                    shaderName);
805         resolvedKey.shaders.push_back(ShaderKey(fileName, stype));
806     }
807     ProgramMap::iterator resitr = resolvedProgramMap.find(resolvedKey);
808     if (resitr != resolvedProgramMap.end()) {
809         program = resitr->second.get();
810         programMap.insert(ProgramMap::value_type(prgKey, program));
811         pass->setAttributeAndModes(program);
812         return;
813     }
814     program = new Program;
815     BOOST_FOREACH(const ShaderKey& skey, resolvedKey.shaders)
816     {
817         const string& fileName = skey.first;
818         Shader::Type stype = (Shader::Type)skey.second;
819         ShaderMap::iterator sitr = shaderMap.find(skey);
820         if (sitr != shaderMap.end()) {
821             program->addShader(sitr->second.get());
822         } else {
823             ref_ptr<Shader> shader = new Shader(stype);
824                         shader->setName(fileName);
825             if (shader->loadShaderSourceFromFile(fileName)) {
826                 program->addShader(shader.get());
827                 shaderMap.insert(ShaderMap::value_type(skey, shader));
828             }
829         }
830     }
831     BOOST_FOREACH(const ProgramKey::AttribKey& key, prgKey.attributes) {
832         program->addBindAttribLocation(key.first, key.second);
833     }
834     const SGPropertyNode* pGeometryVerticesOut
835         = getEffectPropertyChild(effect, prop, "geometry-vertices-out");
836     if (pGeometryVerticesOut)
837         program->setParameter(GL_GEOMETRY_VERTICES_OUT_EXT,
838                               pGeometryVerticesOut->getIntValue());
839     const SGPropertyNode* pGeometryInputType
840         = getEffectPropertyChild(effect, prop, "geometry-input-type");
841     if (pGeometryInputType) {
842         GLint type;
843         findAttr(geometryInputType, pGeometryInputType->getStringValue(), type);
844         program->setParameter(GL_GEOMETRY_INPUT_TYPE_EXT, type);
845     }
846     const SGPropertyNode* pGeometryOutputType
847         = getEffectPropertyChild(effect, prop, "geometry-output-type");
848     if (pGeometryOutputType) {
849         GLint type;
850         findAttr(geometryOutputType, pGeometryOutputType->getStringValue(),
851                  type);
852         program->setParameter(GL_GEOMETRY_OUTPUT_TYPE_EXT, type);
853     }
854     programMap.insert(ProgramMap::value_type(prgKey, program));
855     resolvedProgramMap.insert(ProgramMap::value_type(resolvedKey, program));
856     pass->setAttributeAndModes(program);
857 }
858
859 InstallAttributeBuilder<ShaderProgramBuilder> installShaderProgram("program");
860
861 EffectNameValue<Uniform::Type> uniformTypesInit[] =
862 {
863     {"bool", Uniform::BOOL},
864     {"int", Uniform::INT},
865     {"float", Uniform::FLOAT},
866     {"float-vec3", Uniform::FLOAT_VEC3},
867     {"float-vec4", Uniform::FLOAT_VEC4},
868     {"sampler-1d", Uniform::SAMPLER_1D},
869     {"sampler-1d-shadow", Uniform::SAMPLER_1D_SHADOW},
870     {"sampler-2d", Uniform::SAMPLER_2D},
871     {"sampler-2d-shadow", Uniform::SAMPLER_2D_SHADOW},
872     {"sampler-3d", Uniform::SAMPLER_3D},
873     {"sampler-cube", Uniform::SAMPLER_CUBE}
874 };
875 EffectPropertyMap<Uniform::Type> uniformTypes(uniformTypesInit);
876
877 // Optimization hack for common uniforms.
878 // XXX protect these with a mutex?
879
880 ref_ptr<Uniform> texture0;
881 ref_ptr<Uniform> colorMode[3];
882
883 struct UniformBuilder :public PassAttributeBuilder
884 {
885     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
886                         const SGReaderWriterOptions* options)
887     {
888         if (!texture0.valid()) {
889             texture0 = new Uniform(Uniform::SAMPLER_2D, "texture");
890             texture0->set(0);
891             texture0->setDataVariance(Object::STATIC);
892             for (int i = 0; i < 3; ++i) {
893                 colorMode[i] = new Uniform(Uniform::INT, "colorMode");
894                 colorMode[i]->set(i);
895                 colorMode[i]->setDataVariance(Object::STATIC);
896             }
897         }
898         if (!isAttributeActive(effect, prop))
899             return;
900         const SGPropertyNode* nameProp = prop->getChild("name");
901         const SGPropertyNode* typeProp = prop->getChild("type");
902         const SGPropertyNode* positionedProp = prop->getChild("positioned");
903         const SGPropertyNode* valProp = prop->getChild("value");
904         string name;
905         Uniform::Type uniformType = Uniform::FLOAT;
906         if (nameProp) {
907             name = nameProp->getStringValue();
908         } else {
909             SG_LOG(SG_INPUT, SG_ALERT, "No name for uniform property ");
910             return;
911         }
912         if (!valProp) {
913             SG_LOG(SG_INPUT, SG_ALERT, "No value for uniform property "
914                    << name);
915             return;
916         }
917         if (!typeProp) {
918             props::Type propType = valProp->getType();
919             switch (propType) {
920             case props::BOOL:
921                 uniformType = Uniform::BOOL;
922                 break;
923             case props::INT:
924                 uniformType = Uniform::INT;
925                 break;
926             case props::FLOAT:
927             case props::DOUBLE:
928                 break;          // default float type;
929             case props::VEC3D:
930                 uniformType = Uniform::FLOAT_VEC3;
931                 break;
932             case props::VEC4D:
933                 uniformType = Uniform::FLOAT_VEC4;
934                 break;
935             default:
936                 SG_LOG(SG_INPUT, SG_ALERT, "Can't deduce type of uniform "
937                        << name);
938                 return;
939             }
940         } else {
941             findAttr(uniformTypes, typeProp, uniformType);
942         }
943         ref_ptr<Uniform> uniform = new Uniform;
944         uniform->setName(name);
945         uniform->setType(uniformType);
946         switch (uniformType) {
947         case Uniform::BOOL:
948             initFromParameters(effect, valProp, uniform.get(),
949                                static_cast<bool (Uniform::*)(bool)>(&Uniform::set),
950                                options);
951             break;
952         case Uniform::FLOAT:
953             initFromParameters(effect, valProp, uniform.get(),
954                                static_cast<bool (Uniform::*)(float)>(&Uniform::set),
955                                options);
956             break;
957         case Uniform::FLOAT_VEC3:
958             initFromParameters(effect, valProp, uniform.get(),
959                                static_cast<bool (Uniform::*)(const Vec3&)>(&Uniform::set),
960                                vec3Names, options);
961             break;
962         case Uniform::FLOAT_VEC4:
963             initFromParameters(effect, valProp, uniform.get(),
964                                static_cast<bool (Uniform::*)(const Vec4&)>(&Uniform::set),
965                                vec4Names, options);
966             break;
967         case Uniform::INT:
968         case Uniform::SAMPLER_1D:
969         case Uniform::SAMPLER_2D:
970         case Uniform::SAMPLER_3D:
971         case Uniform::SAMPLER_1D_SHADOW:
972         case Uniform::SAMPLER_2D_SHADOW:
973         case Uniform::SAMPLER_CUBE:
974             initFromParameters(effect, valProp, uniform.get(),
975                                static_cast<bool (Uniform::*)(int)>(&Uniform::set),
976                                options);
977             break;
978         default: // avoid compiler warning
979             break;
980         }
981         // optimize common uniforms
982         if (uniformType == Uniform::SAMPLER_2D || uniformType == Uniform::INT)
983         {
984             int val = 0;
985             uniform->get(val); // 'val' remains unchanged in case of error (Uniform is a non-scalar)
986             if (uniformType == Uniform::SAMPLER_2D && val == 0
987                 && name == "texture") {
988                 uniform = texture0;
989             } else if (uniformType == Uniform::INT && val >= 0 && val < 3
990                        && name == "colorMode") {
991                 uniform = colorMode[val];
992             }
993         }
994         pass->addUniform(uniform.get());
995         if (positionedProp && positionedProp->getBoolValue() && uniformType == Uniform::FLOAT_VEC4) {
996             osg::Vec4 offset;
997             uniform->get(offset);
998             pass->addPositionedUniform( name, offset );
999         }
1000     }
1001 };
1002
1003 InstallAttributeBuilder<UniformBuilder> installUniform("uniform");
1004
1005 // Not sure what to do with "name". At one point I wanted to use it to
1006 // order the passes, but I do support render bin and stuff too...
1007
1008 struct NameBuilder : public PassAttributeBuilder
1009 {
1010     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
1011                         const SGReaderWriterOptions* options)
1012     {
1013         // name can't use <use>
1014         string name = prop->getStringValue();
1015         if (!name.empty())
1016             pass->setName(name);
1017     }
1018 };
1019
1020 InstallAttributeBuilder<NameBuilder> installName("name");
1021
1022 EffectNameValue<PolygonMode::Mode> polygonModeModesInit[] =
1023 {
1024     {"fill", PolygonMode::FILL},
1025     {"line", PolygonMode::LINE},
1026     {"point", PolygonMode::POINT}
1027 };
1028 EffectPropertyMap<PolygonMode::Mode> polygonModeModes(polygonModeModesInit);
1029
1030 struct PolygonModeBuilder : public PassAttributeBuilder
1031 {
1032     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
1033                         const SGReaderWriterOptions* options)
1034     {
1035         if (!isAttributeActive(effect, prop))
1036             return;
1037         const SGPropertyNode* frontProp
1038             = getEffectPropertyChild(effect, prop, "front");
1039         const SGPropertyNode* backProp
1040             = getEffectPropertyChild(effect, prop, "back");
1041         ref_ptr<PolygonMode> pmode = new PolygonMode;
1042         PolygonMode::Mode frontMode = PolygonMode::FILL;
1043         PolygonMode::Mode backMode = PolygonMode::FILL;
1044         if (frontProp) {
1045             findAttr(polygonModeModes, frontProp, frontMode);
1046             pmode->setMode(PolygonMode::FRONT, frontMode);
1047         }
1048         if (backProp) {
1049             findAttr(polygonModeModes, backProp, backMode);
1050             pmode->setMode(PolygonMode::BACK, backMode);
1051         }
1052         pass->setAttribute(pmode.get());
1053     }
1054 };
1055
1056 InstallAttributeBuilder<PolygonModeBuilder> installPolygonMode("polygon-mode");
1057
1058 struct PolygonOffsetBuilder : public PassAttributeBuilder
1059 {
1060     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
1061                         const SGReaderWriterOptions* options)
1062     {
1063         if (!isAttributeActive(effect, prop))
1064             return;
1065         
1066         const SGPropertyNode* factor
1067            = getEffectPropertyChild(effect, prop, "factor");
1068         const SGPropertyNode* units
1069            = getEffectPropertyChild(effect, prop, "units");
1070         
1071         ref_ptr<PolygonOffset> polyoffset = new PolygonOffset;
1072         
1073         polyoffset->setFactor(factor->getFloatValue());
1074         polyoffset->setUnits(units->getFloatValue());
1075
1076         SG_LOG(SG_INPUT, SG_BULK,
1077                    "Set PolygonOffset to " << polyoffset->getFactor() << polyoffset->getUnits() );            
1078
1079         pass->setAttributeAndModes(polyoffset.get(),
1080                                    StateAttribute::OVERRIDE|StateAttribute::ON);
1081     }
1082 };
1083
1084 InstallAttributeBuilder<PolygonOffsetBuilder> installPolygonOffset("polygon-offset");
1085
1086 struct VertexProgramTwoSideBuilder : public PassAttributeBuilder
1087 {
1088     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
1089                         const SGReaderWriterOptions* options)
1090     {
1091         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
1092         if (!realProp)
1093             return;
1094         pass->setMode(GL_VERTEX_PROGRAM_TWO_SIDE,
1095                       (realProp->getValue<bool>()
1096                        ? StateAttribute::ON : StateAttribute::OFF));
1097     }
1098 };
1099
1100 InstallAttributeBuilder<VertexProgramTwoSideBuilder>
1101 installTwoSide("vertex-program-two-side");
1102
1103 struct VertexProgramPointSizeBuilder : public PassAttributeBuilder
1104 {
1105     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
1106                         const SGReaderWriterOptions* options)
1107     {
1108         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
1109         if (!realProp)
1110             return;
1111         pass->setMode(GL_VERTEX_PROGRAM_POINT_SIZE,
1112                       (realProp->getValue<bool>()
1113                        ? StateAttribute::ON : StateAttribute::OFF));
1114     }
1115 };
1116
1117 InstallAttributeBuilder<VertexProgramPointSizeBuilder>
1118 installPointSize("vertex-program-point-size");
1119
1120 EffectNameValue<Depth::Function> depthFunctionInit[] =
1121 {
1122     {"never", Depth::NEVER},
1123     {"less", Depth::LESS},
1124     {"equal", Depth::EQUAL},
1125     {"lequal", Depth::LEQUAL},
1126     {"greater", Depth::GREATER},
1127     {"notequal", Depth::NOTEQUAL},
1128     {"gequal", Depth::GEQUAL},
1129     {"always", Depth::ALWAYS}
1130 };
1131 EffectPropertyMap<Depth::Function> depthFunction(depthFunctionInit);
1132
1133 struct DepthBuilder : public PassAttributeBuilder
1134 {
1135     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
1136                         const SGReaderWriterOptions* options)
1137     {
1138         if (!isAttributeActive(effect, prop))
1139             return;
1140         ref_ptr<Depth> depth = new Depth;
1141         const SGPropertyNode* pfunc
1142             = getEffectPropertyChild(effect, prop, "function");
1143         if (pfunc) {
1144             Depth::Function func = Depth::LESS;
1145             findAttr(depthFunction, pfunc, func);
1146             depth->setFunction(func);
1147         }
1148         const SGPropertyNode* pnear
1149             = getEffectPropertyChild(effect, prop, "near");
1150         if (pnear)
1151             depth->setZNear(pnear->getValue<double>());
1152         const SGPropertyNode* pfar
1153             = getEffectPropertyChild(effect, prop, "far");
1154         if (pfar)
1155             depth->setZFar(pfar->getValue<double>());
1156         const SGPropertyNode* pmask
1157             = getEffectPropertyChild(effect, prop, "write-mask");
1158         if (pmask)
1159             depth->setWriteMask(pmask->getValue<bool>());
1160         const SGPropertyNode* penabled
1161             = getEffectPropertyChild(effect, prop, "enabled");
1162         bool enabled = ( penabled == 0 || penabled->getBoolValue() );
1163         pass->setAttributeAndModes(depth.get(), enabled ? osg::StateAttribute::ON : osg::StateAttribute::OFF);
1164     }
1165 };
1166
1167 InstallAttributeBuilder<DepthBuilder> installDepth("depth");
1168
1169 void buildTechnique(Effect* effect, const SGPropertyNode* prop,
1170                     const SGReaderWriterOptions* options)
1171 {
1172     Technique* tniq = new Technique;
1173     effect->techniques.push_back(tniq);
1174     const SGPropertyNode* predProp = prop->getChild("predicate");
1175     if (!predProp) {
1176         tniq->setAlwaysValid(true);
1177     } else {
1178         try {
1179             TechniquePredParser parser;
1180             parser.setTechnique(tniq);
1181             expression::BindingLayout& layout = parser.getBindingLayout();
1182             /*int contextLoc = */layout.addBinding("__contextId", expression::INT);
1183             SGExpressionb* validExp
1184                 = dynamic_cast<SGExpressionb*>(parser.read(predProp
1185                                                            ->getChild(0)));
1186             if (validExp)
1187                 tniq->setValidExpression(validExp, layout);
1188             else
1189                 throw expression::ParseError("technique predicate is not a boolean expression");
1190         }
1191         catch (expression::ParseError& except)
1192         {
1193             SG_LOG(SG_INPUT, SG_ALERT,
1194                    "parsing technique predicate " << except.getMessage());
1195             tniq->setAlwaysValid(false);
1196         }
1197     }
1198     PropertyList passProps = prop->getChildren("pass");
1199     for (PropertyList::iterator itr = passProps.begin(), e = passProps.end();
1200          itr != e;
1201          ++itr) {
1202         buildPass(effect, tniq, itr->ptr(), options);
1203     }
1204 }
1205
1206 // Specifically for .ac files...
1207 bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
1208 {
1209     SGPropertyNode* paramRoot = makeChild(effectRoot, "parameters");
1210     SGPropertyNode* matNode = paramRoot->getChild("material", 0, true);
1211     Vec4f ambVal, difVal, specVal, emisVal;
1212     float shininess = 0.0f;
1213     const Material* mat = getStateAttribute<Material>(ss);
1214     if (mat) {
1215         ambVal = mat->getAmbient(Material::FRONT_AND_BACK);
1216         difVal = mat->getDiffuse(Material::FRONT_AND_BACK);
1217         specVal = mat->getSpecular(Material::FRONT_AND_BACK);
1218         emisVal = mat->getEmission(Material::FRONT_AND_BACK);
1219         shininess = mat->getShininess(Material::FRONT_AND_BACK);
1220         makeChild(matNode, "active")->setValue(true);
1221         makeChild(matNode, "ambient")->setValue(toVec4d(toSG(ambVal)));
1222         makeChild(matNode, "diffuse")->setValue(toVec4d(toSG(difVal)));
1223         makeChild(matNode, "specular")->setValue(toVec4d(toSG(specVal)));
1224         makeChild(matNode, "emissive")->setValue(toVec4d(toSG(emisVal)));
1225         makeChild(matNode, "shininess")->setValue(shininess);
1226         matNode->getChild("color-mode", 0, true)->setStringValue("diffuse");
1227     } else {
1228         makeChild(matNode, "active")->setValue(false);
1229     }
1230     const ShadeModel* sm = getStateAttribute<ShadeModel>(ss);
1231     string shadeModelString("smooth");
1232     if (sm) {
1233         ShadeModel::Mode smMode = sm->getMode();
1234         if (smMode == ShadeModel::FLAT)
1235             shadeModelString = "flat";
1236     }
1237     makeChild(paramRoot, "shade-model")->setStringValue(shadeModelString);
1238     string cullFaceString("off");
1239     const CullFace* cullFace = getStateAttribute<CullFace>(ss);
1240     if (cullFace) {
1241         switch (cullFace->getMode()) {
1242         case CullFace::FRONT:
1243             cullFaceString = "front";
1244             break;
1245         case CullFace::BACK:
1246             cullFaceString = "back";
1247             break;
1248         case CullFace::FRONT_AND_BACK:
1249             cullFaceString = "front-back";
1250             break;
1251         default:
1252             break;
1253         }
1254     }
1255     makeChild(paramRoot, "cull-face")->setStringValue(cullFaceString);
1256     // Macintosh ATI workaround
1257     bool vertexTwoSide = cullFaceString == "off";
1258     makeChild(paramRoot, "vertex-program-two-side")->setValue(vertexTwoSide);
1259     const BlendFunc* blendFunc = getStateAttribute<BlendFunc>(ss);
1260     SGPropertyNode* blendNode = makeChild(paramRoot, "blend");
1261     if (blendFunc) {
1262         string sourceMode = findName(blendFuncModes, blendFunc->getSource());
1263         string destMode = findName(blendFuncModes, blendFunc->getDestination());
1264         makeChild(blendNode, "active")->setValue(true);
1265         makeChild(blendNode, "source")->setStringValue(sourceMode);
1266         makeChild(blendNode, "destination")->setStringValue(destMode);
1267         makeChild(blendNode, "mode")->setValue(true);
1268     } else {
1269         makeChild(blendNode, "active")->setValue(false);
1270     }
1271     string renderingHint = findName(renderingHints, ss->getRenderingHint());
1272     makeChild(paramRoot, "rendering-hint")->setStringValue(renderingHint);
1273     makeTextureParameters(paramRoot, ss);
1274     return true;
1275 }
1276
1277 // Walk the techniques property tree, building techniques and
1278 // passes.
1279 bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
1280 {
1281     if (_isRealized)
1282         return true;
1283     PropertyList tniqList = root->getChildren("technique");
1284     for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
1285          itr != e;
1286          ++itr)
1287         buildTechnique(this, *itr, options);
1288     _isRealized = true;
1289     return true;
1290 }
1291
1292 void Effect::InitializeCallback::doUpdate(osg::Node* node, osg::NodeVisitor* nv)
1293 {
1294     EffectGeode* eg = dynamic_cast<EffectGeode*>(node);
1295     if (!eg)
1296         return;
1297     Effect* effect = eg->getEffect();
1298     if (!effect)
1299         return;
1300     SGPropertyNode* root = getPropertyRoot();
1301     for (vector<SGSharedPtr<Updater> >::iterator itr = effect->_extraData.begin(),
1302              end = effect->_extraData.end();
1303          itr != end;
1304          ++itr) {
1305         InitializeWhenAdded* adder
1306             = dynamic_cast<InitializeWhenAdded*>(itr->ptr());
1307         if (adder)
1308             adder->initOnAdd(effect, root);
1309     }
1310 }
1311
1312 bool Effect::Key::EqualTo::operator()(const Effect::Key& lhs,
1313                                       const Effect::Key& rhs) const
1314 {
1315     if (lhs.paths.size() != rhs.paths.size()
1316         || !equal(lhs.paths.begin(), lhs.paths.end(), rhs.paths.begin()))
1317         return false;
1318     if (lhs.unmerged.valid() && rhs.unmerged.valid())
1319         return props::Compare()(lhs.unmerged, rhs.unmerged);
1320     else
1321         return lhs.unmerged == rhs.unmerged;
1322 }
1323
1324 size_t hash_value(const Effect::Key& key)
1325 {
1326     size_t seed = 0;
1327     if (key.unmerged.valid())
1328         boost::hash_combine(seed, *key.unmerged);
1329     boost::hash_range(seed, key.paths.begin(), key.paths.end());
1330     return seed;
1331 }
1332
1333 bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
1334 {
1335     const Effect& effect = static_cast<const Effect&>(obj);
1336
1337     fw.indent() << "techniques " << effect.techniques.size() << "\n";
1338     BOOST_FOREACH(const ref_ptr<Technique>& technique, effect.techniques) {
1339         fw.writeObject(*technique);
1340     }
1341     return true;
1342 }
1343
1344 namespace
1345 {
1346 osgDB::RegisterDotOsgWrapperProxy effectProxy
1347 (
1348     new Effect,
1349     "simgear::Effect",
1350     "Object simgear::Effect",
1351     0,
1352     &Effect_writeLocalData
1353     );
1354 }
1355
1356 // Property expressions for technique predicates
1357 template<typename T>
1358 class PropertyExpression : public SGExpression<T>
1359 {
1360 public:
1361     PropertyExpression(SGPropertyNode* pnode) : _pnode(pnode), _listener(NULL) {}
1362     
1363     ~PropertyExpression()
1364     {
1365         delete _listener;
1366     }
1367     
1368     void eval(T& value, const expression::Binding*) const
1369     {
1370         value = _pnode->getValue<T>();
1371     }
1372     
1373     void setListener(SGPropertyChangeListener* l)
1374     {
1375         _listener = l;
1376     }
1377 protected:
1378     SGPropertyNode_ptr _pnode;
1379     SGPropertyChangeListener* _listener;
1380 };
1381
1382 class EffectPropertyListener : public SGPropertyChangeListener
1383 {
1384 public:
1385     EffectPropertyListener(Technique* tniq) : _tniq(tniq) {}
1386     
1387     void valueChanged(SGPropertyNode* node)
1388     {
1389         if (_tniq.valid())
1390             _tniq->refreshValidity();
1391     }
1392     
1393     virtual ~EffectPropertyListener() { }
1394     
1395 protected:
1396     osg::observer_ptr<Technique> _tniq;
1397 };
1398
1399 template<typename T>
1400 Expression* propertyExpressionParser(const SGPropertyNode* exp,
1401                                      expression::Parser* parser)
1402 {
1403     SGPropertyNode_ptr pnode = getPropertyRoot()->getNode(exp->getStringValue(),
1404                                                           true);
1405     PropertyExpression<T>* pexp = new PropertyExpression<T>(pnode);
1406     TechniquePredParser* predParser
1407         = dynamic_cast<TechniquePredParser*>(parser);
1408     if (predParser) {
1409         EffectPropertyListener* l = new EffectPropertyListener(predParser
1410                                                                ->getTechnique());
1411         pexp->setListener(l);
1412         pnode->addChangeListener(l);
1413     }
1414     return pexp;
1415 }
1416
1417 expression::ExpParserRegistrar propertyRegistrar("property",
1418                                                  propertyExpressionParser<bool>);
1419
1420 expression::ExpParserRegistrar propvalueRegistrar("float-property",
1421                                                  propertyExpressionParser<float>);
1422
1423 }