acdream/docs/research/2026-05-31-u4c-flap-fixed-and-residuals-handoff.md
Erik 95b6874c12 docs(render): Phase U.4c — flap fixed + residuals handoff (checkpoint)
Canonical handoff (research note) for the U.4c flap fix + the three residuals the
visual gate revealed (#78 terrain-not-gated-inside, camera-collision need, U.5).
Records the full hypothesis journey (H1/H2 both evidence-disproven) so the next
session doesn't re-walk them. ISSUES.md: flap recorded in Recently-closed; #78
annotated (more visible post-fix). CLAUDE.md: U-phase orientation updated with the
flap-fixed status + the canonical handoff pointer + camera-collision-next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:01:23 +02:00

8.5 KiB
Raw Blame History

Phase U.4c — flap FIXED (root visibility at the player's cell) + residuals handoff (2026-05-31)

TL;DR

The U.4c doorway "flap" is fixed. Root cause (converged on live evidence, after two wrong turns): indoor portal visibility was rooted at the 3rd-person camera EYE, which drifts out of the player's cell (through interior walls into AABB gaps); FindCameraCell then returned a stale cell for 3 grace frames, and from that stale root the doorway portal was culled as "behind" the eye → the exit cell + terrain + building shells all dropped → grey-void collapse. Fix (0ee328a): root indoor visibility (cell resolution + portal-side test) at the PLAYER's cell (retail CellManager::ChangePosition tracks curr_cell by the player; acdream already roots lighting at the player for the same chase-cam reason). The eye still drives the per-frame projection.

Visual gate (2026-05-31): the user confirmed "flap seems gone." But the gate also revealed the indoor pipeline is not yet seamless — three separate, largely-known residuals remain (terrain not gated inside #78, camera-displacement / camera-collision, U.5). Those are a broader new scope than the flap; user chose to checkpoint + hand off. M1.5 "indoor world feels right" needs them.

The fix that shipped (0ee328a)

GameWindow.OnRender: introduce visRootPos = (_playerMode && _playerController is not null) ? _playerController.Position : camPos, and feed it to BOTH _cellVisibility.ComputeVisibility(...) and PortalVisibilityBuilder.Build(clipRoot, visRootPos, …). The camera eye (camPos / envCellViewProj) still drives the per-frame projection and _envCellRenderer.PrepareRenderBatches frustum/render cull. Removed the earlier synthetic builder flap test (it modeled the disproven side-test hypothesis; the fix is integration-level). App tests 151/151. Two-stage Opus review: clean (no Critical/Important; downstream-consumer audit — sky/weather/fog/sun/entity-cull/cache — all internally consistent under player-rooting; cameraInsideCell is now a harmless misnomer → cosmetic rename suggested as a follow-up).

The hypothesis journey (DO NOT re-walk these — all evidence-disproven)

  • H1 (PVS / stab_list grounding) — the spec's leading hypothesis. The live evidence showed the flap is a root problem, not a set-membership problem; force-keeping cells would diverge from retail's walk-reached cell_draw_list. The Layer-1 data (LoadedCell.VisibleCells + SeenOutside, 639f20f) was plumbed and is harmless/available but is NOT what fixed the flap.
  • H2 (port the dat PortalSide into the side test) — I pivoted here on a raw 0/1 anti-correlation in A8CellAudit. DISPROVEN (b5f2bf2): under the (Flags&2)==0 boolean convention + our normal winding, the dat PortalSide sense is byte-identical to our centroid InsideSide at every pose — the swap is a literal no-op. The implementer subagent caught this via the real-data validation loop and refused to ship the no-op.
  • "eye crosses the plane while inside the cell"REFUTED (8941d1e): 0171's local AABB is y∈[-7.65,1.15]; the 0171→0170 portal plane is at y=-7.65 (the cell's min boundary), so an eye genuinely inside 0171 always has side-test D≤0 → always traverses. The eye must be outside the cell for the cull — i.e. a ROOT problem (grace-stale), confirmed by the moving capture.

The decisive apparatus was ACDREAM_PROBE_FLAP (per-frame [flap]/[flap-cam] lines): flap frames were uniformly res=Grace eyeInRoot=n terrain=Skip; good frames eyeInRoot=Y. See docs/research/2026-05-31-u4c-flap-characterization.md (top banner = the converged reading).

Residuals revealed at the visual gate (the NEXT work — NOT the flap)

User observations (inside a Holtburg cottage, this session's cells 0xA9B40162/0174/0175) mapped to cause:

  1. "Floor shows outdoor ground / cellar floor transparent / I see the world from below."Issue #78 family (already known; CLAUDE.md flags it: "outdoor terrain mesh visible inside cottage cellars … indoor-cell visibility culling not gating outdoor terrain"). Terrain isn't occluded by the interior. The flap fix made it MORE visible because terrain now draws again (it was Skipped during the flap). Fix direction: gate outdoor terrain by indoor-cell visibility when the camera/player is in a cell — port retail CEnvCell::find_visible_child_cell (acclient_2013_pseudo_c.txt:311397) / the seen_outside landscape-keep logic; relate #78, #100 secondary finding.

  2. "Outer walls transparent when looking out." → the per-frame clip is projected from the displaced eye (outside the player's cell 79% of frames: 3049 eyeInRoot=n vs 795 =Y). The OutsideView over-includes (scissor AABB fallback fired 320×; portals project far off-screen e.g. ndc=(-9.8,0.7)(-9.4,-10.5)) → terrain bleeds past the window opening. Fix direction: camera collision — retail SmartBox::update_viewer keeps the viewer inside the cell so the clip stays consistent (eyeInRoot ≈ always Y). Reopens the parked camera scope.

  3. "On the stairs: everything grey, I see doors/NPCs/particles from all around (other houses')." → transition-zone cell resolution on the stairwell (player straddling cells) + the displaced eye seeing un-occluded entities. Same camera-displacement + terrain-gating family. (Stairs/cellar is the #98-saga area — complex geometry.)

  4. "Outside looking in: no interior walls."U.5 (outdoor-camera → building-interior peering), already deferred.

Net: M1.5 "indoor world feels right" needs THREE more pieces beyond the flap — #78 terrain gating inside, camera collision (keep the eye in the player's cell), and U.5. The camera-displacement root (residuals 2+3) is the highest-leverage: with the eye constrained to the player's cell, the eye-projected clips become consistent and both 2 and much of 3 resolve.

Apparatus (kept — gated/inert when off; available for the residual work)

  • ACDREAM_PROBE_FLAP=1 (RenderingDiagnostics.ProbeFlapEnabled) — per-frame [flap] (root, eye, localEye, per-portal D/side/proj/clip/NDC, outPolys, vis) + [flap-cam] (res, eyeInRoot, eye, player, terrain mode, outdoorVisible). CellVisibility.LastCameraCellResolution + CameraCellResolution enum added. Per-frame (not cell-change-throttled) so it catches flicker at a stable root. Throwaway — strip when the residual indoor work is done.
  • tools/A8CellAudit/Program.cs portals subcommand — per-portal plane / centroid-InsideSide / dat PortalSide / local AABB / swept-pose O-vs-A-vs-B sense comparison. The tool that disproved H2. PortalFlags.PortalSide = 0x2, ExactMatch = 0x1.

Commit list (all on claude/thirsty-goldberg-51bb9b, UNPUSHED)

0ee328a fix(render): U.4c — root indoor visibility at the player's cell (THE FLAP FIX)
8941d1e research: refute eye-crosses-plane; correct stale H2 note (+ AABB dump)
b5f2bf2 research: DISPROVE the side-test fix (PortalSide port is a no-op)
13d58ca research: characterize the flap on real dat evidence
+ probe commits (ProbeFlapEnabled, NDC/clip probe), the spec (31f265d) + plan (211350b),
  Layer-1 data plumbing (639f20f), the RED-apparatus (cd3ffe3, removed in 0ee328a).

(Plus the converged-note update + this handoff.)

Git state

  • Branch claude/thirsty-goldberg-51bb9b, UNPUSHED (push decision pending with the user).
  • Two git stash entries preserved (#98/#101 WIP) — do NOT drop.

Next-session pickup prompt

U.4c doorway flap is FIXED (commit 0ee328a — root indoor visibility at the player's cell, not the
camera eye; reviewed clean). READ docs/research/2026-05-31-u4c-flap-fixed-and-residuals-handoff.md
FIRST. The visual gate revealed three SEPARATE residuals (NOT the flap): (1) #78-family — outdoor
terrain not gated inside (floor shows ground / see world from below); (2) camera displacement —
the chase eye is outside the player's cell 79% of frames so the eye-projected clip over-includes
(transparent outer walls), fixed by camera collision (retail SmartBox::update_viewer keeping the
eye in the cell); (3) U.5 outside-looking-in (deferred). Highest leverage = camera collision (fixes
2 and much of 3). Apparatus ACDREAM_PROBE_FLAP + A8CellAudit are committed + ready. Brainstorm the
camera-collision-vs-terrain-gating ordering before coding. Do NOT re-try H1 (PVS grounding) or H2
(PortalSide side-test) — both evidence-disproven (see the handoff). NO workarounds.