feat(render) Campaign FW1: street fixture retail-exact except flood depth

Three pinned corrections close the outdoor frame: (1) the camera basis
RE-pinned to +Y-forward - the terrace-edge fixture EXTERNAL ground
truth (the ledge faces the F518 vista, east) overrules the walkabout
motion sweep, whose camera was mouse-turned off the run line; (2)
alwaysDrawObjects (.data default 1 @0x00820ed4) drawn into the
DrawSortCell gate - cell contents draw for every cell of an in-view
block; (3) the building portal side decode data-pinned as the INVERSE
of the cell 0x2 bit (the sweep winning arm). Street result: all 13
buildings in retail exact order (a9b3003c in, aab50002 correctly
absent), punches at exactly 001a and 0022 opening the exact first
cells 16e/164. SOLE remaining delta: look-in flood depth (mine 1 cell,
retail 3-4) - the doorway-still fixture adjudicates the interior flood
in isolation next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-30 11:02:16 +02:00
parent 5f18ba5507
commit 28475d0544
4 changed files with 30 additions and 19 deletions

View file

@ -50,6 +50,11 @@ public sealed class RetailFrameWalk
public WalkPView InteriorPView => _interiorPView; public WalkPView InteriorPView => _interiorPView;
public WalkPView OutdoorPView => _outdoorPView; public WalkPView OutdoorPView => _outdoorPView;
/// <summary>Retail global <c>alwaysDrawObjects</c> (.data default 1
/// @0x00820ed4): cell contents draw for every cell of an in-view block
/// regardless of per-cell visibility.</summary>
public bool AlwaysDrawObjects = true;
/// <summary>The per-frame root (<c>SmartBox::RenderNormalMode</c>). /// <summary>The per-frame root (<c>SmartBox::RenderNormalMode</c>).
/// <paramref name="cameraCell"/> may be null only when the camera is /// <paramref name="cameraCell"/> may be null only when the camera is
/// outdoors.</summary> /// outdoors.</summary>
@ -115,10 +120,15 @@ public sealed class RetailFrameWalk
for (int k = 0; k < cellCount; k++) for (int k = 0; k < cellCount; k++)
{ {
int cellIndex = block.DrawArray[k]; int cellIndex = block.DrawArray[k];
// DrawSortCell runs for in-view cells (alwaysDrawObjects // RenderDeviceD3D::DrawBlock @0x005a19d9: DrawSortCell runs
// default 0); terrain (DrawLandCell) emits no walk event. // when alwaysDrawObjects != 0 (retail .data default 1
if (block.CellInView[cellIndex] == WalkBoundingType.Outside) // @0x00820ed4) OR the cell IsInView; terrain (DrawLandCell)
// emits no walk event.
if (!AlwaysDrawObjects
&& block.CellInView[cellIndex] == WalkBoundingType.Outside)
{
continue; continue;
}
if (block.CellBuildings[cellIndex] is WalkBuilding building) if (block.CellBuildings[cellIndex] is WalkBuilding building)
DrawBuilding(building, activeViews, ctx, sink); DrawBuilding(building, activeViews, ctx, sink);
} }

View file

