]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/Effect.cxx
b819354057f4abe15bf69c45acb0836c8a92a455
[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 "EffectGeode.hxx"
24 #include "Technique.hxx"
25 #include "Pass.hxx"
26 #include "TextureBuilder.hxx"
27
28 #include <algorithm>
29 #include <functional>
30 #include <iterator>
31 #include <map>
32 #include <utility>
33
34 #include <boost/foreach.hpp>
35 #include <boost/tuple/tuple.hpp>
36 #include <boost/tuple/tuple_comparison.hpp>
37
38 #include <osg/AlphaFunc>
39 #include <osg/BlendFunc>
40 #include <osg/CullFace>
41 #include <osg/Drawable>
42 #include <osg/Material>
43 #include <osg/Math>
44 #include <osg/PolygonMode>
45 #include <osg/Program>
46 #include <osg/Referenced>
47 #include <osg/RenderInfo>
48 #include <osg/ShadeModel>
49 #include <osg/StateSet>
50 #include <osg/TexEnv>
51 #include <osg/Texture1D>
52 #include <osg/Texture2D>
53 #include <osg/Texture3D>
54 #include <osg/TextureRectangle>
55 #include <osg/Uniform>
56 #include <osg/Vec4d>
57 #include <osgUtil/CullVisitor>
58 #include <osgDB/FileUtils>
59 #include <osgDB/Input>
60 #include <osgDB/ParameterOutput>
61 #include <osgDB/ReadFile>
62 #include <osgDB/Registry>
63
64 #include <simgear/scene/tgdb/userdata.hxx>
65 #include <simgear/scene/util/SGSceneFeatures.hxx>
66 #include <simgear/scene/util/StateAttributeFactory.hxx>
67 #include <simgear/structure/OSGUtils.hxx>
68 #include <simgear/structure/SGExpression.hxx>
69
70
71
72 namespace simgear
73 {
74 using namespace std;
75 using namespace osg;
76 using namespace osgUtil;
77
78 using namespace effect;
79
80 Effect::Effect()
81     : _cache(0), _isRealized(false)
82 {
83 }
84
85 Effect::Effect(const Effect& rhs, const CopyOp& copyop)
86     : root(rhs.root), parametersProp(rhs.parametersProp), _cache(0),
87       _isRealized(rhs._isRealized)
88 {
89     typedef vector<ref_ptr<Technique> > TechniqueList;
90     for (TechniqueList::const_iterator itr = rhs.techniques.begin(),
91              end = rhs.techniques.end();
92          itr != end;
93          ++itr)
94         techniques.push_back(static_cast<Technique*>(copyop(itr->get())));
95 }
96
97 // Assume that the last technique is always valid.
98 StateSet* Effect::getDefaultStateSet()
99 {
100     Technique* tniq = techniques.back().get();
101     if (!tniq)
102         return 0;
103     Pass* pass = tniq->passes.front().get();
104     return pass;
105 }
106
107 // There should always be a valid technique in an effect.
108
109 Technique* Effect::chooseTechnique(RenderInfo* info)
110 {
111     BOOST_FOREACH(ref_ptr<Technique>& technique, techniques)
112     {
113         if (technique->valid(info) == Technique::VALID)
114             return technique.get();
115     }
116     return 0;
117 }
118
119 void Effect::resizeGLObjectBuffers(unsigned int maxSize)
120 {
121     BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
122     {
123         technique->resizeGLObjectBuffers(maxSize);
124     }
125 }
126
127 void Effect::releaseGLObjects(osg::State* state) const
128 {
129     BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
130     {
131         technique->releaseGLObjects(state);
132     }
133 }
134
135 Effect::~Effect()
136 {
137     delete _cache;
138 }
139
140 void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
141                const osgDB::ReaderWriter::Options* options)
142 {
143     Pass* pass = new Pass;
144     tniq->passes.push_back(pass);
145     for (int i = 0; i < prop->nChildren(); ++i) {
146         const SGPropertyNode* attrProp = prop->getChild(i);
147         PassAttributeBuilder* builder
148             = PassAttributeBuilder::find(attrProp->getNameString());
149         if (builder)
150             builder->buildAttribute(effect, pass, attrProp, options);
151         else
152             SG_LOG(SG_INPUT, SG_ALERT,
153                    "skipping unknown pass attribute " << attrProp->getName());
154     }
155 }
156
157 osg::Vec4f getColor(const SGPropertyNode* prop)
158 {
159     if (prop->nChildren() == 0) {
160         if (prop->getType() == props::VEC4D) {
161             return osg::Vec4f(toOsg(prop->getValue<SGVec4d>()));
162         } else if (prop->getType() == props::VEC3D) {
163             return osg::Vec4f(toOsg(prop->getValue<SGVec3d>()), 1.0f);
164         } else {
165             SG_LOG(SG_INPUT, SG_ALERT,
166                    "invalid color property " << prop->getName() << " "
167                    << prop->getStringValue());
168             return osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
169         }
170     } else {
171         osg::Vec4f result;
172         static const char* colors[] = {"r", "g", "b"};
173         for (int i = 0; i < 3; ++i) {
174             const SGPropertyNode* componentProp = prop->getChild(colors[i]);
175             result[i] = componentProp ? componentProp->getValue<float>() : 0.0f;
176         }
177         const SGPropertyNode* alphaProp = prop->getChild("a");
178         result[3] = alphaProp ? alphaProp->getValue<float>() : 1.0f;
179         return result;
180     }
181 }
182
183 struct LightingBuilder : public PassAttributeBuilder
184 {
185     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
186                         const osgDB::ReaderWriter::Options* options);
187 };
188
189 void LightingBuilder::buildAttribute(Effect* effect, Pass* pass,
190                                      const SGPropertyNode* prop,
191                                      const osgDB::ReaderWriter::Options* options)
192 {
193     const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
194     if (!realProp)
195         return;
196     pass->setMode(GL_LIGHTING, (realProp->getValue<bool>() ? StateAttribute::ON
197                                 : StateAttribute::OFF));
198 }
199
200 InstallAttributeBuilder<LightingBuilder> installLighting("lighting");
201
202 struct ShadeModelBuilder : public PassAttributeBuilder
203 {
204     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
205                         const osgDB::ReaderWriter::Options* options)
206     {
207         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
208         if (!realProp)
209             return;
210         StateAttributeFactory *attrFact = StateAttributeFactory::instance();
211         string propVal = realProp->getStringValue();
212         if (propVal == "flat")
213             pass->setAttribute(attrFact->getFlatShadeModel());
214         else if (propVal == "smooth")
215             pass->setAttribute(attrFact->getSmoothShadeModel());
216         else
217             SG_LOG(SG_INPUT, SG_ALERT,
218                    "invalid shade model property " << propVal);
219     }
220 };
221
222 InstallAttributeBuilder<ShadeModelBuilder> installShadeModel("shade-model");
223
224 struct CullFaceBuilder : PassAttributeBuilder
225 {
226     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
227                         const osgDB::ReaderWriter::Options* options)
228     {
229         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
230         if (!realProp) {
231             pass->setMode(GL_CULL_FACE, StateAttribute::OFF);
232             return;
233         }
234         StateAttributeFactory *attrFact = StateAttributeFactory::instance();
235         string propVal = realProp->getStringValue();
236         if (propVal == "front")
237             pass->setAttributeAndModes(attrFact->getCullFaceFront());
238         else if (propVal == "back")
239             pass->setAttributeAndModes(attrFact->getCullFaceBack());
240         else if (propVal == "front-back")
241             pass->setAttributeAndModes(new CullFace(CullFace::FRONT_AND_BACK));
242         else if (propVal == "off")
243             pass->setMode(GL_CULL_FACE, StateAttribute::OFF);
244         else
245             SG_LOG(SG_INPUT, SG_ALERT,
246                    "invalid cull face property " << propVal);            
247     }    
248 };
249
250 InstallAttributeBuilder<CullFaceBuilder> installCullFace("cull-face");
251
252 EffectNameValue<StateSet::RenderingHint> renderingHintInit[] =
253 {
254     { "default", StateSet::DEFAULT_BIN },
255     { "opaque", StateSet::OPAQUE_BIN },
256     { "transparent", StateSet::TRANSPARENT_BIN }
257 };
258
259 EffectPropertyMap<StateSet::RenderingHint> renderingHints(renderingHintInit);
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         StateSet::RenderingHint renderingHint = StateSet::DEFAULT_BIN;
270         findAttr(renderingHints, realProp, renderingHint);
271         pass->setRenderingHint(renderingHint);
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         if (!isAttributeActive(effect, prop))
283             return;
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     if (!isAttributeActive(effect, prop))
326         return;
327     Material* mat = new Material;
328     const SGPropertyNode* color = 0;
329     if ((color = getEffectPropertyChild(effect, prop, "ambient")))
330         mat->setAmbient(Material::FRONT_AND_BACK, getColor(color));
331     if ((color = getEffectPropertyChild(effect, prop, "ambient-front")))
332         mat->setAmbient(Material::FRONT, getColor(color));
333     if ((color = getEffectPropertyChild(effect, prop, "ambient-back")))
334         mat->setAmbient(Material::BACK, getColor(color));
335     if ((color = getEffectPropertyChild(effect, prop, "diffuse")))
336         mat->setDiffuse(Material::FRONT_AND_BACK, getColor(color));
337     if ((color = getEffectPropertyChild(effect, prop, "diffuse-front")))
338         mat->setDiffuse(Material::FRONT, getColor(color));
339     if ((color = getEffectPropertyChild(effect, prop, "diffuse-back")))
340         mat->setDiffuse(Material::BACK, getColor(color));
341     if ((color = getEffectPropertyChild(effect, prop, "specular")))
342         mat->setSpecular(Material::FRONT_AND_BACK, getColor(color));
343     if ((color = getEffectPropertyChild(effect, prop, "specular-front")))
344         mat->setSpecular(Material::FRONT, getColor(color));
345     if ((color = getEffectPropertyChild(effect, prop, "specular-back")))
346         mat->setSpecular(Material::BACK, getColor(color));
347     if ((color = getEffectPropertyChild(effect, prop, "emissive")))
348         mat->setEmission(Material::FRONT_AND_BACK, getColor(color));
349     if ((color = getEffectPropertyChild(effect, prop, "emissive-front")))
350         mat->setEmission(Material::FRONT, getColor(color));        
351     if ((color = getEffectPropertyChild(effect, prop, "emissive-back")))
352         mat->setEmission(Material::BACK, getColor(color));        
353     const SGPropertyNode* shininess = 0;
354     mat->setShininess(Material::FRONT_AND_BACK, 0.0f);
355     if ((shininess = getEffectPropertyChild(effect, prop, "shininess")))
356         mat->setShininess(Material::FRONT_AND_BACK, shininess->getFloatValue());
357     if ((shininess = getEffectPropertyChild(effect, prop, "shininess-front")))
358         mat->setShininess(Material::FRONT, shininess->getFloatValue());
359     if ((shininess = getEffectPropertyChild(effect, prop, "shininess-back")))
360         mat->setShininess(Material::BACK, shininess->getFloatValue());
361     Material::ColorMode colorMode = Material::OFF;
362     findAttr(colorModes, getEffectPropertyChild(effect, prop, "color-mode"),
363              colorMode);
364     mat->setColorMode(colorMode);
365     pass->setAttribute(mat);
366 }
367
368 InstallAttributeBuilder<MaterialBuilder> installMaterial("material");
369
370 EffectNameValue<BlendFunc::BlendFuncMode> blendFuncModesInit[] =
371 {
372     {"dst-alpha", BlendFunc::DST_ALPHA},
373     {"dst-color", BlendFunc::DST_COLOR},
374     {"one", BlendFunc::ONE},
375     {"one-minus-dst-alpha", BlendFunc::ONE_MINUS_DST_ALPHA},
376     {"one-minus-dst-color", BlendFunc::ONE_MINUS_DST_COLOR},
377     {"one-minus-src-alpha", BlendFunc::ONE_MINUS_SRC_ALPHA},
378     {"one-minus-src-color", BlendFunc::ONE_MINUS_SRC_COLOR},
379     {"src-alpha", BlendFunc::SRC_ALPHA},
380     {"src-alpha-saturate", BlendFunc::SRC_ALPHA_SATURATE},
381     {"src-color", BlendFunc::SRC_COLOR},
382     {"constant-color", BlendFunc::CONSTANT_COLOR},
383     {"one-minus-constant-color", BlendFunc::ONE_MINUS_CONSTANT_COLOR},
384     {"constant-alpha", BlendFunc::CONSTANT_ALPHA},
385     {"one-minus-constant-alpha", BlendFunc::ONE_MINUS_CONSTANT_ALPHA},
386     {"zero", BlendFunc::ZERO}
387 };
388 EffectPropertyMap<BlendFunc::BlendFuncMode> blendFuncModes(blendFuncModesInit);
389
390 struct BlendBuilder : public PassAttributeBuilder
391 {
392     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
393                         const osgDB::ReaderWriter::Options* options)
394     {
395         if (!isAttributeActive(effect, prop))
396             return;
397         // XXX Compatibility with early <blend> syntax; should go away
398         // before a release
399         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
400         if (!realProp)
401             return;
402         if (realProp->nChildren() == 0) {
403             pass->setMode(GL_BLEND, (realProp->getBoolValue()
404                                      ? StateAttribute::ON
405                                      : StateAttribute::OFF));
406             return;
407         }
408
409         const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
410                                                              "mode");
411         // XXX When dynamic parameters are supported, this code should
412         // create the blend function even if the mode is off.
413         if (pmode && !pmode->getValue<bool>()) {
414             pass->setMode(GL_BLEND, StateAttribute::OFF);
415             return;
416         }
417         const SGPropertyNode* psource
418             = getEffectPropertyChild(effect, prop, "source");
419         const SGPropertyNode* pdestination
420             = getEffectPropertyChild(effect, prop, "destination");
421         const SGPropertyNode* psourceRGB
422             = getEffectPropertyChild(effect, prop, "source-rgb");
423         const SGPropertyNode* psourceAlpha
424             = getEffectPropertyChild(effect, prop, "source-alpha");
425         const SGPropertyNode* pdestRGB
426             = getEffectPropertyChild(effect, prop, "destination-rgb");
427         const SGPropertyNode* pdestAlpha
428             = getEffectPropertyChild(effect, prop, "destination-alpha");
429         BlendFunc::BlendFuncMode sourceMode = BlendFunc::ONE;
430         BlendFunc::BlendFuncMode destMode = BlendFunc::ZERO;
431         if (psource)
432             findAttr(blendFuncModes, psource, sourceMode);
433         if (pdestination)
434             findAttr(blendFuncModes, pdestination, destMode);
435         if (psource && pdestination
436             && !(psourceRGB || psourceAlpha || pdestRGB || pdestAlpha)
437             && sourceMode == BlendFunc::SRC_ALPHA
438             && destMode == BlendFunc::ONE_MINUS_SRC_ALPHA) {
439             pass->setAttributeAndModes(StateAttributeFactory::instance()
440                                        ->getStandardBlendFunc());
441             return;
442         }
443         BlendFunc* blendFunc = new BlendFunc;
444         if (psource)
445             blendFunc->setSource(sourceMode);
446         if (pdestination)
447             blendFunc->setDestination(destMode);
448         if (psourceRGB) {
449             BlendFunc::BlendFuncMode sourceRGBMode;
450             findAttr(blendFuncModes, psourceRGB, sourceRGBMode);
451             blendFunc->setSourceRGB(sourceRGBMode);
452         }
453         if (pdestRGB) {
454             BlendFunc::BlendFuncMode destRGBMode;
455             findAttr(blendFuncModes, pdestRGB, destRGBMode);
456             blendFunc->setDestinationRGB(destRGBMode);
457         }
458         if (psourceAlpha) {
459             BlendFunc::BlendFuncMode sourceAlphaMode;
460             findAttr(blendFuncModes, psourceAlpha, sourceAlphaMode);
461             blendFunc->setSourceAlpha(sourceAlphaMode);
462         }
463         if (pdestAlpha) {
464             BlendFunc::BlendFuncMode destAlphaMode;
465             findAttr(blendFuncModes, pdestAlpha, destAlphaMode);
466             blendFunc->setDestinationAlpha(destAlphaMode);
467         }
468         pass->setAttributeAndModes(blendFunc);
469     }
470 };
471
472 InstallAttributeBuilder<BlendBuilder> installBlend("blend");
473
474 EffectNameValue<AlphaFunc::ComparisonFunction> alphaComparisonInit[] =
475 {
476     {"never", AlphaFunc::NEVER},
477     {"less", AlphaFunc::LESS},
478     {"equal", AlphaFunc::EQUAL},
479     {"lequal", AlphaFunc::LEQUAL},
480     {"greater", AlphaFunc::GREATER},
481     {"notequal", AlphaFunc::NOTEQUAL},
482     {"gequal", AlphaFunc::GEQUAL},
483     {"always", AlphaFunc::ALWAYS}
484 };
485 EffectPropertyMap<AlphaFunc::ComparisonFunction>
486 alphaComparison(alphaComparisonInit);
487
488 struct AlphaTestBuilder : public PassAttributeBuilder
489 {
490     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
491                         const osgDB::ReaderWriter::Options* options)
492     {
493         if (!isAttributeActive(effect, prop))
494             return;
495         // XXX Compatibility with early <alpha-test> syntax; should go away
496         // before a release
497         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
498         if (!realProp)
499             return;
500         if (realProp->nChildren() == 0) {
501             pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
502                                      ? StateAttribute::ON
503                                      : StateAttribute::OFF));
504             return;
505         }
506
507         const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
508                                                              "mode");
509         // XXX When dynamic parameters are supported, this code should
510         // create the blend function even if the mode is off.
511         if (pmode && !pmode->getValue<bool>()) {
512             pass->setMode(GL_ALPHA_TEST, StateAttribute::OFF);
513             return;
514         }
515         const SGPropertyNode* pComp = getEffectPropertyChild(effect, prop,
516                                                              "comparison");
517         const SGPropertyNode* pRef = getEffectPropertyChild(effect, prop,
518                                                              "reference");
519         AlphaFunc::ComparisonFunction func = AlphaFunc::ALWAYS;
520         float refValue = 1.0f;
521         if (pComp)
522             findAttr(alphaComparison, pComp, func);
523         if (pRef)
524             refValue = pRef->getValue<float>();
525         if (func == AlphaFunc::GREATER && osg::equivalent(refValue, 1.0f)) {
526             pass->setAttributeAndModes(StateAttributeFactory::instance()
527                                        ->getStandardAlphaFunc());
528         } else {
529             AlphaFunc* alphaFunc = new AlphaFunc;
530             alphaFunc->setFunction(func);
531             alphaFunc->setReferenceValue(refValue);
532             pass->setAttributeAndModes(alphaFunc);
533         }
534     }
535 };
536
537 InstallAttributeBuilder<AlphaTestBuilder> installAlphaTest("alpha-test");
538
539 InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
540
541 typedef map<string, ref_ptr<Program> > ProgramMap;
542 ProgramMap programMap;
543
544 typedef map<string, ref_ptr<Shader> > ShaderMap;
545 ShaderMap shaderMap;
546
547 void reload_shaders()
548 {
549     for(ShaderMap::iterator sitr = shaderMap.begin(); sitr != shaderMap.end(); ++sitr)
550     {
551         Shader *shader = sitr->second.get();
552         string fileName = osgDB::findDataFile(sitr->first);
553         if (!fileName.empty()) {
554             shader->loadShaderSourceFromFile(fileName);
555         }
556     }
557 }
558
559 struct ShaderProgramBuilder : PassAttributeBuilder
560 {
561     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
562                         const osgDB::ReaderWriter::Options* options);
563 };
564
565 void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
566                                           const SGPropertyNode* prop,
567                                           const osgDB::ReaderWriter::Options*
568                                           options)
569 {
570     if (!isAttributeActive(effect, prop))
571         return;
572     PropertyList pVertShaders = prop->getChildren("vertex-shader");
573     PropertyList pFragShaders = prop->getChildren("fragment-shader");
574     string programKey;
575     for (PropertyList::iterator itr = pVertShaders.begin(),
576              e = pVertShaders.end();
577          itr != e;
578          ++itr)
579     {
580         programKey += (*itr)->getStringValue();
581         programKey += ";";
582     }
583     for (PropertyList::iterator itr = pFragShaders.begin(),
584              e = pFragShaders.end();
585          itr != e;
586          ++itr)
587     {
588         programKey += (*itr)->getStringValue();
589         programKey += ";";
590     }
591     Program* program = 0;
592     ProgramMap::iterator pitr = programMap.find(programKey);
593     if (pitr != programMap.end()) {
594         program = pitr->second.get();
595     } else {
596         program = new Program;
597         program->setName(programKey);
598         // Add vertex shaders, then fragment shaders
599         PropertyList& pvec = pVertShaders;
600         Shader::Type stype = Shader::VERTEX;
601         for (int i = 0; i < 2; ++i) {
602             for (PropertyList::iterator nameItr = pvec.begin(), e = pvec.end();
603                  nameItr != e;
604                  ++nameItr) {
605                 string shaderName = (*nameItr)->getStringValue();
606                 ShaderMap::iterator sitr = shaderMap.find(shaderName);
607                 if (sitr != shaderMap.end()) {
608                     program->addShader(sitr->second.get());
609                 } else {
610                     string fileName = osgDB::findDataFile(shaderName, options);
611                     if (!fileName.empty()) {
612                         ref_ptr<Shader> shader = new Shader(stype);
613                         if (shader->loadShaderSourceFromFile(fileName)) {
614                             program->addShader(shader.get());
615                             shaderMap.insert(make_pair(shaderName, shader));
616                         }
617                     }
618                 }
619             }
620             pvec = pFragShaders;
621             stype = Shader::FRAGMENT;
622         }
623         programMap.insert(make_pair(programKey, program));
624     }
625     pass->setAttributeAndModes(program);
626 }
627
628 InstallAttributeBuilder<ShaderProgramBuilder> installShaderProgram("program");
629
630 EffectNameValue<Uniform::Type> uniformTypesInit[] =
631 {
632     {"float", Uniform::FLOAT},
633     {"float-vec3", Uniform::FLOAT_VEC3},
634     {"float-vec4", Uniform::FLOAT_VEC4},
635     {"sampler-1d", Uniform::SAMPLER_1D},
636     {"sampler-2d", Uniform::SAMPLER_2D},
637     {"sampler-3d", Uniform::SAMPLER_3D}
638 };
639 EffectPropertyMap<Uniform::Type> uniformTypes(uniformTypesInit);
640
641 struct UniformBuilder :public PassAttributeBuilder
642 {
643     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
644                         const osgDB::ReaderWriter::Options* options)
645     {
646         if (!isAttributeActive(effect, prop))
647             return;
648         const SGPropertyNode* nameProp = prop->getChild("name");
649         const SGPropertyNode* typeProp = prop->getChild("type");
650         const SGPropertyNode* valProp
651             = getEffectPropertyChild(effect, prop, "value");
652         string name;
653         Uniform::Type uniformType = Uniform::FLOAT;
654         if (nameProp) {
655             name = nameProp->getStringValue();
656         } else {
657             SG_LOG(SG_INPUT, SG_ALERT, "No name for uniform property ");
658             return;
659         }
660         if (!valProp) {
661             SG_LOG(SG_INPUT, SG_ALERT, "No value for uniform property "
662                    << name);
663             return;
664         }
665         if (!typeProp) {
666             props::Type propType = valProp->getType();
667             switch (propType) {
668             case props::FLOAT:
669             case props::DOUBLE:
670                 break;          // default float type;
671             case props::VEC3D:
672                 uniformType = Uniform::FLOAT_VEC3;
673                 break;
674             case props::VEC4D:
675                 uniformType = Uniform::FLOAT_VEC4;
676                 break;
677             default:
678                 SG_LOG(SG_INPUT, SG_ALERT, "Can't deduce type of uniform "
679                        << name);
680                 return;
681             }
682         } else {
683             findAttr(uniformTypes, typeProp, uniformType);
684         }
685         ref_ptr<Uniform> uniform = new Uniform;
686         uniform->setName(name);
687         uniform->setType(uniformType);
688         switch (uniformType) {
689         case Uniform::FLOAT:
690             uniform->set(valProp->getValue<float>());
691             break;
692         case Uniform::FLOAT_VEC3:
693             uniform->set(toOsg(valProp->getValue<SGVec3d>()));
694             break;
695         case Uniform::FLOAT_VEC4:
696             uniform->set(toOsg(valProp->getValue<SGVec4d>()));
697             break;
698         case Uniform::SAMPLER_1D:
699         case Uniform::SAMPLER_2D:
700         case Uniform::SAMPLER_3D:
701             uniform->set(valProp->getValue<int>());
702             break;
703         default: // avoid compiler warning
704             break;
705         }
706         pass->addUniform(uniform.get());
707     }
708 };
709
710 InstallAttributeBuilder<UniformBuilder> installUniform("uniform");
711
712 // Not sure what to do with "name". At one point I wanted to use it to
713 // order the passes, but I do support render bin and stuff too...
714
715 struct NameBuilder : public PassAttributeBuilder
716 {
717     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
718                         const osgDB::ReaderWriter::Options* options)
719     {
720         // name can't use <use>
721         string name = prop->getStringValue();
722         if (!name.empty())
723             pass->setName(name);
724     }
725 };
726
727 InstallAttributeBuilder<NameBuilder> installName("name");
728
729 EffectNameValue<PolygonMode::Mode> polygonModeModesInit[] =
730 {
731     {"fill", PolygonMode::FILL},
732     {"line", PolygonMode::LINE},
733     {"point", PolygonMode::POINT}
734 };
735 EffectPropertyMap<PolygonMode::Mode> polygonModeModes(polygonModeModesInit);
736
737 struct PolygonModeBuilder : public PassAttributeBuilder
738 {
739     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
740                         const osgDB::ReaderWriter::Options* options)
741     {
742         if (!isAttributeActive(effect, prop))
743             return;
744         const SGPropertyNode* frontProp
745             = getEffectPropertyChild(effect, prop, "front");
746         const SGPropertyNode* backProp
747             = getEffectPropertyChild(effect, prop, "back");
748         ref_ptr<PolygonMode> pmode = new PolygonMode;
749         PolygonMode::Mode frontMode = PolygonMode::FILL;
750         PolygonMode::Mode backMode = PolygonMode::FILL;
751         if (frontProp) {
752             findAttr(polygonModeModes, frontProp, frontMode);
753             pmode->setMode(PolygonMode::FRONT, frontMode);
754         }
755         if (backProp) {
756             findAttr(polygonModeModes, backProp, backMode);
757             pmode->setMode(PolygonMode::BACK, backMode);
758         }
759         pass->setAttribute(pmode.get());
760     }
761 };
762
763 InstallAttributeBuilder<PolygonModeBuilder> installPolygonMode("polygon-mode");
764
765 struct VertexProgramTwoSideBuilder : public PassAttributeBuilder
766 {
767     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
768                         const osgDB::ReaderWriter::Options* options)
769     {
770         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
771         if (!realProp)
772             return;
773         pass->setMode(GL_VERTEX_PROGRAM_TWO_SIDE,
774                       (realProp->getValue<bool>()
775                        ? StateAttribute::ON : StateAttribute::OFF));
776     }
777 };
778
779 InstallAttributeBuilder<VertexProgramTwoSideBuilder>
780 installTwoSide("vertex-program-two-side");
781
782 struct VertexProgramPointSizeBuilder : public PassAttributeBuilder
783 {
784     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
785                         const osgDB::ReaderWriter::Options* options)
786     {
787         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
788         if (!realProp)
789             return;
790         pass->setMode(GL_VERTEX_PROGRAM_POINT_SIZE,
791                       (realProp->getValue<bool>()
792                        ? StateAttribute::ON : StateAttribute::OFF));
793     }
794 };
795
796 InstallAttributeBuilder<VertexProgramPointSizeBuilder>
797 installPointSize("vertex-program-point-size");
798
799 void buildTechnique(Effect* effect, const SGPropertyNode* prop,
800                     const osgDB::ReaderWriter::Options* options)
801 {
802     Technique* tniq = new Technique;
803     effect->techniques.push_back(tniq);
804     const SGPropertyNode* predProp = prop->getChild("predicate");
805     if (!predProp) {
806         tniq->setAlwaysValid(true);
807     } else {
808         try {
809             TechniquePredParser parser;
810             parser.setTechnique(tniq);
811             expression::BindingLayout& layout = parser.getBindingLayout();
812             /*int contextLoc = */layout.addBinding("__contextId", expression::INT);
813             SGExpressionb* validExp
814                 = dynamic_cast<SGExpressionb*>(parser.read(predProp
815                                                            ->getChild(0)));
816             if (validExp)
817                 tniq->setValidExpression(validExp, layout);
818             else
819                 throw expression::ParseError("technique predicate is not a boolean expression");
820         }
821         catch (expression::ParseError& except)
822         {
823             SG_LOG(SG_INPUT, SG_ALERT,
824                    "parsing technique predicate " << except.getMessage());
825             tniq->setAlwaysValid(false);
826         }
827     }
828     PropertyList passProps = prop->getChildren("pass");
829     for (PropertyList::iterator itr = passProps.begin(), e = passProps.end();
830          itr != e;
831          ++itr) {
832         buildPass(effect, tniq, itr->ptr(), options);
833     }
834 }
835
836 // Specifically for .ac files...
837 bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
838 {
839     SGPropertyNode* paramRoot = makeChild(effectRoot, "parameters");
840     SGPropertyNode* matNode = paramRoot->getChild("material", 0, true);
841     Vec4f ambVal, difVal, specVal, emisVal;
842     float shininess = 0.0f;
843     const Material* mat = getStateAttribute<Material>(ss);
844     if (mat) {
845         ambVal = mat->getAmbient(Material::FRONT_AND_BACK);
846         difVal = mat->getDiffuse(Material::FRONT_AND_BACK);
847         specVal = mat->getSpecular(Material::FRONT_AND_BACK);
848         emisVal = mat->getEmission(Material::FRONT_AND_BACK);
849         shininess = mat->getShininess(Material::FRONT_AND_BACK);
850         makeChild(matNode, "active")->setValue(true);
851         makeChild(matNode, "ambient")->setValue(toVec4d(toSG(ambVal)));
852         makeChild(matNode, "diffuse")->setValue(toVec4d(toSG(difVal)));
853         makeChild(matNode, "specular")->setValue(toVec4d(toSG(specVal)));
854         makeChild(matNode, "emissive")->setValue(toVec4d(toSG(emisVal)));
855         makeChild(matNode, "shininess")->setValue(shininess);
856         matNode->getChild("color-mode", 0, true)->setStringValue("diffuse");
857     } else {
858         makeChild(matNode, "active")->setValue(false);
859     }
860     const ShadeModel* sm = getStateAttribute<ShadeModel>(ss);
861     string shadeModelString("smooth");
862     if (sm) {
863         ShadeModel::Mode smMode = sm->getMode();
864         if (smMode == ShadeModel::FLAT)
865             shadeModelString = "flat";
866     }
867     makeChild(paramRoot, "shade-model")->setStringValue(shadeModelString);
868     string cullFaceString("off");
869     const CullFace* cullFace = getStateAttribute<CullFace>(ss);
870     if (cullFace) {
871         switch (cullFace->getMode()) {
872         case CullFace::FRONT:
873             cullFaceString = "front";
874             break;
875         case CullFace::BACK:
876             cullFaceString = "back";
877             break;
878         case CullFace::FRONT_AND_BACK:
879             cullFaceString = "front-back";
880             break;
881         default:
882             break;
883         }
884     }
885     makeChild(paramRoot, "cull-face")->setStringValue(cullFaceString);
886     const BlendFunc* blendFunc = getStateAttribute<BlendFunc>(ss);
887     SGPropertyNode* blendNode = makeChild(paramRoot, "blend");
888     if (blendFunc) {
889         string sourceMode = findName(blendFuncModes, blendFunc->getSource());
890         string destMode = findName(blendFuncModes, blendFunc->getDestination());
891         makeChild(blendNode, "active")->setValue(true);
892         makeChild(blendNode, "source")->setStringValue(sourceMode);
893         makeChild(blendNode, "destination")->setStringValue(destMode);
894         makeChild(blendNode, "mode")->setValue(true);
895     } else {
896         makeChild(blendNode, "active")->setValue(false);
897     }
898     string renderingHint = findName(renderingHints, ss->getRenderingHint());
899     makeChild(paramRoot, "rendering-hint")->setStringValue(renderingHint);
900     makeTextureParameters(paramRoot, ss);
901     return true;
902 }
903
904 // Walk the techniques property tree, building techniques and
905 // passes.
906 bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
907 {
908     if (_isRealized)
909         return true;
910     PropertyList tniqList = root->getChildren("technique");
911     for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
912          itr != e;
913          ++itr)
914         buildTechnique(this, *itr, options);
915     _isRealized = true;
916     return true;
917 }
918
919 void Effect::InitializeCallback::doUpdate(osg::Node* node, osg::NodeVisitor* nv)
920 {
921     EffectGeode* eg = dynamic_cast<EffectGeode*>(node);
922     if (!eg)
923         return;
924     Effect* effect = eg->getEffect();
925     if (!effect)
926         return;
927     SGPropertyNode* root = getPropertyRoot();
928     for (vector<SGSharedPtr<Updater> >::iterator itr = effect->_extraData.begin(),
929              end = effect->_extraData.end();
930          itr != end;
931          ++itr) {
932         InitializeWhenAdded* adder
933             = dynamic_cast<InitializeWhenAdded*>(itr->ptr());
934         if (adder)
935             adder->initOnAdd(effect, root);
936     }
937 }
938
939 bool Effect::Key::EqualTo::operator()(const Effect::Key& lhs,
940                                       const Effect::Key& rhs) const
941 {
942     if (lhs.paths.size() != rhs.paths.size()
943         || !equal(lhs.paths.begin(), lhs.paths.end(), rhs.paths.begin()))
944         return false;
945     if (lhs.unmerged.valid() && rhs.unmerged.valid())
946         return props::Compare()(lhs.unmerged, rhs.unmerged);
947     else
948         return lhs.unmerged == rhs.unmerged;
949 }
950
951 size_t hash_value(const Effect::Key& key)
952 {
953     size_t seed = 0;
954     if (key.unmerged.valid())
955         boost::hash_combine(seed, *key.unmerged);
956     boost::hash_range(seed, key.paths.begin(), key.paths.end());
957     return seed;
958 }
959
960 bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
961 {
962     const Effect& effect = static_cast<const Effect&>(obj);
963
964     fw.indent() << "techniques " << effect.techniques.size() << "\n";
965     BOOST_FOREACH(const ref_ptr<Technique>& technique, effect.techniques) {
966         fw.writeObject(*technique);
967     }
968     return true;
969 }
970
971 namespace
972 {
973 osgDB::RegisterDotOsgWrapperProxy effectProxy
974 (
975     new Effect,
976     "simgear::Effect",
977     "Object simgear::Effect",
978     0,
979     &Effect_writeLocalData
980     );
981 }
982
983 // Property expressions for technique predicates
984 class PropertyExpression : public SGExpression<bool>
985 {
986 public:
987     PropertyExpression(SGPropertyNode* pnode) : _pnode(pnode) {}
988     
989     void eval(bool& value, const expression::Binding*) const
990     {
991         value = _pnode->getValue<bool>();
992     }
993 protected:
994     SGPropertyNode_ptr _pnode;
995 };
996
997 class EffectPropertyListener : public SGPropertyChangeListener
998 {
999 public:
1000     EffectPropertyListener(Technique* tniq) : _tniq(tniq) {}
1001     
1002     void valueChanged(SGPropertyNode* node)
1003     {
1004         _tniq->refreshValidity();
1005     }
1006 protected:
1007     osg::ref_ptr<Technique> _tniq;
1008 };
1009
1010 Expression* propertyExpressionParser(const SGPropertyNode* exp,
1011                                      expression::Parser* parser)
1012 {
1013     SGPropertyNode_ptr pnode = getPropertyRoot()->getNode(exp->getStringValue(),
1014                                                           true);
1015     PropertyExpression* pexp = new PropertyExpression(pnode);
1016     TechniquePredParser* predParser
1017         = dynamic_cast<TechniquePredParser*>(parser);
1018     if (predParser)
1019         pnode->addChangeListener(new EffectPropertyListener(predParser
1020                                                             ->getTechnique()));
1021     return pexp;
1022 }
1023
1024 expression::ExpParserRegistrar propertyRegistrar("property",
1025                                                  propertyExpressionParser);
1026
1027 }