fix: complete retail parity stability pass
All checks were successful
CI / linux-portable (push) Successful in 3m41s
CI / windows-gate (push) Successful in 6m49s
CI / release (push) Successful in 3m22s

This commit is contained in:
Erik 2026-08-28 20:01:39 +02:00
parent d3df4cb20a
commit f7aa8e0eb7
131 changed files with 7765 additions and 1190 deletions

View file

@ -11,6 +11,7 @@ using DatReaderWriter.Enums;
using DatReaderWriter.Lib.IO;
using DatReaderWriter.Types;
using Microsoft.Extensions.Logging.Abstractions;
using RetailCullMode = DatReaderWriter.Enums.CullMode;
namespace AcDream.Content.Tests;
@ -180,6 +181,61 @@ public sealed class MeshExtractorSolidFaceExtractionTests
Assert.Equal(4 * 4 * 4, Assert.Single(group.Value).TextureData.Length);
}
[Fact]
public void PrepareCellStructMeshData_LandblockSide_PreservesRetailTriangleFanWinding()
{
var dats = new FakeMeshExtractorDats();
dats.Register(SolidSurfaceId, new Surface
{
Type = SurfaceType.Base1Solid,
ColorValue = new ColorARGB { Alpha = 255, Red = 64, Green = 96, Blue = 128 },
});
var extractor = new MeshExtractor(dats, NullLogger.Instance, sideStagedSink: null);
ObjectMeshData mesh = Assert.IsType<ObjectMeshData>(
extractor.PrepareCellStructMeshData(
id: 1,
BuildQuadCellStruct(RetailCullMode.Landblock),
surfaceOverrides: [1],
Matrix4x4.Identity,
CancellationToken.None));
TextureBatchData batch = Assert.Single(Assert.Single(mesh.TextureBatches).Value);
Assert.Equal(RetailCullMode.Landblock, batch.CullMode);
Assert.Equal([0, 1, 2, 0, 2, 3], batch.Indices);
Assert.Equal(4, mesh.Vertices.Length);
Assert.All(mesh.Vertices, vertex => Assert.Equal(Vector3.UnitZ, vertex.Normal));
}
[Fact]
public void PrepareCellStructMeshData_NoneSide_ExpandsReversedFaceExactlyLikeRetailConstructMesh()
{
var dats = new FakeMeshExtractorDats();
dats.Register(SolidSurfaceId, new Surface
{
Type = SurfaceType.Base1Solid,
ColorValue = new ColorARGB { Alpha = 255, Red = 64, Green = 96, Blue = 128 },
});
var extractor = new MeshExtractor(dats, NullLogger.Instance, sideStagedSink: null);
ObjectMeshData mesh = Assert.IsType<ObjectMeshData>(
extractor.PrepareCellStructMeshData(
id: 1,
BuildQuadCellStruct(RetailCullMode.None),
surfaceOverrides: [1],
Matrix4x4.Identity,
CancellationToken.None));
TextureBatchData batch = Assert.Single(Assert.Single(mesh.TextureBatches).Value);
Assert.Equal(RetailCullMode.None, batch.CullMode);
Assert.Equal(
[0, 1, 2, 0, 2, 3, 6, 5, 4, 7, 6, 4],
batch.Indices);
Assert.Equal(8, mesh.Vertices.Length);
Assert.All(mesh.Vertices.Take(4), vertex => Assert.Equal(Vector3.UnitZ, vertex.Normal));
Assert.All(mesh.Vertices.Skip(4), vertex => Assert.Equal(-Vector3.UnitZ, vertex.Normal));
}
private static void RegisterTexturedQuad(
FakeMeshExtractorDats dats,
SurfaceType surfaceType,
@ -207,6 +263,30 @@ public sealed class MeshExtractorSolidFaceExtractionTests
});
}
private static CellStruct BuildQuadCellStruct(RetailCullMode sidesType) => new()
{
VertexArray = new VertexArray
{
Vertices = new Dictionary<ushort, SWVertex>
{
[0] = new() { Origin = new Vector3(0, 0, 0), Normal = Vector3.UnitZ },
[1] = new() { Origin = new Vector3(1, 0, 0), Normal = Vector3.UnitZ },
[2] = new() { Origin = new Vector3(1, 1, 0), Normal = Vector3.UnitZ },
[3] = new() { Origin = new Vector3(0, 1, 0), Normal = Vector3.UnitZ },
},
},
Polygons = new Dictionary<ushort, Polygon>
{
[0] = new()
{
SidesType = sidesType,
PosSurface = 0,
NegSurface = -1,
VertexIds = [0, 1, 2, 3],
},
},
};
/// <summary>One quad (4 verts), single polygon, PosSurface referencing <paramref name="surfaceId"/>.</summary>
private static GfxObj BuildQuadGfxObj(uint surfaceId, bool noPos)
{