acdream/docs/research/2026-06-11-building-render-acdream-vs-retail-comparison.md
Erik 695eca2c1f BR-1: RESOLVED as already-equivalent - premise falsified by pre-check, equivalence pinned, #113 attribution corrected
The plan's BR-1 ('implement the skipNoTexture draw-time surface gate')
died on its pre-check: acdream ALREADY suppresses every portal fill.
ReplicateProductionEmission_OnPortalFills replicates the exact emission
conditions of the production extractors on the hall/cottage fills:
pos=False neg=False for every one (Stippling.NoPos skips the positive
side at ObjectMeshManager.PrepareGfxObjMeshData:1046,
PrepareCellStructMeshData:1394, CellMesh.Build:44, GfxObjMesh.Build:71;
the fills have no negative surface). There is nothing to gate.

What ships instead: StipplingSurfaceEquivalenceTests - 2,607 polys across
13 building models + 13 environments, ZERO violations both directions:
NoPos <=> untextured-surface. Our build-time skip is proven equivalent to
retail's draw-time skipNoTexture rule (Ghidra 0x0059d4a4, default on
@0x00820e30) on this content. The pin fails loudly if future content
breaks the invariant - the cue to implement the draw-time gate then.

Corrections folded into the plan + comparison docs:
- The #113 phantom residual CANNOT be GfxObj fills (they never reach a
  vertex buffer). Plausible true sites are cell-side: flood-admitted
  cells drawn with the pass-all NoClipSlice when slot-less
  (RetailPViewRenderer.cs:71), and/or cell statics drawn unclipped +
  un-viewcone'd (object-lists-skip-portal-view-gate, confirmed).
  BR-2 opens with the probe that pins which.
- The e46d3d9 user-gate observations (filter removed phantom/doors) were
  confounded - the filter was a provable mesh no-op on shells AND doors.
- Ledger rows solid-surface-skip-missing + the acdream half of
  portal-polys-baked-unconditional re-marked REFUTED-for-fills; the
  retail mechanism descriptions and the un-consumed PortalIndex->
  CBldPortal pairing (BR-4) stand.

Suites: Core 1398 green (1392 baseline + 6 new facts) + the 4 pre-existing
#99-era failures + 1 skip. No production code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 06:25:31 +02:00

438 lines
28 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.

