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.
70 lines
2.8 KiB
C#
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);
|
|
}
|
|
}
|