X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Fmaterial%2FEffect.cxx;h=3ad9316216fd0904e039f9c8caeb79f76092ab2c;hb=d04cf4d8978866eb80a1639b6d4ddfe387338c77;hp=259e6636daed894f1cd3912b0fa52b382016dee1;hpb=fcf72a712384e27c1cc758e987f1fab9afcced4d;p=simgear.git diff --git a/simgear/scene/material/Effect.cxx b/simgear/scene/material/Effect.cxx index 259e6636..3ad93162 100644 --- a/simgear/scene/material/Effect.cxx +++ b/simgear/scene/material/Effect.cxx @@ -14,33 +14,49 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#ifdef HAVE_CONFIG_H +# include +#endif + #include "Effect.hxx" +#include "EffectBuilder.hxx" +#include "EffectGeode.hxx" #include "Technique.hxx" #include "Pass.hxx" +#include "TextureBuilder.hxx" #include #include #include #include #include +#include #include #include -#include +#include #include #include +#include +#include #include +#include #include #include +#include #include #include #include #include #include #include +#include #include +#include #include +#include +#include #include #include #include @@ -50,6 +66,8 @@ #include #include +#include +#include #include #include #include @@ -63,17 +81,23 @@ using namespace std; using namespace osg; using namespace osgUtil; +using namespace effect; + Effect::Effect() + : _cache(0), _isRealized(false) { } Effect::Effect(const Effect& rhs, const CopyOp& copyop) - : root(rhs.root), parametersProp(rhs.parametersProp) + : root(rhs.root), parametersProp(rhs.parametersProp), _cache(0), + _isRealized(rhs._isRealized) { - using namespace boost; - transform(rhs.techniques.begin(), rhs.techniques.end(), - back_inserter(techniques), - bind(simgear::clone_ref, _1, copyop)); + typedef vector > TechniqueList; + for (TechniqueList::const_iterator itr = rhs.techniques.begin(), + end = rhs.techniques.end(); + itr != end; + ++itr) + techniques.push_back(static_cast(copyop(itr->get()))); } // Assume that the last technique is always valid. @@ -116,89 +140,20 @@ void Effect::releaseGLObjects(osg::State* state) const Effect::~Effect() { -} - -class PassAttributeBuilder : public Referenced -{ -public: - virtual void buildAttribute(Effect* effect, Pass* pass, - const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) - = 0; -}; - -typedef map > PassAttrMap; -PassAttrMap passAttrMap; - -template -struct InstallAttributeBuilder -{ - InstallAttributeBuilder(const string& name) - { - passAttrMap.insert(make_pair(name, new T)); - } -}; -// Simple tables of strings and OSG constants. The table intialization -// *must* be in alphabetical order. -template -struct EffectNameValue -{ - // Don't use std::pair because we want to use aggregate intialization. - - const char* first; - T second; - class Compare - { - private: - static bool compare(const char* lhs, const char* rhs) - { - return strcmp(lhs, rhs) < 0; - } - public: - bool operator()(const EffectNameValue& lhs, - const EffectNameValue& rhs) const - { - return compare(lhs.first, rhs.first); - } - bool operator()(const char* lhs, const EffectNameValue& rhs) const - { - return compare(lhs, rhs.first); - } - bool operator()(const EffectNameValue& lhs, const char* rhs) const - { - return compare (lhs.first, rhs); - } - }; -}; - -template -bool findAttr(const ENV (&attrs)[N], const SGPropertyNode* prop, T& result) -{ - if (!prop) - return false; - const char* name = prop->getStringValue(); - if (!name) - return false; - std::pair itrs - = std::equal_range(&attrs[0], &attrs[N], name, typename ENV::Compare()); - if (itrs.first == itrs.second) { - return false; - } else { - result = itrs.first->second; - return true; - } + delete _cache; } void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { Pass* pass = new Pass; tniq->passes.push_back(pass); for (int i = 0; i < prop->nChildren(); ++i) { const SGPropertyNode* attrProp = prop->getChild(i); - PassAttrMap::iterator itr = passAttrMap.find(attrProp->getName()); - if (itr != passAttrMap.end()) - itr->second->buildAttribute(effect, pass, attrProp, options); + PassAttributeBuilder* builder + = PassAttributeBuilder::find(attrProp->getNameString()); + if (builder) + builder->buildAttribute(effect, pass, attrProp, options); else SG_LOG(SG_INPUT, SG_ALERT, "skipping unknown pass attribute " << attrProp->getName()); @@ -209,9 +164,9 @@ osg::Vec4f getColor(const SGPropertyNode* prop) { if (prop->nChildren() == 0) { if (prop->getType() == props::VEC4D) { - return osg::Vec4f(prop->getValue().osg()); + return osg::Vec4f(toOsg(prop->getValue())); } else if (prop->getType() == props::VEC3D) { - return osg::Vec4f(prop->getValue().osg(), 1.0f); + return osg::Vec4f(toOsg(prop->getValue()), 1.0f); } else { SG_LOG(SG_INPUT, SG_ALERT, "invalid color property " << prop->getName() << " " @@ -231,44 +186,15 @@ osg::Vec4f getColor(const SGPropertyNode* prop) } } -// Given a property node from a pass, get its value either from it or -// from the effect parameters. -const SGPropertyNode* getEffectPropertyNode(Effect* effect, - const SGPropertyNode* prop) -{ - if (!prop) - return 0; - if (prop->nChildren() > 0) { - const SGPropertyNode* useProp = prop->getChild("use"); - if (!useProp || !effect->parametersProp) - return prop; - return effect->parametersProp->getNode(useProp->getStringValue()); - } - return prop; -} - -// Get a named child property from pass parameters or effect -// parameters. -const SGPropertyNode* getEffectPropertyChild(Effect* effect, - const SGPropertyNode* prop, - const char* name) -{ - const SGPropertyNode* child = prop->getChild(name); - if (!child) - return 0; - else - return getEffectPropertyNode(effect, child); -} - struct LightingBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options); + const SGReaderWriterXMLOptions* options); }; void LightingBuilder::buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) @@ -282,7 +208,7 @@ InstallAttributeBuilder installLighting("lighting"); struct ShadeModelBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) @@ -304,11 +230,13 @@ InstallAttributeBuilder installShadeModel("shade-model"); struct CullFaceBuilder : PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); - if (!realProp) + if (!realProp) { + pass->setMode(GL_CULL_FACE, StateAttribute::OFF); return; + } StateAttributeFactory *attrFact = StateAttributeFactory::instance(); string propVal = realProp->getStringValue(); if (propVal == "front") @@ -317,6 +245,8 @@ struct CullFaceBuilder : PassAttributeBuilder pass->setAttributeAndModes(attrFact->getCullFaceBack()); else if (propVal == "front-back") pass->setAttributeAndModes(new CullFace(CullFace::FRONT_AND_BACK)); + else if (propVal == "off") + pass->setMode(GL_CULL_FACE, StateAttribute::OFF); else SG_LOG(SG_INPUT, SG_ALERT, "invalid cull face property " << propVal); @@ -325,19 +255,44 @@ struct CullFaceBuilder : PassAttributeBuilder InstallAttributeBuilder installCullFace("cull-face"); +struct ColorMaskBuilder : PassAttributeBuilder +{ + void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, + const SGReaderWriterXMLOptions* options) + { + const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); + if (!realProp) + return; + + ColorMask *mask = new ColorMask; + Vec4 m = getColor(realProp); + mask->setMask(m.r(), m.g(), m.b(), m.a()); + pass->setAttributeAndModes(mask); + } +}; + +InstallAttributeBuilder installColorMask("color-mask"); + +EffectNameValue renderingHintInit[] = +{ + { "default", StateSet::DEFAULT_BIN }, + { "opaque", StateSet::OPAQUE_BIN }, + { "transparent", StateSet::TRANSPARENT_BIN } +}; + +EffectPropertyMap renderingHints(renderingHintInit); + struct HintBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) return; - string propVal = realProp->getStringValue(); - if (propVal == "opaque") - pass->setRenderingHint(StateSet::OPAQUE_BIN); - else if (propVal == "transparent") - pass->setRenderingHint(StateSet::TRANSPARENT_BIN); + StateSet::RenderingHint renderingHint = StateSet::DEFAULT_BIN; + findAttr(renderingHints, realProp, renderingHint); + pass->setRenderingHint(renderingHint); } }; @@ -346,8 +301,10 @@ InstallAttributeBuilder installHint("rendering-hint"); struct RenderBinBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { + if (!isAttributeActive(effect, prop)) + return; const SGPropertyNode* binProp = prop->getChild("bin-number"); binProp = getEffectPropertyNode(effect, binProp); const SGPropertyNode* nameProp = prop->getChild("bin-name"); @@ -371,10 +328,10 @@ InstallAttributeBuilder installRenderBin("render-bin"); struct MaterialBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options); + const SGReaderWriterXMLOptions* options); }; -EffectNameValue colorModes[] = +EffectNameValue colorModeInit[] = { { "ambient", Material::AMBIENT }, { "ambient-and-diffuse", Material::AMBIENT_AND_DIFFUSE }, @@ -383,11 +340,14 @@ EffectNameValue colorModes[] = { "specular", Material::SPECULAR }, { "off", Material::OFF } }; +EffectPropertyMap colorModes(colorModeInit); void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { + if (!isAttributeActive(effect, prop)) + return; Material* mat = new Material; const SGPropertyNode* color = 0; if ((color = getEffectPropertyChild(effect, prop, "ambient"))) @@ -415,6 +375,7 @@ void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass, if ((color = getEffectPropertyChild(effect, prop, "emissive-back"))) mat->setEmission(Material::BACK, getColor(color)); const SGPropertyNode* shininess = 0; + mat->setShininess(Material::FRONT_AND_BACK, 0.0f); if ((shininess = getEffectPropertyChild(effect, prop, "shininess"))) mat->setShininess(Material::FRONT_AND_BACK, shininess->getFloatValue()); if ((shininess = getEffectPropertyChild(effect, prop, "shininess-front"))) @@ -430,229 +391,376 @@ void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass, InstallAttributeBuilder installMaterial("material"); +EffectNameValue blendFuncModesInit[] = +{ + {"dst-alpha", BlendFunc::DST_ALPHA}, + {"dst-color", BlendFunc::DST_COLOR}, + {"one", BlendFunc::ONE}, + {"one-minus-dst-alpha", BlendFunc::ONE_MINUS_DST_ALPHA}, + {"one-minus-dst-color", BlendFunc::ONE_MINUS_DST_COLOR}, + {"one-minus-src-alpha", BlendFunc::ONE_MINUS_SRC_ALPHA}, + {"one-minus-src-color", BlendFunc::ONE_MINUS_SRC_COLOR}, + {"src-alpha", BlendFunc::SRC_ALPHA}, + {"src-alpha-saturate", BlendFunc::SRC_ALPHA_SATURATE}, + {"src-color", BlendFunc::SRC_COLOR}, + {"constant-color", BlendFunc::CONSTANT_COLOR}, + {"one-minus-constant-color", BlendFunc::ONE_MINUS_CONSTANT_COLOR}, + {"constant-alpha", BlendFunc::CONSTANT_ALPHA}, + {"one-minus-constant-alpha", BlendFunc::ONE_MINUS_CONSTANT_ALPHA}, + {"zero", BlendFunc::ZERO} +}; +EffectPropertyMap blendFuncModes(blendFuncModesInit); + struct BlendBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { + if (!isAttributeActive(effect, prop)) + return; + // XXX Compatibility with early syntax; should go away + // before a release const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); if (!realProp) return; - pass->setMode(GL_BLEND, (realProp->getBoolValue() - ? StateAttribute::ON - : StateAttribute::OFF)); + if (realProp->nChildren() == 0) { + pass->setMode(GL_BLEND, (realProp->getBoolValue() + ? StateAttribute::ON + : StateAttribute::OFF)); + return; + } + + const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop, + "mode"); + // XXX When dynamic parameters are supported, this code should + // create the blend function even if the mode is off. + if (pmode && !pmode->getValue()) { + pass->setMode(GL_BLEND, StateAttribute::OFF); + return; + } + const SGPropertyNode* psource + = getEffectPropertyChild(effect, prop, "source"); + const SGPropertyNode* pdestination + = getEffectPropertyChild(effect, prop, "destination"); + const SGPropertyNode* psourceRGB + = getEffectPropertyChild(effect, prop, "source-rgb"); + const SGPropertyNode* psourceAlpha + = getEffectPropertyChild(effect, prop, "source-alpha"); + const SGPropertyNode* pdestRGB + = getEffectPropertyChild(effect, prop, "destination-rgb"); + const SGPropertyNode* pdestAlpha + = getEffectPropertyChild(effect, prop, "destination-alpha"); + BlendFunc::BlendFuncMode sourceMode = BlendFunc::ONE; + BlendFunc::BlendFuncMode destMode = BlendFunc::ZERO; + if (psource) + findAttr(blendFuncModes, psource, sourceMode); + if (pdestination) + findAttr(blendFuncModes, pdestination, destMode); + if (psource && pdestination + && !(psourceRGB || psourceAlpha || pdestRGB || pdestAlpha) + && sourceMode == BlendFunc::SRC_ALPHA + && destMode == BlendFunc::ONE_MINUS_SRC_ALPHA) { + pass->setAttributeAndModes(StateAttributeFactory::instance() + ->getStandardBlendFunc()); + return; + } + BlendFunc* blendFunc = new BlendFunc; + if (psource) + blendFunc->setSource(sourceMode); + if (pdestination) + blendFunc->setDestination(destMode); + if (psourceRGB) { + BlendFunc::BlendFuncMode sourceRGBMode; + findAttr(blendFuncModes, psourceRGB, sourceRGBMode); + blendFunc->setSourceRGB(sourceRGBMode); + } + if (pdestRGB) { + BlendFunc::BlendFuncMode destRGBMode; + findAttr(blendFuncModes, pdestRGB, destRGBMode); + blendFunc->setDestinationRGB(destRGBMode); + } + if (psourceAlpha) { + BlendFunc::BlendFuncMode sourceAlphaMode; + findAttr(blendFuncModes, psourceAlpha, sourceAlphaMode); + blendFunc->setSourceAlpha(sourceAlphaMode); + } + if (pdestAlpha) { + BlendFunc::BlendFuncMode destAlphaMode; + findAttr(blendFuncModes, pdestAlpha, destAlphaMode); + blendFunc->setDestinationAlpha(destAlphaMode); + } + pass->setAttributeAndModes(blendFunc); } }; InstallAttributeBuilder installBlend("blend"); -struct AlphaTestBuilder : public PassAttributeBuilder + +EffectNameValue stencilFunctionInit[] = { - void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) - { - const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); - if (!realProp) - return; - pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue() - ? StateAttribute::ON - : StateAttribute::OFF)); - } + {"never", Stencil::NEVER }, + {"less", Stencil::LESS}, + {"equal", Stencil::EQUAL}, + {"less-or-equal", Stencil::LEQUAL}, + {"greater", Stencil::GREATER}, + {"not-equal", Stencil::NOTEQUAL}, + {"greater-or-equal", Stencil::GEQUAL}, + {"always", Stencil::ALWAYS} }; -InstallAttributeBuilder installAlphaTest("alpha-test"); +EffectPropertyMap stencilFunction(stencilFunctionInit); -EffectNameValue filterModes[] = +EffectNameValue stencilOperationInit[] = { - { "linear", Texture::LINEAR }, - { "linear-mipmap-linear", Texture::LINEAR_MIPMAP_LINEAR}, - { "linear-mipmap-nearest", Texture::LINEAR_MIPMAP_NEAREST}, - { "nearest", Texture::NEAREST}, - { "nearest-mipmap-linear", Texture::NEAREST_MIPMAP_LINEAR}, - { "nearest-mipmap-nearest", Texture::NEAREST_MIPMAP_NEAREST} + {"keep", Stencil::KEEP}, + {"zero", Stencil::ZERO}, + {"replace", Stencil::REPLACE}, + {"increase", Stencil::INCR}, + {"decrease", Stencil::DECR}, + {"invert", Stencil::INVERT}, + {"increase-wrap", Stencil::INCR_WRAP}, + {"decrease-wrap", Stencil::DECR_WRAP} }; -EffectNameValue wrapModes[] = +EffectPropertyMap stencilOperation(stencilOperationInit); + +struct StencilBuilder : public PassAttributeBuilder { - {"clamp", Texture::CLAMP}, - {"clamp-to-border", Texture::CLAMP_TO_BORDER}, - {"clamp-to-edge", Texture::CLAMP_TO_EDGE}, - {"mirror", Texture::MIRROR}, - {"repeat", Texture::REPEAT} + void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, + const SGReaderWriterXMLOptions* options) + { + if (!isAttributeActive(effect, prop)) + return; + + const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop, + "mode"); + if (pmode && !pmode->getValue()) { + pass->setMode(GL_STENCIL, StateAttribute::OFF); + return; + } + const SGPropertyNode* pfunction + = getEffectPropertyChild(effect, prop, "function"); + const SGPropertyNode* pvalue + = getEffectPropertyChild(effect, prop, "value"); + const SGPropertyNode* pmask + = getEffectPropertyChild(effect, prop, "mask"); + const SGPropertyNode* psfail + = getEffectPropertyChild(effect, prop, "stencil-fail"); + const SGPropertyNode* pzfail + = getEffectPropertyChild(effect, prop, "z-fail"); + const SGPropertyNode* ppass + = getEffectPropertyChild(effect, prop, "pass"); + + Stencil::Function func = Stencil::ALWAYS; // Always pass + int ref = 0; + unsigned int mask = ~0u; // All bits on + Stencil::Operation sfailop = Stencil::KEEP; // Keep the old values as default + Stencil::Operation zfailop = Stencil::KEEP; + Stencil::Operation passop = Stencil::KEEP; + + ref_ptr stencilFunc = new Stencil; + + if (pfunction) + findAttr(stencilFunction, pfunction, func); + if (pvalue) + ref = pvalue->getIntValue(); + if (pmask) + mask = pmask->getIntValue(); + + if (psfail) + findAttr(stencilOperation, psfail, sfailop); + if (pzfail) + findAttr(stencilOperation, pzfail, zfailop); + if (ppass) + findAttr(stencilOperation, ppass, passop); + + // Set the stencil operation + stencilFunc->setFunction(func, ref, mask); + + // Set the operation, s-fail, s-pass/z-fail, s-pass/z-pass + stencilFunc->setOperation(sfailop, zfailop, passop); + + // Add the operation to pass + pass->setAttributeAndModes(stencilFunc.get()); + } }; -EffectNameValue texEnvModes[] = +InstallAttributeBuilder installStencil("stencil"); + + +EffectNameValue alphaComparisonInit[] = { - {"add", TexEnv::ADD}, - {"blend", TexEnv::BLEND}, - {"decal", TexEnv::DECAL}, - {"modulate", TexEnv::MODULATE}, - {"replace", TexEnv::REPLACE} + {"never", AlphaFunc::NEVER}, + {"less", AlphaFunc::LESS}, + {"equal", AlphaFunc::EQUAL}, + {"lequal", AlphaFunc::LEQUAL}, + {"greater", AlphaFunc::GREATER}, + {"notequal", AlphaFunc::NOTEQUAL}, + {"gequal", AlphaFunc::GEQUAL}, + {"always", AlphaFunc::ALWAYS} }; +EffectPropertyMap +alphaComparison(alphaComparisonInit); -TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop) +struct AlphaTestBuilder : public PassAttributeBuilder { - const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop, - "mode"); - const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop, - "color"); - if (!modeProp) - return 0; - TexEnv::Mode mode = TexEnv::MODULATE; - findAttr(texEnvModes, modeProp, mode); - if (mode == TexEnv::MODULATE) { - return StateAttributeFactory::instance()->getStandardTexEnv(); + void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, + const SGReaderWriterXMLOptions* options) + { + if (!isAttributeActive(effect, prop)) + return; + // XXX Compatibility with early syntax; should go away + // before a release + const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); + if (!realProp) + return; + if (realProp->nChildren() == 0) { + pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue() + ? StateAttribute::ON + : StateAttribute::OFF)); + return; + } + + const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop, + "mode"); + // XXX When dynamic parameters are supported, this code should + // create the blend function even if the mode is off. + if (pmode && !pmode->getValue()) { + pass->setMode(GL_ALPHA_TEST, StateAttribute::OFF); + return; + } + const SGPropertyNode* pComp = getEffectPropertyChild(effect, prop, + "comparison"); + const SGPropertyNode* pRef = getEffectPropertyChild(effect, prop, + "reference"); + AlphaFunc::ComparisonFunction func = AlphaFunc::ALWAYS; + float refValue = 1.0f; + if (pComp) + findAttr(alphaComparison, pComp, func); + if (pRef) + refValue = pRef->getValue(); + if (func == AlphaFunc::GREATER && osg::equivalent(refValue, 1.0f)) { + pass->setAttributeAndModes(StateAttributeFactory::instance() + ->getStandardAlphaFunc()); + } else { + AlphaFunc* alphaFunc = new AlphaFunc; + alphaFunc->setFunction(func); + alphaFunc->setReferenceValue(refValue); + pass->setAttributeAndModes(alphaFunc); + } } - TexEnv* env = new TexEnv(mode); - if (colorProp) - env->setColor(colorProp->getValue().osg()); - return env; - } +}; -typedef boost::tuple TexTuple; +InstallAttributeBuilder installAlphaTest("alpha-test"); -typedef map > TexMap; +InstallAttributeBuilder textureUnitBuilder("texture-unit"); -TexMap texMap; +// Shader key, used both for shaders with relative and absolute names +typedef pair ShaderKey; -struct TextureUnitBuilder : PassAttributeBuilder +struct ProgramKey { - void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options); + typedef pair AttribKey; + osgDB::FilePathList paths; + vector shaders; + vector attributes; + struct EqualTo + { + bool operator()(const ProgramKey& lhs, const ProgramKey& rhs) const + { + return (lhs.paths.size() == rhs.paths.size() + && equal(lhs.paths.begin(), lhs.paths.end(), + rhs.paths.begin()) + && lhs.shaders.size() == rhs.shaders.size() + && equal (lhs.shaders.begin(), lhs.shaders.end(), + rhs.shaders.begin()) + && lhs.attributes.size() == rhs.attributes.size() + && equal(lhs.attributes.begin(), lhs.attributes.end(), + rhs.attributes.begin())); + } + }; }; -void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass, - const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) +size_t hash_value(const ProgramKey& key) { - // First, all the texture properties - const SGPropertyNode* pTexture2d = prop->getChild("texture2d"); - if (!pTexture2d) - return; - const SGPropertyNode* pImage - = getEffectPropertyChild(effect, pTexture2d, "image"); - if (!pImage) - return; - const char* imageName = pImage->getStringValue(); - Texture::FilterMode minFilter = Texture::LINEAR_MIPMAP_LINEAR; - findAttr(filterModes, getEffectPropertyChild(effect, pTexture2d, "filter"), - minFilter); - Texture::FilterMode magFilter = Texture::LINEAR; - findAttr(filterModes, getEffectPropertyChild(effect, pTexture2d, - "mag-filter"), - magFilter); - const SGPropertyNode* pWrapS - = getEffectPropertyChild(effect, pTexture2d, "wrap-s"); - Texture::WrapMode sWrap = Texture::CLAMP; - findAttr(wrapModes, pWrapS, sWrap); - const SGPropertyNode* pWrapT - = getEffectPropertyChild(effect, pTexture2d, "wrap-t"); - Texture::WrapMode tWrap = Texture::CLAMP; - findAttr(wrapModes, pWrapT, tWrap); - const SGPropertyNode* pWrapR - = getEffectPropertyChild(effect, pTexture2d, "wrap-r"); - Texture::WrapMode rWrap = Texture::CLAMP; - findAttr(wrapModes, pWrapR, rWrap); - TexTuple tuple(imageName, minFilter, magFilter, sWrap, tWrap, rWrap); - TexMap::iterator texIter = texMap.find(tuple); - Texture2D* texture = 0; - if (texIter != texMap.end()) { - texture = texIter->second.get(); - } else { - texture = new Texture2D; - osgDB::ReaderWriter::ReadResult result - = osgDB::Registry::instance()->readImage(imageName, options); - if (result.success()) { - osg::Image* image = result.getImage(); - texture->setImage(image); - int s = image->s(); - int t = image->t(); - - if (s <= t && 32 <= s) { - SGSceneFeatures::instance()->setTextureCompression(texture); - } else if (t < s && 32 <= t) { - SGSceneFeatures::instance()->setTextureCompression(texture); - } - texture->setMaxAnisotropy(SGSceneFeatures::instance() - ->getTextureFilter()); - } else { - SG_LOG(SG_INPUT, SG_ALERT, "failed to load effect texture file " - << imageName); - } - // texture->setDataVariance(osg::Object::STATIC); - texture->setFilter(Texture::MIN_FILTER, minFilter); - texture->setFilter(Texture::MAG_FILTER, magFilter); - texture->setWrap(Texture::WRAP_S, sWrap); - texture->setWrap(Texture::WRAP_T, tWrap); - texture->setWrap(Texture::WRAP_R, rWrap); - texMap.insert(make_pair(tuple, texture)); - } - // Decode the texture unit - int unit = 0; - const SGPropertyNode* pUnit = prop->getChild("unit"); - if (pUnit) { - unit = pUnit->getValue(); - } else { - const SGPropertyNode* pName = prop->getChild("name"); - if (pName) - try { - unit = boost::lexical_cast(pName->getStringValue()); - } catch (boost::bad_lexical_cast& lex) { - SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit " - << lex.what()); - } - } - pass->setTextureAttributeAndModes(unit, texture); - const SGPropertyNode* envProp = prop->getChild("environment"); - if (envProp) { - TexEnv* env = buildTexEnv(effect, envProp); - if (env) - pass->setTextureAttributeAndModes(unit, env); - } + size_t seed = 0; + boost::hash_range(seed, key.paths.begin(), key.paths.end()); + boost::hash_range(seed, key.shaders.begin(), key.shaders.end()); + boost::hash_range(seed, key.attributes.begin(), key.attributes.end()); + return seed; } -InstallAttributeBuilder textureUnitBuilder("texture-unit"); +// XXX Should these be protected by a mutex? Probably -typedef map > ProgramMap; +typedef tr1::unordered_map, + boost::hash, ProgramKey::EqualTo> +ProgramMap; ProgramMap programMap; -typedef map > ShaderMap; +typedef tr1::unordered_map, boost::hash > +ShaderMap; ShaderMap shaderMap; +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); + if (!fileName.empty()) { + shader->loadShaderSourceFromFile(fileName); + } + } +} + struct ShaderProgramBuilder : PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options); + const SGReaderWriterXMLOptions* options); }; void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* + const SGReaderWriterXMLOptions* options) { + using namespace boost; + if (!isAttributeActive(effect, prop)) + return; PropertyList pVertShaders = prop->getChildren("vertex-shader"); PropertyList pFragShaders = prop->getChildren("fragment-shader"); - string programKey; + PropertyList pAttributes = prop->getChildren("attribute"); + ProgramKey prgKey; for (PropertyList::iterator itr = pVertShaders.begin(), e = pVertShaders.end(); itr != e; ++itr) - { - programKey += (*itr)->getStringValue(); - programKey += ";"; - } + prgKey.shaders.push_back(ShaderKey((*itr)->getStringValue(), + Shader::VERTEX)); for (PropertyList::iterator itr = pFragShaders.begin(), e = pFragShaders.end(); itr != e; ++itr) - { - programKey += (*itr)->getStringValue(); - programKey += ";"; + prgKey.shaders.push_back(ShaderKey((*itr)->getStringValue(), + Shader::FRAGMENT)); + for (PropertyList::iterator itr = pAttributes.begin(), + e = pAttributes.end(); + itr != e; + ++itr) { + const SGPropertyNode* pName = getEffectPropertyChild(effect, *itr, + "name"); + const SGPropertyNode* pIndex = getEffectPropertyChild(effect, *itr, + "index"); + if (!pName || ! pIndex) + throw BuilderException("malformed attribute property"); + prgKey.attributes + .push_back(ProgramKey::AttribKey(pName->getStringValue(), + pIndex->getValue())); } + if (options) + prgKey.paths = options->getDatabasePathList(); Program* program = 0; - ProgramMap::iterator pitr = programMap.find(programKey); + ProgramMap::iterator pitr = programMap.find(prgKey); if (pitr != programMap.end()) { program = pitr->second.get(); } else { @@ -665,31 +773,36 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass, nameItr != e; ++nameItr) { string shaderName = (*nameItr)->getStringValue(); - ShaderMap::iterator sitr = shaderMap.find(shaderName); + 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 { - string fileName = osgDB::findDataFile(shaderName, options); - if (!fileName.empty()) { - ref_ptr shader = new Shader(stype); - if (shader->loadShaderSourceFromFile(fileName)) { - shaderMap.insert(make_pair(shaderName, shader)); - program->addShader(shader.get()); - } + ref_ptr shader = new Shader(stype); + if (shader->loadShaderSourceFromFile(fileName)) { + program->addShader(shader.get()); + shaderMap.insert(ShaderMap::value_type(skey, shader)); } } } pvec = pFragShaders; stype = Shader::FRAGMENT; } - programMap.insert(make_pair(programKey, program)); + BOOST_FOREACH(const ProgramKey::AttribKey& key, prgKey.attributes) { + program->addBindAttribLocation(key.first, key.second); + } + programMap.insert(ProgramMap::value_type(prgKey, program)); } pass->setAttributeAndModes(program); } InstallAttributeBuilder installShaderProgram("program"); -EffectNameValue uniformTypes[] = +EffectNameValue uniformTypesInit[] = { {"float", Uniform::FLOAT}, {"float-vec3", Uniform::FLOAT_VEC3}, @@ -698,12 +811,15 @@ EffectNameValue uniformTypes[] = {"sampler-2d", Uniform::SAMPLER_2D}, {"sampler-3d", Uniform::SAMPLER_3D} }; +EffectPropertyMap uniformTypes(uniformTypesInit); struct UniformBuilder :public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { + if (!isAttributeActive(effect, prop)) + return; const SGPropertyNode* nameProp = prop->getChild("name"); const SGPropertyNode* typeProp = prop->getChild("type"); const SGPropertyNode* valProp @@ -749,16 +865,18 @@ struct UniformBuilder :public PassAttributeBuilder uniform->set(valProp->getValue()); break; case Uniform::FLOAT_VEC3: - uniform->set(Vec3f(valProp->getValue().osg())); + uniform->set(toOsg(valProp->getValue())); break; case Uniform::FLOAT_VEC4: - uniform->set(Vec4f(valProp->getValue().osg())); + uniform->set(toOsg(valProp->getValue())); break; case Uniform::SAMPLER_1D: case Uniform::SAMPLER_2D: case Uniform::SAMPLER_3D: uniform->set(valProp->getValue()); break; + default: // avoid compiler warning + break; } pass->addUniform(uniform.get()); } @@ -772,7 +890,7 @@ InstallAttributeBuilder installUniform("uniform"); struct NameBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { // name can't use string name = prop->getStringValue(); @@ -783,18 +901,21 @@ struct NameBuilder : public PassAttributeBuilder InstallAttributeBuilder installName("name"); -EffectNameValue polygonModeModes[] = +EffectNameValue polygonModeModesInit[] = { {"fill", PolygonMode::FILL}, {"line", PolygonMode::LINE}, {"point", PolygonMode::POINT} }; +EffectPropertyMap polygonModeModes(polygonModeModesInit); struct PolygonModeBuilder : public PassAttributeBuilder { void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { + if (!isAttributeActive(effect, prop)) + return; const SGPropertyNode* frontProp = getEffectPropertyChild(effect, prop, "front"); const SGPropertyNode* backProp @@ -815,8 +936,89 @@ struct PolygonModeBuilder : public PassAttributeBuilder }; InstallAttributeBuilder installPolygonMode("polygon-mode"); + +struct VertexProgramTwoSideBuilder : public PassAttributeBuilder +{ + void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, + const SGReaderWriterXMLOptions* options) + { + const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); + if (!realProp) + return; + pass->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, + (realProp->getValue() + ? StateAttribute::ON : StateAttribute::OFF)); + } +}; + +InstallAttributeBuilder +installTwoSide("vertex-program-two-side"); + +struct VertexProgramPointSizeBuilder : public PassAttributeBuilder +{ + void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, + const SGReaderWriterXMLOptions* options) + { + const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop); + if (!realProp) + return; + pass->setMode(GL_VERTEX_PROGRAM_POINT_SIZE, + (realProp->getValue() + ? StateAttribute::ON : StateAttribute::OFF)); + } +}; + +InstallAttributeBuilder +installPointSize("vertex-program-point-size"); + +EffectNameValue depthFunctionInit[] = +{ + {"never", Depth::NEVER}, + {"less", Depth::LESS}, + {"equal", Depth::EQUAL}, + {"lequal", Depth::LEQUAL}, + {"greater", Depth::GREATER}, + {"notequal", Depth::NOTEQUAL}, + {"gequal", Depth::GEQUAL}, + {"always", Depth::ALWAYS} +}; +EffectPropertyMap depthFunction(depthFunctionInit); + +struct DepthBuilder : public PassAttributeBuilder +{ + void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop, + const SGReaderWriterXMLOptions* options) + { + if (!isAttributeActive(effect, prop)) + return; + ref_ptr depth = new Depth; + const SGPropertyNode* pfunc + = getEffectPropertyChild(effect, prop, "function"); + if (pfunc) { + Depth::Function func = Depth::LESS; + findAttr(depthFunction, pfunc, func); + depth->setFunction(func); + } + const SGPropertyNode* pnear + = getEffectPropertyChild(effect, prop, "near"); + if (pnear) + depth->setZNear(pnear->getValue()); + const SGPropertyNode* pfar + = getEffectPropertyChild(effect, prop, "far"); + if (pfar) + depth->setZFar(pnear->getValue()); + const SGPropertyNode* pmask + = getEffectPropertyChild(effect, prop, "write-mask"); + if (pmask) + depth->setWriteMask(pmask->getValue()); + pass->setAttribute(depth.get()); + } +}; + +InstallAttributeBuilder installDepth("depth"); + void buildTechnique(Effect* effect, const SGPropertyNode* prop, - const osgDB::ReaderWriter::Options* options) + const SGReaderWriterXMLOptions* options) { Technique* tniq = new Technique; effect->techniques.push_back(tniq); @@ -828,7 +1030,7 @@ void buildTechnique(Effect* effect, const SGPropertyNode* prop, TechniquePredParser parser; parser.setTechnique(tniq); expression::BindingLayout& layout = parser.getBindingLayout(); - int contextLoc = layout.addBinding("__contextId", expression::INT); + /*int contextLoc = */layout.addBinding("__contextId", expression::INT); SGExpressionb* validExp = dynamic_cast(parser.read(predProp ->getChild(0))); @@ -852,15 +1054,128 @@ void buildTechnique(Effect* effect, const SGPropertyNode* prop, } } +// Specifically for .ac files... +bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss) +{ + SGPropertyNode* paramRoot = makeChild(effectRoot, "parameters"); + SGPropertyNode* matNode = paramRoot->getChild("material", 0, true); + Vec4f ambVal, difVal, specVal, emisVal; + float shininess = 0.0f; + const Material* mat = getStateAttribute(ss); + if (mat) { + ambVal = mat->getAmbient(Material::FRONT_AND_BACK); + difVal = mat->getDiffuse(Material::FRONT_AND_BACK); + specVal = mat->getSpecular(Material::FRONT_AND_BACK); + emisVal = mat->getEmission(Material::FRONT_AND_BACK); + shininess = mat->getShininess(Material::FRONT_AND_BACK); + makeChild(matNode, "active")->setValue(true); + makeChild(matNode, "ambient")->setValue(toVec4d(toSG(ambVal))); + makeChild(matNode, "diffuse")->setValue(toVec4d(toSG(difVal))); + makeChild(matNode, "specular")->setValue(toVec4d(toSG(specVal))); + makeChild(matNode, "emissive")->setValue(toVec4d(toSG(emisVal))); + makeChild(matNode, "shininess")->setValue(shininess); + matNode->getChild("color-mode", 0, true)->setStringValue("diffuse"); + } else { + makeChild(matNode, "active")->setValue(false); + } + const ShadeModel* sm = getStateAttribute(ss); + string shadeModelString("smooth"); + if (sm) { + ShadeModel::Mode smMode = sm->getMode(); + if (smMode == ShadeModel::FLAT) + shadeModelString = "flat"; + } + makeChild(paramRoot, "shade-model")->setStringValue(shadeModelString); + string cullFaceString("off"); + const CullFace* cullFace = getStateAttribute(ss); + if (cullFace) { + switch (cullFace->getMode()) { + case CullFace::FRONT: + cullFaceString = "front"; + break; + case CullFace::BACK: + cullFaceString = "back"; + break; + case CullFace::FRONT_AND_BACK: + cullFaceString = "front-back"; + break; + default: + break; + } + } + makeChild(paramRoot, "cull-face")->setStringValue(cullFaceString); + const BlendFunc* blendFunc = getStateAttribute(ss); + SGPropertyNode* blendNode = makeChild(paramRoot, "blend"); + if (blendFunc) { + string sourceMode = findName(blendFuncModes, blendFunc->getSource()); + string destMode = findName(blendFuncModes, blendFunc->getDestination()); + makeChild(blendNode, "active")->setValue(true); + makeChild(blendNode, "source")->setStringValue(sourceMode); + makeChild(blendNode, "destination")->setStringValue(destMode); + makeChild(blendNode, "mode")->setValue(true); + } else { + makeChild(blendNode, "active")->setValue(false); + } + string renderingHint = findName(renderingHints, ss->getRenderingHint()); + makeChild(paramRoot, "rendering-hint")->setStringValue(renderingHint); + makeTextureParameters(paramRoot, ss); + return true; +} + // Walk the techniques property tree, building techniques and // passes. -bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options) +bool Effect::realizeTechniques(const SGReaderWriterXMLOptions* options) { + if (_isRealized) + return true; PropertyList tniqList = root->getChildren("technique"); for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end(); itr != e; ++itr) buildTechnique(this, *itr, options); + _isRealized = true; + return true; +} + +void Effect::InitializeCallback::doUpdate(osg::Node* node, osg::NodeVisitor* nv) +{ + EffectGeode* eg = dynamic_cast(node); + if (!eg) + return; + Effect* effect = eg->getEffect(); + if (!effect) + return; + SGPropertyNode* root = getPropertyRoot(); + for (vector >::iterator itr = effect->_extraData.begin(), + end = effect->_extraData.end(); + itr != end; + ++itr) { + InitializeWhenAdded* adder + = dynamic_cast(itr->ptr()); + if (adder) + adder->initOnAdd(effect, root); + } +} + +bool Effect::Key::EqualTo::operator()(const Effect::Key& lhs, + const Effect::Key& rhs) const +{ + if (lhs.paths.size() != rhs.paths.size() + || !equal(lhs.paths.begin(), lhs.paths.end(), rhs.paths.begin())) + return false; + if (lhs.unmerged.valid() && rhs.unmerged.valid()) + return props::Compare()(lhs.unmerged, rhs.unmerged); + else + return lhs.unmerged == rhs.unmerged; +} + +size_t hash_value(const Effect::Key& key) +{ + size_t seed = 0; + if (key.unmerged.valid()) + boost::hash_combine(seed, *key.unmerged); + boost::hash_range(seed, key.paths.begin(), key.paths.end()); + return seed; } bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw) @@ -885,5 +1200,49 @@ osgDB::RegisterDotOsgWrapperProxy effectProxy &Effect_writeLocalData ); } + +// Property expressions for technique predicates +class PropertyExpression : public SGExpression +{ +public: + PropertyExpression(SGPropertyNode* pnode) : _pnode(pnode) {} + + void eval(bool& value, const expression::Binding*) const + { + value = _pnode->getValue(); + } +protected: + SGPropertyNode_ptr _pnode; +}; + +class EffectPropertyListener : public SGPropertyChangeListener +{ +public: + EffectPropertyListener(Technique* tniq) : _tniq(tniq) {} + + void valueChanged(SGPropertyNode* node) + { + _tniq->refreshValidity(); + } +protected: + osg::ref_ptr _tniq; +}; + +Expression* propertyExpressionParser(const SGPropertyNode* exp, + expression::Parser* parser) +{ + SGPropertyNode_ptr pnode = getPropertyRoot()->getNode(exp->getStringValue(), + true); + PropertyExpression* pexp = new PropertyExpression(pnode); + TechniquePredParser* predParser + = dynamic_cast(parser); + if (predParser) + pnode->addChangeListener(new EffectPropertyListener(predParser + ->getTechnique())); + return pexp; } +expression::ExpParserRegistrar propertyRegistrar("property", + propertyExpressionParser); + +}