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