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>
14 KiB
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,idx24–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=3cells,terrain=Skip/outPolys=0correct 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
e099b4cbut 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,
639f20fplumbed, 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):
- Three separate gates (terrain / shells / entities) must independently agree to produce a seamless interior. They don't. Retail has one gate (PView).
- 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. - 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.
- 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::ChangePositiontrackscurr_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 oncell_draw_listclosest-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_outsidegate 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.
- 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 inEnvCellRenderer). - Interior static in cell
C→ cellC's region (route via the same per-instance clip slot). - Outside (terrain + outdoor scenery +
ParentCellId==nullstabs) → theOutsideViewregion (union of exit-portal openings). EmptyOutsideView(windowless interior) ⇒ draw NO outdoor geometry. NoParentCellId==nullbypass. - Outdoor root (camera/player in a LandCell, no indoor cell) ⇒ gate = everything (today's normal path).
- Interior cell
- Delete the ad-hoc gates. Collapse GATE #1/#2/#3 into the single clip-to-region.
Remove the
ParentCellId==nullalways-pass inWbDrawDispatcher. Remove the terrain Scissor over-include fallback in favour of retail's exact portal clip. - 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).
- 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)
- Audit the current render data-flow against §3; confirm the three-gate divergence in the live code.
- Verify the target against retail: read PView in full
(
ConstructView/InitCell/ClipPortals/AddViewToPortals/DrawCells/grab_visible_cells/find_visible_child_cell) and WBRenderInsideOut. Do not trust this doc's summary alone — confirm the one-gate model from the source. - Design the unified gate (
superpowers:brainstorming→ spec). This is architectural; brainstorm it, don't free-solo. - Implement the single gate; delete the ad-hoc gates/bypasses.
- 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 —
0ee328aflap fix: root indoor visibility at the player's cell. A correct, retail-faithful PView piece (the set must be player-rooted). Keep. - ORTHOGONAL —
e099b4ccamera-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
CameraCollisionIndoorTestsare accurate for what they cover but are about an orthogonal subsystem.
- the
- 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 stashentries preserved (#98/#101 WIP) — do NOT drop. - Throwaway capture logs
u4c-*.logare 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.