docs: Phase A comparison + Phase B port plan (holistic building-render investigation)
Deliverable 1: docs/research/2026-06-11-building-render-acdream-vs-retail- comparison.md - the acdream-vs-retail architecture comparison synthesized from two ultracode mapping fan-outs (11/12 areas, ~90 agents, every retail claim Ghidra/pc-cited, every acdream claim file:line, 40/76 divergences adversarially verified so far; raw per-area evidence committed under docs/research/2026-06-11-holistic-map/). Headline findings: (1) retail flattens GfxObjs/cells at load exactly like us (ConstructMesh + RemoveNonPortalNodes) - the MDI pipeline survives; (2) the phantom/door mechanism is the skipNoTexture draw-time surface gate (dat-confirmed); (3) retail never geometrically clips world geometry - aperture exactness is a DEPTH discipline (punch maxZ1 / seal maxZ2 / gated clear + far-to-near whole-mesh draws) - reframes #114; (4) flood admission is already faithful, the trigger/depth/multi-view/cone-culling layers are missing; (5) #115 root cause verified (boom damping severed from the published collided viewer); collision A6.P4 design verified with corrections (signed other_portal_id >= 0 gate). Deliverable 2: docs/plans/2026-06-11-building-render-port-plan.md - the phased port plan (BR-1 surface gate, BR-2 depth punch/seal, BR-3 delete the shell chop, BR-4 draw-driven floods, BR-5 viewconeCheck, BR-6 one gate, BR-7 collision A6.P4, BR-8 camera/lighting/LOD) with per-phase acceptance criteria, bug closures, keep-list, and a playable-after-every- phase migration order. AWAITING USER APPROVAL - no implementation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
31ea849277
commit
5e2f99d08e
13 changed files with 2120 additions and 0 deletions
271
docs/plans/2026-06-11-building-render-port-plan.md
Normal file
271
docs/plans/2026-06-11-building-render-port-plan.md
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
# The holistic building-render port plan (Phase B) — one drawing discipline
|
||||
|
||||
**Status: AWAITING USER APPROVAL — no implementation until approved.**
|
||||
Companion to the Phase A comparison:
|
||||
[`docs/research/2026-06-11-building-render-acdream-vs-retail-comparison.md`](../research/2026-06-11-building-render-acdream-vs-retail-comparison.md)
|
||||
(evidence appendices in
|
||||
[`docs/research/2026-06-11-holistic-map/`](../research/2026-06-11-holistic-map/)).
|
||||
Mandate: *"one solution that works every time I walk to a new landblock and
|
||||
walk into a dungeon"* (2026-06-11).
|
||||
|
||||
---
|
||||
|
||||
## 0. The invariant (what "one drawing discipline" means, retail-cited)
|
||||
|
||||
Every phase below moves us toward — and no phase may move us away from — this
|
||||
frame shape, which is retail's (Ghidra-cited in the comparison doc §2):
|
||||
|
||||
1. **Geometry is flattened at load** into surface-batched meshes (we already
|
||||
do this). World geometry is **never geometrically clipped at draw time**.
|
||||
2. **Untextured (solid) surface batches never draw** on building shells and
|
||||
cell meshes (`skipNoTexture`); they do draw on plain objects.
|
||||
3. **Portal polygons are not wall geometry.** They exist per frame only as
|
||||
(a) flood admission tests (`ConstructView`: eye-side ε=0.0002 → clip vs
|
||||
current view → cell loaded) and (b) **invisible depth writes** — far-Z
|
||||
*punch* before an interior draws through an aperture; true-depth *seal*
|
||||
on portals to the outside after the landscape draws.
|
||||
4. **Cells draw whole, far→near, once** (frame stamp); the z-buffer plus the
|
||||
punches/seals produce pixel-exact apertures.
|
||||
5. **Objects and particles are culled per portal view** (sphere vs the view's
|
||||
edge planes — `viewconeCheck`), never clipped, never scissored.
|
||||
6. **One visibility computation feeds everything** — the PView flood. No
|
||||
second BFS, no parallel gate, no distance constants in admission.
|
||||
|
||||
## 1. Keep-list (the code worth saving — explicitly not touched/rewritten)
|
||||
|
||||
- **Mesh pipeline**: `ObjectMeshManager` flatten + global VAO + bindless MDI
|
||||
(`WbDrawDispatcher`) — retail-faithful architecture, confirmed by the
|
||||
`ConstructMesh`/`RemoveNonPortalNodes` finding.
|
||||
- **The flood port**: `PortalVisibilityBuilder` (homogeneous clipper, side
|
||||
tests, reciprocal clip, exact-match skip) + conformance gates
|
||||
(`CornerFloodReplayTests`, `Issue113MeetingHallFloodTests`) — BR-4 adjusts
|
||||
constants/heuristics, it does not rewrite the clipper.
|
||||
- **Membership** (P1 9/9 golden) + **straddle gate** (`414c3de`) +
|
||||
**camera collision sweep** (verbatim `update_viewer`) + **znear=0.1** +
|
||||
**#105 texture flush** + **two-tier streaming** + spawn/snap validation
|
||||
(#107/#111/#112).
|
||||
- Diagnostics/probes and the dat dump harness.
|
||||
|
||||
The M0 freeze list is superseded *for rendering only* by the 2026-06-11
|
||||
mandate; nothing outside building/interior render + interior collision is in
|
||||
scope.
|
||||
|
||||
## 2. Phases
|
||||
|
||||
Ordering rule: each phase lands green (build + full suites + named visual
|
||||
gate) and the client stays playable after every phase. Conformance pins come
|
||||
from the dat harness + the flood replay harnesses; retail constants are cited
|
||||
inline when ported.
|
||||
|
||||
### BR-1 — The draw-time surface gate (kills the phantom class)
|
||||
|
||||
**What:** classify every mesh batch at decode by surface texturedness
|
||||
(`Surface.Type & (Base1Image|Base1ClipMap)`); at draw, skip untextured
|
||||
batches for **building-shell entities and cell meshes only** (plain objects
|
||||
keep drawing them — retail's bypass). Align the cell-side build-time
|
||||
`NoPos`/`NoNeg` drop with this rule: run a dat-wide sweep (all CellStructs +
|
||||
all building models in the populated landblocks) proving
|
||||
`portal-fill ⇔ untextured`; keep the cheaper build-time drop only where the
|
||||
sweep proves equivalence, otherwise move to the draw-time gate. GfxObj-side:
|
||||
fills stay in the mesh but never draw (matches retail exactly).
|
||||
|
||||
- **Closes:** #113 phantom staircase class (hall ramp, cottage "flying
|
||||
stairs", every building's baked fills) — without touching doors (entities).
|
||||
- **Acceptance:** `DumpPortalFillSurfaceTypes`-derived conformance sweep
|
||||
green; hall + hill-cottage phantom gone and doors/windows intact at
|
||||
Holtburg (user gate); all suites green.
|
||||
- **Risk note:** apertures whose flood fails become true holes
|
||||
(retail-identical); per-building floods + DrawPortal look-in already cover
|
||||
the visible cases. If a hole shows at an unflooded aperture, that is BR-2/
|
||||
BR-4 evidence, not a BR-1 regression.
|
||||
- **Size:** ~2 commits (batch metadata plumb + draw gate; sweep test).
|
||||
|
||||
### BR-2 — Aperture depth machinery (punch / seal / clear)
|
||||
|
||||
**What:** port the invisible depth writes:
|
||||
(a) wire `DrawExitPortalMasks` (today an unwired no-op) as a depth-only draw
|
||||
of each outside-leading portal polygon, software-clipped to its view slice
|
||||
(the `ClipToRegion` math already exists), at the portal's **true projected
|
||||
depth** (retail `maxZ2`) — after the landscape slices, indoor roots;
|
||||
(b) add the **far-Z punch** (retail `maxZ1`) on building-aperture flood
|
||||
success on the outdoor + look-in paths, before the interior cells draw;
|
||||
(c) replace the per-slice scissored `ClearDepthSlice` AABB clear with
|
||||
retail's discipline: one full depth clear between the outside stage and the
|
||||
interior stage, gated on whether any seal was drawn (`portalsDrawnCount`);
|
||||
(d) on the look-in path, draw interior-through-aperture **before** the shell
|
||||
mesh (retail `DrawBuilding` order) so the shell's depth closes everything
|
||||
outside the punch.
|
||||
|
||||
- **Closes:** #108 (outdoor terrain sweeping across the upstairs door — the
|
||||
missing true-depth seal is the confirmed `missing-portal-depth-fence`
|
||||
divergence); the outdoor-root depth-discipline gap; part of #109.
|
||||
- **Acceptance:** cellar↔main-floor walk shows no grass sweep (user gate);
|
||||
new harness fact: seal depth = portal plane depth inside the clipped
|
||||
aperture polygon (GL readback test or probe assertion); suites green.
|
||||
- **Size:** ~3 commits (~80 lines of GL + clipper reuse per the area
|
||||
estimate, plus the clear re-shape and order swap).
|
||||
|
||||
### BR-3 — Retire the geometric shell chop; whole-shell far→near draws
|
||||
|
||||
**What:** remove `gl_ClipDistance` as the *enforcement* mechanism for cell
|
||||
shells (both the outdoor-scoped enable from `927fd8f`/`9ce335e` and the
|
||||
never-enabled indoor half — i.e. #114 closes by *deleting* the chop, not
|
||||
perfecting it). Shells draw whole, far→near per `OrderedVisibleCells`
|
||||
(already the order), drawn-once. Clip regions remain for admission, punch
|
||||
shapes, and (BR-5) object culling. The landscape-through-aperture pass keeps
|
||||
its per-slice plane clip for now (open Q: `LScape::draw` internals) — revisit
|
||||
after BR-2 proves the seal protects terrain.
|
||||
|
||||
- **Closes:** #114 (chopped stairs / vanished candle area / barrel-through-
|
||||
wall were artifacts of clipping geometry retail never clips) — jointly
|
||||
with BR-2. Removes the 8-plane budget + slot-0 PASS-ALL as load-bearing
|
||||
for shells.
|
||||
- **Acceptance:** meeting-hall interior + multi-room cottages render
|
||||
unchopped from indoor and outdoor eyes (user gate vs the #114 screenshot
|
||||
set); phantom stays gone (BR-1 unaffected); flood replay gates green.
|
||||
- **Order constraint:** must not land before BR-2 (the depth fence replaces
|
||||
the chop's job at apertures).
|
||||
- **Size:** ~2 commits (mostly deletions + the draw-order assertion).
|
||||
|
||||
### BR-4 — Shell-draw-driven floods + flood fidelity
|
||||
|
||||
**What:** make the building's own draw the flood trigger, retail-shaped:
|
||||
pair the shell GfxObj's `PortalRef.PortalIndex` with its `BuildInfo.Portals`
|
||||
entry (the `outdoor_portal_list` correspondence) and, when a shell survives
|
||||
the cull for a view slice, run each aperture through the ported
|
||||
`ConstructView(CBldPortal)` chain under that slice. Then remove the
|
||||
non-retail machinery the trigger replaces: the 48 m seed constant, the
|
||||
Chebyshev≤1 candidate gather, the `EyeInsidePortalOpening` full-view rescue;
|
||||
adopt retail constants (ε=0.0002; in-plane rejects for building portals);
|
||||
add the 1-px screen-space vertex dedup to `ClipToRegion` output (retail's
|
||||
fixpoint floor) and switch late view growth to in-place propagation
|
||||
(`AddToCell`/`FixCellList`/`AdjustCellView` shape), removing the
|
||||
`MaxReprocessPerCell=16` cap; make `MergeBuildingFrame` union views instead
|
||||
of first-wins and retire single-slot consumers (`CellIdToSlot[0]`); bind
|
||||
nested floods to their originating slot (the `building_view` latch).
|
||||
|
||||
- **Closes:** #109 (binary 48 m pop + first-wins view loss + missing punch
|
||||
are its named mechanisms); the flood-stability family (edge-on doorway
|
||||
residuals); enables interior-visible-through-window parity.
|
||||
- **Acceptance:** flood replay harnesses extended: (a) building flood
|
||||
triggers with no distance constant — admission matches the
|
||||
clip-survival rule across an eye sweep; (b) two-aperture cell holds two
|
||||
views; (c) growth propagates without the cap on a portal-dense fixture;
|
||||
#109 spot user gate; suites green.
|
||||
- **Size:** ~4–5 commits (trigger + pairing; constants/dedup; growth
|
||||
in-place; merge union; deletions).
|
||||
|
||||
### BR-5 — Per-view object + particle culling (viewconeCheck)
|
||||
|
||||
**What:** port `Render::viewconeCheck`: per view slice, lift the per-edge
|
||||
eye planes (each NDC edge + the eye defines a plane — the `view_vertex.plane`
|
||||
analog) and sphere-test every entity and emitter against the slice before
|
||||
draw; route particles through the same gate and the same clip/punch
|
||||
discipline (delete the `BeginDoorwayScissor` AABB path); fix the
|
||||
outdoor-root unattached-emitter drop; gate the weather pass on
|
||||
`is_player_outside` (player cell, not viewer root).
|
||||
|
||||
- **Closes:** particles-through-walls (candle flames in other buildings);
|
||||
rain-indoors-through-doorways; the neighbour-room object over-inclusion
|
||||
half of the old #114 report.
|
||||
- **Acceptance:** flame-through-wall spot at Holtburg (user gate); a
|
||||
conformance fact pinning sphere-vs-slice culling on a fixture; no
|
||||
regression in entity draw counts outdoors (perf probe within noise).
|
||||
- **Size:** ~3 commits.
|
||||
|
||||
### BR-6 — One gate: consolidate visibility + delete legacy paths
|
||||
|
||||
**What:** make the PView flood the only visibility computation:
|
||||
remove the per-frame ACME BFS (`CellVisibility.ComputeVisibilityFromRoot`)
|
||||
by folding its remaining consumers (lighting indoor flag etc.) onto PView/
|
||||
membership outputs; delete or quarantine the confirmed legacy remnants
|
||||
(`InteriorRenderer`, `IndoorDrawPlan` consumers of the old path, the
|
||||
`clipRoot==null` second render branch, the dormant exit-mask wiring once
|
||||
BR-2 rewires it, duplicate frustum implementation); one frustum, one
|
||||
center/radius window.
|
||||
|
||||
- **Closes:** the `dual-live-visibility-computations` inconsistency class
|
||||
(the one-gate rule, `feedback_render_one_gate`); removes the surface area
|
||||
where two gates disagree (future flap-class bugs).
|
||||
- **Acceptance:** gate-audit re-run shows ONE visibility computation per
|
||||
frame; every deletion verified by a launch + the visual gate set; suites
|
||||
green.
|
||||
- **Size:** ~3 commits, mostly deletions (each independently revertable).
|
||||
|
||||
### BR-7 — Interior collision: per-cell shadow lists (A6.P4, verified)
|
||||
|
||||
**What:** ship the A6.P4 architecture with the investigation's corrections:
|
||||
registration builds the cell set by sphere-overlap portal flood (not an XY
|
||||
grid; crosses landblocks), per-cell `shadow_object_list` iteration on the
|
||||
query side (`CheckOtherCells` runs env AND shadow objects per other cell),
|
||||
buildings dispatch through a per-LandCell building channel
|
||||
(`CSortCell.building` shape), `OtherPortalId` widened to signed with the
|
||||
`>= 0` gate (sign-extension Ghidra-proven). Then remove the `b3ce505`
|
||||
stopgap, the A6.P5 `hasExitPortal` widening, and the #90 stickiness
|
||||
workaround.
|
||||
|
||||
- **Closes:** #99 (doors block from both sides), very likely #97; retires
|
||||
three flagged workarounds.
|
||||
- **Acceptance:** A6.P4 spec acceptance (doors block both ways at Holtburg
|
||||
inn + cottages; #98 cellar ascent stays fixed — `CellarUp` harness green);
|
||||
capture/replay comparison on the door apparatus; suites green.
|
||||
- **Size:** the A6.P4 spec's estimate stands (~5 commits); independent of
|
||||
BR-2..BR-5 — may run in parallel with them.
|
||||
|
||||
### BR-8 — Feel tier: camera, lighting, LOD (post-discipline polish)
|
||||
|
||||
- **BR-8a Camera (#115, verified root cause; can land any time):** damp the
|
||||
sought eye FROM the published collided viewer each frame (retail
|
||||
`PlayerPhysicsUpdatedCallback` shape) and apply the computed player fade
|
||||
over the 0.45→0.20 m band. Acceptance: cramped-interior turn feel (user
|
||||
gate). ~1–2 commits.
|
||||
- **BR-8b Lighting (pending verifier confirmation):** interior sun mask
|
||||
(never sun-light interiors), static cell-light burn-in (all lights, not
|
||||
8-nearest), viewer light, per-object light selection, surface
|
||||
luminosity/diffuse. Acceptance: side-by-side interior look vs retail
|
||||
screenshots. Phase-sized; spec before code.
|
||||
- **BR-8c LOD + dedup (low):** per-part degrade selection beyond humanoids;
|
||||
frame-stamp draw dedup. Optional per-cell interleave for draw-order parity
|
||||
is explicitly NOT planned (z-buffer makes it unnecessary; revisit only on
|
||||
evidence).
|
||||
- **Picking refinements** (all-low area): defer; file as issues when the
|
||||
port changes what is clickable.
|
||||
|
||||
## 3. What this plan deliberately does NOT do
|
||||
|
||||
- No per-frame BSP traversal of ordinary geometry (retail doesn't either).
|
||||
- No rewrite of the mesh/MDI pipeline, the flood clipper, membership, or
|
||||
streaming (keep-list).
|
||||
- No `leaf_cells`/`CPartCell` port (path dormant in the 2013 binary — needs
|
||||
runtime proof first).
|
||||
- No transparency-sorting work yet — that area's map is still re-running;
|
||||
fold its findings in as a BR-9 candidate after review (the AlphaList
|
||||
deferral machinery is already decompiled in the Area 1 file).
|
||||
|
||||
## 4. Sequencing summary
|
||||
|
||||
```
|
||||
BR-1 (surface gate) — first; standalone visual win, lowest risk
|
||||
BR-2 (depth punch/seal) — second; enables BR-3
|
||||
BR-3 (delete shell chop) — closes #114 with BR-2
|
||||
BR-4 (draw-driven floods) — closes #109; flood fidelity
|
||||
BR-5 (viewconeCheck) — particles/objects through the same gate
|
||||
BR-6 (one gate + deletions) — consolidation after the discipline is in
|
||||
BR-7 (collision A6.P4) — independent track; may interleave with BR-2..5
|
||||
BR-8 (camera/lighting/LOD) — feel tier; BR-8a may land early
|
||||
```
|
||||
|
||||
Every phase: `dotnet build` + full suites green, conformance pins added with
|
||||
retail citations, named user visual gate, roadmap/ISSUES updated in the same
|
||||
session, and the render digest updated when a phase closes one of the named
|
||||
bugs.
|
||||
|
||||
## 5. Approval asks
|
||||
|
||||
1. Approve the plan shape + ordering (BR-1 → BR-8, BR-7 parallel-capable).
|
||||
2. Approve the deletions implied by BR-3/BR-6 (shell-chop enforcement,
|
||||
ACME BFS visibility, legacy render branches) — all on the strength of the
|
||||
cited evidence that retail has no counterpart.
|
||||
3. Note the verification caveat: ~36/76 divergences still carry UNVERIFIED
|
||||
(resume in flight); BR-1..BR-3's load-bearing claims are either verified
|
||||
or dat-confirmed locally, so approval need not wait on the rest.
|
||||
Loading…
Add table
Add a link
Reference in a new issue