using System.Numerics; using DatReaderWriter.DBObjs; namespace AcDream.Core.Terrain; public sealed record LandblockMeshData(Vertex[] Vertices, uint[] Indices); public static class LandblockMesh { private const int VerticesPerSide = 9; private const int CellsPerSide = VerticesPerSide - 1; private const float CellSize = 24.0f; // Phase 2b: tile terrain textures ~4x per landblock instead of stretching // a single texture across the whole 192-unit patch. private const float TexCoordDivisor = CellsPerSide / 4.0f; public static LandblockMeshData Build( LandBlock block, float[] heightTable, IReadOnlyDictionary terrainTypeToLayer) { ArgumentNullException.ThrowIfNull(heightTable); ArgumentNullException.ThrowIfNull(terrainTypeToLayer); if (heightTable.Length < 256) throw new ArgumentException("heightTable must have 256 entries", nameof(heightTable)); var vertices = new Vertex[VerticesPerSide * VerticesPerSide]; for (int y = 0; y < VerticesPerSide; y++) { for (int x = 0; x < VerticesPerSide; x++) { int vi = y * VerticesPerSide + x; int hi = x * VerticesPerSide + y; float height = heightTable[block.Height[hi]]; // TerrainInfo is bit-packed: bits 0-1 Road, bits 2-6 Type (5-bit // TerrainTextureType enum), bits 11-15 Scenery. The atlas keys on // Type only, matching Region.TerrainInfo.LandSurfaces.TexMerge.TerrainDesc // which lists SurfaceTexture ids per TerrainTextureType. uint terrainType = (uint)block.Terrain[hi].Type; if (!terrainTypeToLayer.TryGetValue(terrainType, out var layer)) layer = 0; vertices[vi] = new Vertex( Position: new Vector3(x * CellSize, y * CellSize, height), Normal: Vector3.UnitZ, TexCoord: new Vector2(x / TexCoordDivisor, y / TexCoordDivisor), TerrainLayer: layer); } } var indices = new uint[CellsPerSide * CellsPerSide * 6]; int idx = 0; for (int y = 0; y < CellsPerSide; y++) { for (int x = 0; x < CellsPerSide; x++) { uint a = (uint)(y * VerticesPerSide + x); uint b = (uint)(y * VerticesPerSide + x + 1); uint c = (uint)((y + 1) * VerticesPerSide + x); uint d = (uint)((y + 1) * VerticesPerSide + x + 1); indices[idx++] = a; indices[idx++] = b; indices[idx++] = d; indices[idx++] = a; indices[idx++] = d; indices[idx++] = c; } } return new LandblockMeshData(vertices, indices); } }