diff --git a/src/AcDream.Core/Terrain/LandblockMesh.cs b/src/AcDream.Core/Terrain/LandblockMesh.cs index 860353e..13e51d5 100644 --- a/src/AcDream.Core/Terrain/LandblockMesh.cs +++ b/src/AcDream.Core/Terrain/LandblockMesh.cs @@ -24,6 +24,14 @@ public static class LandblockMesh if (heightTable.Length < 256) throw new ArgumentException("heightTable must have 256 entries", nameof(heightTable)); + // Precompute all 81 heights in (x,y) grid order so we can do cheap + // neighbor lookups when computing per-vertex normals by central differences. + // Heights are packed x-major (Height[x*9+y]) matching the fix in cc55c3f. + var heights = new float[VerticesPerSide, VerticesPerSide]; + for (int x = 0; x < VerticesPerSide; x++) + for (int y = 0; y < VerticesPerSide; y++) + heights[x, y] = heightTable[block.Height[x * VerticesPerSide + y]]; + var vertices = new Vertex[VerticesPerSide * VerticesPerSide]; for (int y = 0; y < VerticesPerSide; y++) { @@ -31,8 +39,7 @@ public static class LandblockMesh { int vi = y * VerticesPerSide + x; int hi = x * VerticesPerSide + y; - - float height = heightTable[block.Height[hi]]; + float height = heights[x, y]; // TerrainInfo is bit-packed: bits 0-1 Road, bits 2-6 Type (5-bit // TerrainTextureType enum), bits 11-15 Scenery. The atlas keys on @@ -42,9 +49,21 @@ public static class LandblockMesh if (!terrainTypeToLayer.TryGetValue(terrainType, out var layer)) layer = 0; + // Per-vertex normal from central differences on the heightmap. + // Surface is z=h(x,y); tangents Sx=(1,0,dh/dx), Sy=(0,1,dh/dy); + // normal = Sx x Sy = (-dh/dx, -dh/dy, 1), normalized. + // Edge vertices use forward/backward difference instead of central. + int xL = Math.Max(x - 1, 0); + int xR = Math.Min(x + 1, VerticesPerSide - 1); + int yD = Math.Max(y - 1, 0); + int yU = Math.Min(y + 1, VerticesPerSide - 1); + float dx = (heights[xR, y] - heights[xL, y]) / ((xR - xL) * CellSize); + float dy = (heights[x, yU] - heights[x, yD]) / ((yU - yD) * CellSize); + var normal = Vector3.Normalize(new Vector3(-dx, -dy, 1f)); + vertices[vi] = new Vertex( Position: new Vector3(x * CellSize, y * CellSize, height), - Normal: Vector3.UnitZ, + Normal: normal, TexCoord: new Vector2(x / TexCoordDivisor, y / TexCoordDivisor), TerrainLayer: layer); }