diff --git a/SCANsat/SCAN_Map/SCANmap.cs b/SCANsat/SCAN_Map/SCANmap.cs index cbf75931..351b350d 100644 --- a/SCANsat/SCAN_Map/SCANmap.cs +++ b/SCANsat/SCAN_Map/SCANmap.cs @@ -894,6 +894,7 @@ internal Texture2D getPartialMap(bool apply = true) Texture2D readableScaledSpaceMap = SCANcontroller.controller.getVisualMapTexture(body); Texture2D readableScaledSpaceNormalMap = SCANcontroller.controller.getVisualMapNormalTexture(body); + SCANcontroller.NormalMapEncoding normalEncoding = SCANcontroller.controller.GetNormalMapEncoding(body); for (int i = 0; i < map.width; i++) { @@ -1206,7 +1207,8 @@ internal Texture2D getPartialMap(bool apply = true) double opacity = 0.8; - double lumOver = readableScaledSpaceNormalMap.GetPixelBilinear(fLon, fLat).b; + double lumOver = GetZFromNormalMap(readableScaledSpaceNormalMap, normalEncoding, + fLon, fLat); double lum = hslBase.L; if (colorMap) @@ -1477,6 +1479,29 @@ private float getResoureCache(double Lon, double Lat) return resourceCache[ilon, ilat]; } + private double GetZFromNormalMap(Texture2D normalMap, SCANcontroller.NormalMapEncoding encoding, float fLon, float fLat) + { + Color pixel = normalMap.GetPixelBilinear(fLon, fLat); + float nx, ny, nz = 0.0f; + switch (encoding) + { + case SCANcontroller.NormalMapEncoding.BC5: + // BC5: X in R, Y in G, B and A undefined post-blit + nx = pixel.r * 2f - 1f; + ny = pixel.g * 2f - 1f; + nz = Mathf.Sqrt(Mathf.Max(0f, 1f - nx * nx - ny * ny)); + return (nz + 1f) * 0.5f; // BC5 Reconstructed Z + case SCANcontroller.NormalMapEncoding.DXT5NM: + // DXT5_NM: X in A, Y in G, R and B padded + nx = pixel.a * 2f - 1f; + ny = pixel.g * 2f - 1f; + nz = Mathf.Sqrt(Mathf.Max(0f, 1f - nx * nx - ny * ny)); + return (nz + 1f) * 0.5f; // BC5 Reconstructed Z + default: + return pixel.b; // .b contains Z in RGA normal + } + } + #endregion } diff --git a/SCANsat/SCANcontroller.cs b/SCANsat/SCANcontroller.cs index 0f8f4243..1059911a 100644 --- a/SCANsat/SCANcontroller.cs +++ b/SCANsat/SCANcontroller.cs @@ -158,6 +158,17 @@ public static SCANcontroller controller private Dictionary readableScaledSpaceNormalMaps = new Dictionary(); private CelestialBody bigMapBodyScaledSpace; private CelestialBody zoomMapBodyScaledSpace; + + /* Normal Map Encoding Data */ + public enum NormalMapEncoding + { + RGBA, // RGB Normal: .b is Z + DXT5NM, // X in A, Y in G, Z Reconstructed + BC5 // X in R, Y in G, Z Reconstructed + } + + private Dictionary normalMapEncodings = + new Dictionary(); private SCAN_UI_MainMap _mainMap; private SCAN_UI_Instruments _instruments; @@ -1599,6 +1610,12 @@ internal void LoadVisualMapTexture(CelestialBody b, mapSource s) { } + + public NormalMapEncoding GetNormalMapEncoding(CelestialBody b) + { + if (b == null) return NormalMapEncoding.RGBA; + return normalMapEncodings.TryGetValue(b, out var enc) ? enc : NormalMapEncoding.RGBA; + } void GetVisualMapTexturesForBody(CelestialBody b, out Material material, out bool useMaterialForColorMap, out string colorMapTextureName, out string normalMapTextureName) { @@ -1633,7 +1650,7 @@ void GetVisualMapTexturesForBody(CelestialBody b, out Material material, out boo SCANparallaxContinued.LoadParallax(b, ref material); useMaterialForColorMap = false; colorMapTextureName = "_ColorMap"; - normalMapTextureName = null; // for whatever reason, the logic in ScANmap that uses the normal map doesn't work with parallax's normal maps + normalMapTextureName = "_BumpMap"; return; } else if (material.HasProperty("_MainTex")) @@ -1655,7 +1672,7 @@ void GetVisualMapTexturesForBody(CelestialBody b, out Material material, out boo } } - void CacheScaledSpaceTexture(Dictionary cache, CelestialBody b, Material material, string textureName, bool useMaterial) + void CacheScaledSpaceTexture(Dictionary cache, CelestialBody b, Material material, string textureName, bool useMaterial, bool isNormalMap) { if (cache.GetValueOrDefault(b) == null && textureName != null) { @@ -1663,12 +1680,30 @@ void CacheScaledSpaceTexture(Dictionary cache, Celesti if (sourceTexture == null) { Log.Error($"GetTexture returned a null texture for body {b.name}, material {material.name} and texture name {textureName}"); + return; } - else + + if (isNormalMap) { - var colorMap = sourceTexture.isReadable ? sourceTexture : readableTexture(sourceTexture, useMaterial ? material : null); - cache.Add(b, colorMap); + NormalMapEncoding encoding; + switch (sourceTexture.format) + { + case TextureFormat.BC5: + encoding = NormalMapEncoding.BC5; + break; + case TextureFormat.DXT5: + encoding = NormalMapEncoding.DXT5NM; + break; + default: + encoding = NormalMapEncoding.RGBA; + break; + } + + normalMapEncodings[b] = encoding; } + + var colorMap = sourceTexture.isReadable ? sourceTexture : readableTexture(sourceTexture, useMaterial ? material : null, isNormalMap); + cache.Add(b, colorMap); } } @@ -1692,8 +1727,8 @@ internal void LoadVisualMapTexture_Renamed(CelestialBody b, mapSource s) } else { - CacheScaledSpaceTexture(readableScaledSpaceMaps, b, material, colorMapTextureName, useMaterialForColorMap); - CacheScaledSpaceTexture(readableScaledSpaceNormalMaps, b, material, normalMapTextureName, false); + CacheScaledSpaceTexture(readableScaledSpaceMaps, b, material, colorMapTextureName, useMaterialForColorMap, false); + CacheScaledSpaceTexture(readableScaledSpaceNormalMaps, b, material, normalMapTextureName, false, true); } switch (s) @@ -1753,10 +1788,11 @@ internal void UnloadVisualMapTexture(CelestialBody b, mapSource s) GameObject.Destroy(readableScaledSpaceNormalMaps[b]); readableScaledSpaceNormalMaps[b] = null; readableScaledSpaceNormalMaps.Remove(b); + normalMapEncodings.Remove(b); } } - private Texture2D readableTexture(Texture tex, Material mat) + private Texture2D readableTexture(Texture tex, Material mat, bool isNormalMap = false) { if (tex == null) { @@ -1765,7 +1801,10 @@ private Texture2D readableTexture(Texture tex, Material mat) Texture2D readable = new Texture2D(tex.width, tex.height); - var rt = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB, 1); + var readWrite = isNormalMap + ? RenderTextureReadWrite.Linear + : RenderTextureReadWrite.sRGB; + var rt = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.ARGB32, readWrite, 1); if (mat != null) {