#include "shadervisitor.hpp" #include #include #include #include #include #include #include #include #include #include #include "shadermanager.hpp" namespace Shader { ShaderVisitor::ShaderRequirements::ShaderRequirements() : mShaderRequired(false) , mColorMaterial(false) , mVertexColorMode(GL_AMBIENT_AND_DIFFUSE) , mMaterialOverridden(false) , mNormalHeight(false) , mTexStageRequiringTangents(-1) , mNode(NULL) { } ShaderVisitor::ShaderRequirements::~ShaderRequirements() { } ShaderVisitor::ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string &defaultVsTemplate, const std::string &defaultFsTemplate) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mForceShaders(false) , mClampLighting(true) , mForcePerPixelLighting(false) , mAllowedToModifyStateSets(true) , mAutoUseNormalMaps(false) , mAutoUseSpecularMaps(false) , mShaderManager(shaderManager) , mImageManager(imageManager) , mDefaultVsTemplate(defaultVsTemplate) , mDefaultFsTemplate(defaultFsTemplate) { mRequirements.push_back(ShaderRequirements()); } void ShaderVisitor::setForceShaders(bool force) { mForceShaders = force; } void ShaderVisitor::setClampLighting(bool clamp) { mClampLighting = clamp; } void ShaderVisitor::setForcePerPixelLighting(bool force) { mForcePerPixelLighting = force; } void ShaderVisitor::apply(osg::Node& node) { if (node.getStateSet()) { pushRequirements(node); applyStateSet(node.getStateSet(), node); traverse(node); popRequirements(); } else traverse(node); } osg::StateSet* getWritableStateSet(osg::Node& node) { if (!node.getStateSet()) return node.getOrCreateStateSet(); osg::ref_ptr newStateSet = new osg::StateSet(*node.getStateSet(), osg::CopyOp::SHALLOW_COPY); node.setStateSet(newStateSet); return newStateSet.get(); } const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap", "specularMap", "decalMap" }; bool isTextureNameRecognized(const std::string& name) { for (unsigned int i=0; i stateset, osg::Node& node) { osg::StateSet* writableStateSet = NULL; if (mAllowedToModifyStateSets) writableStateSet = node.getStateSet(); const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList(); if (!texAttributes.empty()) { const osg::Texture* diffuseMap = NULL; const osg::Texture* normalMap = NULL; const osg::Texture* specularMap = NULL; for(unsigned int unit=0;unitgetTextureAttribute(unit, osg::StateAttribute::TEXTURE); if (attr) { const osg::Texture* texture = attr->asTexture(); if (texture) { std::string texName = texture->getName(); if ((texName.empty() || !isTextureNameRecognized(texName)) && unit == 0) texName = "diffuseMap"; if (texName == "normalHeightMap") { mRequirements.back().mNormalHeight = true; texName = "normalMap"; } if (!texName.empty()) { mRequirements.back().mTextures[unit] = texName; if (texName == "normalMap") { mRequirements.back().mTexStageRequiringTangents = unit; mRequirements.back().mShaderRequired = true; if (!writableStateSet) writableStateSet = getWritableStateSet(node); // normal maps are by default off since the FFP can't render them, now that we'll use shaders switch to On writableStateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON); normalMap = texture; } else if (texName == "diffuseMap") diffuseMap = texture; else if (texName == "specularMap") specularMap = texture; } else std::cerr << "ShaderVisitor encountered unknown texture " << texture << std::endl; } } } if (mAutoUseNormalMaps && diffuseMap != NULL && normalMap == NULL && diffuseMap->getImage(0)) { std::string normalMapFileName = diffuseMap->getImage(0)->getFileName(); osg::ref_ptr image; bool normalHeight = false; std::string normalHeightMap = normalMapFileName; boost::replace_last(normalHeightMap, ".", mNormalHeightMapPattern + "."); if (mImageManager.getVFS()->exists(normalHeightMap)) { image = mImageManager.getImage(normalHeightMap); normalHeight = true; } else { boost::replace_last(normalMapFileName, ".", mNormalMapPattern + "."); if (mImageManager.getVFS()->exists(normalMapFileName)) { image = mImageManager.getImage(normalMapFileName); } } if (image) { osg::ref_ptr normalMapTex (new osg::Texture2D(image)); normalMapTex->setWrap(osg::Texture::WRAP_S, diffuseMap->getWrap(osg::Texture::WRAP_S)); normalMapTex->setWrap(osg::Texture::WRAP_T, diffuseMap->getWrap(osg::Texture::WRAP_T)); normalMapTex->setFilter(osg::Texture::MIN_FILTER, diffuseMap->getFilter(osg::Texture::MIN_FILTER)); normalMapTex->setFilter(osg::Texture::MAG_FILTER, diffuseMap->getFilter(osg::Texture::MAG_FILTER)); normalMapTex->setMaxAnisotropy(diffuseMap->getMaxAnisotropy()); normalMapTex->setName("normalMap"); int unit = texAttributes.size(); if (!writableStateSet) writableStateSet = getWritableStateSet(node); writableStateSet->setTextureAttributeAndModes(unit, normalMapTex, osg::StateAttribute::ON); mRequirements.back().mTextures[unit] = "normalMap"; mRequirements.back().mTexStageRequiringTangents = unit; mRequirements.back().mShaderRequired = true; mRequirements.back().mNormalHeight = normalHeight; } } if (mAutoUseSpecularMaps && diffuseMap != NULL && specularMap == NULL && diffuseMap->getImage(0)) { std::string specularMapFileName = diffuseMap->getImage(0)->getFileName(); boost::replace_last(specularMapFileName, ".", mSpecularMapPattern + "."); if (mImageManager.getVFS()->exists(specularMapFileName)) { osg::ref_ptr specularMapTex (new osg::Texture2D(mImageManager.getImage(specularMapFileName))); specularMapTex->setWrap(osg::Texture::WRAP_S, diffuseMap->getWrap(osg::Texture::WRAP_S)); specularMapTex->setWrap(osg::Texture::WRAP_T, diffuseMap->getWrap(osg::Texture::WRAP_T)); specularMapTex->setFilter(osg::Texture::MIN_FILTER, diffuseMap->getFilter(osg::Texture::MIN_FILTER)); specularMapTex->setFilter(osg::Texture::MAG_FILTER, diffuseMap->getFilter(osg::Texture::MAG_FILTER)); specularMapTex->setMaxAnisotropy(diffuseMap->getMaxAnisotropy()); specularMapTex->setName("specularMap"); int unit = texAttributes.size(); if (!writableStateSet) writableStateSet = getWritableStateSet(node); writableStateSet->setTextureAttributeAndModes(unit, specularMapTex, osg::StateAttribute::ON); mRequirements.back().mTextures[unit] = "specularMap"; mRequirements.back().mShaderRequired = true; } } } const osg::StateSet::AttributeList& attributes = stateset->getAttributeList(); for (osg::StateSet::AttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it) { if (it->first.first == osg::StateAttribute::MATERIAL) { 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(); } } } } void ShaderVisitor::pushRequirements(osg::Node& node) { mRequirements.push_back(mRequirements.back()); mRequirements.back().mNode = &node; } void ShaderVisitor::popRequirements() { mRequirements.pop_back(); } void ShaderVisitor::createProgram(const ShaderRequirements &reqs) { if (!reqs.mShaderRequired && !mForceShaders) return; osg::Node& node = *reqs.mNode; osg::StateSet* writableStateSet = NULL; if (mAllowedToModifyStateSets) writableStateSet = node.getOrCreateStateSet(); else writableStateSet = getWritableStateSet(node); ShaderManager::DefineMap defineMap; for (unsigned int i=0; i::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt) { defineMap[texIt->second] = "1"; defineMap[texIt->second + std::string("UV")] = std::to_string(texIt->first); } if (!reqs.mColorMaterial) defineMap["colorMode"] = "0"; else { switch (reqs.mVertexColorMode) { case GL_AMBIENT: defineMap["colorMode"] = "3"; break; default: case GL_AMBIENT_AND_DIFFUSE: defineMap["colorMode"] = "2"; break; case GL_EMISSION: defineMap["colorMode"] = "1"; break; } } defineMap["forcePPL"] = mForcePerPixelLighting ? "1" : "0"; defineMap["clamp"] = mClampLighting ? "1" : "0"; defineMap["parallax"] = reqs.mNormalHeight ? "1" : "0"; osg::ref_ptr vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX)); osg::ref_ptr fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT)); if (vertexShader && fragmentShader) { writableStateSet->setAttributeAndModes(mShaderManager.getProgram(vertexShader, fragmentShader), osg::StateAttribute::ON); for (std::map::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt) { writableStateSet->addUniform(new osg::Uniform(texIt->second.c_str(), texIt->first), osg::StateAttribute::ON); } } } bool ShaderVisitor::adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs) { bool useShader = reqs.mShaderRequired || mForceShaders; bool generateTangents = reqs.mTexStageRequiringTangents != -1; bool changed = false; if (mAllowedToModifyStateSets && (useShader || generateTangents)) { // make sure that all UV sets are there for (std::map::const_iterator it = reqs.mTextures.begin(); it != reqs.mTextures.end(); ++it) { if (sourceGeometry.getTexCoordArray(it->first) == NULL) { sourceGeometry.setTexCoordArray(it->first, sourceGeometry.getTexCoordArray(0)); changed = true; } } if (generateTangents) { osg::ref_ptr generator (new osgUtil::TangentSpaceGenerator); generator->generate(&sourceGeometry, reqs.mTexStageRequiringTangents); sourceGeometry.setTexCoordArray(7, generator->getTangentArray(), osg::Array::BIND_PER_VERTEX); changed = true; } } return changed; } void ShaderVisitor::apply(osg::Geometry& geometry) { bool needPop = (geometry.getStateSet() != NULL); if (geometry.getStateSet()) // TODO: check if stateset affects shader permutation before pushing it { pushRequirements(geometry); applyStateSet(geometry.getStateSet(), geometry); } if (!mRequirements.empty()) { const ShaderRequirements& reqs = mRequirements.back(); adjustGeometry(geometry, reqs); createProgram(reqs); } if (needPop) popRequirements(); } void ShaderVisitor::apply(osg::Drawable& drawable) { // non-Geometry drawable (e.g. particle system) bool needPop = (drawable.getStateSet() != NULL); if (drawable.getStateSet()) { pushRequirements(drawable); applyStateSet(drawable.getStateSet(), drawable); } if (!mRequirements.empty()) { const ShaderRequirements& reqs = mRequirements.back(); createProgram(reqs); if (auto rig = dynamic_cast(&drawable)) { osg::ref_ptr sourceGeometry = rig->getSourceGeometry(); if (sourceGeometry && adjustGeometry(*sourceGeometry, reqs)) rig->setSourceGeometry(sourceGeometry); } else if (auto morph = dynamic_cast(&drawable)) { osg::ref_ptr sourceGeometry = morph->getSourceGeometry(); if (sourceGeometry && adjustGeometry(*sourceGeometry, reqs)) morph->setSourceGeometry(sourceGeometry); } } if (needPop) popRequirements(); } void ShaderVisitor::setAllowedToModifyStateSets(bool allowed) { mAllowedToModifyStateSets = allowed; } void ShaderVisitor::setAutoUseNormalMaps(bool use) { mAutoUseNormalMaps = use; } void ShaderVisitor::setNormalMapPattern(const std::string &pattern) { mNormalMapPattern = pattern; } void ShaderVisitor::setNormalHeightMapPattern(const std::string &pattern) { mNormalHeightMapPattern = pattern; } void ShaderVisitor::setAutoUseSpecularMaps(bool use) { mAutoUseSpecularMaps = use; } void ShaderVisitor::setSpecularMapPattern(const std::string &pattern) { mSpecularMapPattern = pattern; } }