acdream/docs/research/2026-05-31-render-architecture-reset-handoff.md
Erik 0013819fa1 docs(render): ARCHITECTURE RESET — indoor render is a 3-gate patchwork; handoff + unified-PView target
A week on the indoor render (Phase U.4 → U.4c → 2026-05-31) fixed the flap but
produced NO shippable progress: walls/ceiling don't seal, outdoor terrain is
visible from inside (#78), the enclosure reads grey/transparent. Root cause is
ARCHITECTURAL, not a bug.

Evidence this session (direct, via the new [shell] probe + screenshots) RULED OUT
every subsystem except the gating architecture: the interior cell shells render
fine (geometry/texture/opaque/depth all correct, zh=0 tr=0); the visibility
traversal computes correct sets + non-empty portal clips; cull mode is fine; the
camera/eye thread was a detour. The residual is that OUTDOOR geometry is not gated
to portal openings when indoors, and acdream enforces visibility THREE inconsistent
ways (TerrainClipMode / per-cell shell clip / entity ParentCellId filter with an
outdoor-stab bypass) instead of retail's ONE PView gate.

This commit is the reset handoff + documentation, not a code fix:
- docs/research/2026-05-31-render-architecture-reset-handoff.md — canonical: honest
  state, evidence ledger (ruled-out / do-not-repeat), the mapped 3-gate patchwork,
  the retail PView target (one traversal → one gate for ALL geometry), the reset
  mission, and a copy-paste pickup prompt.
- docs/architecture/acdream-architecture.md — new "Render Pipeline" SSOT section
  (current divergence + unified-PView target + the one rule: compute visibility
  once, enforce it once). (Doc has pre-existing corruption below this section —
  flagged for separate cleanup.)
- Apparatus: ACDREAM_PROBE_SHELL → [shell] (EnvCellRenderer per-cell prepared/drawn
  geometry + flags) added to RenderingDiagnostics + EnvCellRenderer. Throwaway.
- docs/superpowers/specs/2026-05-31-camera-collision-indoor-engagement-design.md —
  spec for e099b4c (camera collision; now parked as orthogonal to the seam).

Next session: STOP point-fixing; do the architecture reset to a single PView gate.

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

236 lines
14 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.

# Render Architecture Reset — assessment + handoff (2026-05-31)
> **Read this first, in full, before touching any render code.** This is a deliberate
> reset after a week of point-fixing the indoor render with **no shippable progress**.
> The next session's job is NOT another symptom fix — it is to bring acdream's render
> pipeline up to a **single, coherent, retail-faithful design (PView)** and close the
> indoor seams *by construction*.
## 1. The honest state
A full week (Phase U.4 → U.4c → this session) has gone into the indoor render. Net
result: the **doorway "flap" is fixed** (real, kept), but the indoor world is **still
broken** — walls/ceiling don't visually seal, you see outdoor terrain from inside the
cellar ("the world from below"), and the enclosure reads as a grey/transparent void.
The reason there's been no progress is **architectural, not a bug**: the render pipeline
is a **patchwork of three inconsistent visibility gates** bolted onto a half-finished
port of retail's PView. Seams are *structural* — fixing one place (the flap, the camera,
the shells) pops a seam in another. We have been chasing symptoms across different
subsystems (visibility → camera collision → shell rendering) instead of fixing the
architecture. That stops now.
**Mandate from the user (2026-05-31):** *"produce me a solid engine that produces a
retail-faithful experience… look over what we have done, check our design and if it
matches a solid engine design, and solve all our issues."* This handoff is the
assessment; the next session executes the reset.
## 2. Evidence ledger — what is RULED OUT and CONFIRMED (do not re-litigate)
This week was not all waste: it eliminated, **with direct evidence**, every subsystem
*except* the gating architecture. Do not re-investigate these.
**CONFIRMED (direct evidence):**
- **The interior cell shells RENDER correctly.** `[shell]` probe (ACDREAM_PROBE_SHELL):
every visible cell (`0xA9B40170-0175`) is prepared and drawn — `gfx=1`, `idx` 24270,
`tr=0` (not translucent), `zh=0` (textures present), opaque pass, depth-on. So the
shells are NOT the problem (geometry, textures, cull, depth all fine).
- **The visibility traversal computes correct results.** `[flap]` probe: portals
traverse (`TRV`), project, and clip to **non-empty** screen regions (`clip=4/5`),
`vis=3` cells, `terrain=Skip`/`outPolys=0` correct for a windowless cellar.
- **The residual is OUTDOOR geometry not being gated to portal openings when indoors**
(issue #78): screenshots show outdoor terrain + scenery rendered and visible from
*inside* the cellar (from below), and the interior not sealing. The cell mesh renders
but does not occlude / the outside is not clipped to the window.
**RULED OUT (with evidence) — do NOT chase again:**
- The camera / eye position as the root (eye-displacement was reduced by `e099b4c` but
the residuals persisted unchanged — the camera thread was a **detour**).
- Cull mode (cell shells already render double-sided, `EnvCellRenderer.cs:1168`).
- Cell-shell geometry missing / textures missing (`[shell]` proves both present).
- The portal side-test / PortalSide (`b5f2bf2`, byte-identical no-op).
- PVS / stab_list grounding (H1, `639f20f` plumbed, not the fix).
- Camera zoom as the cause (errors persist at minimum zoom).
## 3. The current render architecture (mapped) — the patchwork
Per-frame, inside `GameWindow.OnRender` (~`:7290-7471`):
```
CellVisibility.ComputeVisibility(visRootPos = PLAYER pos) → visible-cell SET + VisibleCellIds + CameraCell
PortalVisibilityBuilder.Build(clipRoot, visRootPos=PLAYER, envCellViewProj=EYE) ← a partial port of retail ConstructView
│ → PortalVisibilityFrame { OutsideView, per-cell CellViews, OrderedVisibleCells }
│ (side-test + ordering use the PLAYER; projection uses the EYE — a split)
ClipFrameAssembler.Assemble(...) → TerrainClipMode {Skip|Scissor|Planes} + CellIdToSlot (per-cell clip regions) + OutdoorVisible
├── TerrainModernRenderer.Draw gated by TerrainClipMode (GATE #1)
├── EnvCellRenderer.Render(shells) gated by envCellShellFilter + per-cell clip slot (GATE #2)
└── WbDrawDispatcher.Draw(entities) gated by ParentCellId ∈ visibleCellIds … BUT outdoor stabs have ParentCellId==null and ALWAYS PASS (GATE #3)
```
**Why this is structurally fragile (the root of all the seams):**
1. **Three separate gates** (terrain / shells / entities) must independently agree to
produce a seamless interior. They don't. Retail has **one** gate (PView).
2. **Outdoor geometry is not gated to portal openings.** Terrain has its own
Skip/Scissor/Planes path (and the Scissor fallback *over-includes*), and outdoor
stabs/scenery (`ParentCellId==null`) **bypass the gate entirely** → you see the
outdoor world from inside (#78). Retail draws the landscape **only** through exit
portals, clipped to the opening.
3. **No single source of truth.** "What is visible and where on screen" is reassembled
from three consumers of one builder, instead of being the builder's authoritative,
uniformly-enforced output.
4. **Player-rooted set vs eye-projected clip split** adds a second axis of fragility
(the flap, the residual degeneracies) — secondary, but real.
This is the crux: **we compute visibility once but enforce it three different,
inconsistent ways.**
## 4. The retail-faithful target — PView: one traversal, one gate
Retail (decomp `docs/research/named-retail/acclient_2013_pseudo_c.txt`) renders the
world through **PView**, a single portal-clipped traversal:
- Root at the **viewer's cell** (`CellManager::ChangePosition` tracks `curr_cell`).
- `PView::ConstructView` (`:433750`) walks the portal graph; `InitCell` (`:432896`)
computes each cell's **screen-space clip region** (the portal opening it's seen
through, recursively intersected with the parent's region); cells go on
`cell_draw_list` closest-first.
- **Exit portals** add the **outside** (landscape) to the view, **clipped to the
exit-portal opening**.
- `PView::DrawCells` (`:432715`) renders each cell **clipped to its region**, and the
landscape **only where an exit portal reveals it**.
- The viewer (eye) is **collided** (`SmartBox::update_viewer` `:92761`) so the
projection stays well-conditioned.
- `CEnvCell::find_visible_child_cell` (`:311397`) + `seen_outside` gate the
landscape-keep.
- **Outdoors**, PView is trivial (the LandCell + neighbours, no indoor clipping).
The defining property: **ONE visibility structure gates ALL geometry** — interior cell
shells, interior statics, and the outside (terrain + scenery) — all clipped to their
PView-derived regions. That is *why retail is seamless by construction.*
Reference port to crib (acdream owns it but **never invokes it**): WorldBuilder's
`RenderInsideOut` / `VisibilityManager`
(`references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs`).
## 5. The solid design for acdream (the reset target)
We already have the hard part (the `PortalVisibilityBuilder` ConstructView port + the
`ClipFrame` clip-plane machinery + per-cell clip slots). The reset is a
**consolidation, not a rewrite**: make the PView output the **single gate for every
geometry type**.
1. **One gate.** Every draw — cell shells, interior statics, terrain, outdoor scenery —
is clipped to its PView region:
- Interior cell `C` → its per-cell clip region (already ≈done in `EnvCellRenderer`).
- Interior static in cell `C` → cell `C`'s region (route via the same per-instance
clip slot).
- **Outside** (terrain + outdoor scenery + `ParentCellId==null` stabs) → the
**`OutsideView` region** (union of exit-portal openings). **Empty `OutsideView`
(windowless interior) ⇒ draw NO outdoor geometry.** **No `ParentCellId==null`
bypass.**
- **Outdoor root** (camera/player in a LandCell, no indoor cell) ⇒ gate = everything
(today's normal path).
2. **Delete the ad-hoc gates.** Collapse GATE #1/#2/#3 into the single clip-to-region.
Remove the `ParentCellId==null` always-pass in `WbDrawDispatcher`. Remove the
terrain Scissor over-include fallback in favour of retail's exact portal clip.
3. **Resolve the set/clip split.** Decide one rooting story (the flap fix proved the
*set* must be player-rooted; the *clip/projection* is the eye's — confirm this is
retail-faithful under the unified gate, or collide the eye so they coincide).
4. **U.5 (eye outside, looking into a building)** is the same machinery with the root at
the eye's cell and the interior shown through the building's portals — design it in,
don't special-case it.
Result, by construction: #78 (outdoor gated to openings), transparent walls (interior
uniformly clipped), grey enclosure (no spurious ungated gaps), terrain-through-floor
(no outdoor outside the window) all resolve together — because there is one consistent
rule instead of three.
## 6. Next-session mission (the reset — in order)
1. **Audit** the current render data-flow against §3; confirm the three-gate divergence
in the live code.
2. **Verify the target against retail**: read PView in full
(`ConstructView`/`InitCell`/`ClipPortals`/`AddViewToPortals`/`DrawCells`/
`grab_visible_cells`/`find_visible_child_cell`) **and** WB `RenderInsideOut`. Do not
trust this doc's summary alone — confirm the one-gate model from the source.
3. **Design** the unified gate (`superpowers:brainstorming` → spec). This is
architectural; brainstorm it, don't free-solo.
4. **Implement** the single gate; delete the ad-hoc gates/bypasses.
5. **Verify** at the visual gate: flap stays fixed; walls/ceiling solid; no
terrain-through-floor; no grey; outdoor visible *only* through windows/doors, clipped.
**Operating rules for the reset:** evidence-first; no point-fixes; no new gate bolted on
to mask a seam (that is exactly what produced this mess). If a fix starts to look like a
fourth special-case gate, stop — it belongs in the one gate.
## 7. What the week produced (status of each artifact)
- **KEEP — `0ee328a` flap fix:** root indoor visibility at the player's cell. A correct,
retail-faithful PView piece (the set must be player-rooted). Keep.
- **ORTHOGONAL — `e099b4c` camera-collision viewer-gate-exemption:** retail-faithful but
**not** the seam fix (the camera thread was a detour). **Open decision:** keep (it's a
real, faithful improvement) or revert to keep the reset branch focused. Recommendation:
**park it** — keep the commit but treat camera collision as out-of-scope for the reset;
revisit only after the unified gate lands. Its sibling diagnosis (`3066460`) +
spec (`docs/superpowers/specs/2026-05-31-camera-collision-indoor-engagement-design.md`)
+ the `CameraCollisionIndoorTests` are accurate for what they cover but are about an
orthogonal subsystem.
- **APPARATUS (throwaway, gated, useful for the reset):**
- `ACDREAM_PROBE_FLAP``[flap]`/`[flap-cam]`/`[flap-sweep]` (visibility + eye + camera sweep).
- `ACDREAM_PROBE_SHELL``[shell]` (per-cell shell render: prepared? geometry? flags?).
Strip both once the reset lands.
- **Disproven hypotheses (do NOT repeat):** H1 (PVS grounding), H2 (PortalSide),
zoom-confound, back-face-cull, eye-displacement-as-root, shell-geometry/texture-missing.
## 8. Git / safety
- Branch `claude/thirsty-goldberg-51bb9b`, **UNPUSHED** (push pending with the user).
- Two `git stash` entries preserved (#98/#101 WIP) — **do NOT drop.**
- Throwaway capture logs `u4c-*.log` are untracked — ignore/delete, do not commit.
## 9. Pickup prompt (copy-paste for the next session)
```
RENDER ARCHITECTURE RESET — acdream indoor pipeline. Continue on branch
claude/thirsty-goldberg-51bb9b (do NOT branch/worktree; preserve the 2 git stashes).
READ FIRST, in full: docs/research/2026-05-31-render-architecture-reset-handoff.md, then
the "Render Pipeline" section of docs/architecture/acdream-architecture.md.
State both altitudes: Working toward M1.5 "indoor world feels right." After a week of
point-fixing with no shippable progress, this is an ARCHITECTURE RESET, not a symptom fix.
THE PROBLEM (root cause, evidence in the handoff): acdream's render pipeline is a
patchwork of THREE inconsistent visibility gates (terrain TerrainClipMode / shell
per-cell clip / entity ParentCellId filter with an outdoor-stab bypass) bolted onto a
half-finished PView port. Seams are structural. The interior cell shells RENDER FINE
(proven); the residual is that OUTDOOR geometry isn't gated to portal openings when
indoors (#78) and the three gates don't agree.
THE TARGET (retail PView, §4-5 of the handoff): ONE portal-visibility traversal whose
output is the SINGLE GATE for ALL geometry — interior shells, interior statics, AND the
outside (terrain + scenery) all clipped to their PView regions; outside drawn ONLY
through exit-portal openings; empty OutsideView ⇒ no outdoor; no ParentCellId==null
bypass; outdoor root ⇒ gate=everything. We already own the builder (PortalVisibilityBuilder
= ConstructView port) + ClipFrame; this is CONSOLIDATION, not a rewrite.
MISSION (in order): (1) audit the current 3-gate data-flow in GameWindow.OnRender
~:7290-7471; (2) VERIFY the one-gate model against retail PView (ConstructView :433750,
InitCell :432896, DrawCells :432715, find_visible_child_cell :311397) + WB RenderInsideOut
(references/WorldBuilder/.../VisibilityManager.cs) — confirm from source, not the handoff
summary; (3) brainstorm + spec the unified gate (superpowers:brainstorming); (4) implement
the single gate, delete the ad-hoc gates/bypasses; (5) visual-verify ALL indoor issues
resolve together.
DO NOT REPEAT (evidence-disproven, handoff §2): camera/eye as root, cull mode, shell
geometry/texture missing, H1 PVS grounding, H2 PortalSide, zoom-confound. DO NOT bolt on
a fourth gate to mask a seam. Evidence-first; no point-fixes; brainstorm the architecture
before coding; only stop for visual verification.
Apparatus ready: ACDREAM_PROBE_FLAP ([flap]/[flap-cam]/[flap-sweep]) + ACDREAM_PROBE_SHELL
([shell]). Open decision: keep or revert e099b4c (camera collision, orthogonal — parked).
Launch per CLAUDE.md "Running the client"; Holtburg cottage cellar/stairs is the test
scenario; visual verification at that threshold is the acceptance gate.
```