From 3e6de8485b3b33e31a5d84102196d387896db661 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 23 Feb 2026 06:46:31 -0800 Subject: [PATCH] Fix ground clutter height sampling and terrain shader GPU crash - Fix HeightMap::getHeight() to use interleaved 17-wide row layout matching MCVT storage (was using wrong 9-wide contiguous indexing) - Guard terrain bump mapping normalize against zero-length vectors to prevent NaN propagation and GPU faults --- assets/shaders/terrain.frag.glsl | 4 +++- assets/shaders/terrain.frag.spv | Bin 11752 -> 12140 bytes src/pipeline/adt_loader.cpp | 26 +++++++------------------- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/assets/shaders/terrain.frag.glsl b/assets/shaders/terrain.frag.glsl index 36203be2..ed6d8273 100644 --- a/assets/shaders/terrain.frag.glsl +++ b/assets/shaders/terrain.frag.glsl @@ -96,7 +96,9 @@ void main() { // Bump strength controls how much texture detail affects lighting const float bumpStrength = 9.0; vec3 perturbation = (dLdx * cross(norm, dpdy) + dLdy * cross(dpdx, norm)) * bumpStrength; - norm = normalize(norm - perturbation); + vec3 candidate = norm - perturbation; + float len2 = dot(candidate, candidate); + norm = (len2 > 1e-8) ? candidate * inversesqrt(len2) : norm; } vec3 lightDir2 = normalize(-lightDir.xyz); diff --git a/assets/shaders/terrain.frag.spv b/assets/shaders/terrain.frag.spv index 5318d52de017eb865f15ccb648ecd4b5b9b69519..b633e01bf700c854b0e0d7aae77ab40446020f8f 100644 GIT binary patch literal 12140 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 Ax&QzG diff --git a/src/pipeline/adt_loader.cpp b/src/pipeline/adt_loader.cpp index 755d0596..af3da5ce 100644 --- a/src/pipeline/adt_loader.cpp +++ b/src/pipeline/adt_loader.cpp @@ -13,25 +13,13 @@ float HeightMap::getHeight(int x, int y) const { return 0.0f; } - // WoW uses 9x9 outer + 8x8 inner vertex layout - // Outer vertices: 0-80 (9x9 grid) - // Inner vertices: 81-144 (8x8 grid between outer vertices) - - // Calculate index based on vertex type - int index; - if (x < 9 && y < 9) { - // Outer vertex - index = y * 9 + x; - } else { - // Inner vertex (between outer vertices) - int innerX = x - 1; - int innerY = y - 1; - if (innerX >= 0 && innerX < 8 && innerY >= 0 && innerY < 8) { - index = 81 + innerY * 8 + innerX; - } else { - return 0.0f; - } - } + // MCVT heights are stored in interleaved 9x17 row-major layout: + // Row 0: 9 outer (indices 0-8), then 8 inner (indices 9-16) + // Row 1: 9 outer (indices 17-25), then 8 inner (indices 26-33) + // ... + // Outer vertex (x, y) is at index: y * 17 + x + int index = y * 17 + x; + if (index < 0 || index >= 145) return 0.0f; return heights[index]; }