mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 01:26:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			408 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			408 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "shadervisitor.hpp"
 | 
						|
 | 
						|
#include <iostream>
 | 
						|
 | 
						|
#include <osg/Texture>
 | 
						|
#include <osg/Material>
 | 
						|
#include <osg/Geometry>
 | 
						|
#include <osg/Image>
 | 
						|
 | 
						|
#include <osgUtil/TangentSpaceGenerator>
 | 
						|
 | 
						|
#include <boost/lexical_cast.hpp>
 | 
						|
#include <boost/algorithm/string.hpp>
 | 
						|
 | 
						|
#include <components/resource/imagemanager.hpp>
 | 
						|
#include <components/vfs/manager.hpp>
 | 
						|
#include <components/sceneutil/riggeometry.hpp>
 | 
						|
 | 
						|
#include "shadermanager.hpp"
 | 
						|
 | 
						|
namespace Shader
 | 
						|
{
 | 
						|
 | 
						|
    ShaderVisitor::ShaderRequirements::ShaderRequirements()
 | 
						|
        : mShaderRequired(false)
 | 
						|
        , mColorMaterial(false)
 | 
						|
        , mVertexColorMode(GL_AMBIENT_AND_DIFFUSE)
 | 
						|
        , mMaterialOverridden(false)
 | 
						|
        , mNormalHeight(false)
 | 
						|
        , mTexStageRequiringTangents(-1)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    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();
 | 
						|
            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<osg::StateSet> 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<sizeof(defaultTextures)/sizeof(defaultTextures[0]); ++i)
 | 
						|
            if (name == defaultTextures[i])
 | 
						|
                return true;
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    void ShaderVisitor::applyStateSet(osg::ref_ptr<osg::StateSet> 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;unit<texAttributes.size();++unit)
 | 
						|
            {
 | 
						|
                const osg::StateAttribute *attr = stateset->getTextureAttribute(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<osg::Image> 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<osg::Texture2D> 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<osg::Texture2D> 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<const osg::Material*>(it->second.first.get());
 | 
						|
                    mRequirements.back().mColorMaterial = (mat->getColorMode() != osg::Material::OFF);
 | 
						|
                    mRequirements.back().mVertexColorMode = mat->getColorMode();
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void ShaderVisitor::pushRequirements()
 | 
						|
    {
 | 
						|
        mRequirements.push_back(mRequirements.back());
 | 
						|
    }
 | 
						|
 | 
						|
    void ShaderVisitor::popRequirements()
 | 
						|
    {
 | 
						|
        mRequirements.pop_back();
 | 
						|
    }
 | 
						|
 | 
						|
    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;
 | 
						|
        for (unsigned int i=0; i<sizeof(defaultTextures)/sizeof(defaultTextures[0]); ++i)
 | 
						|
        {
 | 
						|
            defineMap[defaultTextures[i]] = "0";
 | 
						|
            defineMap[std::string(defaultTextures[i]) + std::string("UV")] = "0";
 | 
						|
        }
 | 
						|
        for (std::map<int, std::string>::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt)
 | 
						|
        {
 | 
						|
            defineMap[texIt->second] = "1";
 | 
						|
            defineMap[texIt->second + std::string("UV")] = boost::lexical_cast<std::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<osg::Shader> vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX));
 | 
						|
        osg::ref_ptr<osg::Shader> fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT));
 | 
						|
 | 
						|
        if (vertexShader && fragmentShader)
 | 
						|
        {
 | 
						|
            writableStateSet->setAttributeAndModes(mShaderManager.getProgram(vertexShader, fragmentShader), osg::StateAttribute::ON);
 | 
						|
 | 
						|
            for (std::map<int, std::string>::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);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void ShaderVisitor::apply(osg::Geometry& geometry)
 | 
						|
    {
 | 
						|
        bool needPop = (geometry.getStateSet() != NULL);
 | 
						|
        if (geometry.getStateSet())
 | 
						|
        {
 | 
						|
            pushRequirements();
 | 
						|
            applyStateSet(geometry.getStateSet(), geometry);
 | 
						|
        }
 | 
						|
 | 
						|
        if (!mRequirements.empty())
 | 
						|
        {
 | 
						|
            const ShaderRequirements& reqs = mRequirements.back();
 | 
						|
 | 
						|
            osg::ref_ptr<osg::Geometry> sourceGeometry = &geometry;
 | 
						|
            SceneUtil::RigGeometry* rig = dynamic_cast<SceneUtil::RigGeometry*>(&geometry);
 | 
						|
            if (rig)
 | 
						|
                sourceGeometry = rig->getSourceGeometry();
 | 
						|
 | 
						|
            if (mAllowedToModifyStateSets)
 | 
						|
            {
 | 
						|
                // make sure that all UV sets are there
 | 
						|
                for (std::map<int, std::string>::const_iterator it = reqs.mTextures.begin(); it != reqs.mTextures.end(); ++it)
 | 
						|
                {
 | 
						|
                    if (sourceGeometry->getTexCoordArray(it->first) == NULL)
 | 
						|
                        sourceGeometry->setTexCoordArray(it->first, sourceGeometry->getTexCoordArray(0));
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (reqs.mTexStageRequiringTangents != -1 && mAllowedToModifyStateSets)
 | 
						|
            {
 | 
						|
                osg::ref_ptr<osgUtil::TangentSpaceGenerator> generator (new osgUtil::TangentSpaceGenerator);
 | 
						|
                generator->generate(sourceGeometry, reqs.mTexStageRequiringTangents);
 | 
						|
 | 
						|
                sourceGeometry->setTexCoordArray(7, generator->getTangentArray(), osg::Array::BIND_PER_VERTEX);
 | 
						|
            }
 | 
						|
 | 
						|
            if (rig)
 | 
						|
                rig->setSourceGeometry(sourceGeometry);
 | 
						|
 | 
						|
            // TODO: find a better place for the stateset
 | 
						|
            if (reqs.mShaderRequired || mForceShaders)
 | 
						|
                createProgram(reqs, geometry);
 | 
						|
        }
 | 
						|
 | 
						|
        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();
 | 
						|
            applyStateSet(drawable.getStateSet(), drawable);
 | 
						|
        }
 | 
						|
 | 
						|
        if (!mRequirements.empty())
 | 
						|
        {
 | 
						|
            const ShaderRequirements& reqs = mRequirements.back();
 | 
						|
            // TODO: find a better place for the stateset
 | 
						|
            if (reqs.mShaderRequired || mForceShaders)
 | 
						|
                createProgram(reqs, drawable);
 | 
						|
        }
 | 
						|
 | 
						|
        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;
 | 
						|
    }
 | 
						|
 | 
						|
}
 |