acdream/docs/research/2026-06-11-building-render-acdream-vs-retail-comparison.md
Erik 5e2f99d08e 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>
2026-06-11 05:54:12 +02:00

26 KiB
Raw Blame History

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:

  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; we draw the solid fills as colored geometry, which is why removing them read as "doors disappeared."

  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::DrawPortalConstructView(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/ 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_viewLScape::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 stackDrawObjCell — 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) ObjectMeshManagerWbDrawDispatcher, ~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 SOLVEDe223325 + 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

  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 — but the correct rule is a surface-type draw gate, which is nearly as simple.
  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.