From faba9be635c52fc4b4ef6830c09c0ff84f49ab1e Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 30 Aug 2026 10:40:22 +0200 Subject: [PATCH] feat(render) Campaign FW1: pin the camera basis; outdoor adjudication state The moving-fixture sweep pinned the pose decode (quat storage w,x,y,z; facing = rotated +X; the consistent triple right=-rot(Y), fwd=rot(X), up=rot(Z) - mean 20.8 deg vs motion, alternatives >=52). With it the street-outdoor BLD roster and order match retail EXACTLY except one ring-1 boundary pair (aab50002 extra / a9b3003c missing). Look-in punches fire at the wrong buildings under BOTH GfxObj plane-winding conventions (retail punched only 001a and 0022 into the a9b4016x cells) - the next adjudication targets the PortalRef.PortalIndex join and the BuildingPortal side-flag decode via a per-building dump. The driving diff test carries the state in its Skip note; the flip toggle stays for the next arm. Co-Authored-By: Claude Fable 5 --- .../Rendering/Walk/WalkTraceConformanceTests.cs | 11 ++++++++--- .../Rendering/Walk/WalkTraceReplay.cs | 9 +++++++-- .../Rendering/Walk/WalkWorldDatAdapter.cs | 11 ++++++++++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs index fde4de27..cd372fbd 100644 --- a/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs +++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs @@ -32,9 +32,14 @@ public sealed class WalkTraceConformanceTests return new DatCollection(datDir!, DatAccessType.Read); } - [Fact(Skip = "FW1 adjudication in progress: the walk over-culls six traced " - + "buildings (incl. block a9b3) and the look-in floods differ — see the " - + "campaign memory. Re-enable as the outdoor gate once adjudicated.")] + [Fact(Skip = "FW1 outdoor adjudication (2026-08-30 state): with the pinned " + + "camera basis the BLD roster+order match retail EXACTLY except one " + + "ring-1 boundary pair (aab50002 extra / a9b3003c missing). The " + + "look-in punches fire at the wrong buildings under BOTH GfxObj " + + "plane-winding conventions (retail: only 001a and 0022, cells " + + "a9b4016x) — adjudicate the PortalRef.PortalIndex join and the " + + "BuildingPortal side-flag decode with a per-building dump next. " + + "Re-enable as the outdoor gate when adjudicated.")] public void Street_outdoor_first_frame_diff() { // The first landscape-involving conformance case: diff-first (the diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceReplay.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceReplay.cs index 7cf72809..63396e8b 100644 --- a/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceReplay.cs +++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceReplay.cs @@ -53,9 +53,14 @@ public sealed class WalkTraceReplayContext : IWalkFrameContext, IRetailFrameWalk _cells = cells; WorldViewpoint = pose.Origin; var rotation = new Quaternion(pose.Q1, pose.Q2, pose.Q3, pose.Q0); - Vector3 forward = Vector3.Transform(Vector3.UnitY, rotation); + // Convention pinned against the moving fixtures (2026-08-30 sweep: + // storage w,x,y,z; the facing direction is the rotated +X axis — + // mean 20.8° vs motion headings, every alternative ≥52°). The + // consistent right-handed triple is then right = −rot(+Y), + // forward = rot(+X), up = rot(+Z). + Vector3 forward = Vector3.Transform(Vector3.UnitX, rotation); Vector3 up = Vector3.Transform(Vector3.UnitZ, rotation); - Vector3 right = Vector3.Transform(Vector3.UnitX, rotation); + Vector3 right = -Vector3.Transform(Vector3.UnitY, rotation); // The exact retail frustum: tan(halfFovY) = ty/vdst, aspect = tx/ty. float fovY = 2f * MathF.Atan(RetailTy / RetailVdst); diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs index a8393cff..dc7f0fcc 100644 --- a/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs +++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs @@ -211,6 +211,12 @@ public static class WalkWorldDatAdapter : null); } + /// Adjudication toggle (2026-08-30): the GfxObj portal-poly + /// plane winding vs the cell convention is under test — the street + /// fixture shows retail punching far fewer look-ins than the first-3 + /// cross convention admits. + public static bool FlipGfxPolygonPlanes; + private static WalkPolygon? BuildGfxPolygon(GfxObj gfxObj, ushort polygonId) { if (!gfxObj.Polygons.TryGetValue(polygonId, out Polygon? poly) @@ -218,11 +224,14 @@ public static class WalkWorldDatAdapter { return null; } - return BuildPolygonFromVertices( + WalkPolygon? built = BuildPolygonFromVertices( poly.VertexIds, id => gfxObj.VertexArray.Vertices.TryGetValue((ushort)id, out SWVertex? v) ? new Vector3(v.Origin.X, v.Origin.Y, v.Origin.Z) : null); + if (built is not null && FlipGfxPolygonPlanes) + built.Plane = new WalkPlane(-built.Plane.Normal, -built.Plane.D); + return built; } private static WalkPolygon? BuildPolygonFromVertices(