#119-residual ROOT CAUSE: the +0.02 m render lift leaked into the portal-visibility graph - horizontal portals side-culled anyone standing on them

The live capture pinned it end to end. BuildInteriorEntitiesForStreaming
lifts the render-side cell transform +0.02 m Z (shell z-fighting vs
terrain - a DRAW concern) and passed that LIFTED transform to
BuildLoadedCell, so every plane in the visibility graph sat 2 cm high.
The portal side test's in-plane window is +-10 mm: an eye standing ON a
floor containing a HORIZONTAL portal (the tower's deck lip 010A->0107,
stair landings, cellar mouths) sits 0-10 mm above the TRUE plane = 10-20
mm BELOW the lifted plane -> outside the window -> the cell behind the
portal side-culled out of the flood. Captured live at the stair top:
root=0xAAB3010A eye z=126.803 vs the portal plane at 126.80, flood=1,
0x0107 (the whole tower interior incl. the staircase) dropped WHILE THE
GAZE LOOKED STRAIGHT AT IT - "stairs disappear and you can walk on
them", and the roof/edge flap as the gaze swung the marginal admissions.
Vertical doorways were immune (the lift slides their planes along
themselves) - exactly why this hit stairs/decks/floors and not doors.

Chase chain (the apparatus did all the work): [viewer] print-on-change
probe with eye@mm -> the user's climb capture -> [viewer-diff] naming
the dropped cells per flip -> headless replay of the exact captured
(eye,fwd) frame: healthy UNLIFTED, reproduces ONLY with the production
lift -> gate-by-gate diagnostic (side test dot=+0.003 unlifted vs
-0.017 lifted; clip + rescue exonerated; knife-edge z-sweep all-stable,
killing the float-chaos theory).

Fix: BuildLoadedCell receives the PHYSICS (unlifted) transform; the
drawn shells keep their lift. The seal/punch fans (which read the
visibility LoadedCell's WorldTransform) now stamp TRUE depth - MORE
consistent with the unlifted terrain they protect.

Pins: CapturedTopOfStairs_MainCellStaysInFlood - arm 1 (unlifted =
post-fix production) asserts the main cell admitted at the captured
frame; arm 2 (lifted) is the mechanism canary asserting the drop, with
instructions if it ever starts passing. Plus the gate-by-gate
diagnostic + knife-edge sweep as the investigation record.

Also this session: Issue127FloodFlipReplayTests (the captured 4 cm
outdoor flip pair replays STABLE across fovs/pre-gate arms - the
outdoor churn is NOT the flood math; remaining #127 = distant-building
admission churn, lower priority now that the tower-cell drops are
explained by the lift), and the [viewer-diff] probe (per-flip added/
removed cell naming - keep, it found this).

Suites: App 242+1skip, Core 1422+2skip, UI 420, Net 294.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-11 19:26:06 +02:00
parent cd12d3dbbc
commit f35cb8b164
4 changed files with 448 additions and 4 deletions

View file

@ -5585,8 +5585,21 @@ public sealed class GameWindow : IDisposable
cellRotation: envCell.Position.Orientation,
staticObjects: System.Array.Empty<(uint, System.Numerics.Vector3, System.Numerics.Quaternion, bool, System.Numerics.Matrix4x4)>());
// Step 4: build LoadedCell for portal visibility (UNCHANGED from pre-A8).
BuildLoadedCell(envCellId, envCell, cellStruct, cellOrigin, cellTransform);
// Step 4: build LoadedCell for portal visibility — with the
// PHYSICS (unlifted) transform. The +0.02 m render lift above
// is a DRAW concern (shell z-fighting vs terrain); feeding it
// into the visibility graph shifted every HORIZONTAL portal
// plane 2 cm up, putting an eye standing on a deck/landing
// 1020 mm BELOW the lifted plane — outside the side test's
// ±10 mm in-plane window — so the cell behind the portal was
// side-culled: the tower-top staircase vanish + roof flap
// (#119-residual; captured live at eye z=126.803 vs the
// 010A→0107 plane at 126.80, reproduced ONLY with the lift in
// TowerAscentReplayTests.CapturedTopOfStairs_*). Vertical
// doorways were immune (the lift slides their planes along
// themselves), which is why this hit exactly stairs, decks,
// and cellar mouths.
BuildLoadedCell(envCellId, envCell, cellStruct, physicsCellOrigin, physicsCellTransform);
// Cache CellStruct physics BSP for indoor collision (UNCHANGED).
_physicsDataCache.CacheCellStruct(envCellId, envCell, cellStruct, physicsCellTransform);
@ -9672,6 +9685,10 @@ public sealed class GameWindow : IDisposable
// #119-residual [viewer] probe state: print-on-change signature so a
// stable climb is silent and every flood/root flip emits exactly one line.
private string? _lastViewerProbeSig;
// #127: previous frame's admitted cell set — the [viewer-diff] line names
// exactly which cells entered/left the flood when the signature changes.
private readonly HashSet<uint> _lastViewerFloodCells = new();
private readonly List<uint> _viewerDiffScratch = new();
private void EmitRetailPViewDiagnostics(
AcDream.App.Rendering.RetailPViewFrameResult result,
@ -9698,6 +9715,37 @@ public sealed class GameWindow : IDisposable
var v = _cameraController?.Active.View ?? System.Numerics.Matrix4x4.Identity;
Console.WriteLine(System.FormattableString.Invariant(
$"[viewer] {sig} eye=({camPos.X:F3},{camPos.Y:F3},{camPos.Z:F3}) fwd=({-v.M13:F4},{-v.M23:F4},{-v.M33:F4}) viewerCell=0x{viewerCellId:X8}"));
// #127 [viewer-diff]: name the cells that entered/left since the
// last emitted signature — the bistable admission self-attributes.
_viewerDiffScratch.Clear();
var cur = result.PortalFrame.OrderedVisibleCells;
var sb = new System.Text.StringBuilder(96);
sb.Append("[viewer-diff] added=[");
bool first = true;
foreach (uint c in cur)
{
if (_lastViewerFloodCells.Contains(c)) continue;
if (!first) sb.Append(',');
sb.Append("0x").Append(c.ToString("X8"));
first = false;
}
sb.Append("] removed=[");
first = true;
foreach (uint c in _lastViewerFloodCells)
{
bool present = false;
for (int ci = 0; ci < cur.Count; ci++)
if (cur[ci] == c) { present = true; break; }
if (present) continue;
if (!first) sb.Append(',');
sb.Append("0x").Append(c.ToString("X8"));
first = false;
}
sb.Append(']');
Console.WriteLine(sb.ToString());
_lastViewerFloodCells.Clear();
foreach (uint c in cur) _lastViewerFloodCells.Add(c);
}
}
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeVisibilityEnabled)