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/util/SGSceneFeatures.hxx>
63 #include <simgear/scene/util/StateAttributeFactory.hxx>
64 #include <simgear/structure/OSGUtils.hxx>
65 #include <simgear/structure/SGExpression.hxx>
73 using namespace osgUtil;
79 Effect::Effect(const Effect& rhs, const CopyOp& copyop)
80 : root(rhs.root), parametersProp(rhs.parametersProp)
82 using namespace boost;
83 transform(rhs.techniques.begin(), rhs.techniques.end(),
84 back_inserter(techniques),
85 bind(simgear::clone_ref<Technique>, _1, copyop));
88 // Assume that the last technique is always valid.
89 StateSet* Effect::getDefaultStateSet()
91 Technique* tniq = techniques.back().get();
94 Pass* pass = tniq->passes.front().get();
98 // There should always be a valid technique in an effect.
100 Technique* Effect::chooseTechnique(RenderInfo* info)
102 BOOST_FOREACH(ref_ptr<Technique>& technique, techniques)
104 if (technique->valid(info) == Technique::VALID)
105 return technique.get();
110 void Effect::resizeGLObjectBuffers(unsigned int maxSize)
112 BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
114 technique->resizeGLObjectBuffers(maxSize);
118 void Effect::releaseGLObjects(osg::State* state) const
120 BOOST_FOREACH(const ref_ptr<Technique>& technique, techniques)
122 technique->releaseGLObjects(state);
130 class PassAttributeBuilder : public Referenced
133 virtual void buildAttribute(Effect* effect, Pass* pass,
134 const SGPropertyNode* prop,
135 const osgDB::ReaderWriter::Options* options)
139 typedef map<const string, ref_ptr<PassAttributeBuilder> > PassAttrMap;
140 PassAttrMap passAttrMap;
143 struct InstallAttributeBuilder
145 InstallAttributeBuilder(const string& name)
147 passAttrMap.insert(make_pair(name, new T));
151 void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
152 const osgDB::ReaderWriter::Options* options)
154 Pass* pass = new Pass;
155 tniq->passes.push_back(pass);
156 for (int i = 0; i < prop->nChildren(); ++i) {
157 const SGPropertyNode* attrProp = prop->getChild(i);
158 PassAttrMap::iterator itr = passAttrMap.find(attrProp->getName());
159 if (itr != passAttrMap.end())
160 itr->second->buildAttribute(effect, pass, attrProp, options);
162 SG_LOG(SG_INPUT, SG_ALERT,
163 "skipping unknown pass attribute " << attrProp->getName());
167 osg::Vec4f getColor(const SGPropertyNode* prop)
169 if (prop->nChildren() == 0) {
170 if (prop->getType() == props::VEC4D) {
171 return osg::Vec4f(prop->getValue<SGVec4d>().osg());
172 } else if (prop->getType() == props::VEC3D) {
173 return osg::Vec4f(prop->getValue<SGVec3d>().osg(), 1.0f);
175 SG_LOG(SG_INPUT, SG_ALERT,
176 "invalid color property " << prop->getName() << " "
177 << prop->getStringValue());
178 return osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
182 static const char* colors[] = {"r", "g", "b"};
183 for (int i = 0; i < 3; ++i) {
184 const SGPropertyNode* componentProp = prop->getChild(colors[i]);
185 result[i] = componentProp ? componentProp->getValue<float>() : 0.0f;
187 const SGPropertyNode* alphaProp = prop->getChild("a");
188 result[3] = alphaProp ? alphaProp->getValue<float>() : 1.0f;
193 struct LightingBuilder : public PassAttributeBuilder
195 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
196 const osgDB::ReaderWriter::Options* options);
199 void LightingBuilder::buildAttribute(Effect* effect, Pass* pass,
200 const SGPropertyNode* prop,
201 const osgDB::ReaderWriter::Options* options)
203 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
206 pass->setMode(GL_LIGHTING, (realProp->getValue<bool>() ? StateAttribute::ON
207 : StateAttribute::OFF));
210 InstallAttributeBuilder<LightingBuilder> installLighting("lighting");
212 struct ShadeModelBuilder : public PassAttributeBuilder
214 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
215 const osgDB::ReaderWriter::Options* options)
217 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
220 StateAttributeFactory *attrFact = StateAttributeFactory::instance();
221 string propVal = realProp->getStringValue();
222 if (propVal == "flat")
223 pass->setAttribute(attrFact->getFlatShadeModel());
224 else if (propVal == "smooth")
225 pass->setAttribute(attrFact->getSmoothShadeModel());
227 SG_LOG(SG_INPUT, SG_ALERT,
228 "invalid shade model property " << propVal);
232 InstallAttributeBuilder<ShadeModelBuilder> installShadeModel("shade-model");
234 struct CullFaceBuilder : PassAttributeBuilder
236 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
237 const osgDB::ReaderWriter::Options* options)
239 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
242 StateAttributeFactory *attrFact = StateAttributeFactory::instance();
243 string propVal = realProp->getStringValue();
244 if (propVal == "front")
245 pass->setAttributeAndModes(attrFact->getCullFaceFront());
246 else if (propVal == "back")
247 pass->setAttributeAndModes(attrFact->getCullFaceBack());
248 else if (propVal == "front-back")
249 pass->setAttributeAndModes(new CullFace(CullFace::FRONT_AND_BACK));
251 SG_LOG(SG_INPUT, SG_ALERT,
252 "invalid cull face property " << propVal);
256 InstallAttributeBuilder<CullFaceBuilder> installCullFace("cull-face");
258 struct HintBuilder : public PassAttributeBuilder
260 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
261 const osgDB::ReaderWriter::Options* options)
263 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
266 string propVal = realProp->getStringValue();
267 if (propVal == "opaque")
268 pass->setRenderingHint(StateSet::OPAQUE_BIN);
269 else if (propVal == "transparent")
270 pass->setRenderingHint(StateSet::TRANSPARENT_BIN);
274 InstallAttributeBuilder<HintBuilder> installHint("rendering-hint");
276 struct RenderBinBuilder : public PassAttributeBuilder
278 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
279 const osgDB::ReaderWriter::Options* options)
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());
290 SG_LOG(SG_INPUT, SG_ALERT,
291 "No render bin number specified in render bin section");
293 SG_LOG(SG_INPUT, SG_ALERT,
294 "No render bin name specified in render bin section");
299 InstallAttributeBuilder<RenderBinBuilder> installRenderBin("render-bin");
301 struct MaterialBuilder : public PassAttributeBuilder
303 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
304 const osgDB::ReaderWriter::Options* options);
307 EffectNameValue<Material::ColorMode> colorModes[] =
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 }
317 void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
318 const SGPropertyNode* prop,
319 const osgDB::ReaderWriter::Options* options)
321 Material* mat = new Material;
322 const SGPropertyNode* color = 0;
323 if ((color = getEffectPropertyChild(effect, prop, "ambient")))
324 mat->setAmbient(Material::FRONT_AND_BACK, getColor(color));
325 if ((color = getEffectPropertyChild(effect, prop, "ambient-front")))
326 mat->setAmbient(Material::FRONT, getColor(color));
327 if ((color = getEffectPropertyChild(effect, prop, "ambient-back")))
328 mat->setAmbient(Material::BACK, getColor(color));
329 if ((color = getEffectPropertyChild(effect, prop, "diffuse")))
330 mat->setDiffuse(Material::FRONT_AND_BACK, getColor(color));
331 if ((color = getEffectPropertyChild(effect, prop, "diffuse-front")))
332 mat->setDiffuse(Material::FRONT, getColor(color));
333 if ((color = getEffectPropertyChild(effect, prop, "diffuse-back")))
334 mat->setDiffuse(Material::BACK, getColor(color));
335 if ((color = getEffectPropertyChild(effect, prop, "specular")))
336 mat->setSpecular(Material::FRONT_AND_BACK, getColor(color));
337 if ((color = getEffectPropertyChild(effect, prop, "specular-front")))
338 mat->setSpecular(Material::FRONT, getColor(color));
339 if ((color = getEffectPropertyChild(effect, prop, "specular-back")))
340 mat->setSpecular(Material::BACK, getColor(color));
341 if ((color = getEffectPropertyChild(effect, prop, "emissive")))
342 mat->setEmission(Material::FRONT_AND_BACK, getColor(color));
343 if ((color = getEffectPropertyChild(effect, prop, "emissive-front")))
344 mat->setEmission(Material::FRONT, getColor(color));
345 if ((color = getEffectPropertyChild(effect, prop, "emissive-back")))
346 mat->setEmission(Material::BACK, getColor(color));
347 const SGPropertyNode* shininess = 0;
348 mat->setShininess(Material::FRONT_AND_BACK, 0.0f);
349 if ((shininess = getEffectPropertyChild(effect, prop, "shininess")))
350 mat->setShininess(Material::FRONT_AND_BACK, shininess->getFloatValue());
351 if ((shininess = getEffectPropertyChild(effect, prop, "shininess-front")))
352 mat->setShininess(Material::FRONT, shininess->getFloatValue());
353 if ((shininess = getEffectPropertyChild(effect, prop, "shininess-back")))
354 mat->setShininess(Material::BACK, shininess->getFloatValue());
355 Material::ColorMode colorMode = Material::OFF;
356 findAttr(colorModes, getEffectPropertyChild(effect, prop, "color-mode"),
358 mat->setColorMode(colorMode);
359 pass->setAttribute(mat);
362 InstallAttributeBuilder<MaterialBuilder> installMaterial("material");
364 struct BlendBuilder : public PassAttributeBuilder
366 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
367 const osgDB::ReaderWriter::Options* options)
369 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
372 pass->setMode(GL_BLEND, (realProp->getBoolValue()
374 : StateAttribute::OFF));
378 InstallAttributeBuilder<BlendBuilder> installBlend("blend");
380 struct AlphaTestBuilder : public PassAttributeBuilder
382 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
383 const osgDB::ReaderWriter::Options* options)
385 const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
388 pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
390 : StateAttribute::OFF));
394 InstallAttributeBuilder<AlphaTestBuilder> installAlphaTest("alpha-test");
396 EffectNameValue<TexEnv::Mode> texEnvModes[] =
398 {"add", TexEnv::ADD},
399 {"blend", TexEnv::BLEND},
400 {"decal", TexEnv::DECAL},
401 {"modulate", TexEnv::MODULATE},
402 {"replace", TexEnv::REPLACE}
405 TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
407 const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
409 const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
413 TexEnv::Mode mode = TexEnv::MODULATE;
414 findAttr(texEnvModes, modeProp, mode);
415 if (mode == TexEnv::MODULATE) {
416 return StateAttributeFactory::instance()->getStandardTexEnv();
418 TexEnv* env = new TexEnv(mode);
420 env->setColor(colorProp->getValue<SGVec4d>().osg());
425 struct TextureUnitBuilder : PassAttributeBuilder
427 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
428 const osgDB::ReaderWriter::Options* options);
431 void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
432 const SGPropertyNode* prop,
433 const osgDB::ReaderWriter::Options* options)
436 // Decode the texture unit
438 const SGPropertyNode* pUnit = prop->getChild("unit");
440 unit = pUnit->getValue<int>();
442 const SGPropertyNode* pName = prop->getChild("name");
445 unit = boost::lexical_cast<int>(pName->getStringValue());
446 } catch (boost::bad_lexical_cast& lex) {
447 SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit "
451 const SGPropertyNode* pType = prop->getChild("type");
456 type = pType->getStringValue();
457 Texture* texture = 0;
459 texture = TextureBuilder::buildFromType(effect, type, prop,
462 catch (BuilderException& e) {
463 SG_LOG(SG_INPUT, SG_ALERT, "No image file for texture, using white ");
464 texture = StateAttributeFactory::instance()->getWhiteTexture();
466 pass->setTextureAttributeAndModes(unit, texture);
467 const SGPropertyNode* envProp = prop->getChild("environment");
469 TexEnv* env = buildTexEnv(effect, envProp);
471 pass->setTextureAttributeAndModes(unit, env);
477 InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
479 typedef map<string, ref_ptr<Program> > ProgramMap;
480 ProgramMap programMap;
482 typedef map<string, ref_ptr<Shader> > ShaderMap;
485 void reload_shaders()
487 for(ShaderMap::iterator sitr = shaderMap.begin(); sitr != shaderMap.end(); ++sitr)
489 Shader *shader = sitr->second.get();
490 string fileName = osgDB::findDataFile(sitr->first);
491 if (!fileName.empty()) {
492 shader->loadShaderSourceFromFile(fileName);
497 struct ShaderProgramBuilder : PassAttributeBuilder
499 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
500 const osgDB::ReaderWriter::Options* options);
503 void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
504 const SGPropertyNode* prop,
505 const osgDB::ReaderWriter::Options*
508 PropertyList pVertShaders = prop->getChildren("vertex-shader");
509 PropertyList pFragShaders = prop->getChildren("fragment-shader");
511 for (PropertyList::iterator itr = pVertShaders.begin(),
512 e = pVertShaders.end();
516 programKey += (*itr)->getStringValue();
519 for (PropertyList::iterator itr = pFragShaders.begin(),
520 e = pFragShaders.end();
524 programKey += (*itr)->getStringValue();
527 Program* program = 0;
528 ProgramMap::iterator pitr = programMap.find(programKey);
529 if (pitr != programMap.end()) {
530 program = pitr->second.get();
532 program = new Program;
533 program->setName(programKey);
534 // Add vertex shaders, then fragment shaders
535 PropertyList& pvec = pVertShaders;
536 Shader::Type stype = Shader::VERTEX;
537 for (int i = 0; i < 2; ++i) {
538 for (PropertyList::iterator nameItr = pvec.begin(), e = pvec.end();
541 string shaderName = (*nameItr)->getStringValue();
542 ShaderMap::iterator sitr = shaderMap.find(shaderName);
543 if (sitr != shaderMap.end()) {
544 program->addShader(sitr->second.get());
546 string fileName = osgDB::findDataFile(shaderName, options);
547 if (!fileName.empty()) {
548 ref_ptr<Shader> shader = new Shader(stype);
549 if (shader->loadShaderSourceFromFile(fileName)) {
550 program->addShader(shader.get());
551 shaderMap.insert(make_pair(shaderName, shader));
557 stype = Shader::FRAGMENT;
559 programMap.insert(make_pair(programKey, program));
561 pass->setAttributeAndModes(program);
564 InstallAttributeBuilder<ShaderProgramBuilder> installShaderProgram("program");
566 EffectNameValue<Uniform::Type> uniformTypes[] =
568 {"float", Uniform::FLOAT},
569 {"float-vec3", Uniform::FLOAT_VEC3},
570 {"float-vec4", Uniform::FLOAT_VEC4},
571 {"sampler-1d", Uniform::SAMPLER_1D},
572 {"sampler-2d", Uniform::SAMPLER_2D},
573 {"sampler-3d", Uniform::SAMPLER_3D}
576 struct UniformBuilder :public PassAttributeBuilder
578 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
579 const osgDB::ReaderWriter::Options* options)
581 const SGPropertyNode* nameProp = prop->getChild("name");
582 const SGPropertyNode* typeProp = prop->getChild("type");
583 const SGPropertyNode* valProp
584 = getEffectPropertyChild(effect, prop, "value");
586 Uniform::Type uniformType = Uniform::FLOAT;
588 name = nameProp->getStringValue();
590 SG_LOG(SG_INPUT, SG_ALERT, "No name for uniform property ");
594 SG_LOG(SG_INPUT, SG_ALERT, "No value for uniform property "
599 props::Type propType = valProp->getType();
603 break; // default float type;
605 uniformType = Uniform::FLOAT_VEC3;
608 uniformType = Uniform::FLOAT_VEC4;
611 SG_LOG(SG_INPUT, SG_ALERT, "Can't deduce type of uniform "
616 findAttr(uniformTypes, typeProp, uniformType);
618 ref_ptr<Uniform> uniform = new Uniform;
619 uniform->setName(name);
620 uniform->setType(uniformType);
621 switch (uniformType) {
623 uniform->set(valProp->getValue<float>());
625 case Uniform::FLOAT_VEC3:
626 uniform->set(Vec3f(valProp->getValue<SGVec3d>().osg()));
628 case Uniform::FLOAT_VEC4:
629 uniform->set(Vec4f(valProp->getValue<SGVec4d>().osg()));
631 case Uniform::SAMPLER_1D:
632 case Uniform::SAMPLER_2D:
633 case Uniform::SAMPLER_3D:
634 uniform->set(valProp->getValue<int>());
636 default: // avoid compiler warning
639 pass->addUniform(uniform.get());
643 InstallAttributeBuilder<UniformBuilder> installUniform("uniform");
645 // Not sure what to do with "name". At one point I wanted to use it to
646 // order the passes, but I do support render bin and stuff too...
648 struct NameBuilder : public PassAttributeBuilder
650 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
651 const osgDB::ReaderWriter::Options* options)
653 // name can't use <use>
654 string name = prop->getStringValue();
660 InstallAttributeBuilder<NameBuilder> installName("name");
662 EffectNameValue<PolygonMode::Mode> polygonModeModes[] =
664 {"fill", PolygonMode::FILL},
665 {"line", PolygonMode::LINE},
666 {"point", PolygonMode::POINT}
669 struct PolygonModeBuilder : public PassAttributeBuilder
671 void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
672 const osgDB::ReaderWriter::Options* options)
674 const SGPropertyNode* frontProp
675 = getEffectPropertyChild(effect, prop, "front");
676 const SGPropertyNode* backProp
677 = getEffectPropertyChild(effect, prop, "back");
678 ref_ptr<PolygonMode> pmode = new PolygonMode;
679 PolygonMode::Mode frontMode = PolygonMode::FILL;
680 PolygonMode::Mode backMode = PolygonMode::FILL;
682 findAttr(polygonModeModes, frontProp, frontMode);
683 pmode->setMode(PolygonMode::FRONT, frontMode);
686 findAttr(polygonModeModes, backProp, backMode);
687 pmode->setMode(PolygonMode::BACK, backMode);
689 pass->setAttribute(pmode.get());
693 InstallAttributeBuilder<PolygonModeBuilder> installPolygonMode("polygon-mode");
694 void buildTechnique(Effect* effect, const SGPropertyNode* prop,
695 const osgDB::ReaderWriter::Options* options)
697 Technique* tniq = new Technique;
698 effect->techniques.push_back(tniq);
699 const SGPropertyNode* predProp = prop->getChild("predicate");
701 tniq->setAlwaysValid(true);
704 TechniquePredParser parser;
705 parser.setTechnique(tniq);
706 expression::BindingLayout& layout = parser.getBindingLayout();
707 /*int contextLoc = */layout.addBinding("__contextId", expression::INT);
708 SGExpressionb* validExp
709 = dynamic_cast<SGExpressionb*>(parser.read(predProp
712 tniq->setValidExpression(validExp, layout);
714 throw expression::ParseError("technique predicate is not a boolean expression");
716 catch (expression::ParseError& except)
718 SG_LOG(SG_INPUT, SG_ALERT,
719 "parsing technique predicate " << except.getMessage());
720 tniq->setAlwaysValid(false);
723 PropertyList passProps = prop->getChildren("pass");
724 for (PropertyList::iterator itr = passProps.begin(), e = passProps.end();
727 buildPass(effect, tniq, itr->ptr(), options);
731 // Walk the techniques property tree, building techniques and
733 bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
735 PropertyList tniqList = root->getChildren("technique");
736 for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
739 buildTechnique(this, *itr, options);
743 bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
745 const Effect& effect = static_cast<const Effect&>(obj);
747 fw.indent() << "techniques " << effect.techniques.size() << "\n";
748 BOOST_FOREACH(const ref_ptr<Technique>& technique, effect.techniques) {
749 fw.writeObject(*technique);
756 osgDB::RegisterDotOsgWrapperProxy effectProxy
760 "Object simgear::Effect",
762 &Effect_writeLocalData