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