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