# acdream vs retail — building/interior render architecture comparison
**Phase A deliverable of the holistic building-render investigation**
(mandate 2026-06-11: *"map acdream's way vs retail, then make a plan how to port
retail's way of doing it once and for all"*; charter:
[2026-06-11-building-render-holistic-port-handoff.md](2026-06-11-building-render-holistic-port-handoff.md)).
**Branch:** `claude/thirsty-goldberg-51bb9b`. **Method:** two ultracode workflow
fan-outs — 12 mapping areas (11 completed; transparency/sorting re-running),
~90 agents, every retail claim cited to Ghidra decompile (port 8081, verified
PDB program) / named pseudo-C (`pc:LINE`) / `acclient.h`, every acdream claim
cited `file:line`, one adversarial verifier per claimed divergence
(Ghidra-first; BN pseudo-C distrusted per project history). Raw per-area
mapping outputs (the full evidence record) live in
[`2026-06-11-holistic-map/`](2026-06-11-holistic-map/).
**Companion deliverable (Phase B):** the phased port plan at
[`docs/plans/2026-06-11-building-render-port-plan.md`](../plans/2026-06-11-building-render-port-plan.md)
— awaiting user approval before any implementation.
---
## 1. Executive summary
The investigation **confirms the mandate's premise and overturns two of our
working theories.** Retail has one drawing discipline; we have fragments of it
plus three mechanisms it doesn't use. The headline findings:
1. **Retail flattens GfxObjs and cells at load, exactly like we do.**
`CGfxObj::InitLoad` builds a flat D3D mesh from the *full* polygon
dictionary (`D3DPolyRender::ConstructMesh`, Ghidra 0x0059dfa0) and then
**deletes every non-portal node from the drawing BSP**
(`BSPTREE::RemoveNonPortalNodes`, Ghidra 0x0053a040). There is no per-frame
BSP traversal of ordinary geometry. Our flatten-at-decode global-VAO
pipeline is retail-faithful as a *base* — the bindless MDI architecture
survives the port intact.
2. **The phantom staircase and the vanished doors are one mechanism, and it is
a draw-time *surface* gate, not a poly filter.** Building/cell meshes skip
surface batches whose `CSurface.type` has neither `BASE1_IMAGE` (0x2) nor
`BASE1_CLIPMAP` (0x4) — the `skipNoTexture` rule (D3DPolyRender inner draw,
Ghidra 0x0059d4a0; default on, data 0x00820e30). Dat-confirmed on our side
(commit `e223325` + `DumpPortalFillSurfaceTypes`): **every** portal-fill
quad on all 13 Holtburg-area building models — door fills, window fills,
*and* the meeting-hall phantom stair-ramp — is `Base1Solid` (untextured).
Retail draws none of them, ever. The doors players see are **door
entities**. *(Execution-day correction, §5: acdream's extractors already
skip all of these via `NoPos` — proven equivalent to retail's rule by
`StipplingSurfaceEquivalenceTests`. The phantom residual is cell-side,
not these fills; see the §5 banner.)*
3. **Retail never geometrically clips cell or shell geometry. Pixel exactness
at apertures is a DEPTH discipline.** Production cell draws are whole
prebuilt meshes (`use_built_mesh`, `DrawEnvCell` pc:427905); the famous
`planeMask=0xffffffff` per-poly path is a legacy fallback whose mask means
*skip all clip edges*. What makes doorways pixel-perfect is:
**(a)** an invisible *depth punch* — the portal polygon, software-clipped
against the accumulated view, drawn depth-always/z-write/alpha-0 at far-Z
(`maxZ1`, opens a building aperture before its interior draws) or at its
true depth (`maxZ2`, seals indoor exits after the landscape draws);
**(b)** far→near cell order with a draw-once frame stamp; **(c)** the
z-buffer. This **reframes #114**: our `gl_ClipDistance` shell chop was
chasing retail's fallback path. The accumulated portal views exist for
*admission*, *object culling*, and *punch shapes* — never to cut geometry.
4. **Per-frame portal machinery is the heart, and we half-have it.** Building
shells draw in two passes (`DrawBuilding`, Ghidra 0x0059f2a0): a
portal-only BSP walk that dispatches each aperture through
`PView::DrawPortal``ConstructView(CBldPortal)` (eye-side vs
`portal_side` at ε=0.0002 → screen-clip vs the current view → cell-loaded
check) — flood + punch on success, *nothing* on failure — then the whole
shell mesh. The `PortalRef.PortalIndex` in the dat indexes the building's
`CBldPortal` array via `outdoor_portal_list` (pc:433920). Our flood port
(R-A1/A2/A2b, keep-listed) already implements faithful analogues of the
admission gates; what's missing is the *trigger* (we use a 48 m seed
constant instead of the shell's own draw), the *depth machinery* (punch
path exists as an unwired no-op), multi-view union (first-wins drops
second apertures), and per-view object culling (`viewconeCheck`).
5. **The adjunct systems diverge in ways users feel.** Camera: retail's boom
damping interpolates *from the published collided viewer* each frame —
shorten-fast/ease-out is emergent; we damp from our own previous damped
eye, severing that feedback (**#115 root cause, verified**). Lighting:
retail bakes *all* static cell lights per-vertex and adds a viewer light;
we cap at 8 viewer-nearest dynamic lights and sun-light interiors when the
player-cell gate says "outside" (unverified, high-confidence). Collision:
retail registers objects into per-cell shadow lists via a sphere-overlap
portal flood at registration; our landblock-wide registry is the #99/A6.P4
debt (**verified**, design corrections noted in the area file).
**Bug attribution coverage:** #113 phantom class → finding 2; #114 indoor crop
→ finding 3; doors-vanish mystery → finding 2 (solved, `e223325`); #108
grass-sweep → missing aperture depth seal + scissored AABB clear (finding 3);
#109 far-door oscillation → 48 m flood pop + first-wins view loss + missing
punch (findings 34); particles-through-walls → scissor-AABB gating instead of
per-view cone culling (finding 4); #99 doors run-through → finding 5
(collision); #115 camera drag → finding 5 (camera).
---
## 2. The retail architecture (2013 client, decomp-cited)
What follows is the synthesis; each area file in
[`2026-06-11-holistic-map/`](2026-06-11-holistic-map/) carries the full
call-chains and citations.
### 2.1 Load time: flatten + prune
- `CGfxObj::InitLoad` (Ghidra 0x005346b0): `BSPTREE::RemoveNonPortalNodes`
prunes the drawing BSP to a skeleton of portal-bearing nodes, then
`D3DPolyRender::ConstructMesh` flattens **all** polygons (portal fills
included) into a surface-batched mesh. EnvCells get the same at
`CEnvCell::UnPack` (ConstructMesh at pc:311085).
- Portal polys live in the same polygon array; `BSPPORTAL` nodes reference
them as `CPortalPoly { portal_index, CPolygon* }` (acclient.h:39075) — the
dat's `PortalRef { PolyId, PortalIndex }` (our `e223325` finding).
### 2.2 Frame composition (outdoor root)
`SmartBox::RenderNormalMode` (0x453aa0): viewer in an EnvCell →
`DrawInside(viewer_cell)`; viewer outdoors → full-screen view + `LScape::draw`
→ per landblock `DrawBlock` → per land cell in view: `DrawLandCell` (terrain)
then `DrawSortCell` = `DrawBuilding(cell->building)` + `DrawObjCell(cell)`
**a building draws exactly when its host land cell draws** (per-cell
interleave, far→near blocks; `CSortCell.building` is the one-building-per-cell
slot, acclient.h:31880).
### 2.3 The building two-pass + portal machinery
`RenderDeviceD3D::DrawBuilding` (0x0059f2a0):
1. `outdoor_pview->outdoor_portal_list = building->portals` — installs the
CBldPortal lookup the shell's portal polys index.
2. **Pass 1 (portals):** `CPhysicsPart::Draw(part, 1)` → the pruned BSP walk
(`build_draw_portals_only` modes 1 then 2) submits each portal poly →
`PView::DrawPortal` (0x005a5ab0) resolves
`outdoor_portal_list[portal_index]`, pushes fresh `portal_view` slots onto
every stab-list cell, and runs `ConstructView(CBldPortal)` (0x005a59a0):
- eye side vs `portal_side` at ε=0.0002 — in-plane rejects outright;
- `GetClip` — the portal polygon software-clipped against the **current**
view (full screen outdoors; a doorway slot if this building is itself
seen through a portal); empty → fail, *no distance constant exists*;
- `CEnvCell::GetVisible` — target cell loaded;
- success mode 1 → **far-Z depth punch** of the clipped aperture
(`DrawPortalPolyInternal`, alpha-0, depth-always, z-write, `maxZ1`);
- success mode 2 → recurse `ConstructView(CEnvCell)` (the BFS flood) and
`DrawCells` draws the interior into the punched aperture.
The `building_view` latch binds nested floods to the view slot they were
discovered under (saved/-1/restored around `DrawPortal`, pc:427906-427914).
3. **Pass 2 (shell):** `ObjBuildingOrBuildingPart=1`;
`CPhysicsPart::Draw(part, 0)` draws the whole constructed mesh — with
`skipNoTexture` skipping every untextured (solid) surface batch. Interior
pixels survive only inside the punched aperture.
### 2.4 The indoor flood + DrawCells
`PView::DrawInside` (0x005a5860): root view = full-screen quad;
`ConstructView(CEnvCell)` floods via `InitCell` (eye-side per portal,
ε=0.0002; entered-portal back-walk block) + `ClipPortals` (per accumulated
view: project portal, homogeneous Sutherland-Hodgman `polyClipFinish`
near-W clip first, then each view edge; pixel-exact, no plane budget) +
`AddViewToPortals` (first discovery enqueues; growth propagates **in place**
via `AddToCell`/`FixCellList`/`AdjustCellView` with the `update_count`
watermark; termination from `copy_view`'s 1-pixel vertex dedup, not a cap).
A multi-portal cell accumulates a **list** of view polygons — union-as-list,
all views consumed downstream.
`PView::DrawCells` (0x005a4840):
1. If any outside views: `PortalList=&outside_view`**`LScape::draw`** (the
landscape through the accumulated doorway views) → conditional full
**depth clear** (gated on `portalsDrawnCount`) → far→near per cell per
view: **z-seal** every portal leading outside (`other_cell_id==0xFFFF`) at
its true projected depth (`maxZ2`) — terrain seen through the door keeps
its pixels; interior geometry farther than the door plane z-fails inside
the aperture.
2. Cells far→near per view via `DrawEnvCell` — whole prebuilt mesh, drawn
once (`GetDrawnThisFrame` frame-stamp), **never clipped**.
3. Per cell: `PortalList = cell's view stack``DrawObjCell` — every object
sphere-tested per view by `Render::viewconeCheck` (0x0054c250: sphere vs
eye plane + each `view_vertex.plane`); objects are **culled, never
clipped**. Translucent batches defer to the AlphaList and flush sorted.
### 2.5 The adjuncts
- **Camera** (`wf2-camera-viewer.md`): `CameraManager::UpdateCamera`
interpolates the sought pose **from the published collided viewer**
(`PlayerPhysicsUpdatedCallback` passes `&this->viewer`) with
α = stiffness·dt·10 (stiffness 0.45); `SmartBox::update_viewer` re-sweeps
pivot→sought *every frame* (0.3 m sphere) and publishes the raw collided
stop + `viewer_cell = sphere_path.curr_cell`. No explicit boom smoothing
exists — the feel is emergent from the collided-feedback loop. Player mesh
fades over the 0.45→0.20 m approach band
(`SetTranslucencyHierarchical`).
- **Lighting** (`wf2-indoor-lighting.md`, unverified): per-cell static lights
burn into vertices; interiors are never sun-lit; a white viewer light rides
above the player; per-object light selection against object bounds.
- **Sky/weather/scenery** (`wf2-sky-weather-scenery.md`, unverified): weather
gates on `is_player_outside`; rain cylinder at world-absolute z; scenery
draws per land cell, participating in the same per-cell interleave.
- **Collision** (`wf1-interior-collision.md`, verified): registration builds
the cell set by a sphere-overlap **portal flood** (`add_shadows_to_cells` /
`find_cell_list` family); queries iterate per-cell `shadow_object_list`;
buildings dispatch through `CSortCell.building` (per-cell channel);
`check_building_transit` gates `other_portal_id >= 0` (sign-extension
Ghidra-proven — BN renders it unsigned, the invented-sign failure mode).
- **Picking** (`wf2-picking-selection.md`, unverified): object-sphere/poly
arbitration inside the draw traversal's visibility, not a parallel ray.
---
## 3. The acdream architecture today
Mapped in full in the area files; the short form, with keep/replace verdicts:
| Subsystem | Today | Verdict |
|---|---|---|
| Mesh pipeline (flatten → global VAO → bindless MDI) | `ObjectMeshManager``WbDrawDispatcher`, ~12-15 GL calls/frame | **KEEP** — matches retail's flatten-at-load |
| PView flood (admission) | `PortalVisibilityBuilder` — faithful homogeneous clipper, side tests, reciprocal clip, exact-match skip | **KEEP** (R-A1/A2/A2b + dac8f6a, conformance-gated) — adjust constants/heuristics per ledger |
| Flood trigger | 48 m per-building seed over Chebyshev≤1 landblocks, outdoor roots only | **REPLACE** with shell-draw-driven `DrawPortal` (retail has no distance constant) |
| Aperture enforcement | `gl_ClipDistance` shell chop (outdoor-scoped, #114) + scissored AABB depth clear + unwired `DrawExitPortalMasks` | **REPLACE** with retail depth punch/seal/clear discipline; shells draw whole |
| Portal-fill suppression | GfxObjs: none (fills drawn — phantom class); cells: build-time `NoPos`/`NoNeg` stippling drop | **REPLACE/ALIGN** with draw-time `skipNoTexture` surface gate (dat-confirmed equivalent on audited cells) |
| Object/particle visibility | Cell-membership buckets; particles scissor-AABB | **EXTEND** with per-view `viewconeCheck`; route particles through the same gate |
| Building physics/collision | Landblock-wide `ShadowObjectRegistry` + `b3ce505` gate (#99) | **REPLACE** per A6.P4 (verified, with corrections: signed `OtherPortalId` + `>=0` gate, per-cell building channel) |
| Membership / straddle gate / streaming / camera collision / znear / texture flush | P1 9/9 golden; `414c3de`; A.5; verbatim `update_viewer`; 0.1; `c787201` | **KEEP** (charter keep-list, re-confirmed by verifiers) |
| Visibility computations | **TWO live systems**: `CellVisibility.ComputeVisibilityFromRoot` (ACME BFS) *and* `PortalVisibilityBuilder` (verified) + legacy remnants (`InteriorRenderer`, `IndoorDrawPlan`, dual frustum impls) | **CONSOLIDATE** to one gate (PView) + delete dead paths |
| Camera boom | Damps from own previous damped eye; fade computed, never applied | **FIX** feedback anchor (#115, verified) + apply fade |
| Lighting | Single scene UBO, 8 viewer-nearest lights, player-cell sun gate | **EXTEND** per lighting area (burn-in, viewer light, interior sun mask) — pending verification |
---
## 4. The divergence ledger
76 divergences across 11 mapped areas (the 12th, transparency/sorting, is
re-running). Ranked within area; verification = adversarial re-derivation
(Ghidra-first). `UNVERIFIED` rows had their verifier interrupted by the
session token limit — a resume is in flight; treat them as high-confidence
mapped claims, not yet adversarially proven. Full evidence per row in the
area files.
### Where each open bug lands
| Bug | Primary divergences |
|---|---|
| #113 phantom geometry class | `solid-surface-skip-missing` (gfxobj), `portal-polys-baked-unconditional` (shells) |
| Door-vanish mystery | **SOLVED**`e223325` + surface-type dump; same rows as #113 |
| #114 indoor crop | `shell-chop-vs-depth-discipline`, `missing-aperture-depth-punch`, `multiview-loss-first-wins`, `knife-edge-epsilon-and-rescue` (interior-cells) |
| #108 grass-sweep | `missing-portal-depth-fence` (culling, **confirmed**), `depth-clear-shape-and-order`, `eight-plane-budget-passall` |
| #109 far-door oscillation | `building-flood-seeding-48m-cutoff` (adjusted: pop mechanism confirmed, 48 m linkage unmeasured), `multiview-loss-first-wins`, `missing-portal-depth-fence` |
| particles-through-walls | `particles-not-cell-resident` (statics), `object-particle-gating` (interior-cells), `particles-third-gate-tier` (gates) |
| #99 door run-through | `registration-cell-set-not-portal-flood`, `flat-object-query-not-per-cell` (collision, both **confirmed**) |
| #115 camera drag | `boom-no-collided-feedback` (**confirmed**) |
| Indoor "feels right" (M1.5) | the six `indoor-lighting` rows (unverified) |
### Ledger (severity / verdict / one-line)
**Area 1 — GfxObj draw** (`wf1-gfxobj-draw.md`)
| Sev | Verdict | Divergence |
|---|---|---|
| CRIT | adjusted | `portal-poly-conditional-pass-missing` — no per-frame z-punch/z-seal/ConstructView pass on portal polys |
| HIGH | **REFUTED for fills** (BR-1 pre-check, §5 banner) | `solid-surface-skip-missing` — acdream's NoPos build-time skip already covers them, proven equivalent |
| MED | confirmed | `degrade-lod-scoped-to-humanoids` — retail degrades every non-player part per frame |
| MED | adjusted | `no-per-view-entity-pass` — no per-portal-view re-cull of objects |
| MED | confirmed | `stippling-semantics-divergence` — WB's NoPos/NoNeg side-drop vs retail batch flag + sides_type |
| LOW | adjusted | `no-frame-dedup` — no GetDrawnThisFrame frame-stamp |
**Area 2 — Building shells** (`wf1-building-shells.md`)
| Sev | Verdict | Divergence |
|---|---|---|
| CRIT | **acdream half REFUTED** (BR-1 pre-check); retail half + missing PortalIndex→CBldPortal pairing stand | `portal-polys-baked-unconditional` — fills are NOT drawn (NoPos skip); the un-consumed pairing remains real (BR-4) |
| CRIT | unverified | `no-per-slot-building-draw` — building never draws per view slot; floods not shell-draw-driven |
| HIGH | adjusted | `flood-gate-shape` — 48 m seed + 0.01 ε + eye-inside rescue vs retail's no-distance chain (analogues otherwise faithful) |
| HIGH | unverified | `aperture-depth-machinery` — far-Z punch missing; particles scissor-only |
| MED | confirmed | `building-not-in-physics-cell-graph` — per-cell building channel missing; `other_portal_id>=0` gate missing (sign-extension proven) |
| LOW | unverified | `leaf-cells-unported` — retail path itself appears dormant; do not port without runtime proof |
**Area 3 — Interior cells** (`wf1-interior-cells.md`)
| Sev | Verdict | Divergence |
|---|---|---|
| CRIT | unverified | `shell-chop-vs-depth-discipline` — we clip shell geometry; retail clips nothing (depth discipline) |
| CRIT | unverified | `missing-aperture-depth-punch` — DrawExitPortalMasks unwired; AABB far-clear wrong shape/value |
| HIGH | unverified | `multiview-loss-first-wins` — MergeBuildingFrame drops views; CellIdToSlot keeps slices[0] |
| HIGH | unverified | `eight-plane-budget-passall` — >8 edges → slot-0 PASS-ALL; scissor fallback unimplemented |
| HIGH | unverified | `knife-edge-epsilon-and-rescue` — 0.01 vs 0.0002 ε + non-retail 1.75 m full-view rescue |
| MED | unverified | `growth-requeue-vs-in-place` — re-enqueue + cap-16 vs retail in-place propagation + 1-px dedup floor |
| MED | unverified | `object-particle-gating` — membership-only culling; particles scissored |
| MED | unverified | `portal-poly-suppression-criterion` — build-time stippling vs retail draw-time surface gate |
**Area 4 — Statics + dynamics** (`wf1-statics-dynamics.md`)
| Sev | Verdict | Divergence |
|---|---|---|
| CRIT | unverified | `building-portal-polys-unconditional` — (cross-ref Area 2) |
| CRIT | adjusted | `particles-not-cell-resident` — owner-bucket + 2D scissor vs emitter-cell residency + per-slot cone |
| HIGH | confirmed | `single-cell-buckets-vs-shadow-parts` — one ParentCellId bucket vs register-in-every-overlapped-cell + draw-once |
| HIGH | confirmed | `shells-drawn-whole-in-retail-production` — the #114 reframe anchor |
| MED | confirmed | `no-per-slot-viewcone-for-meshes` |
| MED | **refuted** | `livedynamic-dropped-indoors` — claim did not survive; see area file |
| LOW | adjusted | `outdoor-objects-redrawn-per-slice` |
| LOW | adjusted | `per-cell-depth-sort-missing` |
**Area 5 — Culling/frame composition** (`wf1-culling.md`)
| Sev | Verdict | Divergence |
|---|---|---|
| CRIT | confirmed | `missing-portal-depth-fence` — the maxZ2 fence after the clear is absent; hook unwired |
| CRIT | adjusted | `approximate-portal-clip-for-landscape` — ≤8 GL planes + AABB scissor vs exact software clip |
| HIGH | adjusted | `depth-clear-shape-and-order` — per-slice scissored clears after all slices vs one gated full clear |
| HIGH | unverified | `portal-poly-conditional-draw` — (cross-ref Areas 1/2) |
| HIGH | adjusted | `building-flood-seeding-48m-cutoff` — pop mechanism confirmed; #109@48 m unmeasured |
| MED | confirmed | `entity-cull-no-portal-viewcone` |
| MED | adjusted | `weather-gate-player-vs-viewer` — rain through doorways while inside |
| MED | adjusted | `unattached-particles-dropped-outdoors` |
| LOW | confirmed | `global-passes-vs-per-cell-interleave` |
**Area 6 — Interior collision** (`wf1-interior-collision.md`)
| Sev | Verdict | Divergence |
|---|---|---|
| CRIT | confirmed | `registration-cell-set-not-portal-flood` — XY grid vs sphere-overlap portal flood |
| CRIT | confirmed | `flat-object-query-not-per-cell` — one radial query vs per-cell shadow-list iteration |
| HIGH | adjusted | `building-shell-as-shadow-object` — landblock-wide entries vs per-LandCell building channel |
| HIGH | confirmed | `check-other-cells-env-only` — retail runs env AND shadow objects per other cell |
| MED | adjusted | `a6p5-topology-widening` — wider than retail's straddle gate (pending A6.P4) |
| MED | confirmed | `single-landblock-grid-clamp` — registration clamps to own landblock |
| LOW | confirmed | `movement-reregistration-source` — fresh grid vs transition's own cell array |
**Area 2.1 — Camera/viewer** (`wf2-camera-viewer.md`)
| Sev | Verdict | Divergence |
|---|---|---|
| HIGH | confirmed | `boom-no-collided-feedback` — sought eye never re-anchors to published collided viewer (#115 root cause) |
| MED | confirmed | `player-fade-computed-not-applied` |
| LOW | confirmed | `sought-position-lacks-cell-identity` |
| LOW | adjusted | `camera-input-scalars-unverified` |
**Area 2.2 — Indoor lighting** (`wf2-indoor-lighting.md`) — all unverified
| Sev | Divergence |
|---|---|
| CRIT | `interior-sun-bleed` — interiors sun-lit when player-cell gate says outside |
| HIGH | `no-static-light-burnin` — interiors capped at 8 viewer-nearest lights vs all static lights baked |
| MED | `no-per-object-light-selection`; `no-viewer-light`; `surface-luminosity-diffuse-ignored` |
| LOW | `dynamic-entity-lights-unregistered` |
**Area 2.3 — Sky/weather/scenery** (`wf2-sky-weather-scenery.md`) — all unverified
| Sev | Divergence |
|---|---|
| CRIT | `outside-portal-zstamp-missing` — (same family as the depth fence) |
| HIGH | `weather-indoor-gate`; `particles-not-portal-clipped` |
| MED | `no-nested-building-flood-through-outside-view` |
| LOW | `outdoor-objects-flat-bucket`; `rain-anchor-z-relative`; `weather-enabled-toggle-absent` |
**Area 2.5 — Visibility-gates audit** (`wf2-visibility-gates-audit.md`)
| Sev | Verdict | Divergence |
|---|---|---|
| HIGH | confirmed | `object-lists-skip-portal-view-gate` |
| HIGH | confirmed | `indoor-shell-clip-disabled` — indoor roots have NO draw-side discipline today |
| HIGH | unverified | `particles-third-gate-tier` |
| MED | confirmed | `dual-live-visibility-computations` — ACME BFS + retail flood both run per frame |
| MED | adjusted | `landscape-redrawn-per-outside-slice` |
| MED | unverified | `flood-convergence-heuristics`; `drawportal-membership-rule-mismatch`; `livedynamic-invisible-under-interior-roots` |
| MED | confirmed | `exit-portal-mask-pass-dormant` |
| MED | adjusted | `legacy-outdoor-branch-remnant` — clipRoot==null second path |
| LOW | unverified | `dual-frustum-implementations` |
**Area 2.6 — Picking** (`wf2-picking-selection.md`) — all unverified, all ≤ medium
`pick-outside-draw-traversal`; `occluder-stricter-and-looser-than-retail`;
`no-poly-stage-no-poly-beats-sphere`; `no-selected-in-view-tracking`.
---
## 5. Mysteries resolved this session
> **⚠ EXECUTION-DAY CORRECTION (2026-06-11, BR-1 pre-check).** The claim that
> acdream *draws* the solid portal fills is **FALSE** — all four extraction
> paths skip `Stippling.NoPos` positive sides
> (`ObjectMeshManager.PrepareGfxObjMeshData:1046`,
> `PrepareCellStructMeshData:1394`, `CellMesh.Build:44`,
> `GfxObjMesh.Build:71`), and the fills have no negative surface
> (`ReplicateProductionEmission_OnPortalFills`: pos=False/neg=False for every
> fill). The equivalence pin (`StipplingSurfaceEquivalenceTests`, 2,607
> polys, 0 violations) proves our build-time skip ⇔ retail's draw-time
> `skipNoTexture` on this content. Consequences: the ledger rows
> `solid-surface-skip-missing` (Area 1) and the acdream half of
> `portal-polys-baked-unconditional` (Area 2) are **REFUTED for the fills**
> (the retail-side mechanism descriptions stand); the e46d3d9 user-gate
> observations were confounded (the filter was a provable mesh no-op on
> shells and doors); and the **#113 phantom residual is cell-side** —
> flood-admitted cells drawn with the pass-all `NoClipSlice` when slot-less
> (`RetailPViewRenderer.cs:71`) and/or unclipped un-viewcone'd cell statics
> (`object-lists-skip-portal-view-gate`, confirmed). BR-2 opens with the
> probe that pins which. The mapping agents missed the `:1046` skip — score
> one for "verify what call sites actually pass."
1. **Door-vanish (charter §4.1)** — SOLVED, dat-proven (`e223325` +
`DumpPortalFillSurfaceTypes`): the e46d3d9 filter walked only
`node.Polygons`, never `node.Portals` (`PortalRef`); every dropped poly was
a portal fill; all fills are `Base1Solid`; retail skips them via
`skipNoTexture`; visible doors are entities. **No static filter can be
correct** — and per the execution-day correction above, no filter was
*needed*: the fills were never drawn, and the gate's "doors vanished"
observation was confounded.
2. **#114's real shape (charter §4.2)** — retail does not crop indoor
geometry; it punches/seals depth at apertures and draws far→near. The
"admission-quality vs draw-quality regions" framing dissolves: regions
only ever needed to be admission-quality (+ punch shapes).
3. **#115 (charter §4.5)** — root cause confirmed: missing collided-viewer
feedback into the damping origin; plus the player fade is computed but
never applied.
4. **#108/#109 (charter §4 re-test list)** — concrete mechanisms named (depth
fence, clear shape, flood pop, view loss) — see ledger.
5. **Charter §4.3 (cottage entry transparency)** — not separately
investigated; the straddle gate + flood stability work landed earlier;
re-test after the port phases land.
## 6. Open questions carried into the port plan
The area files carry ~30 open questions; the load-bearing ones:
1. ~~Where does retail draw the textured fill of a building portal poly?~~
**Answered**: nowhere at Holtburg — all fills untextured
(`DumpPortalFillSurfaceTypes`). A dat-wide sweep should pin the invariant
before relying on it globally (plan P1 acceptance).
2. `LScape::draw` internals — does retail clip terrain polys against
outside views or only cull blocks/cells per view? (Affects how faithful
our per-slice terrain clip needs to be once the punch exists.)
3. `PView::DrawPortal` mode 3 (seal-on-failure) — who calls it; matters for
unstreamed interiors.
4. Window-type CBldPortals — do they flood (stab lists + GetVisible-able
targets)? Decides window treatment in P1/P4 gates.
5. `cdstW` near-W constant; `CBldPortal.sidedness` semantics; the
`DrawMesh` skipNoTexture else-branch latch — pin during P1/P2
implementation.
6. Dungeon same-volume overlap sweep — z-only indoor compositing assumes
non-overlapping cell volumes; one-off dat sweep before declaring P3 done.
## 7. Verification status + how to finish it
- 40/76 divergences adversarially verified (1 refuted, 11 adjusted with
corrected claims — the corrections are folded into this doc's ledger).
- Both workflows are resumable; a resume was launched for the remaining
verifiers, the transparency/sorting map, and both completeness critics
(run IDs `wf_475e012b-f74`, `wf_dd8381c7-c0a`). This doc's ledger should be
refreshed from the final JSONs when they land.