acdream/tests/AcDream.App.Tests/Rendering/Walk/WalkProductionWorldConformanceTests.cs
Erik b10ad662b0 feat(render) Campaign FW3.1: production walk world data behind the seam
The retail frame walk's world model now materializes from production
landblock-build owners through the legal IDatReaderWriter seam, with
zero frame wiring (FW3.2 roots the frame):

- WalkCellFactory: WalkCell built in the SAME pass as LoadedCell
  (EnvCellLandblockBuild.BuildVisibilityCell) from the raw portal
  Flags/polygons/planes/stab lists already parsed there; stored as
  LoadedCell.Walk, committed atomically with the cell. The
  fixture-pinned decodes (inverse-0x2 portal side, 0xFFFF->0xFFFFFFFF
  exit widening) live here.
- WalkBuildingFactory + WalkBuildingRegistry: the production
  WalkBuilding build (drawing BSP with PORT nodes, degrade ladder,
  portal sides/stab lists, sort center, model frame) from the SAME
  LandBlockInfo the streaming build already fetches, under the
  factory's existing DAT lock - closing the gap where BuildingLoader
  drops every walk field at load.
- WalkLandscapeAssembler: the retail 51x51 viewer-centred grid
  (mid_radius 25) fed incrementally from landblock publish/retire;
  per-block z-slab (heightTable[max]+200 / [min]-1) computed
  worker-side in LandblockBuildFactory from the heights already in
  hand. O(1) SetViewer on same-block frames.
- WalkProductionFrameContext: the walk's frame contexts over
  CellVisibility + WalkBuildingRegistry with a generic
  inverse-view-projection ray caster (rays feed cross products only -
  scale-free) and the znear=0.1 CY plane.
- Publication: LandblockRenderPublisher owns both walk registries,
  publishing in the same AdvanceCompleteOne step as BuildingRegistry
  and retiring in RemoveBuildingRegistry - same commit, same
  retirement, no new ticket stage.

Conformance: ALL TEN oracle fixtures replay identically through the
PRODUCTION builders (WalkProductionWorldConformanceTests) - same
signatures as the test adapter, first run. Known gap documented for
FW3.2: far-tier landblocks carry no EnvCell transaction, so their
z-slab never reaches the assembler.

Suites: full Release build 0 warnings; Walk lane 186/1 skip;
hermetic 6,738/0 (+24); RuntimeDatAccessArchitectureTests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 13:19:22 +02:00

