diff --git a/src/AcDream.App/Rendering/TerrainModernRenderer.cs b/src/AcDream.App/Rendering/TerrainModernRenderer.cs index efa54ea..e70a955 100644 --- a/src/AcDream.App/Rendering/TerrainModernRenderer.cs +++ b/src/AcDream.App/Rendering/TerrainModernRenderer.cs @@ -17,7 +17,14 @@ namespace AcDream.App.Rendering; /// public sealed unsafe class TerrainModernRenderer : IDisposable { - private const int VertsPerLandblock = LandblockMesh.VerticesPerLandblock; // 384 + // VertsPerLandblock MUST stay divisible by 6 — terrain_modern.vert uses + // `gl_VertexID % 6` to pick the cell-corner index (BL/BR/TR/TL), and + // because we bake `slot * VertsPerLandblock` into indices CPU-side and + // pass BaseVertex=0 to MultiDrawElementsIndirect, gl_VertexID becomes + // `slot * VertsPerLandblock + local_index`. The shader's modulo-6 only + // reduces to `local_index % 6` because 384 is a multiple of 6. Changing + // either constant without auditing the shader will silently mis-render. + private const int VertsPerLandblock = LandblockMesh.VerticesPerLandblock; // 384 (= 64 cells * 6 verts) private const int IndicesPerLandblock = VertsPerLandblock; private const int VertexSize = 40; // sizeof(TerrainVertex) private const int IndexSize = sizeof(uint); @@ -89,6 +96,10 @@ public sealed unsafe class TerrainModernRenderer : IDisposable throw new ArgumentException( $"Expected {VertsPerLandblock} vertices, got {meshData.Vertices.Length}", nameof(meshData)); + if (meshData.Indices.Length != IndicesPerLandblock) + throw new ArgumentException( + $"Expected {IndicesPerLandblock} indices, got {meshData.Indices.Length}", + nameof(meshData)); if (_idToSlot.ContainsKey(landblockId)) RemoveLandblock(landblockId);