diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 57f784b2c..52a0b2c61 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -174,6 +174,7 @@ 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()->setNormalHeightMapPattern(Settings::Manager::getString("normal height 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")); @@ -197,7 +198,8 @@ namespace MWRender mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), - new TerrainStorage(mResourceSystem->getVFS(), Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), + new TerrainStorage(mResourceSystem->getVFS(), Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), + Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")), Mask_Terrain, &mResourceSystem->getSceneManager()->getShaderManager(), mUnrefQueue.get())); diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index cc1c3a0bc..98386690b 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -9,8 +9,8 @@ namespace MWRender { - TerrainStorage::TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) - : ESMTerrain::Storage(vfs, normalMapPattern, autoUseNormalMaps, specularMapPattern, autoUseSpecularMaps) + TerrainStorage::TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) + : ESMTerrain::Storage(vfs, normalMapPattern, normalHeightMapPattern, autoUseNormalMaps, specularMapPattern, autoUseSpecularMaps) { } diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 759c7dfcf..6fa8b98ce 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -14,7 +14,7 @@ namespace MWRender virtual const ESM::LandTexture* getLandTexture(int index, short plugin); public: - TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); + TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", const std::string& normalHeightMapPatteern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 86d1e08e6..8a592bfc8 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -18,9 +18,10 @@ namespace ESMTerrain const float defaultHeight = ESM::Land::DEFAULT_HEIGHT; - Storage::Storage(const VFS::Manager *vfs, const std::string& normalMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) + Storage::Storage(const VFS::Manager *vfs, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) : mVFS(vfs) , mNormalMapPattern(normalMapPattern) + , mNormalHeightMapPattern(normalHeightMapPattern) , mAutoUseNormalMaps(autoUseNormalMaps) , mSpecularMapPattern(specularMapPattern) , mAutoUseSpecularMaps(autoUseSpecularMaps) @@ -512,26 +513,26 @@ namespace ESMTerrain return found->second; Terrain::LayerInfo info; - //info.mParallax = false; + info.mParallax = false; info.mSpecular = false; info.mDiffuseMap = texture; - /* - std::string texture_ = texture; - boost::replace_last(texture_, ".", "_nh."); - - if (mVFS->exists(texture_)) - { - info.mNormalMap = texture_; - info.mParallax = true; - } - */ if (mAutoUseNormalMaps) { std::string texture_ = texture; - boost::replace_last(texture_, ".", mNormalMapPattern + "."); + boost::replace_last(texture_, ".", mNormalHeightMapPattern + "."); if (mVFS->exists(texture_)) + { info.mNormalMap = texture_; + info.mParallax = true; + } + else + { + texture_ = texture; + boost::replace_last(texture_, ".", mNormalMapPattern + "."); + if (mVFS->exists(texture_)) + info.mNormalMap = texture_; + } } if (mAutoUseSpecularMaps) @@ -554,7 +555,7 @@ namespace ESMTerrain { Terrain::LayerInfo info; info.mDiffuseMap = "textures\\_land_default.dds"; - //info.mParallax = false; + info.mParallax = false; info.mSpecular = false; return info; } diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 7b8b844ff..092998b2e 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -27,7 +27,7 @@ namespace ESMTerrain virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; public: - Storage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); + Storage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", const std::string& normalHeightMapPattern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); /// Data is loaded first, if necessary. Will return a 0-pointer if there is no data for /// any of the data types specified via \a flags. Will also return a 0-pointer if there @@ -110,6 +110,7 @@ namespace ESMTerrain OpenThreads::Mutex mLayerInfoMutex; std::string mNormalMapPattern; + std::string mNormalHeightMapPattern; bool mAutoUseNormalMaps; std::string mSpecularMapPattern; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 7bbf08f3a..7078af8d0 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -241,6 +241,11 @@ namespace Resource mNormalMapPattern = pattern; } + void SceneManager::setNormalHeightMapPattern(const std::string &pattern) + { + mNormalHeightMapPattern = pattern; + } + void SceneManager::setAutoUseSpecularMaps(bool use) { mAutoUseSpecularMaps = use; diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 00eb68e76..a2107fc4a 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -63,6 +63,9 @@ namespace Resource /// @see ShaderVisitor::setNormalMapPattern void setNormalMapPattern(const std::string& pattern); + /// @see ShaderVisitor::setNormalHeightMapPattern + void setNormalHeightMapPattern(const std::string& pattern); + void setAutoUseSpecularMaps(bool use); void setSpecularMapPattern(const std::string& pattern); @@ -138,6 +141,7 @@ namespace Resource bool mForcePerPixelLighting; bool mAutoUseNormalMaps; std::string mNormalMapPattern; + std::string mNormalHeightMapPattern; bool mAutoUseSpecularMaps; std::string mSpecularMapPattern; diff --git a/components/terrain/defs.hpp b/components/terrain/defs.hpp index 10095bec0..c2342c50d 100644 --- a/components/terrain/defs.hpp +++ b/components/terrain/defs.hpp @@ -18,7 +18,7 @@ namespace Terrain { std::string mDiffuseMap; std::string mNormalMap; - //bool mParallax; // Height info in normal map alpha channel? + bool mParallax; // Height info in normal map alpha channel? bool mSpecular; // Specular info in diffuse map alpha channel? bool requiresShaders() const { return !mNormalMap.empty() || mSpecular; } diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 1b6cd5d44..fa87c53ec 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -157,6 +157,7 @@ namespace Terrain defineMap["blendMap"] = !firstLayer ? "1" : "0"; defineMap["colorMode"] = "2"; defineMap["specularMap"] = it->mSpecular ? "1" : "0"; + defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0"; osg::ref_ptr vertexShader = shaderManager.getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX); osg::ref_ptr fragmentShader = shaderManager.getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT); diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp index 61d5724d7..c13b21e8f 100644 --- a/components/terrain/material.hpp +++ b/components/terrain/material.hpp @@ -23,6 +23,7 @@ namespace Terrain { osg::ref_ptr mDiffuseMap; osg::ref_ptr mNormalMap; // optional + bool mParallax; bool mSpecular; }; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index c717a94f6..231634e1e 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -160,6 +160,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) { TextureLayer textureLayer; + textureLayer.mParallax = it->mParallax; textureLayer.mSpecular = it->mSpecular; osg::ref_ptr texture = mTextureCache[it->mDiffuseMap]; if (!texture) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c886cee70..ef3d95b3d 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -200,6 +200,10 @@ 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 +# Alternative filename pattern to probe for when detecting normal maps. Files with this pattern are expected to include 'height' in the alpha channel. +# This height is used for parallax effects. Works for both terrain and objects. +normal height map pattern = _nh + # The filename pattern to probe for when detecting object specular maps (see 'auto use object specular maps') specular map pattern = _spec diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index db43cbd3e..e1b519932 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -22,35 +22,53 @@ varying vec4 lighting; varying vec4 passColor; #endif varying vec3 passViewPos; -varying vec3 passViewNormal; +varying vec3 passNormal; + +#if @parallax +#define PARALLAX_SCALE 0.04 +#define PARALLAX_BIAS -0.02 +uniform mat4 osg_ViewMatrixInverse; +#endif #include "lighting.glsl" void main() { - vec2 diffuseMapUV = (gl_TextureMatrix[0] * vec4(uv, 0.0, 1.0)).xy; + vec2 adjustedUV = (gl_TextureMatrix[0] * vec4(uv, 0.0, 1.0)).xy; - vec4 diffuseTex = texture2D(diffuseMap, diffuseMapUV); - gl_FragData[0] = vec4(diffuseTex.xyz, 1.0); +#if @normalMap + vec4 normalTex = texture2D(normalMap, adjustedUV); -#if @blendMap - vec2 blendMapUV = (gl_TextureMatrix[1] * vec4(uv, 0.0, 1.0)).xy; - gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a; -#endif + vec3 normalizedNormal = normalize(passNormal); + vec3 tangent = vec3(1.0, 0.0, 0.0); + vec3 binormal = normalize(cross(tangent, normalizedNormal)); + tangent = normalize(cross(normalizedNormal, binormal)); // note, now we need to re-cross to derive tangent again because it wasn't orthonormal + mat3 tbn = mat3(tangent, binormal, normalizedNormal); - vec3 viewNormal = passViewNormal; + vec3 viewNormal = normalize(gl_NormalMatrix * (tbn * (normalTex.xyz * 2.0 - 1.0))); +#else + vec3 viewNormal = normalize(gl_NormalMatrix * passNormal); +#endif -#if @normalMap - vec3 normalTex = texture2D(normalMap, diffuseMapUV).xyz; +#if @parallax + vec3 cameraPos = osg_ViewMatrixInverse[3].xyz; + vec3 eyeDir = normalize(cameraPos - (osg_ViewMatrixInverse * vec4(passViewPos, 1)).xyz); + vec3 TSeyeDir = normalize((vec4(normalize(tbn * eyeDir),0)).xyz); - vec3 viewTangent = (gl_ModelViewMatrix * vec4(1.0, 0.0, 0.0, 0.0)).xyz; - vec3 viewBinormal = normalize(cross(viewTangent, viewNormal)); - viewTangent = normalize(cross(viewNormal, viewBinormal)); // note, now we need to re-cross to derive tangent again because it wasn't orthonormal - mat3 tbn = mat3(viewTangent, viewBinormal, viewNormal); + adjustedUV += TSeyeDir.xy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS ); - viewNormal = normalize(tbn * (normalTex * 2.0 - 1.0)); + // update normal using new coordinates + normalTex = texture2D(normalMap, adjustedUV); + viewNormal = normalize(gl_NormalMatrix * (tbn * (normalTex.xyz * 2.0 - 1.0))); #endif + vec4 diffuseTex = texture2D(diffuseMap, adjustedUV); + gl_FragData[0] = vec4(diffuseTex.xyz, 1.0); + +#if @blendMap + vec2 blendMapUV = (gl_TextureMatrix[1] * vec4(uv, 0.0, 1.0)).xy; + gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a; +#endif #if !PER_PIXEL_LIGHTING gl_FragData[0] *= lighting; diff --git a/files/shaders/terrain_vertex.glsl b/files/shaders/terrain_vertex.glsl index 46071d7f0..e2a180153 100644 --- a/files/shaders/terrain_vertex.glsl +++ b/files/shaders/terrain_vertex.glsl @@ -11,7 +11,7 @@ varying vec4 lighting; varying vec4 passColor; #endif varying vec3 passViewPos; -varying vec3 passViewNormal; +varying vec3 passNormal; #include "lighting.glsl" @@ -22,14 +22,14 @@ void main(void) vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; - vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); #if !PER_PIXEL_LIGHTING + vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); lighting = doLighting(viewPos.xyz, viewNormal, gl_Color); #else passColor = gl_Color; #endif - passViewNormal = viewNormal; + passNormal = gl_Normal.xyz; passViewPos = viewPos.xyz; uv = gl_MultiTexCoord0.xy;