diff --git a/src/AcDream.App/Rendering/Walk/WalkWorld.cs b/src/AcDream.App/Rendering/Walk/WalkWorld.cs
index 84a30759..8a3f60b0 100644
--- a/src/AcDream.App/Rendering/Walk/WalkWorld.cs
+++ b/src/AcDream.App/Rendering/Walk/WalkWorld.cs
@@ -36,6 +36,11 @@ public sealed class WalkCell
public WalkPolygon[] PortalPolygons = [];
public uint[] StabList = [];
+ /// Cell-local → landblock-local (retail CEnvCell.pos;
+ /// the frame the flood's plane tests and projections run in).
+ public Matrix4x4 WorldTransform = Matrix4x4.Identity;
+ public Matrix4x4 InverseWorldTransform = Matrix4x4.Identity;
+
// ---- walk state (retail: fields on CEnvCell) ----
public int NumView;
public readonly List PortalViews = new();
diff --git a/src/AcDream.App/Rendering/Walk/WalkWorldDatAdapter.cs b/src/AcDream.App/Rendering/Walk/WalkWorldDatAdapter.cs
index 767abf72..5e9fdcd7 100644
--- a/src/AcDream.App/Rendering/Walk/WalkWorldDatAdapter.cs
+++ b/src/AcDream.App/Rendering/Walk/WalkWorldDatAdapter.cs
@@ -58,12 +58,21 @@ public static class WalkWorldDatAdapter
?? new WalkPolygon();
}
+ Matrix4x4 worldTransform =
+ Matrix4x4.CreateFromQuaternion(envCell.Position.Orientation)
+ * Matrix4x4.CreateTranslation(
+ envCell.Position.Origin.X,
+ envCell.Position.Origin.Y,
+ envCell.Position.Origin.Z);
+ Matrix4x4.Invert(worldTransform, out Matrix4x4 inverse);
return new WalkCell
{
CellId = cellId,
Portals = portals,
PortalPolygons = polygons,
StabList = envCell.VisibleCells.Select(v => lbMask | v).ToArray(),
+ WorldTransform = worldTransform,
+ InverseWorldTransform = inverse,
};
}
diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs
new file mode 100644
index 00000000..8c064669
--- /dev/null
+++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs
@@ -0,0 +1,60 @@
+using AcDream.App.Tests.Rendering;
+using AcDream.App.Rendering.Walk;
+using DatReaderWriter;
+using DatReaderWriter.Options;
+
+namespace AcDream.App.Tests.Rendering.Walk;
+
+///
+/// FW1's conformance gate, first slice: replay pose-stamped oracle
+/// fixtures through the ported walk and require the identical event
+/// sequence. The projection-light interior fixtures gate first
+/// (docs/research/2026-08-30-fw-walk-oracle/README.md, posed round);
+/// the outdoor/landscape fixtures join as the landscape world build
+/// lands.
+///
+[Trait("Lane", "InstalledDat")]
+public sealed class WalkTraceConformanceTests
+{
+ private sealed class Recorder : IWalkEventSink
+ {
+ public readonly List Events = new();
+ public void Emit(in WalkEvent walkEvent) => Events.Add(walkEvent);
+ }
+
+ private static DatCollection OpenDats()
+ {
+ string? datDir = CornerFloodReplayTests.ResolveDatDir();
+ if (datDir is null)
+ {
+ Assert.Fail("Lane=InstalledDat requires an installed retail DAT directory; see docs/release-gate.md.");
+ }
+ return new DatCollection(datDir!, DatAccessType.Read);
+ }
+
+ [Fact]
+ public void Foundry_deep_reproduces_every_complete_frame_exactly()
+ {
+ IReadOnlyList frames = WalkOracleTrace.Load("posed/foundry-deep");
+ Assert.NotEmpty(frames);
+ using DatCollection dats = OpenDats();
+ Dictionary cells =
+ WalkWorldDatAdapter.BuildInteriorCells(dats, 0xA9B40000u);
+ var landscape = new WalkLandscape { MidWidth = 1, Blocks = new WalkLandBlock?[1] };
+
+ foreach (WalkOracleFrame frame in frames)
+ {
+ Assert.NotNull(frame.Pose);
+ WalkCell camera = Assert.Contains(frame.Pose!.CellId, cells);
+ var ctx = new WalkTraceReplayContext(frame.Pose, cells);
+ var walk = new RetailFrameWalk();
+ var recorder = new Recorder();
+
+ walk.WalkFrame(frame.Pose.CellId, camera, landscape, ctx, recorder);
+
+ Assert.Equal(
+ WalkTraceReplayContext.Signature(frame),
+ WalkTraceReplayContext.Signature(recorder.Events));
+ }
+ }
+}
diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceReplay.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceReplay.cs
new file mode 100644
index 00000000..c6ac9e18
--- /dev/null
+++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceReplay.cs
@@ -0,0 +1,110 @@
+using System.Numerics;
+using AcDream.App.Rendering.Walk;
+
+namespace AcDream.App.Tests.Rendering.Walk;
+
+///
+/// The FW1 conformance replay harness: reconstructs the camera state from a
+/// pose-stamped oracle frame and drives the ported walk over
+/// adapter-built world data. Convention notes (adjudicate against the
+/// fixtures, loudly, on any mismatch):
+///
+/// - The dumped quaternion is retail Frame storage order w,x,y,z
+/// (q0=w) — unit-norm verified on the captures.
+/// - Retail's frame axes: +Y forward, +Z up (the camera looks along
+/// the rotated +Y).
+/// - Pose origin is landblock-local, the same space the adapter's
+/// cell transforms produce.
+///
+///
+public sealed class WalkTraceReplayContext : IWalkFrameContext, IRetailFrameWalkContext
+{
+ private sealed class BasisRayCaster(
+ Vector3 right, Vector3 forward, Vector3 up,
+ float halfWidth, float halfHeight, float focal) : IWalkRayCaster
+ {
+ public Vector3 RayThrough(float screenX, float screenY)
+ {
+ // Screen origin top-left, y down (the xformStart convention).
+ float nx = (screenX - halfWidth) / halfWidth;
+ float ny = (halfHeight - screenY) / halfHeight;
+ return right * (nx / focal) + forward + up * (ny / focal);
+ }
+ }
+
+ private readonly Dictionary _cells;
+ private readonly Matrix4x4 _viewProjection;
+ private readonly IWalkRayCaster _rays;
+
+ public WalkTraceReplayContext(
+ WalkOraclePose pose, Dictionary cells,
+ float viewportWidth = 800f, float viewportHeight = 600f,
+ float verticalFovRadians = 1.0f)
+ {
+ _cells = cells;
+ WorldViewpoint = pose.Origin;
+ var rotation = new Quaternion(pose.Q1, pose.Q2, pose.Q3, pose.Q0);
+ Vector3 forward = Vector3.Transform(Vector3.UnitY, rotation);
+ Vector3 up = Vector3.Transform(Vector3.UnitZ, rotation);
+ Vector3 right = Vector3.Transform(Vector3.UnitX, rotation);
+
+ Matrix4x4 view = Matrix4x4.CreateLookAt(pose.Origin, pose.Origin + forward, up);
+ Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView(
+ verticalFovRadians, viewportWidth / viewportHeight, 0.1f, 5000f);
+ _viewProjection = view * projection;
+
+ ViewportWidth = viewportWidth;
+ ViewportHeight = viewportHeight;
+ float focal = MathF.Tan(verticalFovRadians / 2f);
+ _rays = new BasisRayCaster(
+ right, forward, up, viewportWidth / 2f, viewportHeight / 2f, focal);
+
+ // The retail CY near plane: N = forward, d = −dot(eye, forward) − znear.
+ CyPlane = new WalkPlane(forward, -Vector3.Dot(pose.Origin, forward) - 0.1f);
+ }
+
+ public Vector3 ViewpointIn(WalkCell cell)
+ => Vector3.Transform(WorldViewpoint, cell.InverseWorldTransform);
+
+ public Matrix4x4 ObjectToClip(WalkCell cell)
+ => cell.WorldTransform * _viewProjection;
+
+ public WalkCell? GetVisible(uint cellId) => _cells.GetValueOrDefault(cellId);
+ public IWalkRayCaster Rays => _rays;
+ public Vector3 WorldViewpoint { get; }
+ public float ViewportWidth { get; }
+ public float ViewportHeight { get; }
+ public WalkPlane CyPlane { get; }
+ public IWalkFrameContext CellContext => this;
+ public void SetActiveView(WalkPortalView views, int index) { }
+
+ public Vector3 ViewpointInBuilding(WalkBuilding building) => WorldViewpoint;
+
+ public int ClipBuildingPolygon(
+ WalkBuilding building, WalkPolygon polygon, int side, Span output)
+ => 0; // building look-ins join the conformance surface with the landscape fixtures
+
+ // ---- signatures for comparing walk output to oracle frames ----
+
+ public static string Signature(IEnumerable events)
+ => string.Join("|", events.Select(e => e.Kind switch
+ {
+ WalkEventKind.Landscape => "LS",
+ WalkEventKind.Building => $"BLD:{e.CellId:x8}",
+ WalkEventKind.DrawInside => $"DI:{e.CellId:x8}",
+ WalkEventKind.DrawCells =>
+ $"DC:ov={e.OutsideViewCount}:{string.Join(',', e.Cells.Select(c => c.ToString("x8")))}",
+ _ => "?",
+ }));
+
+ public static string Signature(WalkOracleFrame frame)
+ => string.Join("|", frame.Events.Select(e => e.Kind switch
+ {
+ WalkOracleEventKind.Landscape => "LS",
+ WalkOracleEventKind.Building => $"BLD:{e.CellId!.Value:x8}",
+ WalkOracleEventKind.DrawInside => $"DI:{e.CellId!.Value:x8}",
+ WalkOracleEventKind.DrawCells =>
+ $"DC:ov={e.OutsideViewCount}:{string.Join(',', e.Cells.Select(c => c.ToString("x8")))}",
+ _ => "?",
+ }));
+}