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

123 lines
8.5 KiB
Markdown
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.

# 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.
```