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