diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ef1b469e3..dde283b68 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -174,6 +174,8 @@ namespace MWRender resourceSystem->getSceneManager()->setForcePerPixelLighting(Settings::Manager::getBool("force per pixel lighting", "Shaders")); resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool("auto use object normal maps", "Shaders")); resourceSystem->getSceneManager()->setNormalMapPattern(Settings::Manager::getString("normal map pattern", "Shaders")); + resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders")); + resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); osg::ref_ptr sceneRoot = new SceneUtil::LightManager; sceneRoot->setLightingMask(Mask_Lighting); @@ -340,7 +342,9 @@ namespace MWRender { setAmbientColour(SceneUtil::colourFromRGB(cell->mAmbi.mAmbient)); - mSunLight->setDiffuse(SceneUtil::colourFromRGB(cell->mAmbi.mSunlight)); + osg::Vec4f diffuse = SceneUtil::colourFromRGB(cell->mAmbi.mSunlight); + mSunLight->setDiffuse(diffuse); + mSunLight->setSpecular(diffuse); mSunLight->setDirection(osg::Vec3f(1.f,-1.f,-1.f)); } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 00030edc5..d0d10a3cd 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -274,6 +274,16 @@ namespace Resource mNormalMapPattern = pattern; } + void SceneManager::setAutoUseSpecularMaps(bool use) + { + mAutoUseSpecularMaps = use; + } + + void SceneManager::setSpecularMapPattern(const std::string &pattern) + { + mSpecularMapPattern = pattern; + } + SceneManager::~SceneManager() { // this has to be defined in the .cpp file as we can't delete incomplete types @@ -403,6 +413,8 @@ namespace Resource shaderVisitor.setForcePerPixelLighting(mForcePerPixelLighting); shaderVisitor.setAutoUseNormalMaps(mAutoUseNormalMaps); shaderVisitor.setNormalMapPattern(mNormalMapPattern); + shaderVisitor.setAutoUseSpecularMaps(mAutoUseSpecularMaps); + shaderVisitor.setSpecularMapPattern(mSpecularMapPattern); loaded->accept(shaderVisitor); // share state diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index d78466260..00eb68e76 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -63,6 +63,10 @@ namespace Resource /// @see ShaderVisitor::setNormalMapPattern void setNormalMapPattern(const std::string& pattern); + void setAutoUseSpecularMaps(bool use); + + void setSpecularMapPattern(const std::string& pattern); + void setShaderPath(const std::string& path); /// Get a read-only copy of this scene "template" @@ -134,6 +138,8 @@ namespace Resource bool mForcePerPixelLighting; bool mAutoUseNormalMaps; std::string mNormalMapPattern; + bool mAutoUseSpecularMaps; + std::string mSpecularMapPattern; osg::ref_ptr mInstanceCache; diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index a69a479e3..279954eb0 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -21,7 +21,7 @@ namespace Shader { ShaderVisitor::ShaderRequirements::ShaderRequirements() - : mHasNormalMap(false) + : mShaderRequired(false) , mColorMaterial(false) , mVertexColorMode(GL_AMBIENT_AND_DIFFUSE) , mMaterialOverridden(false) @@ -41,6 +41,7 @@ namespace Shader , mForcePerPixelLighting(false) , mAllowedToModifyStateSets(true) , mAutoUseNormalMaps(false) + , mAutoUseSpecularMaps(false) , mShaderManager(shaderManager) , mImageManager(imageManager) , mDefaultVsTemplate(defaultVsTemplate) @@ -97,6 +98,7 @@ namespace Shader { 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); @@ -111,7 +113,7 @@ namespace Shader if (texture->getName() == "normalMap") { mRequirements.back().mTexStageRequiringTangents = unit; - mRequirements.back().mHasNormalMap = true; + 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 @@ -120,6 +122,8 @@ namespace Shader } if (texture->getName() == "diffuseMap") diffuseMap = texture; + if (texture->getName() == "specularMap") + specularMap = texture; } else std::cerr << "ShaderVisitor encountered unknown texture " << texture << std::endl; @@ -154,7 +158,30 @@ namespace Shader writableStateSet->setTextureAttributeAndModes(unit, normalMapTex, osg::StateAttribute::ON); mRequirements.back().mTextures[unit] = "normalMap"; mRequirements.back().mTexStageRequiringTangents = unit; - mRequirements.back().mHasNormalMap = true; + mRequirements.back().mShaderRequired = true; + } + } + if (mAutoUseSpecularMaps && diffuseMap != NULL && specularMap == NULL) + { + std::string specularMap = diffuseMap->getImage(0)->getFileName(); + boost::replace_last(specularMap, ".", mSpecularMapPattern + "."); + if (mImageManager.getVFS()->exists(specularMap)) + { + std::cout << "using specmap " << specularMap << std::endl; + osg::ref_ptr specularMapTex (new osg::Texture2D(mImageManager.getImage(specularMap))); + 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; } } } @@ -196,7 +223,7 @@ namespace Shader writableStateSet = getWritableStateSet(node); ShaderManager::DefineMap defineMap; - const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap" }; + const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap", "specularMap" }; for (unsigned int i=0; i::const_iterator it = reqs.mTextures.begin(); it != reqs.mTextures.end(); ++it) + { + if (geometry.getTexCoordArray(it->first) == NULL) + geometry.setTexCoordArray(it->first, geometry.getTexCoordArray(0)); + } + } + if (reqs.mTexStageRequiringTangents != -1 && mAllowedToModifyStateSets) + { osg::ref_ptr generator (new osgUtil::TangentSpaceGenerator); generator->generate(&geometry, reqs.mTexStageRequiringTangents); @@ -266,7 +300,7 @@ namespace Shader } // TODO: find a better place for the stateset - if (reqs.mHasNormalMap || mForceShaders) + if (reqs.mShaderRequired || mForceShaders) createProgram(reqs, geometry); } @@ -289,7 +323,7 @@ namespace Shader { const ShaderRequirements& reqs = mRequirements.back(); // TODO: find a better place for the stateset - if (reqs.mHasNormalMap || mForceShaders) + if (reqs.mShaderRequired || mForceShaders) createProgram(reqs, drawable); } @@ -312,4 +346,14 @@ namespace Shader mNormalMapPattern = pattern; } + void ShaderVisitor::setAutoUseSpecularMaps(bool use) + { + mAutoUseSpecularMaps = use; + } + + void ShaderVisitor::setSpecularMapPattern(const std::string &pattern) + { + mSpecularMapPattern = pattern; + } + } diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index 645a92969..2f957dfbf 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -40,6 +40,10 @@ namespace Shader void setNormalMapPattern(const std::string& pattern); + void setAutoUseSpecularMaps(bool use); + + void setSpecularMapPattern(const std::string& pattern); + virtual void apply(osg::Node& node); virtual void apply(osg::Drawable& drawable); @@ -59,6 +63,9 @@ namespace Shader bool mAutoUseNormalMaps; std::string mNormalMapPattern; + bool mAutoUseSpecularMaps; + std::string mSpecularMapPattern; + ShaderManager& mShaderManager; Resource::ImageManager& mImageManager; @@ -72,7 +79,7 @@ namespace Shader osg::ref_ptr mDiffuseMap; - bool mHasNormalMap; + bool mShaderRequired; bool mColorMaterial; // osg::Material::ColorMode diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 7403375c0..98c33e834 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -184,12 +184,18 @@ clamp lighting = true # Affects objects. auto use object normal maps = false +auto use object specular maps = false + # See 'auto use object normal maps'. Affects terrain. auto use terrain normal maps = false +auto use terrain specular maps = false + # The filename pattern to probe for when detecting normal maps (see 'auto use object normal maps', 'auto use terrain normal maps') normal map pattern = _n +specular map pattern = _spec + [Input] # Capture control of the cursor prevent movement outside the window. diff --git a/files/shaders/lighting.glsl b/files/shaders/lighting.glsl index d3c51d3cd..d706563ba 100644 --- a/files/shaders/lighting.glsl +++ b/files/shaders/lighting.glsl @@ -40,3 +40,14 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor) #endif return lightResult; } + + +vec3 getSpecular(vec3 viewNormal, vec3 viewPos, float shininess, vec3 matSpec) +{ + vec3 lightDir = normalize(gl_LightSource[0].position.xyz); + float NdotL = max(dot(viewNormal, lightDir), 0.0); + if (NdotL < 0) + return vec3(0,0,0); + vec3 halfVec = normalize(lightDir - viewPos); + return pow(max(dot(viewNormal, halfVec), 0.0), 128) * gl_LightSource[0].specular.xyz * matSpec; +} diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 6366da82b..f572c24bb 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -32,6 +32,11 @@ varying vec2 envMapUV; uniform vec4 envMapColor; #endif +#if @specularMap +uniform sampler2D specularMap; +varying vec2 specularMapUV; +#endif + varying float depth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -39,10 +44,10 @@ varying float depth; #if !PER_PIXEL_LIGHTING varying vec4 lighting; #else -varying vec3 passViewPos; -varying vec3 passViewNormal; varying vec4 passColor; #endif +varying vec3 passViewPos; +varying vec3 passViewNormal; #include "lighting.glsl" @@ -62,9 +67,7 @@ void main() gl_FragData[0].xyz *= texture2D(darkMap, darkMapUV).xyz; #endif -#if PER_PIXEL_LIGHTING vec3 viewNormal = passViewNormal; -#endif #if @normalMap vec3 normalTex = texture2D(normalMap, normalMapUV).xyz; @@ -90,6 +93,17 @@ void main() gl_FragData[0].xyz += texture2D(envMap, envMapUV).xyz * envMapColor.xyz; #endif +#if @specularMap + vec4 specTex = texture2D(specularMap, specularMapUV); + float shininess = specTex.a * 255; + vec3 matSpec = specTex.xyz; +#else + float shininess = gl_FrontMaterial.shininess; + vec3 matSpec = gl_FrontMaterial.specular.xyz; +#endif + + gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec); + float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); } diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index a2c0e7d21..b1004aad3 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -25,6 +25,10 @@ varying vec3 viewTangent; varying vec2 envMapUV; #endif +#if @specularMap +varying vec2 specularMapUV; +#endif + varying float depth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -32,10 +36,10 @@ varying float depth; #if !PER_PIXEL_LIGHTING varying vec4 lighting; #else -varying vec3 passViewPos; -varying vec3 passViewNormal; varying vec4 passColor; #endif +varying vec3 passViewPos; +varying vec3 passViewNormal; #include "lighting.glsl" @@ -76,11 +80,15 @@ void main(void) viewTangent = normalize(gl_NormalMatrix * gl_MultiTexCoord7.xyz); #endif +#if @specularMap + specularMapUV = (gl_TextureMatrix[@specularMapUV] * gl_MultiTexCoord@specularMapUV).xy; +#endif + #if !PER_PIXEL_LIGHTING lighting = doLighting(viewPos.xyz, viewNormal, gl_Color); #else - passViewPos = viewPos.xyz; - passViewNormal = viewNormal; passColor = gl_Color; #endif + passViewPos = viewPos.xyz; + passViewNormal = viewNormal; }