Ports the decompiled AC client ground-truth oracle and exhaustive conformance test suite from WorldBuilder-ACME-Edition into acdream's test project. ClientReference.cs: faithful C# port of CLandBlockStruct.cpp with IsSWtoNECut, GetPalCode, GetVertexHeight, GetVertexPosition. ClientConformanceTests.cs verifies acdream's implementations match: - SplitDirection: 9 spot-checks + 25,600-cell full sweep (0 mismatches) - PalCode: 5 spot-checks + 256 exhaustive roads + 1M exhaustive types - Height sampling: flat terrain exact match, vertex corners match, interpolated points in-range - TerrainSurface.SampleZ agrees with TerrainBlending split direction - Constants match (CellSize=24, CellsPerBlock=8, BlockLength=192) 27 new tests. 310 total (201 core + 109 net), all green. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
75 lines
2.6 KiB
C#
75 lines
2.6 KiB
C#
using System.Numerics;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace AcDream.Core.Tests.Terrain;
|
|
|
|
/// <summary>
|
|
/// Faithful C# port of the original AC client terrain algorithms,
|
|
/// translated from decompiled C++ in acclient-source-split/CLandBlockStruct.cpp.
|
|
/// Serves as the ground-truth oracle for conformance testing.
|
|
///
|
|
/// Ported from WorldBuilder-ACME-Edition/WorldBuilder.Tests/ClientReference.cs.
|
|
/// All formulas use signed int arithmetic with unchecked wrapping to match x86 behavior.
|
|
/// </summary>
|
|
public static class ClientReference
|
|
{
|
|
/// <summary>
|
|
/// Port of CLandBlockStruct::ConstructPolygons at offset 0x00531D10.
|
|
/// Returns true when the triangle split goes from SW to NE (SWtoNEcut=1).
|
|
/// </summary>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static bool IsSWtoNECut(int globalCellX, int globalCellY)
|
|
{
|
|
unchecked
|
|
{
|
|
int v7 = globalCellY * (214614067 * globalCellX + 1813693831)
|
|
- 1109124029 * globalCellX - 1369149221;
|
|
return (double)(uint)v7 * 2.3283064e-10 >= 0.5;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Port of pal_code[0] from CLandBlockStruct::GetCellRotation at offset 0x00532170.
|
|
/// Corner order: 0=(ix,iy), 1=(ix+1,iy), 2=(ix+1,iy+1), 3=(ix,iy+1)
|
|
/// </summary>
|
|
public static uint GetPalCode(
|
|
int r0, int t0,
|
|
int r1, int t1,
|
|
int r2, int t2,
|
|
int r3, int t3,
|
|
int texSize = 1)
|
|
{
|
|
unchecked
|
|
{
|
|
return (uint)(t3
|
|
+ (texSize << 28)
|
|
+ 32 * (t2 + 32 * (t1 + 32 * (t0 + 32 * (r3 + 4 * (r2 + 4 * (r1 + 4 * r0)))))));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Port of CLandBlockStruct::ConstructVertices at offset 0x005328D0.
|
|
/// Height = LandDefs::Land_Height_Table[height_byte]
|
|
/// </summary>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float GetVertexHeight(float[] landHeightTable, byte heightByte)
|
|
{
|
|
return landHeightTable[heightByte];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Port of CLandBlockStruct::ConstructVertices vertex position.
|
|
/// Each vertex is at (ix * polySize, iy * polySize, height).
|
|
/// </summary>
|
|
public static Vector3 GetVertexPosition(float[] landHeightTable, byte heightByte, int ix, int iy, float polySize = 24f)
|
|
{
|
|
return new Vector3(ix * polySize, iy * polySize, landHeightTable[heightByte]);
|
|
}
|
|
|
|
public const int MapWidth = 255;
|
|
public const int MapHeight = 255;
|
|
public const float CellSize = 24.0f;
|
|
public const int CellsPerBlock = 8;
|
|
public const float RoadWidth = 5.0f;
|
|
public const float BlockLength = 192.0f;
|
|
}
|