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