1 // Copyright (C) 2008 - 2009 Tim Moore timoore@redhat.com
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.
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.
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.
18 # include <simgear_config.h>
22 #include "EffectBuilder.hxx"
23 #include "Technique.hxx"
25 #include "TextureBuilder.hxx"
33 #include <boost/bind.hpp>
34 #include <boost/foreach.hpp>
35 #include <boost/lexical_cast.hpp>
36 #include <boost/tuple/tuple.hpp>
37 #include <boost/tuple/tuple_comparison.hpp>
39 #include <osg/CullFace>
40 #include <osg/Drawable>
41 #include <osg/Material>
42 #include <osg/PolygonMode>
43 #include <osg/Program>
44 #include <osg/Referenced>
45 #include <osg/RenderInfo>
46 #include <osg/ShadeModel>
47 #include <osg/StateSet>
49 #include <osg/Texture1D>
50 #include <osg/Texture2D>
51 #include <osg/Texture3D>
52 #include <osg/TextureRectangle>
53 #include <osg/Uniform>
55 #include <osgUtil/CullVisitor>
56 #include <osgDB/FileUtils>
57 #include <osgDB/Input>
58 #include <osgDB/ParameterOutput>
59 #include <osgDB/ReadFile>
60 #include <osgDB/Registry>
62 #include <simgear/scene/tgdb/userdata.hxx>
63 #include <simgear/scene/util/SGSceneFeatures.hxx>
64 #include <simgear/scene/util/StateAttributeFactory.hxx>
65 #include <simgear/structure/OSGUtils.hxx>
66 #include <simgear/structure/SGExpression.hxx>
74 using namespace osgUtil;
80 Effect::Effect(const Effect& rhs, const CopyOp& copyop)
81 : root(rhs.root), parametersProp(rhs.parametersProp)
83 using namespace boost;
84 transform(rhs.techniques.begin(), rhs.techniques.end(),
85 back_inserter(techniques),
86 bind(simgear::clone_ref<Technique>, _1, copyop));
89 // Assume that the last technique is always valid.
90 StateSet* Effect::getDefaultStateSet()
92 Technique* tniq = techniques.back().get();
95 Pass* pass = tniq->passes.front().get();
99 // There should always be a valid technique in an effect.
101 Technique* Effect::chooseTechnique(RenderInfo* info)
103 BOOST_FOREACH(ref_ptr<Technique>& technique, techniques)
105 if (technique->valid(info) == Technique::VALID)
106 return technique.get();
111 void Effect::resizeGLObjectBuffers(unsigned int maxSize)
113 BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
115 technique->resizeGLObjectBuffers(maxSize);
119 void Effect::releaseGLObjects(osg::State* state) const
121 BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
123 technique->releaseGLObjects(state);
131 class PassAttributeBuilder : public Referenced
134 virtual void buildAttribute(Effect* effect, Pass* pass,
135 const SGPropertyNode* prop,
136 const osgDB::ReaderWriter::Options* options)
140 typedef map<const string, ref_ptr<PassAttributeBuilder> > PassAttrMap;
141 PassAttrMap passAttrMap;
144 struct InstallAttributeBuilder
146 InstallAttributeBuilder(const string& name)
148 passAttrMap.insert(make_pair(name, new T));
152 void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
153 const osgDB::ReaderWriter::Options* options)
155 Pass* pass = new Pass;
156 tniq->passes.push_back(pass);
157 for (int i = 0; i < prop->nChildren(); ++i) {
158 const SGPropertyNode* attrProp = prop->getChild(i);
159 PassAttrMap::iterator itr = passAttrMap.find(attrProp->getName());
160 if (itr != passAttrMap.end())
161 itr->second->buildAttribute(effect, pass, attrProp, options);
163 SG_LOG(SG_INPUT, SG_ALERT,
164 "skipping unknown pass attribute " << attrProp->getName());
168 osg::Vec4f getColor(const SGPropertyNode* prop)
170 if (prop->nChildren() == 0) {
171 if (prop->getType() == props::VEC4D) {
172 return osg::Vec4f(toOsg(prop->getValue<SGVec4d>()));
173 } else if (prop->getType() == props::VEC3D) {
174 return osg::Vec4f(toOsg(prop->getValue<SGVec3d>()), 1.0f);
176 SG_LOG(SG_INPUT, SG_ALERT,
177 "invalid color property " << prop->getName() << " "
178 << prop->getStringValue());
179 return osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
183 static const char* colors[] = {"r", "g", "b"};
184 for (int i = 0; i < 3; ++i) {
185 const SGPropertyNode* componentProp = prop->getChild(colors[i]);
186 result[i] = componentProp ? componentProp->getValue<float>() : 0.0f;
188 const SGPropertyNode* alphaProp = prop->getChild("a");
189 result[3] = alphaProp ? alphaProp->getValue<float>() : 1.0f;
194 struct LightingBuilder : public PassAttributeBuilder
196 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
197 const osgDB::ReaderWriter::Options* options);
200 void LightingBuilder::buildAttribute(Effect* effect, Pass* pass,
201 const SGPropertyNode* prop,
202 const osgDB::ReaderWriter::Options* options)
204 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
207 pass->setMode(GL_LIGHTING, (realProp->getValue<bool>() ? StateAttribute::ON
208 : StateAttribute::OFF));
211 InstallAttributeBuilder<LightingBuilder> installLighting("lighting");
213 struct ShadeModelBuilder : public PassAttributeBuilder
215 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
216 const osgDB::ReaderWriter::Options* options)
218 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
221 StateAttributeFactory *attrFact = StateAttributeFactory::instance();
222 string propVal = realProp->getStringValue();
223 if (propVal == "flat")
224 pass->setAttribute(attrFact->getFlatShadeModel());
225 else if (propVal == "smooth")
226 pass->setAttribute(attrFact->getSmoothShadeModel());
228 SG_LOG(SG_INPUT, SG_ALERT,
229 "invalid shade model property " << propVal);
233 InstallAttributeBuilder<ShadeModelBuilder> installShadeModel("shade-model");
235 struct CullFaceBuilder : PassAttributeBuilder
237 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
238 const osgDB::ReaderWriter::Options* options)
240 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
243 StateAttributeFactory *attrFact = StateAttributeFactory::instance();
244 string propVal = realProp->getStringValue();
245 if (propVal == "front")
246 pass->setAttributeAndModes(attrFact->getCullFaceFront());
247 else if (propVal == "back")
248 pass->setAttributeAndModes(attrFact->getCullFaceBack());
249 else if (propVal == "front-back")
250 pass->setAttributeAndModes(new CullFace(CullFace::FRONT_AND_BACK));
252 SG_LOG(SG_INPUT, SG_ALERT,
253 "invalid cull face property " << propVal);
257 InstallAttributeBuilder<CullFaceBuilder> installCullFace("cull-face");
259 struct HintBuilder : public PassAttributeBuilder
261 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
262 const osgDB::ReaderWriter::Options* options)
264 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
267 string propVal = realProp->getStringValue();
268 if (propVal == "opaque")
269 pass->setRenderingHint(StateSet::OPAQUE_BIN);
270 else if (propVal == "transparent")
271 pass->setRenderingHint(StateSet::TRANSPARENT_BIN);
275 InstallAttributeBuilder<HintBuilder> installHint("rendering-hint");
277 struct RenderBinBuilder : public PassAttributeBuilder
279 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
280 const osgDB::ReaderWriter::Options* options)
282 const SGPropertyNode* binProp = prop->getChild("bin-number");
283 binProp = getEffectPropertyNode(effect, binProp);
284 const SGPropertyNode* nameProp = prop->getChild("bin-name");
285 nameProp = getEffectPropertyNode(effect, nameProp);
286 if (binProp && nameProp) {
287 pass->setRenderBinDetails(binProp->getIntValue(),
288 nameProp->getStringValue());
291 SG_LOG(SG_INPUT, SG_ALERT,
292 "No render bin number specified in render bin section");
294 SG_LOG(SG_INPUT, SG_ALERT,
295 "No render bin name specified in render bin section");
300 InstallAttributeBuilder<RenderBinBuilder> installRenderBin("render-bin");
302 struct MaterialBuilder : public PassAttributeBuilder
304 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
305 const osgDB::ReaderWriter::Options* options);
308 EffectNameValue<Material::ColorMode> colorModes[] =
310 { "ambient", Material::AMBIENT },
311 { "ambient-and-diffuse", Material::AMBIENT_AND_DIFFUSE },
312 { "diffuse", Material::DIFFUSE },
313 { "emissive", Material::EMISSION },
314 { "specular", Material::SPECULAR },
315 { "off", Material::OFF }
318 void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
319 const SGPropertyNode* prop,
320 const osgDB::ReaderWriter::Options* options)
322 Material* mat = new Material;
323 const SGPropertyNode* color = 0;
324 if ((color = getEffectPropertyChild(effect, prop, "ambient")))
325 mat->setAmbient(Material::FRONT_AND_BACK, getColor(color));
326 if ((color = getEffectPropertyChild(effect, prop, "ambient-front")))
327 mat->setAmbient(Material::FRONT, getColor(color));
328 if ((color = getEffectPropertyChild(effect, prop, "ambient-back")))
329 mat->setAmbient(Material::BACK, getColor(color));
330 if ((color = getEffectPropertyChild(effect, prop, "diffuse")))
331 mat->setDiffuse(Material::FRONT_AND_BACK, getColor(color));
332 if ((color = getEffectPropertyChild(effect, prop, "diffuse-front")))
333 mat->setDiffuse(Material::FRONT, getColor(color));
334 if ((color = getEffectPropertyChild(effect, prop, "diffuse-back")))
335 mat->setDiffuse(Material::BACK, getColor(color));
336 if ((color = getEffectPropertyChild(effect, prop, "specular")))
337 mat->setSpecular(Material::FRONT_AND_BACK, getColor(color));
338 if ((color = getEffectPropertyChild(effect, prop, "specular-front")))
339 mat->setSpecular(Material::FRONT, getColor(color));
340 if ((color = getEffectPropertyChild(effect, prop, "specular-back")))
341 mat->setSpecular(Material::BACK, getColor(color));
342 if ((color = getEffectPropertyChild(effect, prop, "emissive")))
343 mat->setEmission(Material::FRONT_AND_BACK, getColor(color));
344 if ((color = getEffectPropertyChild(effect, prop, "emissive-front")))
345 mat->setEmission(Material::FRONT, getColor(color));
346 if ((color = getEffectPropertyChild(effect, prop, "emissive-back")))
347 mat->setEmission(Material::BACK, getColor(color));
348 const SGPropertyNode* shininess = 0;
349 mat->setShininess(Material::FRONT_AND_BACK, 0.0f);
350 if ((shininess = getEffectPropertyChild(effect, prop, "shininess")))
351 mat->setShininess(Material::FRONT_AND_BACK, shininess->getFloatValue());
352 if ((shininess = getEffectPropertyChild(effect, prop, "shininess-front")))
353 mat->setShininess(Material::FRONT, shininess->getFloatValue());
354 if ((shininess = getEffectPropertyChild(effect, prop, "shininess-back")))
355 mat->setShininess(Material::BACK, shininess->getFloatValue());
356 Material::ColorMode colorMode = Material::OFF;
357 findAttr(colorModes, getEffectPropertyChild(effect, prop, "color-mode"),
359 mat->setColorMode(colorMode);
360 pass->setAttribute(mat);
363 InstallAttributeBuilder<MaterialBuilder> installMaterial("material");
365 struct BlendBuilder : public PassAttributeBuilder
367 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
368 const osgDB::ReaderWriter::Options* options)
370 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
373 pass->setMode(GL_BLEND, (realProp->getBoolValue()
375 : StateAttribute::OFF));
379 InstallAttributeBuilder<BlendBuilder> installBlend("blend");
381 struct AlphaTestBuilder : public PassAttributeBuilder
383 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
384 const osgDB::ReaderWriter::Options* options)
386 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
389 pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
391 : StateAttribute::OFF));
395 InstallAttributeBuilder<AlphaTestBuilder> installAlphaTest("alpha-test");
397 EffectNameValue<TexEnv::Mode> texEnvModes[] =
399 {"add", TexEnv::ADD},
400 {"blend", TexEnv::BLEND},
401 {"decal", TexEnv::DECAL},
402 {"modulate", TexEnv::MODULATE},
403 {"replace", TexEnv::REPLACE}
406 TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
408 const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
410 const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
414 TexEnv::Mode mode = TexEnv::MODULATE;
415 findAttr(texEnvModes, modeProp, mode);
416 if (mode == TexEnv::MODULATE) {
417 return StateAttributeFactory::instance()->getStandardTexEnv();
419 TexEnv* env = new TexEnv(mode);
421 env->setColor(toOsg(colorProp->getValue<SGVec4d>()));
426 struct TextureUnitBuilder : PassAttributeBuilder
428 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
429 const osgDB::ReaderWriter::Options* options);
432 void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
433 const SGPropertyNode* prop,
434 const osgDB::ReaderWriter::Options* options)
437 // Decode the texture unit
439 const SGPropertyNode* pUnit = prop->getChild("unit");
441 unit = pUnit->getValue<int>();
443 const SGPropertyNode* pName = prop->getChild("name");
446 unit = boost::lexical_cast<int>(pName->getStringValue());
447 } catch (boost::bad_lexical_cast& lex) {
448 SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit "
452 const SGPropertyNode* pType = prop->getChild("type");
457 type = pType->getStringValue();
458 Texture* texture = 0;
460 texture = TextureBuilder::buildFromType(effect, type, prop,
463 catch (BuilderException& e) {
464 SG_LOG(SG_INPUT, SG_ALERT, "No image file for texture, using white ");
465 texture = StateAttributeFactory::instance()->getWhiteTexture();
467 pass->setTextureAttributeAndModes(unit, texture);
468 const SGPropertyNode* envProp = prop->getChild("environment");
470 TexEnv* env = buildTexEnv(effect, envProp);
472 pass->setTextureAttributeAndModes(unit, env);
478 InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
480 typedef map<string, ref_ptr<Program> > ProgramMap;
481 ProgramMap programMap;
483 typedef map<string, ref_ptr<Shader> > ShaderMap;
486 void reload_shaders()
488 for(ShaderMap::iterator sitr = shaderMap.begin(); sitr != shaderMap.end(); ++sitr)
490 Shader *shader = sitr->second.get();
491 string fileName = osgDB::findDataFile(sitr->first);
492 if (!fileName.empty()) {
493 shader->loadShaderSourceFromFile(fileName);
498 struct ShaderProgramBuilder : PassAttributeBuilder
500 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
501 const osgDB::ReaderWriter::Options* options);
504 void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
505 const SGPropertyNode* prop,
506 const osgDB::ReaderWriter::Options*
509 PropertyList pVertShaders = prop->getChildren("vertex-shader");
510 PropertyList pFragShaders = prop->getChildren("fragment-shader");
512 for (PropertyList::iterator itr = pVertShaders.begin(),
513 e = pVertShaders.end();
517 programKey += (*itr)->getStringValue();
520 for (PropertyList::iterator itr = pFragShaders.begin(),
521 e = pFragShaders.end();
525 programKey += (*itr)->getStringValue();
528 Program* program = 0;
529 ProgramMap::iterator pitr = programMap.find(programKey);
530 if (pitr != programMap.end()) {
531 program = pitr->second.get();
533 program = new Program;
534 program->setName(programKey);
535 // Add vertex shaders, then fragment shaders
536 PropertyList& pvec = pVertShaders;
537 Shader::Type stype = Shader::VERTEX;
538 for (int i = 0; i < 2; ++i) {
539 for (PropertyList::iterator nameItr = pvec.begin(), e = pvec.end();
542 string shaderName = (*nameItr)->getStringValue();
543 ShaderMap::iterator sitr = shaderMap.find(shaderName);
544 if (sitr != shaderMap.end()) {
545 program->addShader(sitr->second.get());
547 string fileName = osgDB::findDataFile(shaderName, options);
548 if (!fileName.empty()) {
549 ref_ptr<Shader> shader = new Shader(stype);
550 if (shader->loadShaderSourceFromFile(fileName)) {
551 program->addShader(shader.get());
552 shaderMap.insert(make_pair(shaderName, shader));
558 stype = Shader::FRAGMENT;
560 programMap.insert(make_pair(programKey, program));
562 pass->setAttributeAndModes(program);
565 InstallAttributeBuilder<ShaderProgramBuilder> installShaderProgram("program");
567 EffectNameValue<Uniform::Type> uniformTypes[] =
569 {"float", Uniform::FLOAT},
570 {"float-vec3", Uniform::FLOAT_VEC3},
571 {"float-vec4", Uniform::FLOAT_VEC4},
572 {"sampler-1d", Uniform::SAMPLER_1D},
573 {"sampler-2d", Uniform::SAMPLER_2D},
574 {"sampler-3d", Uniform::SAMPLER_3D}
577 struct UniformBuilder :public PassAttributeBuilder
579 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
580 const osgDB::ReaderWriter::Options* options)
582 const SGPropertyNode* nameProp = prop->getChild("name");
583 const SGPropertyNode* typeProp = prop->getChild("type");
584 const SGPropertyNode* valProp
585 = getEffectPropertyChild(effect, prop, "value");
587 Uniform::Type uniformType = Uniform::FLOAT;
589 name = nameProp->getStringValue();
591 SG_LOG(SG_INPUT, SG_ALERT, "No name for uniform property ");
595 SG_LOG(SG_INPUT, SG_ALERT, "No value for uniform property "
600 props::Type propType = valProp->getType();
604 break; // default float type;
606 uniformType = Uniform::FLOAT_VEC3;
609 uniformType = Uniform::FLOAT_VEC4;
612 SG_LOG(SG_INPUT, SG_ALERT, "Can't deduce type of uniform "
617 findAttr(uniformTypes, typeProp, uniformType);
619 ref_ptr<Uniform> uniform = new Uniform;
620 uniform->setName(name);
621 uniform->setType(uniformType);
622 switch (uniformType) {
624 uniform->set(valProp->getValue<float>());
626 case Uniform::FLOAT_VEC3:
627 uniform->set(toOsg(valProp->getValue<SGVec3d>()));
629 case Uniform::FLOAT_VEC4:
630 uniform->set(toOsg(valProp->getValue<SGVec4d>()));
632 case Uniform::SAMPLER_1D:
633 case Uniform::SAMPLER_2D:
634 case Uniform::SAMPLER_3D:
635 uniform->set(valProp->getValue<int>());
637 default: // avoid compiler warning
640 pass->addUniform(uniform.get());
644 InstallAttributeBuilder<UniformBuilder> installUniform("uniform");
646 // Not sure what to do with "name". At one point I wanted to use it to
647 // order the passes, but I do support render bin and stuff too...
649 struct NameBuilder : public PassAttributeBuilder
651 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
652 const osgDB::ReaderWriter::Options* options)
654 // name can't use <use>
655 string name = prop->getStringValue();
661 InstallAttributeBuilder<NameBuilder> installName("name");
663 EffectNameValue<PolygonMode::Mode> polygonModeModes[] =
665 {"fill", PolygonMode::FILL},
666 {"line", PolygonMode::LINE},
667 {"point", PolygonMode::POINT}
670 struct PolygonModeBuilder : public PassAttributeBuilder
672 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
673 const osgDB::ReaderWriter::Options* options)
675 const SGPropertyNode* frontProp
676 = getEffectPropertyChild(effect, prop, "front");
677 const SGPropertyNode* backProp
678 = getEffectPropertyChild(effect, prop, "back");
679 ref_ptr<PolygonMode> pmode = new PolygonMode;
680 PolygonMode::Mode frontMode = PolygonMode::FILL;
681 PolygonMode::Mode backMode = PolygonMode::FILL;
683 findAttr(polygonModeModes, frontProp, frontMode);
684 pmode->setMode(PolygonMode::FRONT, frontMode);
687 findAttr(polygonModeModes, backProp, backMode);
688 pmode->setMode(PolygonMode::BACK, backMode);
690 pass->setAttribute(pmode.get());
694 InstallAttributeBuilder<PolygonModeBuilder> installPolygonMode("polygon-mode");
695 void buildTechnique(Effect* effect, const SGPropertyNode* prop,
696 const osgDB::ReaderWriter::Options* options)
698 Technique* tniq = new Technique;
699 effect->techniques.push_back(tniq);
700 const SGPropertyNode* predProp = prop->getChild("predicate");
702 tniq->setAlwaysValid(true);
705 TechniquePredParser parser;
706 parser.setTechnique(tniq);
707 expression::BindingLayout& layout = parser.getBindingLayout();
708 /*int contextLoc = */layout.addBinding("__contextId", expression::INT);
709 SGExpressionb* validExp
710 = dynamic_cast<SGExpressionb*>(parser.read(predProp
713 tniq->setValidExpression(validExp, layout);
715 throw expression::ParseError("technique predicate is not a boolean expression");
717 catch (expression::ParseError& except)
719 SG_LOG(SG_INPUT, SG_ALERT,
720 "parsing technique predicate " << except.getMessage());
721 tniq->setAlwaysValid(false);
724 PropertyList passProps = prop->getChildren("pass");
725 for (PropertyList::iterator itr = passProps.begin(), e = passProps.end();
728 buildPass(effect, tniq, itr->ptr(), options);
732 // Walk the techniques property tree, building techniques and
734 bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
736 PropertyList tniqList = root->getChildren("technique");
737 for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
740 buildTechnique(this, *itr, options);
744 bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
746 const Effect& effect = static_cast<const Effect&>(obj);
748 fw.indent() << "techniques " << effect.techniques.size() << "\n";
749 BOOST_FOREACH(const ref_ptr<Technique>& technique, effect.techniques) {
750 fw.writeObject(*technique);
757 osgDB::RegisterDotOsgWrapperProxy effectProxy
761 "Object simgear::Effect",
763 &Effect_writeLocalData
767 // Property expressions for technique predicates
768 class PropertyExpression : public SGExpression<bool>
771 PropertyExpression(SGPropertyNode* pnode) : _pnode(pnode) {}
773 void eval(bool& value, const expression::Binding*) const
775 value = _pnode->getValue<bool>();
778 SGPropertyNode_ptr _pnode;
781 class EffectPropertyListener : public SGPropertyChangeListener
784 EffectPropertyListener(Technique* tniq) : _tniq(tniq) {}
786 void valueChanged(SGPropertyNode* node)
788 _tniq->refreshValidity();
791 osg::ref_ptr<Technique> _tniq;
794 Expression* propertyExpressionParser(const SGPropertyNode* exp,
795 expression::Parser* parser)
797 SGPropertyNode_ptr pnode = getPropertyRoot()->getNode(exp->getStringValue(),
799 PropertyExpression* pexp = new PropertyExpression(pnode);
800 TechniquePredParser* predParser
801 = dynamic_cast<TechniquePredParser*>(parser);
803 pnode->addChangeListener(new EffectPropertyListener(predParser
808 expression::ExpParserRegistrar propertyRegistrar("property",
809 propertyExpressionParser);