]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/Effect.cxx
3bd14cc3ab11efa1393f53ba55a5967d0a57506a
[simgear.git] / simgear / scene / material / Effect.cxx
1 // Copyright (C) 2008 - 2009  Tim Moore timoore@redhat.com
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Library General Public
5 // License as published by the Free Software Foundation; either
6 // version 2 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Library General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16
17 #ifdef HAVE_CONFIG_H
18 #  include <simgear_config.h>
19 #endif
20
21 #include "Effect.hxx"
22 #include "EffectBuilder.hxx"
23 #include "Technique.hxx"
24 #include "Pass.hxx"
25 #include "TextureBuilder.hxx"
26
27 #include <algorithm>
28 #include <functional>
29 #include <iterator>
30 #include <map>
31 #include <utility>
32
33 #include <boost/bind.hpp>
34 #include <boost/foreach.hpp>
35 #include <boost/lexical_cast.hpp>
36 #include <boost/tuple/tuple.hpp>
37 #include <boost/tuple/tuple_comparison.hpp>
38
39 #include <osg/CullFace>
40 #include <osg/Drawable>
41 #include <osg/Material>
42 #include <osg/PolygonMode>
43 #include <osg/Program>
44 #include <osg/Referenced>
45 #include <osg/RenderInfo>
46 #include <osg/ShadeModel>
47 #include <osg/StateSet>
48 #include <osg/TexEnv>
49 #include <osg/Texture1D>
50 #include <osg/Texture2D>
51 #include <osg/Texture3D>
52 #include <osg/TextureRectangle>
53 #include <osg/Uniform>
54 #include <osg/Vec4d>
55 #include <osgUtil/CullVisitor>
56 #include <osgDB/FileUtils>
57 #include <osgDB/Input>
58 #include <osgDB/ParameterOutput>
59 #include <osgDB/ReadFile>
60 #include <osgDB/Registry>
61
62 #include <simgear/scene/tgdb/userdata.hxx>
63 #include <simgear/scene/util/SGSceneFeatures.hxx>
64 #include <simgear/scene/util/StateAttributeFactory.hxx>
65 #include <simgear/structure/OSGUtils.hxx>
66 #include <simgear/structure/SGExpression.hxx>
67
68
69
70 namespace simgear
71 {
72 using namespace std;
73 using namespace osg;
74 using namespace osgUtil;
75
76 using namespace effect;
77
78 Effect::Effect()
79 {
80 }
81
82 Effect::Effect(const Effect& rhs, const CopyOp& copyop)
83     : root(rhs.root), parametersProp(rhs.parametersProp)
84 {
85     using namespace boost;
86     transform(rhs.techniques.begin(), rhs.techniques.end(),
87               back_inserter(techniques),
88               bind(simgear::clone_ref<Technique>, _1, copyop));
89 }
90
91 // Assume that the last technique is always valid.
92 StateSet* Effect::getDefaultStateSet()
93 {
94     Technique* tniq = techniques.back().get();
95     if (!tniq)
96         return 0;
97     Pass* pass = tniq->passes.front().get();
98     return pass;
99 }
100
101 // There should always be a valid technique in an effect.
102
103 Technique* Effect::chooseTechnique(RenderInfo* info)
104 {
105     BOOST_FOREACH(ref_ptr<Technique>& technique, techniques)
106     {
107         if (technique->valid(info) == Technique::VALID)
108             return technique.get();
109     }
110     return 0;
111 }
112
113 void Effect::resizeGLObjectBuffers(unsigned int maxSize)
114 {
115     BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
116     {
117         technique->resizeGLObjectBuffers(maxSize);
118     }
119 }
120
121 void Effect::releaseGLObjects(osg::State* state) const
122 {
123     BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
124     {
125         technique->releaseGLObjects(state);
126     }
127 }
128
129 Effect::~Effect()
130 {
131 }
132
133 class PassAttributeBuilder : public Referenced
134 {
135 public:
136     virtual void buildAttribute(Effect* effect, Pass* pass,
137                                 const SGPropertyNode* prop,
138                                 const osgDB::ReaderWriter::Options* options)
139     = 0;
140 };
141
142 typedef map<const string, ref_ptr<PassAttributeBuilder> > PassAttrMap;
143 PassAttrMap passAttrMap;
144
145 template<typename T>
146 struct InstallAttributeBuilder
147 {
148     InstallAttributeBuilder(const string& name)
149     {
150         passAttrMap.insert(make_pair(name, new T));
151     }
152 };
153
154 void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
155                const osgDB::ReaderWriter::Options* options)
156 {
157     Pass* pass = new Pass;
158     tniq->passes.push_back(pass);
159     for (int i = 0; i < prop->nChildren(); ++i) {
160         const SGPropertyNode* attrProp = prop->getChild(i);
161         PassAttrMap::iterator itr = passAttrMap.find(attrProp->getName());
162         if (itr != passAttrMap.end())
163             itr->second->buildAttribute(effect, pass, attrProp, options);
164         else
165             SG_LOG(SG_INPUT, SG_ALERT,
166                    "skipping unknown pass attribute " << attrProp->getName());
167     }
168 }
169
170 osg::Vec4f getColor(const SGPropertyNode* prop)
171 {
172     if (prop->nChildren() == 0) {
173         if (prop->getType() == props::VEC4D) {
174             return osg::Vec4f(toOsg(prop->getValue<SGVec4d>()));
175         } else if (prop->getType() == props::VEC3D) {
176             return osg::Vec4f(toOsg(prop->getValue<SGVec3d>()), 1.0f);
177         } else {
178             SG_LOG(SG_INPUT, SG_ALERT,
179                    "invalid color property " << prop->getName() << " "
180                    << prop->getStringValue());
181             return osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
182         }
183     } else {
184         osg::Vec4f result;
185         static const char* colors[] = {"r", "g", "b"};
186         for (int i = 0; i < 3; ++i) {
187             const SGPropertyNode* componentProp = prop->getChild(colors[i]);
188             result[i] = componentProp ? componentProp->getValue<float>() : 0.0f;
189         }
190         const SGPropertyNode* alphaProp = prop->getChild("a");
191         result[3] = alphaProp ? alphaProp->getValue<float>() : 1.0f;
192         return result;
193     }
194 }
195
196 struct LightingBuilder : public PassAttributeBuilder
197 {
198     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
199                         const osgDB::ReaderWriter::Options* options);
200 };
201
202 void LightingBuilder::buildAttribute(Effect* effect, Pass* pass,
203                                      const SGPropertyNode* prop,
204                                      const osgDB::ReaderWriter::Options* options)
205 {
206     const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
207     if (!realProp)
208         return;
209     pass->setMode(GL_LIGHTING, (realProp->getValue<bool>() ? StateAttribute::ON
210                                 : StateAttribute::OFF));
211 }
212
213 InstallAttributeBuilder<LightingBuilder> installLighting("lighting");
214
215 struct ShadeModelBuilder : public PassAttributeBuilder
216 {
217     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
218                         const osgDB::ReaderWriter::Options* options)
219     {
220         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
221         if (!realProp)
222             return;
223         StateAttributeFactory *attrFact = StateAttributeFactory::instance();
224         string propVal = realProp->getStringValue();
225         if (propVal == "flat")
226             pass->setAttribute(attrFact->getFlatShadeModel());
227         else if (propVal == "smooth")
228             pass->setAttribute(attrFact->getSmoothShadeModel());
229         else
230             SG_LOG(SG_INPUT, SG_ALERT,
231                    "invalid shade model property " << propVal);
232     }
233 };
234
235 InstallAttributeBuilder<ShadeModelBuilder> installShadeModel("shade-model");
236
237 struct CullFaceBuilder : PassAttributeBuilder
238 {
239     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
240                         const osgDB::ReaderWriter::Options* options)
241     {
242         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
243         if (!realProp)
244             return;
245         StateAttributeFactory *attrFact = StateAttributeFactory::instance();
246         string propVal = realProp->getStringValue();
247         if (propVal == "front")
248             pass->setAttributeAndModes(attrFact->getCullFaceFront());
249         else if (propVal == "back")
250             pass->setAttributeAndModes(attrFact->getCullFaceBack());
251         else if (propVal == "front-back")
252             pass->setAttributeAndModes(new CullFace(CullFace::FRONT_AND_BACK));
253         else
254             SG_LOG(SG_INPUT, SG_ALERT,
255                    "invalid cull face property " << propVal);            
256     }    
257 };
258
259 InstallAttributeBuilder<CullFaceBuilder> installCullFace("cull-face");
260
261 struct HintBuilder : public PassAttributeBuilder
262 {
263     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
264                         const osgDB::ReaderWriter::Options* options)
265     {
266         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
267         if (!realProp)
268             return;
269         string propVal = realProp->getStringValue();
270         if (propVal == "opaque")
271             pass->setRenderingHint(StateSet::OPAQUE_BIN);
272         else if (propVal == "transparent")
273             pass->setRenderingHint(StateSet::TRANSPARENT_BIN);
274     }    
275 };
276
277 InstallAttributeBuilder<HintBuilder> installHint("rendering-hint");
278
279 struct RenderBinBuilder : public PassAttributeBuilder
280 {
281     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
282                         const osgDB::ReaderWriter::Options* options)
283     {
284         const SGPropertyNode* binProp = prop->getChild("bin-number");
285         binProp = getEffectPropertyNode(effect, binProp);
286         const SGPropertyNode* nameProp = prop->getChild("bin-name");
287         nameProp = getEffectPropertyNode(effect, nameProp);
288         if (binProp && nameProp) {
289             pass->setRenderBinDetails(binProp->getIntValue(),
290                                       nameProp->getStringValue());
291         } else {
292             if (!binProp)
293                 SG_LOG(SG_INPUT, SG_ALERT,
294                        "No render bin number specified in render bin section");
295             if (!nameProp)
296                 SG_LOG(SG_INPUT, SG_ALERT,
297                        "No render bin name specified in render bin section");
298         }
299     }
300 };
301
302 InstallAttributeBuilder<RenderBinBuilder> installRenderBin("render-bin");
303
304 struct MaterialBuilder : public PassAttributeBuilder
305 {
306     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
307                         const osgDB::ReaderWriter::Options* options);
308 };
309
310 EffectNameValue<Material::ColorMode> colorModeInit[] =
311 {
312     { "ambient", Material::AMBIENT },
313     { "ambient-and-diffuse", Material::AMBIENT_AND_DIFFUSE },
314     { "diffuse", Material::DIFFUSE },
315     { "emissive", Material::EMISSION },
316     { "specular", Material::SPECULAR },
317     { "off", Material::OFF }
318 };
319 EffectPropertyMap<Material::ColorMode> colorModes(colorModeInit);
320
321 void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
322                                      const SGPropertyNode* prop,
323                                      const osgDB::ReaderWriter::Options* options)
324 {
325     Material* mat = new Material;
326     const SGPropertyNode* color = 0;
327     if ((color = getEffectPropertyChild(effect, prop, "ambient")))
328         mat->setAmbient(Material::FRONT_AND_BACK, getColor(color));
329     if ((color = getEffectPropertyChild(effect, prop, "ambient-front")))
330         mat->setAmbient(Material::FRONT, getColor(color));
331     if ((color = getEffectPropertyChild(effect, prop, "ambient-back")))
332         mat->setAmbient(Material::BACK, getColor(color));
333     if ((color = getEffectPropertyChild(effect, prop, "diffuse")))
334         mat->setDiffuse(Material::FRONT_AND_BACK, getColor(color));
335     if ((color = getEffectPropertyChild(effect, prop, "diffuse-front")))
336         mat->setDiffuse(Material::FRONT, getColor(color));
337     if ((color = getEffectPropertyChild(effect, prop, "diffuse-back")))
338         mat->setDiffuse(Material::BACK, getColor(color));
339     if ((color = getEffectPropertyChild(effect, prop, "specular")))
340         mat->setSpecular(Material::FRONT_AND_BACK, getColor(color));
341     if ((color = getEffectPropertyChild(effect, prop, "specular-front")))
342         mat->setSpecular(Material::FRONT, getColor(color));
343     if ((color = getEffectPropertyChild(effect, prop, "specular-back")))
344         mat->setSpecular(Material::BACK, getColor(color));
345     if ((color = getEffectPropertyChild(effect, prop, "emissive")))
346         mat->setEmission(Material::FRONT_AND_BACK, getColor(color));
347     if ((color = getEffectPropertyChild(effect, prop, "emissive-front")))
348         mat->setEmission(Material::FRONT, getColor(color));        
349     if ((color = getEffectPropertyChild(effect, prop, "emissive-back")))
350         mat->setEmission(Material::BACK, getColor(color));        
351     const SGPropertyNode* shininess = 0;
352     mat->setShininess(Material::FRONT_AND_BACK, 0.0f);
353     if ((shininess = getEffectPropertyChild(effect, prop, "shininess")))
354         mat->setShininess(Material::FRONT_AND_BACK, shininess->getFloatValue());
355     if ((shininess = getEffectPropertyChild(effect, prop, "shininess-front")))
356         mat->setShininess(Material::FRONT, shininess->getFloatValue());
357     if ((shininess = getEffectPropertyChild(effect, prop, "shininess-back")))
358         mat->setShininess(Material::BACK, shininess->getFloatValue());
359     Material::ColorMode colorMode = Material::OFF;
360     findAttr(colorModes, getEffectPropertyChild(effect, prop, "color-mode"),
361              colorMode);
362     mat->setColorMode(colorMode);
363     pass->setAttribute(mat);
364 }
365
366 InstallAttributeBuilder<MaterialBuilder> installMaterial("material");
367
368 struct BlendBuilder : public PassAttributeBuilder
369 {
370     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
371                         const osgDB::ReaderWriter::Options* options)
372     {
373         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
374         if (!realProp)
375             return;
376         pass->setMode(GL_BLEND, (realProp->getBoolValue()
377                                  ? StateAttribute::ON
378                                  : StateAttribute::OFF));
379     }
380 };
381
382 InstallAttributeBuilder<BlendBuilder> installBlend("blend");
383
384 struct AlphaTestBuilder : public PassAttributeBuilder
385 {
386     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
387                         const osgDB::ReaderWriter::Options* options)
388     {
389         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
390         if (!realProp)
391             return;
392         pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
393                                       ? StateAttribute::ON
394                                       : StateAttribute::OFF));
395     }
396 };
397
398 InstallAttributeBuilder<AlphaTestBuilder> installAlphaTest("alpha-test");
399
400 EffectNameValue<TexEnv::Mode> texEnvModesInit[] =
401 {
402     {"add", TexEnv::ADD},
403     {"blend", TexEnv::BLEND},
404     {"decal", TexEnv::DECAL},
405     {"modulate", TexEnv::MODULATE},
406     {"replace", TexEnv::REPLACE}
407 };
408 EffectPropertyMap<TexEnv::Mode> texEnvModes(texEnvModesInit);
409
410 TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
411 {
412     const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
413                                                             "mode");
414     const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
415                                                              "color");
416     if (!modeProp)
417         return 0;
418     TexEnv::Mode mode = TexEnv::MODULATE;
419     findAttr(texEnvModes, modeProp, mode);
420     if (mode == TexEnv::MODULATE) {
421         return StateAttributeFactory::instance()->getStandardTexEnv();
422     }
423     TexEnv* env = new TexEnv(mode);
424     if (colorProp)
425         env->setColor(toOsg(colorProp->getValue<SGVec4d>()));
426     return env;
427  }
428
429
430 struct TextureUnitBuilder : PassAttributeBuilder
431 {
432     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
433                         const osgDB::ReaderWriter::Options* options);
434 };
435
436 void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
437                                         const SGPropertyNode* prop,
438                                         const osgDB::ReaderWriter::Options* options)
439 {
440
441     // Decode the texture unit
442     int unit = 0;
443     const SGPropertyNode* pUnit = prop->getChild("unit");
444     if (pUnit) {
445         unit = pUnit->getValue<int>();
446     } else {
447         const SGPropertyNode* pName = prop->getChild("name");
448         if (pName)
449             try {
450                 unit = boost::lexical_cast<int>(pName->getStringValue());
451             } catch (boost::bad_lexical_cast& lex) {
452                 SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit "
453                        << lex.what());
454             }
455     }
456     const SGPropertyNode* pType = prop->getChild("type");
457     string type;
458     if (!pType)
459         type = "2d";
460     else
461         type = pType->getStringValue();
462     Texture* texture = 0;
463     try {
464         texture = TextureBuilder::buildFromType(effect, type, prop,
465                                                 options);
466     }
467     catch (BuilderException& e) {
468         SG_LOG(SG_INPUT, SG_ALERT, "No image file for texture, using white ");
469         texture = StateAttributeFactory::instance()->getWhiteTexture();
470     }
471     pass->setTextureAttributeAndModes(unit, texture);
472     const SGPropertyNode* envProp = prop->getChild("environment");
473     if (envProp) {
474         TexEnv* env = buildTexEnv(effect, envProp);
475         if (env)
476             pass->setTextureAttributeAndModes(unit, env);
477     }
478 }
479
480
481
482 InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
483
484 typedef map<string, ref_ptr<Program> > ProgramMap;
485 ProgramMap programMap;
486
487 typedef map<string, ref_ptr<Shader> > ShaderMap;
488 ShaderMap shaderMap;
489
490 void reload_shaders()
491 {
492     for(ShaderMap::iterator sitr = shaderMap.begin(); sitr != shaderMap.end(); ++sitr)
493     {
494         Shader *shader = sitr->second.get();
495         string fileName = osgDB::findDataFile(sitr->first);
496         if (!fileName.empty()) {
497             shader->loadShaderSourceFromFile(fileName);
498         }
499     }
500 }
501
502 struct ShaderProgramBuilder : PassAttributeBuilder
503 {
504     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
505                         const osgDB::ReaderWriter::Options* options);
506 };
507
508 void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
509                                           const SGPropertyNode* prop,
510                                           const osgDB::ReaderWriter::Options*
511                                           options)
512 {
513     PropertyList pVertShaders = prop->getChildren("vertex-shader");
514     PropertyList pFragShaders = prop->getChildren("fragment-shader");
515     string programKey;
516     for (PropertyList::iterator itr = pVertShaders.begin(),
517              e = pVertShaders.end();
518          itr != e;
519          ++itr)
520     {
521         programKey += (*itr)->getStringValue();
522         programKey += ";";
523     }
524     for (PropertyList::iterator itr = pFragShaders.begin(),
525              e = pFragShaders.end();
526          itr != e;
527          ++itr)
528     {
529         programKey += (*itr)->getStringValue();
530         programKey += ";";
531     }
532     Program* program = 0;
533     ProgramMap::iterator pitr = programMap.find(programKey);
534     if (pitr != programMap.end()) {
535         program = pitr->second.get();
536     } else {
537         program = new Program;
538         program->setName(programKey);
539         // Add vertex shaders, then fragment shaders
540         PropertyList& pvec = pVertShaders;
541         Shader::Type stype = Shader::VERTEX;
542         for (int i = 0; i < 2; ++i) {
543             for (PropertyList::iterator nameItr = pvec.begin(), e = pvec.end();
544                  nameItr != e;
545                  ++nameItr) {
546                 string shaderName = (*nameItr)->getStringValue();
547                 ShaderMap::iterator sitr = shaderMap.find(shaderName);
548                 if (sitr != shaderMap.end()) {
549                     program->addShader(sitr->second.get());
550                 } else {
551                     string fileName = osgDB::findDataFile(shaderName, options);
552                     if (!fileName.empty()) {
553                         ref_ptr<Shader> shader = new Shader(stype);
554                         if (shader->loadShaderSourceFromFile(fileName)) {
555                             program->addShader(shader.get());
556                             shaderMap.insert(make_pair(shaderName, shader));
557                         }
558                     }
559                 }
560             }
561             pvec = pFragShaders;
562             stype = Shader::FRAGMENT;
563         }
564         programMap.insert(make_pair(programKey, program));
565     }
566     pass->setAttributeAndModes(program);
567 }
568
569 InstallAttributeBuilder<ShaderProgramBuilder> installShaderProgram("program");
570
571 EffectNameValue<Uniform::Type> uniformTypesInit[] =
572 {
573     {"float", Uniform::FLOAT},
574     {"float-vec3", Uniform::FLOAT_VEC3},
575     {"float-vec4", Uniform::FLOAT_VEC4},
576     {"sampler-1d", Uniform::SAMPLER_1D},
577     {"sampler-2d", Uniform::SAMPLER_2D},
578     {"sampler-3d", Uniform::SAMPLER_3D}
579 };
580 EffectPropertyMap<Uniform::Type> uniformTypes(uniformTypesInit);
581
582 struct UniformBuilder :public PassAttributeBuilder
583 {
584     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
585                         const osgDB::ReaderWriter::Options* options)
586     {
587         const SGPropertyNode* nameProp = prop->getChild("name");
588         const SGPropertyNode* typeProp = prop->getChild("type");
589         const SGPropertyNode* valProp
590             = getEffectPropertyChild(effect, prop, "value");
591         string name;
592         Uniform::Type uniformType = Uniform::FLOAT;
593         if (nameProp) {
594             name = nameProp->getStringValue();
595         } else {
596             SG_LOG(SG_INPUT, SG_ALERT, "No name for uniform property ");
597             return;
598         }
599         if (!valProp) {
600             SG_LOG(SG_INPUT, SG_ALERT, "No value for uniform property "
601                    << name);
602             return;
603         }
604         if (!typeProp) {
605             props::Type propType = valProp->getType();
606             switch (propType) {
607             case props::FLOAT:
608             case props::DOUBLE:
609                 break;          // default float type;
610             case props::VEC3D:
611                 uniformType = Uniform::FLOAT_VEC3;
612                 break;
613             case props::VEC4D:
614                 uniformType = Uniform::FLOAT_VEC4;
615                 break;
616             default:
617                 SG_LOG(SG_INPUT, SG_ALERT, "Can't deduce type of uniform "
618                        << name);
619                 return;
620             }
621         } else {
622             findAttr(uniformTypes, typeProp, uniformType);
623         }
624         ref_ptr<Uniform> uniform = new Uniform;
625         uniform->setName(name);
626         uniform->setType(uniformType);
627         switch (uniformType) {
628         case Uniform::FLOAT:
629             uniform->set(valProp->getValue<float>());
630             break;
631         case Uniform::FLOAT_VEC3:
632             uniform->set(toOsg(valProp->getValue<SGVec3d>()));
633             break;
634         case Uniform::FLOAT_VEC4:
635             uniform->set(toOsg(valProp->getValue<SGVec4d>()));
636             break;
637         case Uniform::SAMPLER_1D:
638         case Uniform::SAMPLER_2D:
639         case Uniform::SAMPLER_3D:
640             uniform->set(valProp->getValue<int>());
641             break;
642         default: // avoid compiler warning
643             break;
644         }
645         pass->addUniform(uniform.get());
646     }
647 };
648
649 InstallAttributeBuilder<UniformBuilder> installUniform("uniform");
650
651 // Not sure what to do with "name". At one point I wanted to use it to
652 // order the passes, but I do support render bin and stuff too...
653
654 struct NameBuilder : public PassAttributeBuilder
655 {
656     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
657                         const osgDB::ReaderWriter::Options* options)
658     {
659         // name can't use <use>
660         string name = prop->getStringValue();
661         if (!name.empty())
662             pass->setName(name);
663     }
664 };
665
666 InstallAttributeBuilder<NameBuilder> installName("name");
667
668 EffectNameValue<PolygonMode::Mode> polygonModeModesInit[] =
669 {
670     {"fill", PolygonMode::FILL},
671     {"line", PolygonMode::LINE},
672     {"point", PolygonMode::POINT}
673 };
674 EffectPropertyMap<PolygonMode::Mode> polygonModeModes(polygonModeModesInit);
675
676 struct PolygonModeBuilder : public PassAttributeBuilder
677 {
678     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
679                         const osgDB::ReaderWriter::Options* options)
680     {
681         const SGPropertyNode* frontProp
682             = getEffectPropertyChild(effect, prop, "front");
683         const SGPropertyNode* backProp
684             = getEffectPropertyChild(effect, prop, "back");
685         ref_ptr<PolygonMode> pmode = new PolygonMode;
686         PolygonMode::Mode frontMode = PolygonMode::FILL;
687         PolygonMode::Mode backMode = PolygonMode::FILL;
688         if (frontProp) {
689             findAttr(polygonModeModes, frontProp, frontMode);
690             pmode->setMode(PolygonMode::FRONT, frontMode);
691         }
692         if (backProp) {
693             findAttr(polygonModeModes, backProp, backMode);
694             pmode->setMode(PolygonMode::BACK, backMode);
695         }
696         pass->setAttribute(pmode.get());
697     }
698 };
699
700 InstallAttributeBuilder<PolygonModeBuilder> installPolygonMode("polygon-mode");
701 void buildTechnique(Effect* effect, const SGPropertyNode* prop,
702                     const osgDB::ReaderWriter::Options* options)
703 {
704     Technique* tniq = new Technique;
705     effect->techniques.push_back(tniq);
706     const SGPropertyNode* predProp = prop->getChild("predicate");
707     if (!predProp) {
708         tniq->setAlwaysValid(true);
709     } else {
710         try {
711             TechniquePredParser parser;
712             parser.setTechnique(tniq);
713             expression::BindingLayout& layout = parser.getBindingLayout();
714             /*int contextLoc = */layout.addBinding("__contextId", expression::INT);
715             SGExpressionb* validExp
716                 = dynamic_cast<SGExpressionb*>(parser.read(predProp
717                                                            ->getChild(0)));
718             if (validExp)
719                 tniq->setValidExpression(validExp, layout);
720             else
721                 throw expression::ParseError("technique predicate is not a boolean expression");
722         }
723         catch (expression::ParseError& except)
724         {
725             SG_LOG(SG_INPUT, SG_ALERT,
726                    "parsing technique predicate " << except.getMessage());
727             tniq->setAlwaysValid(false);
728         }
729     }
730     PropertyList passProps = prop->getChildren("pass");
731     for (PropertyList::iterator itr = passProps.begin(), e = passProps.end();
732          itr != e;
733          ++itr) {
734         buildPass(effect, tniq, itr->ptr(), options);
735     }
736 }
737
738 // Walk the techniques property tree, building techniques and
739 // passes.
740 bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
741 {
742     PropertyList tniqList = root->getChildren("technique");
743     for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
744          itr != e;
745          ++itr)
746         buildTechnique(this, *itr, options);
747     return true;
748 }
749
750 bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
751 {
752     const Effect& effect = static_cast<const Effect&>(obj);
753
754     fw.indent() << "techniques " << effect.techniques.size() << "\n";
755     BOOST_FOREACH(const ref_ptr<Technique>& technique, effect.techniques) {
756         fw.writeObject(*technique);
757     }
758     return true;
759 }
760
761 namespace
762 {
763 osgDB::RegisterDotOsgWrapperProxy effectProxy
764 (
765     new Effect,
766     "simgear::Effect",
767     "Object simgear::Effect",
768     0,
769     &Effect_writeLocalData
770     );
771 }
772
773 // Property expressions for technique predicates
774 class PropertyExpression : public SGExpression<bool>
775 {
776 public:
777     PropertyExpression(SGPropertyNode* pnode) : _pnode(pnode) {}
778     
779     void eval(bool& value, const expression::Binding*) const
780     {
781         value = _pnode->getValue<bool>();
782     }
783 protected:
784     SGPropertyNode_ptr _pnode;
785 };
786
787 class EffectPropertyListener : public SGPropertyChangeListener
788 {
789 public:
790     EffectPropertyListener(Technique* tniq) : _tniq(tniq) {}
791     
792     void valueChanged(SGPropertyNode* node)
793     {
794         _tniq->refreshValidity();
795     }
796 protected:
797     osg::ref_ptr<Technique> _tniq;
798 };
799
800 Expression* propertyExpressionParser(const SGPropertyNode* exp,
801                                      expression::Parser* parser)
802 {
803     SGPropertyNode_ptr pnode = getPropertyRoot()->getNode(exp->getStringValue(),
804                                                           true);
805     PropertyExpression* pexp = new PropertyExpression(pnode);
806     TechniquePredParser* predParser
807         = dynamic_cast<TechniquePredParser*>(parser);
808     if (predParser)
809         pnode->addChangeListener(new EffectPropertyListener(predParser
810                                                             ->getTechnique()));
811     return pexp;
812 }
813
814 expression::ExpParserRegistrar propertyRegistrar("property",
815                                                  propertyExpressionParser);
816
817 }