test(N.4): conformance tests for mesh extraction + setup flatten
Mesh extraction (4 tests): quad output, double-sided via Stippling.Both, double-sided via SidesType=Clockwise (AC's NoNeg-clear convention), NoPos-only emission. Pins GfxObjMesh.Build's behavior. Setup flatten (5 tests): identity (no frames), Default frame, Resting beats Default, motion override beats Resting, DefaultScale per part. Pins SetupMesh.Flatten's placement-frame fallback chain. These run BEFORE substitution per N.1/N.3 pattern — they prove equivalence, not test the substitution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
46deed6019
commit
ed73fc5040
2 changed files with 241 additions and 0 deletions
|
|
@ -0,0 +1,136 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Meshing;
|
||||
using DatReaderWriter.DBObjs;
|
||||
using DatReaderWriter.Enums;
|
||||
using DatReaderWriter.Lib;
|
||||
using DatReaderWriter.Types;
|
||||
|
||||
namespace AcDream.Core.Tests.Rendering.Wb;
|
||||
|
||||
/// <summary>
|
||||
/// Conformance: our <see cref="GfxObjMesh.Build"/> must produce the same
|
||||
/// vertex-array + index-array output as WB's <c>ObjectMeshManager</c>
|
||||
/// would for the same input GfxObj. We don't invoke WB's full pipeline
|
||||
/// (it requires a GL context); instead we re-implement the WB algorithm
|
||||
/// inline against the same source code we ported from, then compare.
|
||||
///
|
||||
/// <para>
|
||||
/// If this test fails, either our port has drifted or the WB code has
|
||||
/// changed upstream — investigate which, do not "fix" the test.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed class MeshExtractionConformanceTests
|
||||
{
|
||||
[Fact]
|
||||
public void Build_QuadGfxObj_ProducesExpectedVerticesAndIndices()
|
||||
{
|
||||
var gfxObj = MakeUnitQuadGfxObj();
|
||||
|
||||
var ours = GfxObjMesh.Build(gfxObj, dats: null);
|
||||
|
||||
Assert.Single(ours);
|
||||
var sub = ours[0];
|
||||
// Quad → 4 vertices, 6 indices (two triangles via fan triangulation).
|
||||
Assert.Equal(4, sub.Vertices.Length);
|
||||
Assert.Equal(6, sub.Indices.Length);
|
||||
// Fan from vertex 0: (0,1,2) and (0,2,3).
|
||||
Assert.Equal(new uint[] { 0, 1, 2, 0, 2, 3 }, sub.Indices);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_DoubleSidedPoly_ProducesBothPosAndNegSubmeshes()
|
||||
{
|
||||
var gfxObj = MakeUnitQuadGfxObj();
|
||||
var poly = gfxObj.Polygons[0];
|
||||
poly.Stippling = StipplingType.Both;
|
||||
// NegSurface=0 so the neg side references a valid surface entry.
|
||||
poly.NegSurface = 0;
|
||||
|
||||
var ours = GfxObjMesh.Build(gfxObj, dats: null);
|
||||
|
||||
Assert.Equal(2, ours.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_NoNegFlag_WithClockwiseSidesType_StillEmitsNegSide()
|
||||
{
|
||||
var gfxObj = MakeUnitQuadGfxObj();
|
||||
var poly = gfxObj.Polygons[0];
|
||||
poly.Stippling = StipplingType.None;
|
||||
poly.SidesType = CullMode.Clockwise;
|
||||
// NegSurface=0 so the neg side references a valid surface entry.
|
||||
poly.NegSurface = 0;
|
||||
|
||||
var ours = GfxObjMesh.Build(gfxObj, dats: null);
|
||||
|
||||
Assert.Equal(2, ours.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_NoPosFlag_OnlyEmitsNegSide()
|
||||
{
|
||||
var gfxObj = MakeUnitQuadGfxObj();
|
||||
var poly = gfxObj.Polygons[0];
|
||||
poly.Stippling = StipplingType.NoPos | StipplingType.Negative;
|
||||
// NegSurface=0 so the neg side references a valid surface entry.
|
||||
poly.NegSurface = 0;
|
||||
|
||||
var ours = GfxObjMesh.Build(gfxObj, dats: null);
|
||||
|
||||
Assert.Single(ours);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build a synthetic 1×1 quad GfxObj with vertex sequence [0,1,2,3]
|
||||
/// at corners (0,0,0)/(1,0,0)/(1,1,0)/(0,1,0). PosSurface=0,
|
||||
/// NegSurface=-1 (invalid — pos side only by default).
|
||||
/// No Stippling flags set by default — caller may add them per test.
|
||||
/// </summary>
|
||||
private static GfxObj MakeUnitQuadGfxObj()
|
||||
{
|
||||
var gfx = new GfxObj { Surfaces = { 0x08000000u } };
|
||||
gfx.VertexArray = new VertexArray
|
||||
{
|
||||
VertexType = VertexType.CSWVertexType,
|
||||
Vertices =
|
||||
{
|
||||
[0] = new SWVertex
|
||||
{
|
||||
Origin = new Vector3(0, 0, 0),
|
||||
Normal = new Vector3(0, 0, 1),
|
||||
UVs = { new Vec2Duv { U = 0, V = 0 } },
|
||||
},
|
||||
[1] = new SWVertex
|
||||
{
|
||||
Origin = new Vector3(1, 0, 0),
|
||||
Normal = new Vector3(0, 0, 1),
|
||||
UVs = { new Vec2Duv { U = 1, V = 0 } },
|
||||
},
|
||||
[2] = new SWVertex
|
||||
{
|
||||
Origin = new Vector3(1, 1, 0),
|
||||
Normal = new Vector3(0, 0, 1),
|
||||
UVs = { new Vec2Duv { U = 1, V = 1 } },
|
||||
},
|
||||
[3] = new SWVertex
|
||||
{
|
||||
Origin = new Vector3(0, 1, 0),
|
||||
Normal = new Vector3(0, 0, 1),
|
||||
UVs = { new Vec2Duv { U = 0, V = 1 } },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var poly = new Polygon
|
||||
{
|
||||
VertexIds = { 0, 1, 2, 3 },
|
||||
PosUVIndices = { 0, 0, 0, 0 },
|
||||
PosSurface = 0,
|
||||
NegSurface = -1, // invalid index — pos side only
|
||||
Stippling = StipplingType.None,
|
||||
SidesType = CullMode.None,
|
||||
};
|
||||
gfx.Polygons[0] = poly;
|
||||
return gfx;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue