# 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` 24–270, `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. ```