From 66f9e0d4598c66e17dbe161e4edf6a2a799a783b Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 30 Aug 2026 11:07:29 +0200 Subject: [PATCH] feat(render) Campaign FW1: SIX OF TEN FIXTURES FULLY CONFORMANT Three final pins complete the still-fixture set: (1) the CELL portal side decode is the INVERSE of the 0x2 bit (uniform with the building convention; the doorway-still flood proved it - ov=2 n=3 exact, and foundry-deep stays green); (2) interior frames key the landscape order off the OUTSIDE-projected landcell (get_outside_cell_id - derived from the camera origin); (3) the outdoor pview has draw_landscape=FALSE so look-in floods discard exit portals - the ov=0 pattern of every traced look-in. CONFORMANT: foundry-deep (every frame), doorway-still, street-outdoor, terrace-center, terrace-edge (the #456 acceptance pose), cathedral-arrival - full frames identical to retail. The moving four diverge only at punch-edge frames (walkabout F9, foundry-entry F67) - pose-timing sensitivity parked in the driver Skip note. Co-Authored-By: Claude Fable 5 --- .../Rendering/Walk/RetailFrameWalk.cs | 6 +- .../Rendering/Walk/WalkLandscapeDatBuilder.cs | 29 +++- .../Walk/WalkLookInGateSweepTests.cs | 2 +- .../Rendering/Walk/WalkPortalGateDumpTests.cs | 2 +- .../Walk/WalkTraceConformanceTests.cs | 128 ++++++++++++++++-- .../Rendering/Walk/WalkWorldDatAdapter.cs | 5 +- 6 files changed, 158 insertions(+), 14 deletions(-) diff --git a/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs b/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs index 8ee37b49..57a4dabe 100644 --- a/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs +++ b/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs @@ -44,7 +44,11 @@ public interface IRetailFrameWalkContext : IWalkBuildingFrameContext public sealed class RetailFrameWalk { private readonly WalkPView _interiorPView = new(); - private readonly WalkPView _outdoorPView = new(); + // The outdoor pview never draws the landscape through its look-in + // floods (the landscape is already drawn when buildings punch), so its + // draw_landscape is FALSE and look-in DCs always read ov=0 — exactly + // the pv=…/ov=0 pattern of every look-in in the oracle traces. + private readonly WalkPView _outdoorPView = new() { DrawLandscape = false }; private readonly WalkPortalView _defaultView = new(); public WalkPView InteriorPView => _interiorPView; diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkLandscapeDatBuilder.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkLandscapeDatBuilder.cs index f858150c..57367bce 100644 --- a/tests/AcDream.App.Tests/Rendering/Walk/WalkLandscapeDatBuilder.cs +++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkLandscapeDatBuilder.cs @@ -33,7 +33,25 @@ public static class WalkLandscapeDatBuilder private static int SideCellCountForRing(int ring) => ring <= 1 ? 8 : ring == 2 ? 4 : ring <= 4 ? 2 : 1; - public static BuiltWorld Build(DatCollection dats, uint cameraCellId) + /// Per-frame viewer placement for moving replays (the camera + /// stays within the anchor block in every moving fixture). + public static void SetViewer(WalkLandscape landscape, uint cameraCellId, Vector3 cameraOrigin) + { + uint low = cameraCellId & 0xFFFFu; + if (low >= 1 && low <= 0x40) + { + int cellIndex = (int)low - 1; + landscape.ViewerCellX = cellIndex / 8; + landscape.ViewerCellY = cellIndex % 8; + } + else + { + landscape.ViewerCellX = Math.Clamp((int)MathF.Floor(cameraOrigin.X / 24f), 0, 7); + landscape.ViewerCellY = Math.Clamp((int)MathF.Floor(cameraOrigin.Y / 24f), 0, 7); + } + } + + public static BuiltWorld Build(DatCollection dats, uint cameraCellId, Vector3 cameraOrigin = default) { Region region = (Region)dats.Get(0x13000000u)!; float[] heightTable = region.LandDefs.LandHeightTable; @@ -57,6 +75,15 @@ public static class WalkLandscapeDatBuilder landscape.ViewerCellX = cellIndex / 8; landscape.ViewerCellY = cellIndex % 8; } + else + { + // Interior camera: retail keys the landscape order off the + // OUTSIDE-projected landcell (Position::get_outside_cell_id via + // SmartBox::RenderNormalMode seen_outside arm) - derive it from + // the camera origin (block-local, 24 m cells). + landscape.ViewerCellX = Math.Clamp((int)MathF.Floor(cameraOrigin.X / 24f), 0, 7); + landscape.ViewerCellY = Math.Clamp((int)MathF.Floor(cameraOrigin.Y / 24f), 0, 7); + } var cells = new Dictionary(); var buildings = new Dictionary(); diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkLookInGateSweepTests.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkLookInGateSweepTests.cs index f8cbf2b6..c798d628 100644 --- a/tests/AcDream.App.Tests/Rendering/Walk/WalkLookInGateSweepTests.cs +++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkLookInGateSweepTests.cs @@ -66,7 +66,7 @@ public sealed class WalkLookInGateSweepTests try { WalkLandscapeDatBuilder.BuiltWorld world = - WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId); + WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin); var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells) { Buildings = world.Buildings, diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkPortalGateDumpTests.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkPortalGateDumpTests.cs index 2049bfb0..06127420 100644 --- a/tests/AcDream.App.Tests/Rendering/Walk/WalkPortalGateDumpTests.cs +++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkPortalGateDumpTests.cs @@ -31,7 +31,7 @@ public sealed class WalkPortalGateDumpTests Assert.NotNull(frame.Pose); using var dats = new DatCollection(datDir!, DatAccessType.Read); WalkLandscapeDatBuilder.BuiltWorld world = - WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId); + WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin); var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells) { Buildings = world.Buildings, diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs index cd372fbd..ae4032c0 100644 --- a/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs +++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs @@ -32,14 +32,7 @@ public sealed class WalkTraceConformanceTests return new DatCollection(datDir!, DatAccessType.Read); } - [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.")] + [Fact] public void Street_outdoor_first_frame_diff() { // The first landscape-involving conformance case: diff-first (the @@ -51,7 +44,7 @@ public sealed class WalkTraceConformanceTests WalkOracleFrame frame = frames[1]; // frame 2: pose settled (marker timing) Assert.NotNull(frame.Pose); WalkLandscapeDatBuilder.BuiltWorld world = - WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId); + WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin); var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells) { Buildings = world.Buildings, @@ -68,6 +61,123 @@ public sealed class WalkTraceConformanceTests $"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}"); } + [Fact] + public void Doorway_still_first_frame_diff() + { + // Interior flood adjudication: DI + DC(ov=2, n=3) + the landscape + // through two exit views — tests the flood depth in isolation from + // the building look-in machinery. + IReadOnlyList frames = + WalkOracleTrace.Load("posed/holtburg-doorway-still"); + Assert.NotEmpty(frames); + using DatCollection dats = OpenDats(); + WalkOracleFrame frame = frames[1]; + Assert.NotNull(frame.Pose); + WalkLandscapeDatBuilder.BuiltWorld world = + WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin); + var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells) + { + Buildings = world.Buildings, + }; + WalkCell camera = Assert.Contains(frame.Pose.CellId, world.Cells); + var walk = new RetailFrameWalk(); + var recorder = new Recorder(); + + walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder); + + string expected = WalkTraceReplayContext.Signature(frame); + string actual = WalkTraceReplayContext.Signature(recorder.Events); + Assert.True( + expected == actual, + $"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}"); + } + + [Theory] + [InlineData("posed/terrace-center")] + [InlineData("posed/terrace-edge")] + [InlineData("posed/cathedral-arrival")] + public void Still_fixture_first_frame_reproduces_exactly(string fixture) + { + IReadOnlyList frames = WalkOracleTrace.Load(fixture); + Assert.NotEmpty(frames); + using DatCollection dats = OpenDats(); + WalkOracleFrame frame = frames[1]; + Assert.NotNull(frame.Pose); + WalkLandscapeDatBuilder.BuiltWorld world = + WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin); + var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells) + { + Buildings = world.Buildings, + }; + WalkCell? camera = (frame.Pose.CellId & 0xFFFFu) >= 0x100 + ? Assert.Contains(frame.Pose.CellId, world.Cells) + : null; + var walk = new RetailFrameWalk(); + var recorder = new Recorder(); + + walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder); + + string expected = WalkTraceReplayContext.Signature(frame); + string actual = WalkTraceReplayContext.Signature(recorder.Events); + Assert.True( + expected == actual, + $"walk diverged from retail ({fixture})\nEXPECTED: {expected}\nACTUAL: {actual}"); + } + + [Theory(Skip = "FW1 moving-fixture tail (2026-08-30): the six STILL fixtures are " + + "fully conformant; the moving four diverge only at punch-edge frames " + + "(walkout/transitions F2: retail punches 001e where the replay does " + + "not; walkabout diverges at F9, foundry-entry at F67) - pose-timing/" + + "threshold sensitivity to adjudicate next (the exact pose-application " + + "point within the frame).")] + [InlineData("posed/holtburg-walkout")] + [InlineData("posed/holtburg-transitions")] + [InlineData("posed/holtburg-walkabout")] + [InlineData("posed/foundry-entry")] + public void Moving_fixture_reproduces_every_pairable_frame(string fixture) + { + // Marker timing: the pose stamped at frame N+1 is the camera state + // frame N drew with (fixture README) — pair events(N) with + // pose(N+1). One walk + one landscape carry state across frames, + // reproducing retail's cross-frame caches (the stale cell-order + // quirk included). + IReadOnlyList frames = WalkOracleTrace.Load(fixture); + Assert.True(frames.Count >= 3); + using DatCollection dats = OpenDats(); + WalkOraclePose anchor = frames[1].Pose!; + WalkLandscapeDatBuilder.BuiltWorld world = + WalkLandscapeDatBuilder.Build(dats, anchor.CellId, anchor.Origin); + var walk = new RetailFrameWalk(); + + for (int n = 1; n < frames.Count - 1; n++) + { + WalkOracleFrame frame = frames[n]; + WalkOraclePose pose = frames[n + 1].Pose!; + Assert.NotNull(pose); + WalkLandscapeDatBuilder.SetViewer(world.Landscape, pose.CellId, pose.Origin); + var ctx = new WalkTraceReplayContext(pose, world.Cells) + { + Buildings = world.Buildings, + }; + WalkCell? camera = null; + if ((pose.CellId & 0xFFFFu) >= 0x100) + { + Assert.True( + world.Cells.TryGetValue(pose.CellId, out camera), + $"frame {frame.Number}: interior camera cell {pose.CellId:x8} not loaded"); + } + var recorder = new Recorder(); + + walk.WalkFrame(pose.CellId, camera, world.Landscape, ctx, recorder); + + string expected = WalkTraceReplayContext.Signature(frame); + string actual = WalkTraceReplayContext.Signature(recorder.Events); + Assert.True( + expected == actual, + $"frame {frame.Number} diverged ({fixture})\nEXPECTED: {expected}\nACTUAL: {actual}"); + } + } + [Fact] public void Foundry_deep_reproduces_every_complete_frame_exactly() { diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs index 88b46fe3..b0ee798a 100644 --- a/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs +++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs @@ -63,7 +63,10 @@ public static class WalkWorldDatAdapter ? 0xFFFFFFFFu : lbMask | portal.OtherCellId, PolygonIndex = i, - PortalSide = ((ushort)portal.Flags & 0x2) != 0 ? 1 : 0, + // Data-pinned 2026-08-30 (the doorway-still flood): cell + // portal_side is the INVERSE of the 0x2 bit — the same + // convention the building-portal sweep pinned. + PortalSide = ((ushort)portal.Flags & 0x2) != 0 ? 0 : 1, ExactMatch = ((ushort)portal.Flags & 0x1) != 0, OtherPortalId = unchecked((short)portal.OtherPortalId), };