321 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AcDream.App.Rendering;
using AcDream.App.Tests.Rendering;
using AcDream.App.Rendering.Walk;
using AcDream.Content;
using DatReaderWriter;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Options;
using System.Numerics;
namespace AcDream.App.Tests.Rendering.Walk;
/// <summary>
/// Campaign FW3.1's load-bearing deliverable: the ten pose-stamped oracle
/// fixtures (docs/research/2026-08-30-fw-walk-oracle/), replayed through the
/// PRODUCTION world-data builders
/// (<see cref="WalkCellFactory"/>/<see cref="WalkBuildingFactory"/>
/// /<see cref="WalkLandscapeAssembler"/> — the same code
/// <c>EnvCellLandblockBuildBuilder</c>/<c>LandblockBuildFactory</c> run at
/// real landblock-build time, consuming ONLY the legal
/// <see cref="IDatReaderWriter"/> seam), must reproduce the identical walk
/// output <see cref="WalkTraceConformanceTests"/> already proves against the
/// FW1 test adapter (<c>WalkWorldDatAdapter</c>/<c>WalkLandscapeDatBuilder</c>).
///
/// This class deliberately does NOT touch <see cref="WalkTraceConformanceTests"/>
/// (frozen — must stay green untouched) but reuses its driver/signature
/// helpers verbatim: <see cref="WalkTraceReplayContext"/>,
/// <see cref="WalkOracleTrace"/>, <see cref="WalkOraclePose"/>,
/// <see cref="WalkOracleFrame"/>. <see cref="WalkTraceReplayContext.Buildings"/>
/// is typed against the TEST adapter's <c>WalkWorldDatAdapter.BuildingEntry</c>
/// record — structurally identical to <see cref="WalkBuildingFactory.Entry"/>
/// (same three fields) — so <see cref="BuildProductionWorld"/> below adapts
/// one into the other rather than touching the shared context type.
///
/// <see cref="BuildProductionWorld"/> mirrors the FW1 harness's
/// <c>WalkLandscapeDatBuilder.Build</c> loop structure and ring math
/// EXACTLY (51×51 grid, <c>WalkLandscapeAssembler.MidRadius</c> = 25,
/// buildings/stab-cells only at full resolution), but every DATA-BUILDING
/// step below it — cells, buildings, the drawing BSP, z-slab — calls the
/// PRODUCTION functions. That is the whole conformance claim: the grid/ring
/// assembly is proven once (FW1's existing gate, via
/// <see cref="WalkLandscapeAssembler"/>'s own port of that same math); this
/// class proves the DATA those slots are filled with is identical to the
/// FW1 test adapter's.
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class WalkProductionWorldConformanceTests
{
private sealed class Recorder : IWalkEventSink
{
public readonly List<WalkEvent> 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);
}
/// <summary>Builds the walk's world data (landscape + interior cells +
/// buildings) for a camera pose through the PRODUCTION builders, in the
/// exact shape <see cref="WalkTraceReplayContext"/> expects. Mirrors
/// <c>WalkLandscapeDatBuilder.Build</c>'s grid loop; every cell/building
/// constructed inside it comes from <see cref="WalkCellFactory"/> /
/// <see cref="WalkBuildingFactory"/>.</summary>
private static (WalkLandscapeAssembler Assembler, Dictionary<uint, WalkCell> Cells,
Dictionary<WalkBuilding, WalkWorldDatAdapter.BuildingEntry> Buildings)
BuildProductionWorld(IDatReaderWriter dats, uint cameraCellId, Vector3 cameraOrigin)
{
int cameraBlockX = (int)(cameraCellId >> 24);
int cameraBlockY = (int)((cameraCellId >> 16) & 0xFF);
var assembler = new WalkLandscapeAssembler();
var cells = new Dictionary<uint, WalkCell>();
var buildings = new Dictionary<WalkBuilding, WalkWorldDatAdapter.BuildingEntry>();
Region region = (Region)dats.Get<Region>(0x13000000u)!;
float[] heightTable = region.LandDefs.LandHeightTable;
for (int gx = 0; gx < WalkLandscapeAssembler.GridWidth; gx++)
{
for (int gy = 0; gy < WalkLandscapeAssembler.GridWidth; gy++)
{
int blockX = cameraBlockX + gx - WalkLandscapeAssembler.MidRadius;
int blockY = cameraBlockY + gy - WalkLandscapeAssembler.MidRadius;
if (blockX < 0 || blockX > 0xFF || blockY < 0 || blockY > 0xFF)
continue;
uint landblockId = (uint)((blockX << 24) | (blockY << 16));
if (dats.Get<LandBlock>(landblockId | 0xFFFFu) is not LandBlock landBlock)
continue;
byte maxByte = 0, minByte = 255;
foreach (byte h in landBlock.Height)
{
if (h > maxByte) maxByte = h;
if (h < minByte) minByte = h;
}
float maxZ = heightTable[maxByte] + 200f;
float minZ = heightTable[minByte] - 1f;
var blockOffset = new Vector3(
(gx - WalkLandscapeAssembler.MidRadius) * WalkLandscape.BlockLength,
(gy - WalkLandscapeAssembler.MidRadius) * WalkLandscape.BlockLength,
0f);
var blockBuildings = new List<WalkBuildingFactory.Entry>();
if (WalkLandscapeAssembler.SideCellCountForRing(WalkLandscapeAssembler.RingOf(gx, gy)) == 8)
{
LandBlockInfo? info = dats.Get<LandBlockInfo>(landblockId | 0xFFFEu);
blockBuildings = WalkBuildingFactory.Build(dats, landblockId, info?.Buildings, blockOffset);
foreach (WalkBuildingFactory.Entry entry in blockBuildings)
{
buildings[entry.Building] = new WalkWorldDatAdapter.BuildingEntry(
entry.Building, entry.WorldTransform, entry.InverseWorldTransform);
// Retail's loaded-interior rule (CLandBlock::init_buildings
// @0052fd80 -> add_to_stablist -> grab_visible_cells): a
// full-res block loads exactly its buildings' portal stab
// cells, matching WalkLandscapeDatBuilder's harness scope.
foreach (WalkBldPortal portal in entry.Building.Portals)
{
if (portal.OtherCellId != 0xFFFFFFFFu && !cells.ContainsKey(portal.OtherCellId))
{
WalkCell? c = WalkCellFactory.BuildCell(dats, portal.OtherCellId, blockOffset);
if (c is not null) cells[c.CellId] = c;
}
foreach (uint stab in portal.StabList)
{
if (cells.ContainsKey(stab)) continue;
WalkCell? c = WalkCellFactory.BuildCell(dats, stab, blockOffset);
if (c is not null) cells[c.CellId] = c;
}
}
}
}
assembler.PublishLandblock(landblockId, maxZ, minZ, blockBuildings);
}
}
assembler.SetViewer(cameraCellId, cameraOrigin);
return (assembler, cells, buildings);
}
[Fact]
public void Street_outdoor_first_frame_diff()
{
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load("posed/holtburg-street-outdoor");
Assert.NotEmpty(frames);
using DatCollection dats = OpenDats();
using var adapter = new DatCollectionAdapter(dats);
WalkOracleFrame frame = frames[1];
Assert.NotNull(frame.Pose);
(WalkLandscapeAssembler assembler, Dictionary<uint, WalkCell> cells,
Dictionary<WalkBuilding, WalkWorldDatAdapter.BuildingEntry> buildings) =
BuildProductionWorld(adapter, frame.Pose!.CellId, frame.Pose.Origin);
var ctx = new WalkTraceReplayContext(frame.Pose, cells) { Buildings = buildings };
var walk = new RetailFrameWalk();
var recorder = new Recorder();
walk.WalkFrame(frame.Pose.CellId, null, assembler.Landscape, ctx, recorder);
string expected = WalkTraceReplayContext.Signature(frame);
string actual = WalkTraceReplayContext.Signature(recorder.Events);
Assert.True(
expected == actual,
$"production walk diverged from the FW1 test adapter\nEXPECTED: {expected}\nACTUAL: {actual}");
}
[Fact]
public void Doorway_still_first_frame_diff()
{
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load("posed/holtburg-doorway-still");
Assert.NotEmpty(frames);
using DatCollection dats = OpenDats();
using var adapter = new DatCollectionAdapter(dats);
WalkOracleFrame frame = frames[1];
Assert.NotNull(frame.Pose);
(WalkLandscapeAssembler assembler, Dictionary<uint, WalkCell> cells,
Dictionary<WalkBuilding, WalkWorldDatAdapter.BuildingEntry> buildings) =
BuildProductionWorld(adapter, frame.Pose!.CellId, frame.Pose.Origin);
var ctx = new WalkTraceReplayContext(frame.Pose, cells) { Buildings = buildings };
WalkCell camera = Assert.Contains(frame.Pose.CellId, cells);
// Matches WalkTraceConformanceTests.Doorway_still_first_frame_diff:
// this capture ran under cdb load with Render::deg_mul depressed to
// the portless-arm threshold. Same environment pin, same reason.
var walk = new RetailFrameWalk { DegradeMultiplier = 0f };
var recorder = new Recorder();
walk.WalkFrame(frame.Pose.CellId, camera, assembler.Landscape, ctx, recorder);
string expected = WalkTraceReplayContext.Signature(frame);
string actual = WalkTraceReplayContext.Signature(recorder.Events);
Assert.True(
expected == actual,
$"production walk diverged from the FW1 test adapter\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<WalkOracleFrame> frames = WalkOracleTrace.Load(fixture);
Assert.NotEmpty(frames);
using DatCollection dats = OpenDats();
using var adapter = new DatCollectionAdapter(dats);
WalkOracleFrame frame = frames[1];
Assert.NotNull(frame.Pose);
(WalkLandscapeAssembler assembler, Dictionary<uint, WalkCell> cells,
Dictionary<WalkBuilding, WalkWorldDatAdapter.BuildingEntry> buildings) =
BuildProductionWorld(adapter, frame.Pose!.CellId, frame.Pose.Origin);
var ctx = new WalkTraceReplayContext(frame.Pose, cells) { Buildings = buildings };
WalkCell? camera = (frame.Pose.CellId & 0xFFFFu) >= 0x100
? Assert.Contains(frame.Pose.CellId, cells)
: null;
var walk = new RetailFrameWalk();
var recorder = new Recorder();
walk.WalkFrame(frame.Pose.CellId, camera, assembler.Landscape, ctx, recorder);
string expected = WalkTraceReplayContext.Signature(frame);
string actual = WalkTraceReplayContext.Signature(recorder.Events);
Assert.True(
expected == actual,
$"production walk diverged from the FW1 test adapter ({fixture})\nEXPECTED: {expected}\nACTUAL: {actual}");
}
[Fact]
public void Foundry_entry_reproduces_every_frame_before_the_f67_order_segment()
{
// Same F67-F79 parked segment as WalkTraceConformanceTests (the
// building-a9b40036-root-plane viewpoint question is a walk/ported-
// algorithm question, not a world-data question — out of scope for
// this class). Frames 1-66 must still reproduce exactly.
MovingFixtureReplay("posed/foundry-entry", stopBeforeFrame: 67);
}
[Theory]
[InlineData("posed/holtburg-walkout")]
[InlineData("posed/holtburg-transitions")]
[InlineData("posed/holtburg-walkabout")]
public void Moving_fixture_reproduces_every_pairable_frame(string fixture)
=> MovingFixtureReplay(fixture);
private void MovingFixtureReplay(string fixture, int stopBeforeFrame = int.MaxValue)
{
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(fixture);
Assert.True(frames.Count >= 3);
using DatCollection dats = OpenDats();
using var adapter = new DatCollectionAdapter(dats);
WalkOraclePose anchor = frames[1].Pose!;
(WalkLandscapeAssembler assembler, Dictionary<uint, WalkCell> cells,
Dictionary<WalkBuilding, WalkWorldDatAdapter.BuildingEntry> buildings) =
BuildProductionWorld(adapter, anchor.CellId, anchor.Origin);
var walk = new RetailFrameWalk();
for (int n = 1; n < frames.Count - 1; n++)
{
WalkOracleFrame frame = frames[n];
if (frame.Number >= stopBeforeFrame) break;
string expected = WalkTraceReplayContext.Signature(frame);
string? firstActual = null;
bool matched = false;
foreach (WalkOraclePose pose in new[] { frames[n + 1].Pose!, frame.Pose! })
{
Assert.NotNull(pose);
assembler.SetViewer(pose.CellId, pose.Origin);
var ctx = new WalkTraceReplayContext(pose, cells) { Buildings = buildings };
WalkCell? camera = null;
if ((pose.CellId & 0xFFFFu) >= 0x100)
{
Assert.True(
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, assembler.Landscape, ctx, recorder);
string actual = WalkTraceReplayContext.Signature(recorder.Events);
firstActual ??= actual;
if (actual == expected)
{
matched = true;
break;
}
}
Assert.True(
matched,
$"frame {frame.Number} diverged under both adjacent poses ({fixture})\n"
+ $"EXPECTED: {expected}\nACTUAL: {firstActual}");
}
}
[Fact]
public void Foundry_deep_reproduces_every_complete_frame_exactly()
{
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load("posed/foundry-deep");
Assert.NotEmpty(frames);
using DatCollection dats = OpenDats();
using var adapter = new DatCollectionAdapter(dats);
Dictionary<uint, WalkCell> cells =
WalkCellFactory.BuildInteriorCells(adapter, 0xA9B40000u, Vector3.Zero);
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));
}
}
}