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>
26 KiB
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).
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/.
Companion deliverable (Phase B): the phased port plan at
docs/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:
-
Retail flattens GfxObjs and cells at load, exactly like we do.
CGfxObj::InitLoadbuilds 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. -
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.typehas neitherBASE1_IMAGE(0x2) norBASE1_CLIPMAP(0x4) — theskipNoTexturerule (D3DPolyRender inner draw, Ghidra 0x0059d4a0; default on, data 0x00820e30). Dat-confirmed on our side (commite223325+DumpPortalFillSurfaceTypes): every portal-fill quad on all 13 Holtburg-area building models — door fills, window fills, and the meeting-hall phantom stair-ramp — isBase1Solid(untextured). Retail draws none of them, ever. The doors players see are door entities; we draw the solid fills as colored geometry, which is why removing them read as "doors disappeared." -
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,DrawEnvCellpc:427905); the famousplaneMask=0xffffffffper-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: ourgl_ClipDistanceshell chop was chasing retail's fallback path. The accumulated portal views exist for admission, object culling, and punch shapes — never to cut geometry. -
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 throughPView::DrawPortal→ConstructView(CBldPortal)(eye-side vsportal_sideat ε=0.0002 → screen-clip vs the current view → cell-loaded check) — flood + punch on success, nothing on failure — then the whole shell mesh. ThePortalRef.PortalIndexin the dat indexes the building'sCBldPortalarray viaoutdoor_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). -
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 3–4); 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/ carries the full
call-chains and citations.
2.1 Load time: flatten + prune
CGfxObj::InitLoad(Ghidra 0x005346b0):BSPTREE::RemoveNonPortalNodesprunes the drawing BSP to a skeleton of portal-bearing nodes, thenD3DPolyRender::ConstructMeshflattens all polygons (portal fills included) into a surface-batched mesh. EnvCells get the same atCEnvCell::UnPack(ConstructMesh at pc:311085).- Portal polys live in the same polygon array;
BSPPORTALnodes reference them asCPortalPoly { portal_index, CPolygon* }(acclient.h:39075) — the dat'sPortalRef { PolyId, PortalIndex }(oure223325finding).
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):
outdoor_pview->outdoor_portal_list = building->portals— installs the CBldPortal lookup the shell's portal polys index.- Pass 1 (portals):
CPhysicsPart::Draw(part, 1)→ the pruned BSP walk (build_draw_portals_onlymodes 1 then 2) submits each portal poly →PView::DrawPortal(0x005a5ab0) resolvesoutdoor_portal_list[portal_index], pushes freshportal_viewslots onto every stab-list cell, and runsConstructView(CBldPortal)(0x005a59a0):- eye side vs
portal_sideat ε=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) andDrawCellsdraws the interior into the punched aperture. Thebuilding_viewlatch binds nested floods to the view slot they were discovered under (saved/-1/restored aroundDrawPortal, pc:427906-427914).
- eye side vs
- Pass 2 (shell):
ObjBuildingOrBuildingPart=1;CPhysicsPart::Draw(part, 0)draws the whole constructed mesh — withskipNoTextureskipping 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):
- If any outside views:
PortalList=&outside_view→LScape::draw(the landscape through the accumulated doorway views) → conditional full depth clear (gated onportalsDrawnCount) → 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. - Cells far→near per view via
DrawEnvCell— whole prebuilt mesh, drawn once (GetDrawnThisFrameframe-stamp), never clipped. - Per cell:
PortalList = cell's view stack→DrawObjCell— every object sphere-tested per view byRender::viewconeCheck(0x0054c250: sphere vs eye plane + eachview_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::UpdateCamerainterpolates the sought pose from the published collided viewer (PlayerPhysicsUpdatedCallbackpasses&this->viewer) with α = stiffness·dt·10 (stiffness 0.45);SmartBox::update_viewerre-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 onis_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_listfamily); queries iterate per-cellshadow_object_list; buildings dispatch throughCSortCell.building(per-cell channel);check_building_transitgatesother_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 | adjusted | solid-surface-skip-missing — untextured (solid) batches drawn on building/cell meshes retail skips |
| 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 | unverified | portal-polys-baked-unconditional — fills baked + drawn; PortalIndex→CBldPortal pairing never consumed |
| 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
- Door-vanish (charter §4.1) — SOLVED, dat-proven (
e223325+DumpPortalFillSurfaceTypes): thee46d3d9filter walked onlynode.Polygons, nevernode.Portals(PortalRef); every dropped poly was a portal fill; all fills areBase1Solid; retail skips them viaskipNoTexture; visible doors are entities. No static filter can be correct — but the correct rule is a surface-type draw gate, which is nearly as simple. - #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).
- #115 (charter §4.5) — root cause confirmed: missing collided-viewer feedback into the damping origin; plus the player fade is computed but never applied.
- #108/#109 (charter §4 re-test list) — concrete mechanisms named (depth fence, clear shape, flood pop, view loss) — see ledger.
- 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:
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).LScape::drawinternals — 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.)PView::DrawPortalmode 3 (seal-on-failure) — who calls it; matters for unstreamed interiors.- Window-type CBldPortals — do they flood (stab lists + GetVisible-able targets)? Decides window treatment in P1/P4 gates.
cdstWnear-W constant;CBldPortal.sidednesssemantics; theDrawMeshskipNoTexture else-branch latch — pin during P1/P2 implementation.- 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.