@ -24,14 +24,11 @@ public sealed class WalkLookInGateSweepTests
public void Emit(in WalkEvent walkEvent) => Events.Add(walkEvent); public void Emit(in WalkEvent walkEvent) => Events.Add(walkEvent);
} }
[Fact(Skip = "FW1 adjudication driver (2026-08-30 final state): with the " [Fact(Skip = "FW1 near-win state (2026-08-30): with +Y-forward, alwaysDrawObjects, "
+ "exact get_degrade rule (slack 100, ideal thresholds, sort-center " + "the exact degrade rule and side mode 1, the street fixture matches retail "
+ "distance) the level-0 set is correct — the residual divergence is " + "EXCEPT look-in flood depth (mine D[16e]/D[164]; retail D[16e,16a,16c]/"
+ "the per-portal side/clip gate: my arms punch {0017,001e,0026,0036}, " + "D[164,162,167,169]) - adjudicate the interior flood propagation next "
+ "retail punched exactly {001a,0022} — a near-complement suggesting " + "(the doorway-still fixture tests it in isolation).")]
+ "one more polarity at the building-portal gate. Next instrument: "
+ "per-portal dump for 001a (retail punches) vs 001e (retail doesn't): "
+ "side values, plane orientation, eye side, clip result per arm.")]
public void Sweep_the_lookin_gate_decodes_against_the_street_fixture() public void Sweep_the_lookin_gate_decodes_against_the_street_fixture()
{ {
string? datDir = CornerFloodReplayTests.ResolveDatDir(); string? datDir = CornerFloodReplayTests.ResolveDatDir();

View file

@ -53,14 +53,16 @@ public sealed class WalkTraceReplayContext : IWalkFrameContext, IRetailFrameWalk
_cells = cells; _cells = cells;
WorldViewpoint = pose.Origin; WorldViewpoint = pose.Origin;
var rotation = new Quaternion(pose.Q1, pose.Q2, pose.Q3, pose.Q0); var rotation = new Quaternion(pose.Q1, pose.Q2, pose.Q3, pose.Q0);
// Convention pinned against the moving fixtures (2026-08-30 sweep: // Basis convention RE-pinned 2026-08-30 (second pass): storage
// storage w,x,y,z; the facing direction is the rotated +X axis — // w,x,y,z; forward = rotated +Y — the retail Frame convention. The
// mean 20.8° vs motion headings, every alternative ≥52°). The // motion sweep briefly favored +X, but the walkabout camera was
// consistent right-handed triple is then right = rot(+Y), // mouse-turned off the run direction; the terrace-edge fixture's
// forward = rot(+X), up = rot(+Z). // EXTERNAL ground truth (the ledge faces the F518 vista, east)
Vector3 forward = Vector3.Transform(Vector3.UnitX, rotation); // decodes east ONLY under +Y-forward, and +Y makes the street
// fixture's punch on/off-screen pattern match retail 4-for-4.
Vector3 forward = Vector3.Transform(Vector3.UnitY, rotation);
Vector3 up = Vector3.Transform(Vector3.UnitZ, rotation); Vector3 up = Vector3.Transform(Vector3.UnitZ, rotation);
Vector3 right = -Vector3.Transform(Vector3.UnitY, rotation); Vector3 right = Vector3.Transform(Vector3.UnitX, rotation);
// The exact retail frustum: tan(halfFovY) = ty/vdst, aspect = tx/ty. // The exact retail frustum: tan(halfFovY) = ty/vdst, aspect = tx/ty.
float fovY = 2f * MathF.Atan(RetailTy / RetailVdst); float fovY = 2f * MathF.Atan(RetailTy / RetailVdst);

View file

@ -242,7 +242,9 @@ public static class WalkWorldDatAdapter
/// <summary>Adjudication toggle: the BuildingPortal side-flag decode. /// <summary>Adjudication toggle: the BuildingPortal side-flag decode.
/// 0 = (Flags &amp; 0x2), 1 = inverted 0x2, 2 = (Flags &amp; 0x1), /// 0 = (Flags &amp; 0x2), 1 = inverted 0x2, 2 = (Flags &amp; 0x1),
/// 3 = inverted 0x1.</summary> /// 3 = inverted 0x1.</summary>
public static int BuildingSideMode; public static int BuildingSideMode = 1; // data-pinned 2026-08-30: building
// portal_side = the INVERSE of the cell decode's 0x2 bit (the sweep's
// winning arm - punches land at retail's exact buildings/cells with it).
private static int DecodeBuildingSide(ushort flags) => BuildingSideMode switch private static int DecodeBuildingSide(ushort flags) => BuildingSideMode switch
{ {