research(render): Phase U.4c — refute eye-crosses-plane; correct stale H2 note

A8CellAudit portals now dumps each cell's local AABB. Real flap cells: 0171 local
y in [-7.65, 1.15], 0170 in [-8.61, -7.65]; the 0171->0170 portal plane is at
y=-7.65 (0171's MIN boundary), no overlap. So an eye genuinely inside 0171 always
has side-test D<=0 -> always traverses 0171->0170; the side test cannot cull 0170
while the eye is in 0171. The flap therefore requires the eye OUTSIDE 0171 while
root is still 0171 (cache/grace/3rd-person camera) -> a camera-cell-resolution
issue, not the side test (H2, disproven) and not the per-frame PVS set (H1, in
doubt). Mechanism still unconfirmed -> needs a live eye-pos capture. Stale H2
conclusion in the characterization note corrected with a banner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-31 10:36:30 +02:00
parent b5f2bf2b8f
commit 8941d1e6e5
2 changed files with 31 additions and 0 deletions

View file

@ -1,5 +1,23 @@
# U.4c flap characterization — real-dat evidence (Task U.4c-1) # U.4c flap characterization — real-dat evidence (Task U.4c-1)
> **⚠️ CORRECTION (2026-05-31, later same day) — the H2 conclusion below is WRONG.**
> Two follow-on evidence steps overturned the "port the side test (H2)" conclusion this note reaches:
> 1. **PortalSide swap is a no-op** (`docs/research/2026-05-31-u4c-initcell-pseudocode.md`): the
> dat `PortalSide` sense, under the `(Flags & 2)==0` boolean convention + our normal winding, is
> byte-identical to our centroid `InsideSide` at every pose. The "anti-correlation" this note saw
> is in the raw 0/1 values, which cancel. Swapping changes nothing.
> 2. **The eye cannot cross the `0171→0170` plane while inside `0171`** (real AABBs): `0171` local
> y ∈ [-7.65, 1.15]; the portal plane is at local y = -7.65 (`0171`'s min boundary). The eye in
> `0171` always has side-test `D ≤ 0` → always traverses. So the side test can only cull `0170`
> when the eye is OUTSIDE `0171` while root is still `0171` (cache/grace/3rd-person pull-back) — a
> camera-cell-resolution issue, not the side test and not the per-frame PVS set. Mechanism is NOT
> yet confirmed (needs live eye-position + root-cell + per-portal drop-reason at the flap frames).
> The raw dat data in this note (topology, planes, `seenOutside=Y`, `PortalSide` flags) is correct
> and still useful; only the **H2 fix conclusion** is retracted. See the pseudocode note for the
> disproof and the handoff at the session end for the convergence plan.
**Date:** 2026-05-31. **Tool:** `tools/A8CellAudit/Program.cs` `portals` subcommand (extended this **Date:** 2026-05-31. **Tool:** `tools/A8CellAudit/Program.cs` `portals` subcommand (extended this
session to dump per-portal plane + centroid-`InsideSide` + dat `PortalSide`). DAT-only, no live session. session to dump per-portal plane + centroid-`InsideSide` + dat `PortalSide`). DAT-only, no live session.

View file

@ -261,6 +261,19 @@ static void DumpCellPortals(DatCollection dats, uint envCellId)
Console.WriteLine($" SIDES: {sideText}"); Console.WriteLine($" SIDES: {sideText}");
} }
// U.4c H3 check: local AABB over all cellStruct verts. If the cell's AABB extends
// PAST one of its own portal planes, a camera eye in that region is in this cell's
// AABB (so an AABB-based FindCameraCell keeps rooting here) yet geometrically on the
// neighbour's side of the portal (so the per-frame side test culls that portal) →
// the flap's stale-root region.
if (cellStruct is not null && cellStruct.VertexArray.Vertices.Count > 0)
{
var mn = new Vector3(float.MaxValue); var mx = new Vector3(float.MinValue);
foreach (var v in cellStruct.VertexArray.Vertices.Values)
{ mn = Vector3.Min(mn, v.Origin); mx = Vector3.Max(mx, v.Origin); }
Console.WriteLine($" localAABB: min=({mn.X:F2},{mn.Y:F2},{mn.Z:F2}) max=({mx.X:F2},{mx.Y:F2},{mx.Z:F2})");
}
Console.WriteLine( Console.WriteLine(
$"summary: cell=0x{envCellId:X8} portals={envCell.CellPortals.Count} " + $"summary: cell=0x{envCellId:X8} portals={envCell.CellPortals.Count} " +
$"exits(0xFFFF)={exitCount} interior={interiorCount} " + $"exits(0xFFFF)={exitCount} interior={interiorCount} " +