From 58afe1ba23fe7c0fafd4e163fedf161ffd6bf9a1 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 23 Mar 2024 19:46:31 +0300 Subject: [PATCH 1/5] Support red-green normal maps --- apps/opencs/model/world/data.cpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 1 + components/resource/imagemanager.cpp | 3 +-- components/shader/shadervisitor.cpp | 20 ++++++++++++++++++++ components/shader/shadervisitor.hpp | 1 + files/shaders/compatibility/bs/default.frag | 3 +++ files/shaders/compatibility/groundcover.frag | 6 +++++- files/shaders/compatibility/objects.frag | 6 +++++- files/shaders/compatibility/terrain.frag | 6 +++++- 9 files changed, 42 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 470ce041316..4198e1b9803 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -152,6 +152,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data mResourceSystem = std::make_unique(mVFS.get(), expiryDelay, &mEncoder.getStatelessEncoder()); + // FIXME: this is severely out of date (see #7595) Shader::ShaderManager::DefineMap defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines(); Shader::ShaderManager::DefineMap shadowDefines = SceneUtil::ShadowManager::getShadowsDisabledDefines(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index dc71e455b2c..d5c5321f8e0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -446,6 +446,7 @@ namespace MWRender globalDefines["useOVR_multiview"] = "0"; globalDefines["numViews"] = "1"; globalDefines["disableNormals"] = "1"; + globalDefines["reconstructNormalZ"] = "0"; for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++) globalDefines[itr->first] = itr->second; diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp index a7d2ef61a1f..e7cc9f03e51 100644 --- a/components/resource/imagemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -78,8 +78,7 @@ namespace Resource } break; } - // not bothering with checks for other compression formats right now, we are unlikely to ever use those - // anyway + // not bothering with checks for other compression formats right now default: return true; } diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 7bce9de2a64..3867f1f43e2 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -184,6 +184,7 @@ namespace Shader , mAdditiveBlending(false) , mDiffuseHeight(false) , mNormalHeight(false) + , mReconstructNormalZ(false) , mTexStageRequiringTangents(-1) , mSoftParticles(false) , mNode(nullptr) @@ -429,6 +430,7 @@ namespace Shader normalMapTex->setFilter(osg::Texture::MAG_FILTER, diffuseMap->getFilter(osg::Texture::MAG_FILTER)); normalMapTex->setMaxAnisotropy(diffuseMap->getMaxAnisotropy()); normalMapTex->setName("normalMap"); + normalMap = normalMapTex; int unit = texAttributes.size(); if (!writableStateSet) @@ -440,6 +442,23 @@ namespace Shader mRequirements.back().mNormalHeight = normalHeight; } } + + if (normalMap != nullptr && normalMap->getImage(0)) + { + // Special handling for red-green normal maps (e.g. BC5 or R8G8). + switch (normalMap->getImage(0)->getPixelFormat()) + { + case GL_RG: + case GL_RG_INTEGER: + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: + case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: + { + mRequirements.back().mReconstructNormalZ = true; + mRequirements.back().mNormalHeight = false; + } + } + } + if (mAutoUseSpecularMaps && diffuseMap != nullptr && specularMap == nullptr && diffuseMap->getImage(0)) { std::string specularMapFileName = diffuseMap->getImage(0)->getFileName(); @@ -629,6 +648,7 @@ namespace Shader defineMap["diffuseParallax"] = reqs.mDiffuseHeight ? "1" : "0"; defineMap["parallax"] = reqs.mNormalHeight ? "1" : "0"; + defineMap["reconstructNormalZ"] = reqs.mReconstructNormalZ ? "1" : "0"; writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode)); addedState->addUniform("colorMode"); diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index a8e79ec9951..9ce0819bd3d 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -110,6 +110,7 @@ namespace Shader bool mDiffuseHeight; // true if diffuse map has height info in alpha channel bool mNormalHeight; // true if normal map has height info in alpha channel + bool mReconstructNormalZ; // used for red-green normal maps (e.g. BC5) // -1 == no tangents required int mTexStageRequiringTangents; diff --git a/files/shaders/compatibility/bs/default.frag b/files/shaders/compatibility/bs/default.frag index 77131c6a52c..d2c8de0b221 100644 --- a/files/shaders/compatibility/bs/default.frag +++ b/files/shaders/compatibility/bs/default.frag @@ -77,6 +77,9 @@ void main() vec3 specularColor = getSpecularColor().xyz; #if @normalMap vec4 normalTex = texture2D(normalMap, normalMapUV); +#if @reconstructNormalZ + normalTex.z = sqrt(1.0 - dot(normalTex.xy, normalTex.xy)); +#endif vec3 viewNormal = normalToView(normalTex.xyz * 2.0 - 1.0); specularColor *= normalTex.a; #else diff --git a/files/shaders/compatibility/groundcover.frag b/files/shaders/compatibility/groundcover.frag index dfdd6518c39..aab37d465d0 100644 --- a/files/shaders/compatibility/groundcover.frag +++ b/files/shaders/compatibility/groundcover.frag @@ -59,7 +59,11 @@ void main() gl_FragData[0].a = alphaTest(gl_FragData[0].a, alphaRef); #if @normalMap - vec3 viewNormal = normalToView(texture2D(normalMap, normalMapUV).xyz * 2.0 - 1.0); + vec4 normalTex = texture2D(normalMap, normalMapUV); +#if @reconstructNormalZ + normalTex.z = sqrt(1.0 - dot(normalTex.xy, normalTex.xy)); +#endif + vec3 viewNormal = normalToView(normalTex.xyz * 2.0 - 1.0); #else vec3 viewNormal = normalToView(normalize(passNormal)); #endif diff --git a/files/shaders/compatibility/objects.frag b/files/shaders/compatibility/objects.frag index 56c7abf27c7..eb5b79a0c28 100644 --- a/files/shaders/compatibility/objects.frag +++ b/files/shaders/compatibility/objects.frag @@ -167,7 +167,11 @@ vec2 screenCoords = gl_FragCoord.xy / screenRes; gl_FragData[0].a = alphaTest(gl_FragData[0].a, alphaRef); #if @normalMap - vec3 viewNormal = normalToView(texture2D(normalMap, normalMapUV + offset).xyz * 2.0 - 1.0); + vec4 normalTex = texture2D(normalMap, normalMapUV + offset); +#if @reconstructNormalZ + normalTex.z = sqrt(1.0 - dot(normalTex.xy, normalTex.xy)); +#endif + vec3 viewNormal = normalToView(normalTex.xyz * 2.0 - 1.0); #else vec3 viewNormal = normalize(gl_NormalMatrix * passNormal); #endif diff --git a/files/shaders/compatibility/terrain.frag b/files/shaders/compatibility/terrain.frag index abc7425eb05..f45f1f024e4 100644 --- a/files/shaders/compatibility/terrain.frag +++ b/files/shaders/compatibility/terrain.frag @@ -63,7 +63,11 @@ void main() #endif #if @normalMap - vec3 viewNormal = normalToView(texture2D(normalMap, adjustedUV).xyz * 2.0 - 1.0); + vec4 normalTex = texture2D(normalMap, adjustedUV); +#if @reconstructNormalZ + normalTex.z = sqrt(1.0 - dot(normalTex.xy, normalTex.xy)); +#endif + vec3 viewNormal = normalToView(normalTex.xyz * 2.0 - 1.0); #else vec3 viewNormal = normalize(gl_NormalMatrix * passNormal); #endif From cf6b95ae7cf739c00fe00919bc1959b99deca515 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 23 Mar 2024 23:10:29 +0300 Subject: [PATCH 2/5] Document some technical details regarding normal-mapping --- .../modding/texture-modding/texture-basics.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/source/reference/modding/texture-modding/texture-basics.rst b/docs/source/reference/modding/texture-modding/texture-basics.rst index 78ae0077049..8bbf018fbac 100644 --- a/docs/source/reference/modding/texture-modding/texture-basics.rst +++ b/docs/source/reference/modding/texture-modding/texture-basics.rst @@ -25,6 +25,19 @@ Content creators need to know that OpenMW uses the DX format for normal maps, an See the section `Automatic use`_ further down below for detailed information. +The RGB channels of the normal map are used to store XYZ components of tangent space normals and the alpha channel of the normal map may be used to store a height map used for parallax. + +This is different from the setup used in Bethesda games that use the traditional pipeline, which may store specular information in the alpha channel. + +Special pixel formats that only store two color channels exist and are used by Bethesda games that employ a PBR-based pipeline. Compressed red-green formats are optimized for use with normal maps and suffer from far less quality degradation than S3TC-compressed normal maps of equivalent size. + +OpenMW supports the use of such pixel formats. When a red-green normal map is provided, the Z component of the normal will be reconstructed based on XY components it stores. +Naturally, since these formats cannot provide an alpha channel, they do not support parallax. + +Keep in mind, however, that while the necessary hardware support is widespread for compressed red-green formats, it is less ubiquitous than the support for S3TC family of compressed formats. +Should you run into the consequences of this, you might want to convert such textures into an uncompressed red-green format such as R8G8. +Be careful not to try and convert such textures into a full-color format as the previously non-existent blue channel would then be used. + Specular Mapping ################ From 3c0c1717a98951f6e0809911aaef12be3d29d37b Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 28 Mar 2024 07:43:33 +0300 Subject: [PATCH 3/5] Fix red-green normal map handling for terrain --- apps/opencs/model/world/data.cpp | 1 - apps/openmw/mwrender/renderingmanager.cpp | 1 - components/sceneutil/util.cpp | 14 ++++++++++++++ components/sceneutil/util.hpp | 2 ++ components/shader/shadervisitor.cpp | 15 +++++---------- components/terrain/material.cpp | 15 ++++++++++++++- 6 files changed, 35 insertions(+), 13 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 4198e1b9803..470ce041316 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -152,7 +152,6 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data mResourceSystem = std::make_unique(mVFS.get(), expiryDelay, &mEncoder.getStatelessEncoder()); - // FIXME: this is severely out of date (see #7595) Shader::ShaderManager::DefineMap defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines(); Shader::ShaderManager::DefineMap shadowDefines = SceneUtil::ShadowManager::getShadowsDisabledDefines(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d5c5321f8e0..dc71e455b2c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -446,7 +446,6 @@ namespace MWRender globalDefines["useOVR_multiview"] = "0"; globalDefines["numViews"] = "1"; globalDefines["disableNormals"] = "1"; - globalDefines["reconstructNormalZ"] = "0"; for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++) globalDefines[itr->first] = itr->second; diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index ab600de11d0..a5629ee092f 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -286,4 +286,18 @@ namespace SceneUtil mOperationQueue->add(operation); } + bool isRedGreenPixelFormat(GLenum format) + { + switch (format) + { + case GL_RG: + case GL_RG_INTEGER: + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: + case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: + return true; + } + + return false; + } + } diff --git a/components/sceneutil/util.hpp b/components/sceneutil/util.hpp index 29fee09176c..0f4b82bbe06 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -112,6 +112,8 @@ namespace SceneUtil protected: osg::ref_ptr mOperationQueue; }; + + bool isRedGreenPixelFormat(GLenum format); } #endif diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 3867f1f43e2..600e35a22ae 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -445,17 +446,11 @@ namespace Shader if (normalMap != nullptr && normalMap->getImage(0)) { - // Special handling for red-green normal maps (e.g. BC5 or R8G8). - switch (normalMap->getImage(0)->getPixelFormat()) + // Special handling for red-green normal maps (e.g. BC5 or R8G8) + if (SceneUtil::isRedGreenPixelFormat(normalMap->getImage(0)->getPixelFormat())) { - case GL_RG: - case GL_RG_INTEGER: - case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: - case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: - { - mRequirements.back().mReconstructNormalZ = true; - mRequirements.back().mNormalHeight = false; - } + mRequirements.back().mReconstructNormalZ = true; + mRequirements.back().mNormalHeight = false; } } diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index fafe2dcb580..10dbeb9838d 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -271,18 +272,30 @@ namespace Terrain stateset->addUniform(UniformCollection::value().mBlendMap); } + bool parallax = it->mNormalMap && it->mParallax; + bool reconstructNormalZ = false; + if (it->mNormalMap) { stateset->setTextureAttributeAndModes(2, it->mNormalMap); stateset->addUniform(UniformCollection::value().mNormalMap); + + // Special handling for red-green normal maps (e.g. BC5 or R8G8). + const osg::Image* image = it->mNormalMap->getImage(0); + if (image && SceneUtil::isRedGreenPixelFormat(image->getPixelFormat())) + { + reconstructNormalZ = true; + parallax = false; + } } Shader::ShaderManager::DefineMap defineMap; defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0"; defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0"; defineMap["specularMap"] = it->mSpecular ? "1" : "0"; - defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0"; + defineMap["parallax"] = parallax ? "1" : "0"; defineMap["writeNormals"] = (it == layers.end() - 1) ? "1" : "0"; + defineMap["reconstructNormalZ"] = reconstructNormalZ ? "1" : "0"; Stereo::shaderStereoDefines(defineMap); stateset->setAttributeAndModes(shaderManager.getProgram("terrain", defineMap)); From 443e341ae763cc906324e2445b52b894be87b610 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 16 Apr 2024 02:52:25 +0300 Subject: [PATCH 4/5] Generalize unsized pixel format computation --- components/sceneutil/util.cpp | 117 ++++++++++++++++++++++++++-- components/sceneutil/util.hpp | 4 +- components/shader/shadervisitor.cpp | 10 ++- components/terrain/material.cpp | 13 +++- 4 files changed, 132 insertions(+), 12 deletions(-) diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index a5629ee092f..21a753df12f 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -286,18 +286,125 @@ namespace SceneUtil mOperationQueue->add(operation); } - bool isRedGreenPixelFormat(GLenum format) + GLenum computeUnsizedPixelFormat(GLenum format) { switch (format) { - case GL_RG: - case GL_RG_INTEGER: + // Try compressed formats first, they're more likely to be used + + // Generic + case GL_COMPRESSED_ALPHA_ARB: + return GL_ALPHA; + case GL_COMPRESSED_INTENSITY_ARB: + return GL_INTENSITY; + case GL_COMPRESSED_LUMINANCE_ALPHA_ARB: + return GL_LUMINANCE_ALPHA; + case GL_COMPRESSED_LUMINANCE_ARB: + return GL_LUMINANCE; + case GL_COMPRESSED_RGB_ARB: + return GL_RGB; + case GL_COMPRESSED_RGBA_ARB: + return GL_RGBA; + + // S3TC + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: + return GL_RGB; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + return GL_RGBA; + + // RGTC + case GL_COMPRESSED_RED_RGTC1_EXT: + case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT: + return GL_RED; case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: - return true; + return GL_RG; + + // PVRTC + case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: + case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: + return GL_RGB; + case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: + case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: + return GL_RGBA; + + // ETC + case GL_COMPRESSED_R11_EAC: + case GL_COMPRESSED_SIGNED_R11_EAC: + return GL_RED; + case GL_COMPRESSED_RG11_EAC: + case GL_COMPRESSED_SIGNED_RG11_EAC: + return GL_RG; + case GL_ETC1_RGB8_OES: + case GL_COMPRESSED_RGB8_ETC2: + case GL_COMPRESSED_SRGB8_ETC2: + return GL_RGB; + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_RGBA8_ETC2_EAC: + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + return GL_RGBA; + + // ASTC + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: + return GL_RGBA; + + // Plug in some holes computePixelFormat has, you never know when these could come in handy + case GL_INTENSITY4: + case GL_INTENSITY8: + case GL_INTENSITY12: + case GL_INTENSITY16: + return GL_INTENSITY; + + case GL_LUMINANCE4: + case GL_LUMINANCE8: + case GL_LUMINANCE12: + case GL_LUMINANCE16: + return GL_LUMINANCE; + + case GL_LUMINANCE4_ALPHA4: + case GL_LUMINANCE6_ALPHA2: + case GL_LUMINANCE8_ALPHA8: + case GL_LUMINANCE12_ALPHA4: + case GL_LUMINANCE12_ALPHA12: + case GL_LUMINANCE16_ALPHA16: + return GL_LUMINANCE_ALPHA; } - return false; + return osg::Image::computePixelFormat(format); } } diff --git a/components/sceneutil/util.hpp b/components/sceneutil/util.hpp index 0f4b82bbe06..b76f46a6886 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -113,7 +113,9 @@ namespace SceneUtil osg::ref_ptr mOperationQueue; }; - bool isRedGreenPixelFormat(GLenum format); + // Compute the unsized format equivalent to the given pixel format + // Unlike osg::Image::computePixelFormat, this also covers compressed formats + GLenum computeUnsizedPixelFormat(GLenum format); } #endif diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 600e35a22ae..2676ea3168d 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -447,10 +447,14 @@ namespace Shader if (normalMap != nullptr && normalMap->getImage(0)) { // Special handling for red-green normal maps (e.g. BC5 or R8G8) - if (SceneUtil::isRedGreenPixelFormat(normalMap->getImage(0)->getPixelFormat())) + switch (SceneUtil::computeUnsizedPixelFormat(normalMap->getImage(0)->getPixelFormat())) { - mRequirements.back().mReconstructNormalZ = true; - mRequirements.back().mNormalHeight = false; + case GL_RG: + case GL_RG_INTEGER: + { + mRequirements.back().mReconstructNormalZ = true; + mRequirements.back().mNormalHeight = false; + } } } diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 10dbeb9838d..09d2680acd1 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -282,10 +282,17 @@ namespace Terrain // Special handling for red-green normal maps (e.g. BC5 or R8G8). const osg::Image* image = it->mNormalMap->getImage(0); - if (image && SceneUtil::isRedGreenPixelFormat(image->getPixelFormat())) + if (image) { - reconstructNormalZ = true; - parallax = false; + switch (SceneUtil::computeUnsizedPixelFormat(image->getPixelFormat())) + { + case GL_RG: + case GL_RG_INTEGER: + { + reconstructNormalZ = true; + parallax = false; + } + } } } From d8f19c6e7b8fac335cd42b599bb68cab82b75159 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 16 Apr 2024 03:29:47 +0300 Subject: [PATCH 5/5] Changelog (two-channel normal maps, #7932) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aeb030bda5a..8f2ee0eb63e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -225,6 +225,7 @@ Feature #7875: Disable MyGUI windows snapping Feature #7914: Do not allow to move GUI windows out of screen Feature #7923: Don't show non-existent higher ranks for factions with fewer than 9 ranks + Feature #7932: Support two-channel normal maps Task #5896: Do not use deprecated MyGUI properties Task #6085: Replace boost::filesystem with std::filesystem Task #6149: Dehardcode Lua API_REVISION