diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 65ac7d877..3d030febf 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1246,13 +1246,15 @@ namespace MWRender material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); - // FIXME: regenerate shader (mVertexColorMode) - mObjectRoot->setStateSet(stateset); + + mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } else { mObjectRoot->setStateSet(NULL); + + mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } setRenderBin(); diff --git a/apps/openmw/mwrender/util.cpp b/apps/openmw/mwrender/util.cpp index 0e1eaf0f4..876ec285f 100644 --- a/apps/openmw/mwrender/util.cpp +++ b/apps/openmw/mwrender/util.cpp @@ -18,6 +18,7 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou osg::ref_ptr tex = new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture)); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + tex->setName("diffuseMap"); osg::ref_ptr stateset; if (node->getStateSet()) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index be0cd7dd7..172a6511a 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -228,6 +228,16 @@ namespace Resource mForceShaders = force; } + void SceneManager::recreateShaders(osg::ref_ptr node) + { + Shader::ShaderVisitor shaderVisitor(*mShaderManager.get(), "objects_vertex.glsl", "objects_fragment.glsl"); + shaderVisitor.setForceShaders(mForceShaders); + shaderVisitor.setClampLighting(mClampLighting); + shaderVisitor.setForcePerPixelLighting(mForcePerPixelLighting); + shaderVisitor.setAllowedToModifyStateSets(false); + node->accept(shaderVisitor); + } + void SceneManager::setClampLighting(bool clamp) { mClampLighting = clamp; diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 2ef9f614b..db3de3f48 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -40,6 +40,9 @@ namespace Resource SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager); ~SceneManager(); + /// Re-create shaders for this node, need to call this if texture stages or vertex color mode have changed. + void recreateShaders(osg::ref_ptr node); + /// @see ShaderVisitor::setForceShaders void setForceShaders(bool force); diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 67362e0c6..fcb9b8057 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -19,6 +19,7 @@ namespace Shader : mHasNormalMap(false) , mColorMaterial(false) , mVertexColorMode(GL_AMBIENT_AND_DIFFUSE) + , mMaterialOverridden(false) , mTexStageRequiringTangents(-1) { } @@ -28,6 +29,7 @@ namespace Shader , mForceShaders(false) , mClampLighting(false) , mForcePerPixelLighting(false) + , mAllowedToModifyStateSets(true) , mShaderManager(shaderManager) , mDefaultVsTemplate(defaultVsTemplate) , mDefaultFsTemplate(defaultFsTemplate) @@ -55,7 +57,7 @@ namespace Shader if (node.getStateSet()) { pushRequirements(); - applyStateSet(node.getStateSet()); + applyStateSet(node.getStateSet(), node); traverse(node); popRequirements(); } @@ -63,8 +65,21 @@ namespace Shader traverse(node); } - void ShaderVisitor::applyStateSet(osg::StateSet* stateset) + osg::StateSet* getWritableStateSet(osg::Node& node) { + if (!node.getStateSet()) + return node.getOrCreateStateSet(); + + osg::ref_ptr newStateSet = osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY); + node.setStateSet(newStateSet); + return newStateSet.get(); + } + + void ShaderVisitor::applyStateSet(osg::ref_ptr stateset, osg::Node& node) + { + osg::StateSet* writableStateSet = NULL; + if (mAllowedToModifyStateSets) + writableStateSet = node.getStateSet(); const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList(); for(unsigned int unit=0;unitsetTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON); + writableStateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON); } } else @@ -90,7 +107,12 @@ namespace Shader } } // remove state that has no effect when rendering with shaders - stateset->removeTextureAttribute(unit, osg::StateAttribute::TEXENV); + if (stateset->getTextureAttribute(unit, osg::StateAttribute::TEXENV)) + { + if (!writableStateSet) + writableStateSet = getWritableStateSet(node); + writableStateSet->removeTextureAttribute(unit, osg::StateAttribute::TEXENV); + } } const osg::StateSet::AttributeList& attributes = stateset->getAttributeList(); @@ -98,9 +120,15 @@ namespace Shader { if (it->first.first == osg::StateAttribute::MATERIAL) { - const osg::Material* mat = static_cast(it->second.first.get()); - mRequirements.back().mColorMaterial = (mat->getColorMode() != osg::Material::OFF); - mRequirements.back().mVertexColorMode = mat->getColorMode(); + if (!mRequirements.back().mMaterialOverridden || it->second.second & osg::StateAttribute::PROTECTED) + { + if (it->second.second & osg::StateAttribute::OVERRIDE) + mRequirements.back().mMaterialOverridden = true; + + const osg::Material* mat = static_cast(it->second.first.get()); + mRequirements.back().mColorMaterial = (mat->getColorMode() != osg::Material::OFF); + mRequirements.back().mVertexColorMode = mat->getColorMode(); + } } } } @@ -115,8 +143,14 @@ namespace Shader mRequirements.pop_back(); } - void ShaderVisitor::createProgram(const ShaderRequirements &reqs, osg::StateSet *stateset) + void ShaderVisitor::createProgram(const ShaderRequirements &reqs, osg::Node& node) { + osg::StateSet* writableStateSet = NULL; + if (mAllowedToModifyStateSets) + writableStateSet = node.getOrCreateStateSet(); + else + writableStateSet = getWritableStateSet(node); + ShaderManager::DefineMap defineMap; const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap" }; for (unsigned int i=0; isetAttributeAndModes(mShaderManager.getProgram(vertexShader, fragmentShader), osg::StateAttribute::ON); + writableStateSet->setAttributeAndModes(mShaderManager.getProgram(vertexShader, fragmentShader), osg::StateAttribute::ON); for (std::map::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt) { - stateset->addUniform(new osg::Uniform(texIt->second.c_str(), texIt->first), osg::StateAttribute::ON); + writableStateSet->addUniform(new osg::Uniform(texIt->second.c_str(), texIt->first), osg::StateAttribute::ON); } } } @@ -169,7 +203,7 @@ namespace Shader if (geometry.getStateSet()) { pushRequirements(); - applyStateSet(geometry.getStateSet()); + applyStateSet(geometry.getStateSet(), geometry); } if (!mRequirements.empty()) @@ -185,7 +219,7 @@ namespace Shader // TODO: find a better place for the stateset if (reqs.mHasNormalMap || mForceShaders) - createProgram(reqs, geometry.getOrCreateStateSet()); + createProgram(reqs, geometry); } if (needPop) @@ -200,7 +234,7 @@ namespace Shader if (drawable.getStateSet()) { pushRequirements(); - applyStateSet(drawable.getStateSet()); + applyStateSet(drawable.getStateSet(), drawable); } if (!mRequirements.empty()) @@ -208,11 +242,16 @@ namespace Shader const ShaderRequirements& reqs = mRequirements.back(); // TODO: find a better place for the stateset if (reqs.mHasNormalMap || mForceShaders) - createProgram(reqs, drawable.getOrCreateStateSet()); + createProgram(reqs, drawable); } if (needPop) popRequirements(); } + void ShaderVisitor::setAllowedToModifyStateSets(bool allowed) + { + mAllowedToModifyStateSets = allowed; + } + } diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index fb29d503f..c301db47d 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -25,12 +25,17 @@ namespace Shader /// Setting force = true will cause all shaders to use per-pixel lighting, regardless of having a bump map. void setForcePerPixelLighting(bool force); + /// Set if we are allowed to modify StateSets encountered in the graph (default true). + /// @par If set to false, then instead of modifying, the StateSet will be cloned and this new StateSet will be assigned to the node. + /// @par This option is useful when the ShaderVisitor is run on a "live" subgraph that may have already been submitted for rendering. + void setAllowedToModifyStateSets(bool allowed); + virtual void apply(osg::Node& node); virtual void apply(osg::Drawable& drawable); virtual void apply(osg::Geometry& geometry); - void applyStateSet(osg::StateSet* stateset); + void applyStateSet(osg::ref_ptr stateset, osg::Node& node); void pushRequirements(); void popRequirements(); @@ -39,6 +44,7 @@ namespace Shader bool mForceShaders; bool mClampLighting; bool mForcePerPixelLighting; + bool mAllowedToModifyStateSets; ShaderManager& mShaderManager; @@ -54,6 +60,7 @@ namespace Shader bool mColorMaterial; // osg::Material::ColorMode int mVertexColorMode; + bool mMaterialOverridden; // -1 == no tangents required int mTexStageRequiringTangents; @@ -63,7 +70,7 @@ namespace Shader std::string mDefaultVsTemplate; std::string mDefaultFsTemplate; - void createProgram(const ShaderRequirements& reqs, osg::StateSet* stateset); + void createProgram(const ShaderRequirements& reqs, osg::Node& node); }; }