X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Fmaterial%2FEffect.cxx;h=c934815760ced8359c0c3678fde2e9b865544deb;hb=430e72b42404cd07685de13c767a9ca443ad1f49;hp=f2bcbcdc619cbbba615011667e3de8b80a0be6cf;hpb=7fe40bce86ebd460d06ae8e76d3c64a6c9d58379;p=simgear.git diff --git a/simgear/scene/material/Effect.cxx b/simgear/scene/material/Effect.cxx index f2bcbcdc..c9348157 100644 --- a/simgear/scene/material/Effect.cxx +++ b/simgear/scene/material/Effect.cxx @@ -1,4 +1,4 @@ -// Copyright (C) 2008 - 2009 Tim Moore timoore@redhat.com +// Copyright (C) 2008 - 2010 Tim Moore timoore33@gmail.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -66,13 +67,14 @@ #include #include -#include +#include #include +#include #include #include #include #include - +#include namespace simgear @@ -89,7 +91,7 @@ Effect::Effect() } Effect::Effect(const Effect& rhs, const CopyOp& copyop) - : root(rhs.root), parametersProp(rhs.parametersProp), _cache(0), + : osg::Object(rhs,copyop), root(rhs.root), parametersProp(rhs.parametersProp), _cache(0), _isRealized(rhs._isRealized) { typedef vector > TechniqueList; @@ -98,6 +100,8 @@ Effect::Effect(const Effect& rhs, const CopyOp& copyop) itr != end; ++itr) techniques.push_back(static_cast(copyop(itr->get()))); + + generator = rhs.generator; } // Assume that the last technique is always valid. @@ -110,6 +114,13 @@ StateSet* Effect::getDefaultStateSet() return pass; } +int Effect::getGenerator(Effect::Generator what) const +{ + std::map::const_iterator it = generator.find(what); + if(it == generator.end()) return -1; + else return it->second; +} + // There should always be a valid technique in an effect. Technique* Effect::chooseTechnique(RenderInfo* info) @@ -144,7 +155,7 @@ Effect::~Effect() } void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { Pass* pass = new Pass; tniq->passes.push_back(pass); @@ -193,12 +204,12 @@ osg::Vec4f getColor(const SGPropertyNode* prop) struct LightingBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options); + const SGReaderWriterOptions* options); }; void LightingBuilder::buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) @@ -212,7 +223,7 @@ InstallAttributeBuilder installLighting("lighting"); struct ShadeModelBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) @@ -234,7 +245,7 @@ InstallAttributeBuilder installShadeModel("shade-model"); struct CullFaceBuilder : PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) { @@ -262,7 +273,7 @@ InstallAttributeBuilder installCullFace("cull-face"); struct ColorMaskBuilder : PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) @@ -270,7 +281,7 @@ struct ColorMaskBuilder : PassAttributeBuilder ColorMask *mask = new ColorMask; Vec4 m = getColor(realProp); - mask->setMask(m.r(), m.g(), m.b(), m.a()); + mask->setMask(m.r() > 0.0, m.g() > 0.0, m.b() > 0.0, m.a() > 0.0); pass->setAttributeAndModes(mask); } }; @@ -289,7 +300,7 @@ EffectPropertyMap renderingHints(renderingHintInit); struct HintBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) @@ -305,7 +316,7 @@ InstallAttributeBuilder installHint("rendering-hint"); struct RenderBinBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { if (!isAttributeActive(effect, prop)) return; @@ -332,7 +343,7 @@ InstallAttributeBuilder installRenderBin("render-bin"); struct MaterialBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options); + const SGReaderWriterOptions* options); }; EffectNameValue colorModeInit[] = @@ -348,7 +359,7 @@ EffectPropertyMap colorModes(colorModeInit); void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { if (!isAttributeActive(effect, prop)) return; @@ -418,7 +429,7 @@ EffectPropertyMap blendFuncModes(blendFuncModesInit); struct BlendBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { if (!isAttributeActive(effect, prop)) return; @@ -531,7 +542,7 @@ EffectPropertyMap stencilOperation(stencilOperationInit); struct StencilBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { if (!isAttributeActive(effect, prop)) return; @@ -609,7 +620,7 @@ alphaComparison(alphaComparisonInit); struct AlphaTestBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { if (!isAttributeActive(effect, prop)) return; @@ -660,7 +671,12 @@ InstallAttributeBuilder installAlphaTest("alpha-test"); InstallAttributeBuilder textureUnitBuilder("texture-unit"); // Shader key, used both for shaders with relative and absolute names -typedef pair ShaderKey; +typedef pair ShaderKey; + +inline ShaderKey makeShaderKey(SGPropertyNode_ptr& ptr, int shaderType) +{ + return ShaderKey(ptr->getStringValue(), shaderType); +} struct ProgramKey { @@ -700,6 +716,7 @@ typedef tr1::unordered_map, boost::hash, ProgramKey::EqualTo> ProgramMap; ProgramMap programMap; +ProgramMap resolvedProgramMap; // map with resolved shader file names typedef tr1::unordered_map, boost::hash > ShaderMap; @@ -709,10 +726,10 @@ void reload_shaders() { for(ShaderMap::iterator sitr = shaderMap.begin(); sitr != shaderMap.end(); ++sitr) { - Shader *shader = sitr->second.get(); - string fileName = osgDB::findDataFile(sitr->first.first); + Shader *shader = sitr->second.get(); + string fileName = SGModelLib::findDataFile(sitr->first.first); if (!fileName.empty()) { - shader->loadShaderSourceFromFile(fileName); + shader->loadShaderSourceFromFile(fileName); } } } @@ -720,33 +737,51 @@ void reload_shaders() struct ShaderProgramBuilder : PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options); + const SGReaderWriterOptions* options); +}; + + +EffectNameValue geometryInputTypeInit[] = +{ + {"points", GL_POINTS}, + {"lines", GL_LINES}, + {"lines-adjacency", GL_LINES_ADJACENCY_EXT}, + {"triangles", GL_TRIANGLES}, + {"triangles-adjacency", GL_TRIANGLES_ADJACENCY_EXT}, +}; +EffectPropertyMap +geometryInputType(geometryInputTypeInit); + + +EffectNameValue geometryOutputTypeInit[] = +{ + {"points", GL_POINTS}, + {"line-strip", GL_LINE_STRIP}, + {"triangle-strip", GL_TRIANGLE_STRIP} }; +EffectPropertyMap +geometryOutputType(geometryOutputTypeInit); void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* + const SGReaderWriterOptions* options) { using namespace boost; if (!isAttributeActive(effect, prop)) return; PropertyList pVertShaders = prop->getChildren("vertex-shader"); + PropertyList pGeomShaders = prop->getChildren("geometry-shader"); PropertyList pFragShaders = prop->getChildren("fragment-shader"); PropertyList pAttributes = prop->getChildren("attribute"); ProgramKey prgKey; - for (PropertyList::iterator itr = pVertShaders.begin(), - e = pVertShaders.end(); - itr != e; - ++itr) - prgKey.shaders.push_back(ShaderKey((*itr)->getStringValue(), - Shader::VERTEX)); - for (PropertyList::iterator itr = pFragShaders.begin(), - e = pFragShaders.end(); - itr != e; - ++itr) - prgKey.shaders.push_back(ShaderKey((*itr)->getStringValue(), - Shader::FRAGMENT)); + std::back_insert_iterator > inserter(prgKey.shaders); + transform(pVertShaders.begin(), pVertShaders.end(), inserter, + boost::bind(makeShaderKey, _1, Shader::VERTEX)); + transform(pGeomShaders.begin(), pGeomShaders.end(), inserter, + boost::bind(makeShaderKey, _1, Shader::GEOMETRY)); + transform(pFragShaders.begin(), pFragShaders.end(), inserter, + boost::bind(makeShaderKey, _1, Shader::FRAGMENT)); for (PropertyList::iterator itr = pAttributes.begin(), e = pAttributes.end(); itr != e; @@ -767,40 +802,75 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass, ProgramMap::iterator pitr = programMap.find(prgKey); if (pitr != programMap.end()) { program = pitr->second.get(); - } else { - program = new Program; - // Add vertex shaders, then fragment shaders - PropertyList& pvec = pVertShaders; - Shader::Type stype = Shader::VERTEX; - for (int i = 0; i < 2; ++i) { - for (PropertyList::iterator nameItr = pvec.begin(), e = pvec.end(); - nameItr != e; - ++nameItr) { - string shaderName = (*nameItr)->getStringValue(); - string fileName = osgDB::findDataFile(shaderName, options); - if (fileName.empty()) - throw BuilderException(string("couldn't find shader ") + - shaderName); - ShaderKey skey(fileName, stype); - ShaderMap::iterator sitr = shaderMap.find(skey); - if (sitr != shaderMap.end()) { - program->addShader(sitr->second.get()); - } else { - ref_ptr shader = new Shader(stype); - if (shader->loadShaderSourceFromFile(fileName)) { - program->addShader(shader.get()); - shaderMap.insert(ShaderMap::value_type(skey, shader)); - } - } + pass->setAttributeAndModes(program); + return; + } + // The program wasn't in the map using the load path passed in with + // the options, but it might have already been loaded using a + // different load path i.e., its shaders were found in the fg data + // directory. So, resolve the shaders' file names and look in the + // resolvedProgramMap for a program using those shaders. + ProgramKey resolvedKey; + resolvedKey.attributes = prgKey.attributes; + BOOST_FOREACH(const ShaderKey& shaderKey, prgKey.shaders) + { + const string& shaderName = shaderKey.first; + Shader::Type stype = (Shader::Type)shaderKey.second; + string fileName = SGModelLib::findDataFile(shaderName, options); + if (fileName.empty()) + throw BuilderException(string("couldn't find shader ") + + shaderName); + resolvedKey.shaders.push_back(ShaderKey(fileName, stype)); + } + ProgramMap::iterator resitr = resolvedProgramMap.find(resolvedKey); + if (resitr != resolvedProgramMap.end()) { + program = resitr->second.get(); + programMap.insert(ProgramMap::value_type(prgKey, program)); + pass->setAttributeAndModes(program); + return; + } + program = new Program; + BOOST_FOREACH(const ShaderKey& skey, resolvedKey.shaders) + { + const string& fileName = skey.first; + Shader::Type stype = (Shader::Type)skey.second; + ShaderMap::iterator sitr = shaderMap.find(skey); + if (sitr != shaderMap.end()) { + program->addShader(sitr->second.get()); + } else { + ref_ptr shader = new Shader(stype); + shader->setName(fileName); + if (shader->loadShaderSourceFromFile(fileName)) { + program->addShader(shader.get()); + shaderMap.insert(ShaderMap::value_type(skey, shader)); } - pvec = pFragShaders; - stype = Shader::FRAGMENT; - } - BOOST_FOREACH(const ProgramKey::AttribKey& key, prgKey.attributes) { - program->addBindAttribLocation(key.first, key.second); } - programMap.insert(ProgramMap::value_type(prgKey, program)); } + BOOST_FOREACH(const ProgramKey::AttribKey& key, prgKey.attributes) { + program->addBindAttribLocation(key.first, key.second); + } + const SGPropertyNode* pGeometryVerticesOut + = getEffectPropertyChild(effect, prop, "geometry-vertices-out"); + if (pGeometryVerticesOut) + program->setParameter(GL_GEOMETRY_VERTICES_OUT_EXT, + pGeometryVerticesOut->getIntValue()); + const SGPropertyNode* pGeometryInputType + = getEffectPropertyChild(effect, prop, "geometry-input-type"); + if (pGeometryInputType) { + GLint type; + findAttr(geometryInputType, pGeometryInputType->getStringValue(), type); + program->setParameter(GL_GEOMETRY_INPUT_TYPE_EXT, type); + } + const SGPropertyNode* pGeometryOutputType + = getEffectPropertyChild(effect, prop, "geometry-output-type"); + if (pGeometryOutputType) { + GLint type; + findAttr(geometryOutputType, pGeometryOutputType->getStringValue(), + type); + program->setParameter(GL_GEOMETRY_OUTPUT_TYPE_EXT, type); + } + programMap.insert(ProgramMap::value_type(prgKey, program)); + resolvedProgramMap.insert(ProgramMap::value_type(resolvedKey, program)); pass->setAttributeAndModes(program); } @@ -808,6 +878,8 @@ InstallAttributeBuilder installShaderProgram("program"); EffectNameValue uniformTypesInit[] = { + {"bool", Uniform::BOOL}, + {"int", Uniform::INT}, {"float", Uniform::FLOAT}, {"float-vec3", Uniform::FLOAT_VEC3}, {"float-vec4", Uniform::FLOAT_VEC4}, @@ -820,15 +892,32 @@ EffectNameValue uniformTypesInit[] = }; EffectPropertyMap uniformTypes(uniformTypesInit); +// Optimization hack for common uniforms. +// XXX protect these with a mutex? + +ref_ptr texture0; +ref_ptr colorMode[3]; + struct UniformBuilder :public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { + if (!texture0.valid()) { + texture0 = new Uniform(Uniform::SAMPLER_2D, "texture"); + texture0->set(0); + texture0->setDataVariance(Object::STATIC); + for (int i = 0; i < 3; ++i) { + colorMode[i] = new Uniform(Uniform::INT, "colorMode"); + colorMode[i]->set(i); + colorMode[i]->setDataVariance(Object::STATIC); + } + } if (!isAttributeActive(effect, prop)) return; const SGPropertyNode* nameProp = prop->getChild("name"); const SGPropertyNode* typeProp = prop->getChild("type"); + const SGPropertyNode* positionedProp = prop->getChild("positioned"); const SGPropertyNode* valProp = prop->getChild("value"); string name; Uniform::Type uniformType = Uniform::FLOAT; @@ -846,6 +935,12 @@ struct UniformBuilder :public PassAttributeBuilder if (!typeProp) { props::Type propType = valProp->getType(); switch (propType) { + case props::BOOL: + uniformType = Uniform::BOOL; + break; + case props::INT: + uniformType = Uniform::INT; + break; case props::FLOAT: case props::DOUBLE: break; // default float type; @@ -867,6 +962,11 @@ struct UniformBuilder :public PassAttributeBuilder uniform->setName(name); uniform->setType(uniformType); switch (uniformType) { + case Uniform::BOOL: + initFromParameters(effect, valProp, uniform.get(), + static_cast(&Uniform::set), + options); + break; case Uniform::FLOAT: initFromParameters(effect, valProp, uniform.get(), static_cast(&Uniform::set), @@ -882,6 +982,7 @@ struct UniformBuilder :public PassAttributeBuilder static_cast(&Uniform::set), vec4Names, options); break; + case Uniform::INT: case Uniform::SAMPLER_1D: case Uniform::SAMPLER_2D: case Uniform::SAMPLER_3D: @@ -895,7 +996,25 @@ struct UniformBuilder :public PassAttributeBuilder default: // avoid compiler warning break; } + // optimize common uniforms + if (uniformType == Uniform::SAMPLER_2D || uniformType == Uniform::INT) + { + int val = 0; + uniform->get(val); // 'val' remains unchanged in case of error (Uniform is a non-scalar) + if (uniformType == Uniform::SAMPLER_2D && val == 0 + && name == "texture") { + uniform = texture0; + } else if (uniformType == Uniform::INT && val >= 0 && val < 3 + && name == "colorMode") { + uniform = colorMode[val]; + } + } pass->addUniform(uniform.get()); + if (positionedProp && positionedProp->getBoolValue() && uniformType == Uniform::FLOAT_VEC4) { + osg::Vec4 offset; + uniform->get(offset); + pass->addPositionedUniform( name, offset ); + } } }; @@ -907,7 +1026,7 @@ InstallAttributeBuilder installUniform("uniform"); struct NameBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { // name can't use string name = prop->getStringValue(); @@ -929,7 +1048,7 @@ EffectPropertyMap polygonModeModes(polygonModeModesInit); struct PolygonModeBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { if (!isAttributeActive(effect, prop)) return; @@ -954,10 +1073,38 @@ struct PolygonModeBuilder : public PassAttributeBuilder InstallAttributeBuilder installPolygonMode("polygon-mode"); +struct PolygonOffsetBuilder : public PassAttributeBuilder +{ + void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, + const SGReaderWriterOptions* options) + { + if (!isAttributeActive(effect, prop)) + return; + + const SGPropertyNode* factor + = getEffectPropertyChild(effect, prop, "factor"); + const SGPropertyNode* units + = getEffectPropertyChild(effect, prop, "units"); + + ref_ptr polyoffset = new PolygonOffset; + + polyoffset->setFactor(factor->getFloatValue()); + polyoffset->setUnits(units->getFloatValue()); + + SG_LOG(SG_INPUT, SG_BULK, + "Set PolygonOffset to " << polyoffset->getFactor() << polyoffset->getUnits() ); + + pass->setAttributeAndModes(polyoffset.get(), + StateAttribute::OVERRIDE|StateAttribute::ON); + } +}; + +InstallAttributeBuilder installPolygonOffset("polygon-offset"); + struct VertexProgramTwoSideBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) @@ -974,7 +1121,7 @@ installTwoSide("vertex-program-two-side"); struct VertexProgramPointSizeBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) @@ -1004,7 +1151,7 @@ EffectPropertyMap depthFunction(depthFunctionInit); struct DepthBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { if (!isAttributeActive(effect, prop)) return; @@ -1023,19 +1170,22 @@ struct DepthBuilder : public PassAttributeBuilder const SGPropertyNode* pfar = getEffectPropertyChild(effect, prop, "far"); if (pfar) - depth->setZFar(pnear->getValue()); + depth->setZFar(pfar->getValue()); const SGPropertyNode* pmask = getEffectPropertyChild(effect, prop, "write-mask"); if (pmask) depth->setWriteMask(pmask->getValue()); - pass->setAttribute(depth.get()); + const SGPropertyNode* penabled + = getEffectPropertyChild(effect, prop, "enabled"); + bool enabled = ( penabled == 0 || penabled->getBoolValue() ); + pass->setAttributeAndModes(depth.get(), enabled ? osg::StateAttribute::ON : osg::StateAttribute::OFF); } }; InstallAttributeBuilder installDepth("depth"); void buildTechnique(Effect* effect, const SGPropertyNode* prop, - const SGReaderWriterXMLOptions* options) + const SGReaderWriterOptions* options) { Technique* tniq = new Technique; effect->techniques.push_back(tniq); @@ -1121,6 +1271,9 @@ bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss) } } makeChild(paramRoot, "cull-face")->setStringValue(cullFaceString); + // Macintosh ATI workaround + bool vertexTwoSide = cullFaceString == "off"; + makeChild(paramRoot, "vertex-program-two-side")->setValue(vertexTwoSide); const BlendFunc* blendFunc = getStateAttribute(ss); SGPropertyNode* blendNode = makeChild(paramRoot, "blend"); if (blendFunc) { @@ -1141,7 +1294,7 @@ bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss) // Walk the techniques property tree, building techniques and // passes. -bool Effect::realizeTechniques(const SGReaderWriterXMLOptions* options) +bool Effect::realizeTechniques(const SGReaderWriterOptions* options) { if (_isRealized) return true; @@ -1219,14 +1372,15 @@ osgDB::RegisterDotOsgWrapperProxy effectProxy } // Property expressions for technique predicates -class PropertyExpression : public SGExpression +template +class PropertyExpression : public SGExpression { public: PropertyExpression(SGPropertyNode* pnode) : _pnode(pnode) {} - void eval(bool& value, const expression::Binding*) const + void eval(T& value, const expression::Binding*) const { - value = _pnode->getValue(); + value = _pnode->getValue(); } protected: SGPropertyNode_ptr _pnode; @@ -1239,18 +1393,20 @@ public: void valueChanged(SGPropertyNode* node) { - _tniq->refreshValidity(); + if (_tniq.valid()) + _tniq->refreshValidity(); } protected: - osg::ref_ptr _tniq; + osg::observer_ptr _tniq; }; +template Expression* propertyExpressionParser(const SGPropertyNode* exp, expression::Parser* parser) { SGPropertyNode_ptr pnode = getPropertyRoot()->getNode(exp->getStringValue(), true); - PropertyExpression* pexp = new PropertyExpression(pnode); + PropertyExpression* pexp = new PropertyExpression(pnode); TechniquePredParser* predParser = dynamic_cast(parser); if (predParser) @@ -1260,6 +1416,9 @@ Expression* propertyExpressionParser(const SGPropertyNode* exp, } expression::ExpParserRegistrar propertyRegistrar("property", - propertyExpressionParser); + propertyExpressionParser); + +expression::ExpParserRegistrar propvalueRegistrar("float-property", + propertyExpressionParser); }