docs(render): Phase R0 — lock the render-redesign design spec (brainstorm outcome)
Resolves the plan §3 open questions with the user this session: - object/entity/particle draw = LITERAL PER-CELL LOOP (retail DrawCells), not a global MDI batch with per-instance clip. Fidelity > perf > blast-radius. - sequencing = HOLISTIC: build the per-cell DrawInside directly; no intermediate global-pass gate-fix. First visual gate = sealed cottage interior, no bleed. - terrain in the seal = FAITHFUL: drawn only through the exit-portal clip, never as a floor under the interior. Inventory's 'relax Skip' suggestion REJECTED as a non-retail workaround; grey-floor = a sealing bug (verify cell mesh in R1). - WB mesh pipeline KEPT (per-cell draws from the global buffers, batched within a cell); two-camera invariant preserved (eye projects, player cell roots visibility). Phases (holistic): R1 unified per-cell DrawInside (the core) -> R2 outside-looking-in (DrawPortal) -> R3 dungeons -> R4 polish+cleanup. Each ends GREEN + a user visual gate. Retail anchors cited throughout (RenderNormalMode 0x453aa0, DrawCells 0x5a4840, etc). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
21bf97ed35
commit
7aca79f8eb
2 changed files with 336 additions and 0 deletions
|
|
@ -134,6 +134,13 @@ rewrite.** No stencil two-pipe, no `isInside` gate, no AABB grace-frame, no WB `
|
|||
---
|
||||
|
||||
## 3. Open questions for the R0 brainstorm
|
||||
|
||||
> **RESOLVED 2026-06-02** in [`docs/superpowers/specs/2026-06-02-render-pipeline-redesign-design.md`](../specs/2026-06-02-render-pipeline-redesign-design.md) §1.
|
||||
> Outcome: object/entity/particle draw = **literal per-cell loop** (retail `DrawCells`), not global MDI;
|
||||
> sequencing = **holistic** (build the per-cell `DrawInside` directly, no intermediate global-pass gate-fix);
|
||||
> terrain in the seal = **faithful** (only through the exit-portal clip; the "relax Skip" suggestion is
|
||||
> rejected as a workaround); WB mesh pipeline kept (per-cell draws from the global buffers); two-camera
|
||||
> invariant preserved (eye projects, player cell roots visibility). The design spec is the locked authority.
|
||||
- **Outdoor scenery while a door is open:** when indoors looking out, the visible outdoor scenery
|
||||
(the cottage across the street) must draw — but clipped to the doorway. Does that come for free
|
||||
from "LScape through the exit portal" (terrain + scenery both), or does scenery need its own
|
||||
|
|
|
|||
|
|
@ -0,0 +1,329 @@
|
|||
# Render Pipeline Redesign — Locked Design Spec (R0 outcome, 2026-06-02)
|
||||
|
||||
> **This is the design locked in the R0 brainstorm.** It supersedes the open questions in
|
||||
> [`docs/superpowers/plans/2026-06-02-render-pipeline-redesign-plan.md`](2026-06-02-render-pipeline-redesign-plan.md) §3
|
||||
> with concrete decisions, and is the input `writing-plans` turns into the implementation plan.
|
||||
>
|
||||
> **Read first (context, in order):**
|
||||
> 1. [`docs/research/2026-06-02-render-pipeline-redesign-handoff.md`](../../research/2026-06-02-render-pipeline-redesign-handoff.md) — proven root cause (§2), the three-gate failure (§3), the retail target (§5).
|
||||
> 2. [`docs/research/2026-06-02-retail-render-pipeline-full-reference.md`](../../research/2026-06-02-retail-render-pipeline-full-reference.md) — the retail PView pipeline + the `DrawCells` seal mechanics (the algorithm being ported).
|
||||
> 3. [`docs/research/2026-06-02-acdream-render-pipeline-inventory-and-failures.md`](../../research/2026-06-02-acdream-render-pipeline-inventory-and-failures.md) — the concrete bugs (the `WbDrawDispatcher.cs:1756` bypass; the parallel BFS; the terrain Skip model).
|
||||
> 4. [`docs/research/2026-06-02-render-reference-crosscheck.md`](../../research/2026-06-02-render-reference-crosscheck.md) — why WB's two-pipe stencil is the wrong model (do NOT reintroduce).
|
||||
|
||||
---
|
||||
|
||||
## 0. Mandate (user, 2026-06-02 — non-negotiable)
|
||||
|
||||
FULLY WORKING outdoor + indoor + dungeon rendering. No flaps, no missing textures, no transparent
|
||||
walls, no terrain leaking into cellars, no entity/particle bleed-through, outside-looking-in works.
|
||||
**No shortcuts, no bandaids, no quick fixes** — take the architecturally-correct path even if slower;
|
||||
redesign the whole pipeline if needed; **port from retail** (the oracle is `docs/research/named-retail/`);
|
||||
do more research mid-session rather than guess.
|
||||
|
||||
---
|
||||
|
||||
## 1. Decisions locked in the R0 brainstorm
|
||||
|
||||
These are the resolutions to the plan §3 open questions, decided with the user this session. They are
|
||||
binding for the implementation plan.
|
||||
|
||||
1. **Object / entity / particle draw = LITERAL PER-CELL LOOP.** A faithful port of retail
|
||||
`PView::DrawCells`' per-cell loops (iterate `cell_draw_list` closest-first; draw each visible cell's
|
||||
shell + objects + particles, clipped to that cell's portal-derived region). **NOT** the alternative
|
||||
of keeping one global `glMultiDrawElementsIndirect` pass with per-instance clip slots. The user's
|
||||
stated priority order: **retail-structural fidelity > perf > minimal blast-radius.**
|
||||
|
||||
2. **Sequencing = HOLISTIC.** Build the per-cell `DrawInside` **directly**. Do **not** ship an
|
||||
intermediate "fix the gate on the existing global pass" step. The first phase is the full per-cell
|
||||
`DrawInside`; the first visual gate is the **sealed cottage interior with no bleed**. (Verification
|
||||
with the probes + the user's eyes still happens at natural checkpoints *within* the per-cell build —
|
||||
that is evidence discipline on the real architecture, not the rejected throwaway intermediate.)
|
||||
|
||||
3. **Terrain in the seal = FAITHFUL.** Terrain (and outdoor scenery, and sky/weather) is drawn **only
|
||||
through the exit-portal clip region**, never as a floor under the interior. The inventory doc's
|
||||
"REDESIGN item #4" suggestion — *relax `TerrainClipMode.Skip` for `seen_outside=true` cottages so the
|
||||
cellar floor stops going grey* — is **REJECTED as a non-retail workaround.** The "grey floor / grey
|
||||
world in cellar" symptom is a **sealing bug** (the closed cell mesh is not covering those pixels: a
|
||||
missing or back-facing floor polygon, or the GL clear color showing through a gap). The faithful fix
|
||||
is to make the cell mesh seal; verified by the `[shell]` probe + a dat dump of the cellar EnvCell mesh.
|
||||
|
||||
4. **WB mesh pipeline KEPT; orchestration restructured.** The global VAO/VBO/IBO, GfxObj decode, and
|
||||
texture cache stay. What changes is that draws are issued **per visible cell** from those global
|
||||
buffers (batched *within* a cell where the geometry allows), not one global batch across all entities.
|
||||
"Keep the WB mesh pipeline, restructure the orchestration" still holds.
|
||||
|
||||
5. **Two-camera invariant PRESERVED.** The 3rd-person eye drives the **projection** (`envCellViewProj`);
|
||||
the **player's physics cell** (`CellGraph.CurrCell`) roots **visibility** and the portal-side test.
|
||||
This is the U.4c flap fix and it is kept verbatim.
|
||||
|
||||
---
|
||||
|
||||
## 2. The one model (the inversion)
|
||||
|
||||
There is **one** top-level decision per frame — a faithful port of retail
|
||||
`SmartBox::RenderNormalMode @ 0x453aa0` (pc:92635):
|
||||
|
||||
```
|
||||
RenderWorld(viewer):
|
||||
if clipRoot == null: # viewer in an outdoor LandCell (retail is_player_outside: id&0xFFFF < 0x100)
|
||||
DrawOutside() # today's outdoor path: terrain + scenery entities + sky/weather.
|
||||
# building INTERIORS reached via DrawPortal (R2 — outside-looking-in).
|
||||
else: # viewer in an EnvCell (clipRoot != null)
|
||||
DrawInside(clipRoot) # ONE PView flood — nothing else. The outdoor world is NOT drawn;
|
||||
# it enters ONLY through clipped exit portals, inside DrawInside.
|
||||
```
|
||||
|
||||
`DrawInside` is a faithful port of `PView::DrawInside @ 0x5a5860` → `ConstructView @ 0x5a57b0` →
|
||||
`DrawCells @ 0x5a4840`:
|
||||
|
||||
```
|
||||
DrawInside(cell):
|
||||
frame = PortalVisibilityBuilder.Build(cell, playerPos, eyeViewProj) # KEEP — the PVS is correct
|
||||
if frame.OutsideView non-empty: # an exit portal is in view
|
||||
DrawLandscapeClippedTo(frame.OutsideView) # terrain + outdoor scenery entities + sky, thru the doorway
|
||||
ConditionalDepthOnlyClear(frame.OutsideView)# Z only — never color → no blue hole
|
||||
for cellId in frame.OrderedVisibleCells: # the per-cell loop (retail DrawCells loops 1-3)
|
||||
clip = frame.CellViews[cellId]
|
||||
DrawCellShell(cellId, clip) # EnvCellRenderer.Render(pass, {cellId}) — closed mesh
|
||||
DrawCellObjects(cellId, clip) # ONLY this cell's entities, clipped to its region
|
||||
DrawCellParticles(cellId, clip) # ONLY this cell's particles — solves #104 for free
|
||||
```
|
||||
|
||||
**Visibility *is* the cull.** No global entity pass; no second visibility computation. The bleed cannot
|
||||
happen by construction — the outdoor world is never iterated when inside (except the clipped doorway).
|
||||
This is the inversion of the current bug: acdream draws the outdoor world and *then* gates a few cell
|
||||
shells on top through three inconsistent gates (handoff §3); retail never visits the outdoor world when
|
||||
inside.
|
||||
|
||||
---
|
||||
|
||||
## 3. Keep / Build / Remove
|
||||
|
||||
### KEEP (proven correct — handoff §4, inventory §5)
|
||||
- `PortalVisibilityBuilder` — the PView BFS. Produces `OutsideView`, `CellViews` (per-cell NDC clip),
|
||||
`OrderedVisibleCells` (closest-first), `CrossBuildingViews`. The single visibility authority.
|
||||
- `ClipFrame` / `ClipFrameAssembler` / `ClipPlaneSet` / `PortalView` — the clip-plane machinery and the
|
||||
per-cell NDC→GPU slot packing. (`ClipFrameAssembly` already exposes `CellIdToSlot`, `OutdoorSlot`,
|
||||
`OutdoorVisible`, `TerrainMode`, `HasOutsideView`, `OutsideViewNdcAabb` — everything the seal needs.)
|
||||
- `EnvCellRenderer` mesh/MDI/texture path, including `Render(pass, filter)` (a single-cell filter set
|
||||
drives one cell — the hook the per-cell loop uses).
|
||||
- `TerrainModernRenderer` (the renderer; only its caller's clip mode changes).
|
||||
- The WB mesh pipeline (global VAO/VBO, GfxObj decode, `TextureCache`).
|
||||
- The membership fix `59f3a13` (`[cell-transit]` confirms correct cell tracking — do NOT reopen).
|
||||
- The Stage-4 sky NDC-clip + the conditional doorway Z-clear (`ce2edad`/`b595cfb`).
|
||||
- All diagnostic probes (`ACDREAM_PROBE_CELL` / `_VIS` / `_SHELL` / `_FLAP`).
|
||||
|
||||
### BUILD (the redesign)
|
||||
- A new **`DrawInside` orchestrator** (the §2 per-cell loop) that replaces the current
|
||||
"global terrain → global shells → global entity pass" structure in `GameWindow.OnRender`
|
||||
(~7250–7610).
|
||||
- The **binary top-level decision** (`clipRoot == null` → `DrawOutside`; else → `DrawInside` only).
|
||||
- A **per-cell entity dispatch** issued from the global buffers, batched within a cell.
|
||||
- A **per-cell particle draw** (each cell's particles in its clip scope).
|
||||
- The **landscape-through-the-door** composition (terrain + outdoor scenery + sky clipped to `OutsideView`)
|
||||
+ the conditional Z-only clear.
|
||||
- (R2) the **outside-looking-in `DrawPortal`** path (separate outdoor pview).
|
||||
|
||||
### REMOVE (dead / divergent — scheduled in the final polish phase so it can't destabilize earlier work)
|
||||
- The dormant WB-two-pipe scaffolding: `Building`, `BuildingLoader`, `ExitPortalPolygons` stencil-marking,
|
||||
the occlusion-query state (`QueryId`/`WasVisible`), and the `IsShellScopedSet` / `BuildingShellAnchorCellId`
|
||||
anchor machinery (`IsShellScopedSet` already returns `false` — U.1 deleted the live two-pipe).
|
||||
- `CellVisibility` as a **rendering gate** — its `VisibleCellIds` set retires as the entity gate. Its
|
||||
`CameraCell` / root-selection role (the indoor/outdoor root decision) **stays**.
|
||||
- The `CullMode.Landblock → None` double-sided stopgap (`EnvCellRenderer.cs:~1216`) — replace with the
|
||||
correct per-polygon winding once the seal is verified.
|
||||
|
||||
---
|
||||
|
||||
## 4. The seal mechanics (`PView::DrawCells @ 0x5a4840` port)
|
||||
|
||||
Two parts: the landscape-through-the-door block, then the per-cell loop. (Full retail verbatim:
|
||||
research doc A §4.)
|
||||
|
||||
### 4.1 Landscape through the door (only when `OutsideView` is non-empty)
|
||||
- Draw the "landscape" clipped to the doorway's screen silhouette (`OutsideView`). Retail does this with
|
||||
one `LScape::draw` (`PortalList = this`). In acdream the landscape is split across three renderers, so
|
||||
this one step becomes:
|
||||
- **terrain** (`TerrainModernRenderer`, gated by the clip-plane UBO in Planes mode, or `glScissor` to
|
||||
the `OutsideView` AABB in Scissor mode),
|
||||
- **outdoor scenery entities** (`ParentCellId == null`, clipped to `OutsideView` / `OutdoorSlot`),
|
||||
- **sky / weather** (`SkyRenderer`, `gl_ClipDistance` against the same UBO — the Stage-4 path, kept).
|
||||
- Then a **conditional depth-only clear** scissored to the `OutsideView` AABB
|
||||
(`Clear(4, …)` — flag 4 = depth buffer only, retail pc:432731). Color is preserved (terrain painted
|
||||
through the door stays); depth resets so interior geometry composites without z-fighting at the doorway
|
||||
edge. **There is NEVER a color clear in the indoor path** — that is structurally why there is no blue hole.
|
||||
|
||||
### 4.2 The per-cell loop over `OrderedVisibleCells` (closest-first; each cell clipped to `CellViews[cellId]`)
|
||||
- **Shell** (retail DrawCells Loop 2 — `DrawEnvCell`): `EnvCellRenderer.Render(pass, {cellId})` draws the
|
||||
cell's **closed** mesh (floor + walls + ceiling) from the cell's dat geometry. The ceiling and floor seal
|
||||
*because they are authored in the dat mesh* — there is **no "cap the ceiling" step**. Doorways are genuine
|
||||
holes in the mesh, so the landscape from §4.1 shows through them. (Retail Loop 1 stencils the exit-portal
|
||||
openings; acdream's mesh-hole + the §4.1 Z-clear achieve the same composition — **R1 verifies** whether an
|
||||
explicit portal stencil is needed or the mesh-hole + Z-clear suffice.)
|
||||
- **Objects** (Loop 3 — `DrawObjCellForDummies`): only this cell's entities (`object_list` equivalent),
|
||||
clipped to the cell's region. Live-dynamic entities (player/NPCs/items, `serverGuid != 0`) render
|
||||
unclipped (depth only) per retail.
|
||||
- **Particles:** only this cell's particles, in the cell's clip scope (closes #104 — no per-instance slot
|
||||
needed because the draw is already per-cell).
|
||||
- **Self-contained GL state:** each per-cell draw SETS every GL state it depends on (view-proj, blend,
|
||||
depth-mask, cull, front-face, A2C) — never inherited (memory `render-self-contained-gl-state`; this bit
|
||||
`EnvCellRenderer` three times in U.4).
|
||||
|
||||
### 4.3 Why the seal holds (the four guarantees — retail doc A §4.5)
|
||||
1. **No blue hole:** outdoors is drawn first (clipped to `OutsideView`); the only clear is Z-only +
|
||||
conditional; color survives in the doorway.
|
||||
2. **Sealed ceiling/walls/floor:** each visible cell's mesh is a closed box; portal holes are the only
|
||||
openings.
|
||||
3. **No outdoor bleed-in:** the landscape paints only through exit-portal clip regions; if `OutsideView`
|
||||
is empty (dungeon, or facing away from the door), no terrain/sky is drawn at all.
|
||||
4. **No object/particle bleed:** objects/particles are drawn per-cell, only for cells in
|
||||
`OrderedVisibleCells`, clipped to the cell's region.
|
||||
|
||||
---
|
||||
|
||||
## 5. The per-cell loop & batching (how the mesh pipeline is preserved)
|
||||
|
||||
The user chose the literal per-cell loop over global MDI; this section pins down how that coexists with the
|
||||
KEPT mesh pipeline so the plan does not regress perf catastrophically or break the working mesh path.
|
||||
|
||||
- **Geometry storage is unchanged.** All cell shells and entity meshes live in the global VAO/VBO/IBO; each
|
||||
batch references its slice via `BaseVertex` / `FirstIndex`. The per-cell loop issues draws against those
|
||||
slices — it does not re-upload or re-pack geometry per cell.
|
||||
- **Batched within a cell.** Each cell's draw still groups by mesh/material and issues an MDI call for that
|
||||
cell's object set (per-cell MDI), rather than a draw call per object. So we lose *cross-cell* batching
|
||||
(the retail-faithful cost the user accepted) but keep *within-cell* batching. A cottage interior is a
|
||||
handful of visible cells, so the per-cell call count is small.
|
||||
- **Clip is the cell's region, applied per-cell.** Because the loop is per-cell, the cell's clip region is
|
||||
bound once before that cell's draws (clip-plane UBO / `gl_ClipDistance`), not per-instance. This is what
|
||||
lets particles (no instanceID) be clipped — the #104 win.
|
||||
- **`EnvCellRenderer`** is driven with a single-cell filter (`Render(pass, {cellId})`) per loop iteration,
|
||||
or a small per-cell render entry is added if the snapshot model needs it (decided in the plan; the
|
||||
existing filter path is the starting point).
|
||||
- **Entity dispatch** becomes per-cell: instead of `WbDrawDispatcher.Draw(... visibleCellIds ...)` walking
|
||||
all entities once, the orchestrator asks for each visible cell's entities (entities whose `ParentCellId`
|
||||
== cellId) and issues that cell's batched draw. The outdoor-scenery entities (`ParentCellId == null`) are
|
||||
drawn in the §4.1 landscape-through-door step, clipped to `OutsideView`, NOT in any interior cell's loop.
|
||||
|
||||
**Perf note (risk, not a blocker):** per-cell dispatch raises draw-call count vs the single global MDI. The
|
||||
N.6 baseline showed CPU dominates GPU by 30–50× and the GPU sits at ~3.6% of frame budget, so there is
|
||||
headroom; a cottage is a few cells. If a complex interior or dungeon regresses, within-cell batching +
|
||||
the closest-first order (early-Z) are the mitigations. Measure at the R1 gate; do not pre-optimize.
|
||||
|
||||
---
|
||||
|
||||
## 6. Component-level design
|
||||
|
||||
| Concern | Component | Change |
|
||||
|---|---|---|
|
||||
| Top-level decision | `GameWindow.OnRender` | New binary branch: `clipRoot == null` → `DrawOutside` (existing); else → `DrawInside` only. Remove the "global outdoor draw then shells/entities on top" structure. |
|
||||
| Visibility | `PortalVisibilityBuilder` | **Keep.** Sole authority. Root at the player cell, project from the eye (§1.5 invariant). |
|
||||
| Clip packing | `ClipFrameAssembler` / `ClipFrame` | **Keep.** Per-cell clip slots + `OutsideView` already produced; the per-cell loop consumes `CellViews` directly and/or the assembled slots. |
|
||||
| Indoor orchestration | **new** `DrawInside` orchestrator (App layer) | The §2 per-cell loop. Owns: landscape-through-door, conditional Z-clear, the per-cell shell/object/particle draws. Lives in `AcDream.App.Rendering` (not `GameWindow.cs` per Code Structure Rule 1). |
|
||||
| Cell shells | `EnvCellRenderer` | Driven per-cell (`Render(pass, {cellId})`). Keep the mesh/MDI/texture path. Self-contained GL state. |
|
||||
| Entities | `WbDrawDispatcher` | Restructured: per-cell entity draw (entities by `ParentCellId == cellId`); outdoor scenery drawn in the landscape-through-door step. Delete the `:1756` `ParentCellId==null → return true` bypass and the dead `IsShellScopedSet` branch. |
|
||||
| Particles | `ParticleRenderer` | Per-cell draw in the cell's clip scope (give particles a cell via the owning entity's `ParentCellId`). Closes #104. |
|
||||
| Terrain | `TerrainModernRenderer` | Drawn only in the landscape-through-door step (Planes/Scissor clip). When `OutsideView` empty → not drawn (faithful Skip). |
|
||||
| Sky/weather | `SkyRenderer` | Drawn in the landscape-through-door step, `gl_ClipDistance` clip (Stage-4, kept). |
|
||||
| Root selection | `CellVisibility` | Keep `CameraCell` / root selection. Retire `VisibleCellIds` as a render gate. |
|
||||
| Outside-looking-in | **new** `DrawPortal` path (R2) | Separate outdoor pview; `ConstructView(CBldPortal)` recursion; `DrawCells` the interior through the door's clip. |
|
||||
|
||||
---
|
||||
|
||||
## 7. Phase plan (holistic) + visual gates + retail anchors
|
||||
|
||||
> Each phase ends GREEN (build + `dotnet test`) **and** at a **user visual gate**. A seal is verified on
|
||||
> screen (probes + the user's eyes), never off the test suite — the lesson that produced this redesign
|
||||
> (handoff §9). Phases are NOT batched past a gate.
|
||||
|
||||
### R1 — Unified per-cell `DrawInside` (the core) — THE make-or-break phase
|
||||
**Retail anchors:** `RenderNormalMode @ 0x453aa0` (binary decision), `PView::DrawInside @ 0x5a5860`,
|
||||
`ConstructView @ 0x5a57b0`, `DrawCells @ 0x5a4840` (the seal + the three per-cell loops), fact 8
|
||||
(visibility is the cull).
|
||||
- Build the binary top-level decision (indoor → `DrawInside` only; the global outdoor pass is not issued).
|
||||
- Build the per-cell loop: landscape-through-door (terrain + outdoor scenery + sky, clipped to `OutsideView`)
|
||||
→ conditional Z-only clear → per-cell closed shells → per-cell objects → per-cell particles.
|
||||
- Delete the `WbDrawDispatcher.cs:1756` bypass (outdoor scenery now enters only via the landscape-through-door
|
||||
step). Retire the `CellVisibility.VisibleCellIds` render gate.
|
||||
- **Verify the cell mesh seals** (the grey-floor investigation): `[shell]` probe + a dat dump of the cellar
|
||||
EnvCell mesh; confirm floor + walls + ceiling are present and front-facing; decide stencil-vs-mesh-hole for
|
||||
the doorway (§4.2).
|
||||
- Internal checkpoints (probes + eyes, on the real per-cell architecture): (a) sealed shells + landscape-thru-door;
|
||||
(b) per-cell objects (no entity bleed); (c) per-cell particles (no smoke bleed).
|
||||
- **Gate (visual):** Holtburg cottage + cellar — sealed interior (opaque walls, solid floor, ceiling),
|
||||
sky/rain through the door only, **no blue hole, no terrain under the floor, no grey floor**, no
|
||||
entity/scenery/particle bleed.
|
||||
|
||||
### R2 — Outside-looking-in (`DrawPortal`)
|
||||
**Retail anchors:** `PView::DrawPortal @ 0x5a5ab0`, `ConstructView(CBldPortal) @ 0x5a59a0`, fact 9.
|
||||
- On the outdoor path, for each visible building door, run a separate `outdoor_pview`:
|
||||
`ConstructView(CBldPortal)` (side-test the door plane, clip to the opening) → recurse into the interior →
|
||||
`DrawCells` the interior through the door's clip. Same machinery as `DrawInside`.
|
||||
- **Gate (visual):** standing in the street facing the cottage door/window, the **sealed interior** renders
|
||||
through the opening — not transparent walls.
|
||||
|
||||
### R3 — Dungeons
|
||||
**Retail anchors:** fact 10 (emergent — all-EnvCell, `seen_outside == 0`, no exit portals), the
|
||||
`update_count` watermark (fact 12 / #95), `grab_visible_cells @ 0x52e220` (landscape iff `seen_outside`).
|
||||
- Validate the all-EnvCell path on a real dungeon: `OutsideView` stays empty → no terrain/sky by
|
||||
construction; sealed cells; the watermark BFS converges (confirm #95 closed — no FPS collapse).
|
||||
- **Gate (visual):** a real dungeon is sealed, no terrain/sky, no FPS collapse from the BFS.
|
||||
|
||||
### R4 — Polish + cleanup + conformance
|
||||
- Resolve the `CullMode.Landblock → None` double-sided stopgap (the real per-polygon winding).
|
||||
- Remove the dormant WB-two-pipe scaffolding (`Building`, `BuildingLoader`, stencil/occlusion, anchor machinery).
|
||||
- Headless conformance tests (G1–G4 below). Confirm no missing textures across dungeons.
|
||||
- Update the roadmap; flip the M1.5 milestone; memory notes.
|
||||
- **Gate (visual):** cottage + dungeon + outside-looking-in, all sealed and seamless.
|
||||
|
||||
---
|
||||
|
||||
## 8. Risks
|
||||
|
||||
- **R1 is a large phase** (the holistic choice). Mitigation: build behind internal probe/eyes checkpoints on
|
||||
the real per-cell architecture; keep the outdoor path working throughout (the 99% case is unaffected — the
|
||||
binary branch leaves `DrawOutside` intact).
|
||||
- **Per-cell draw-call count** vs the single global MDI (§5). Mitigation: within-cell batching + closest-first
|
||||
early-Z; measure at the R1 gate; the N.6 baseline shows large CPU/GPU headroom.
|
||||
- **The grey-floor cause is unconfirmed** — it may be a missing dat floor polygon, a winding/cull issue, or a
|
||||
clear-color gap. Mitigation: evidence-first in R1 (probe + dat dump) before any fix; do **not** relax the
|
||||
faithful terrain Skip (decision §1.3).
|
||||
- **Do not reintroduce the abandoned approaches** (handoff §9): no stencil two-pipe, no `isInside` gate, no
|
||||
AABB grace-frame for the visibility root.
|
||||
|
||||
## 9. No-shortcuts rules (enforced on every task — plan §5)
|
||||
1. A fast-but-wrong path is rejected for the retail-faithful path; the tradeoff is noted in the commit.
|
||||
2. No suppression flags, grace periods, or `if (problem) return early` guards at a symptom site.
|
||||
3. Every AC-specific behavior cites a retail decomp anchor (address + pseudo-C line).
|
||||
4. Mid-session research over guessing — if the retail behavior is unclear, read the decomp / attach cdb.
|
||||
5. Each phase ends GREEN (build + tests) AND at a user visual gate. The seal is verified on screen.
|
||||
|
||||
## 10. Conformance / acceptance gates (headless asserts — retail doc A §7 CL-G)
|
||||
- **G1 (cottage, `seen_outside`):** interior sealed; sky/rain through the door; no blue hole; no transparent
|
||||
walls; no bleed. `OutsideView` non-empty ⇒ the landscape-through-door step runs.
|
||||
- **G2 (dungeon, `seen_outside == 0`):** sealed; no terrain/sky; `OutsideView` empty ⇒ landscape step NOT run;
|
||||
BFS converges (watermark) without blowup (#95).
|
||||
- **G3 (outside-looking-in):** facing an open cottage door from the street, the interior renders through the
|
||||
doorway (R2).
|
||||
- **G4 (invariants):** PVS root id == physics `CurrCell.Id` every frame; a cell receiving two clip slices is
|
||||
processed once per slice (watermark); eye drives projection, player cell roots visibility.
|
||||
|
||||
## 11. Decomp anchor index (verified in the research docs this session)
|
||||
```
|
||||
SmartBox::RenderNormalMode 0x00453aa0 pc:92635 binary decision (DrawInside vs LScape::draw)
|
||||
SmartBox::is_player_outside 0x00451e80 pc:90996 low-word objcell_id < 0x100
|
||||
CellManager::ChangePosition 0x004559b0 pc:94601 keep/release landscape on seen_outside
|
||||
CEnvCell::grab_visible_cells 0x0052e220 pc:311878 self+stab; landscape iff seen_outside (@311893)
|
||||
PView::DrawInside 0x005a5860 pc:433793
|
||||
PView::ConstructView(CEnvCell) 0x005a57b0 pc:433750 the BFS worklist
|
||||
PView::ConstructView(CBldPortal) 0x005a59a0 pc:433827 exterior→interior recursion (outside-looking-in)
|
||||
PView::InitCell 0x005a4b70 pc:432896 per-portal sidedness; update_count = view_count
|
||||
PView::ClipPortals 0x005a5520 pc:433572 exit portal → outside_view (@433662); interior → OtherPortalClip
|
||||
PView::AddViewToPortals 0x005a52d0 pc:433446 enqueue neighbours / SetOtherSeen
|
||||
PView::DrawCells 0x005a4840 pc:432709 LScape-thru-door + Z-clear + 3 per-cell loops
|
||||
PView::DrawPortal 0x005a5ab0 pc:433895 outside-looking-in entry
|
||||
CEnvCell::find_visible_child_cell 0x0052dc50 pc:311397 point → child cell via portals/stab_list (camera child)
|
||||
LScape::draw 0x00506330 terrain+sky; clipped via Render::PortalList
|
||||
portal_view_type.update_count acclient.h:32346 watermark — BFS convergence (#95/#102)
|
||||
CCellStruct drawing_bsp acclient.h:32275 closed cell mesh (floor+walls+ceiling); no cap step
|
||||
CCellPortal.other_cell_id acclient.h:32300 0xFFFFFFFF (low 0xFFFF) ⇒ EXIT PORTAL
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue