acdream/src/AcDream.Core/Terrain/LandblockMesh.cs
Erik 78ce099440 fix(core): LandblockMesh keys atlas lookup on TerrainInfo.Type
Task 1's subagent used the raw ushort as the map key because the test
used raw ushort 7 as the value. But the atlas map is built from
Region.TerrainInfo.LandSurfaces.TexMerge.TerrainDesc which keys on
TerrainTextureType enum values, extracted from bits 2-6 of the
TerrainInfo ushort per DatReaderWriter's Types/TerrainInfo.cs.

Reverts to using block.Terrain[hi].Type so the Task 2 TerrainAtlas can
actually find matching keys against real dat terrain. The test is
updated to encode Type=7 correctly as (7 << 2) in the raw ushort.
2026-04-10 20:18:09 +02:00

70 lines
2.8 KiB
C#

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<uint, uint> 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);
}
}