Files the independent review corpus (fable/opus/external + audit, DAT-format, F418 DAT-geometry, retail decomp reconstruction) and the synthesis that adjudicates their disagreements: retail roots INTERIOR at the posed terrace defect pose with a ONE-CELL flood and ov=1 (the free captures rooted outdoor - both were real); the falls are outdoor landblock emitters 0xCF418000-13; the "thin seam bands" claim is DAT-refuted (full-size exits; the seam is a coincident double exit-to-outside plane at y=48); clear+seals live inside retail''s ov>0 block with a one-frame seal latch. Carries the instrument-first plan (falls-owner trace, depth checkpoints, pose conformance) and the decomp-pinned repair sequence toward FW4''s single-owner goal. Also sweeps in the probe scaffolding slice 5''s path-limited add had missed (RenderingDiagnostics field + the launch-options row wording). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7.3 KiB
Cathedral review — retail interior-root frame reconstruction (companion report)
Delivered 2026-08-30 by the decomp-reconstruction agent (BN pseudo-C + live Ghidra arbitration on 8081 + acclient.h + symbols.json). THE retail mechanism reference for the synthesis. Condensed; all pc:/address citations preserved.
Frame skeleton (interior root)
SceneTool::BeginScene@0x0043dad0: full Clear(7) — COLOR+depth+ stencil — every frame (pc:68673-68686).Flipbumps m_nFrameStamp at frame end (pc:428642).RenderNormalMode@0x00453aa0: interior camera → optional LScape::update_viewpoint (only when viewer_cell->seen_outside, pc:92667-71) → DrawInside(viewer_cell) → unconditional final drain FlushAlphaList(0f) (pc:92687).- TWO PView singletons (pc:427800/427813):
indoor_pview(draw_landscape =1) for the root;outdoor_pview(0) for building look-ins — look-in floods can never re-trigger landscape/clear/seals (their outside_view.view_count is always 0; ClipPortals never raises it when draw_landscape=0, pc:433666-84). PView::DrawInside@0x005a5860: push root view (full viewport) →ConstructView(root, 0xffff)— PURE CPU flood, draws nothing; exit portals merge clipped apertures into outside_view (pc:433662-76, cliplandscape=1 @0x00820f4c).
PView::DrawCells @0x005a4840 — ALL of this inside ov > 0 (pc:432715):
Render::PortalList = &outside_view;LScape::draw(pc:432718-19).- Full outdoor alpha drain FlushAlphaList(0f) (pc:432720).
m_nFrameStamp += 1(pc:432722) — re-arms part dedupe so a part can draw in BOTH the outdoor and interior passes of one frame (straddlers draw twice; "emit once" is anti-retail).- Gated z-only clear (pc:432725-32, Ghidra-verified):
forceClear(const 0) || portalsDrawnCount != 0— the counter is incremented ONLY by own-depth portal stamps (seals, arg2==0, pc:424569-70) → a ONE-FRAME LATCH: last frame's seals arm this frame's clear. Steady-state vista frames clear every frame; the FIRST such frame does not. - Exit seals (pc:432737-807): per flooded cell (far→near), per view,
EVERY portal with other_cell 0xffffffff:
DrawPortalPolyInternal(poly, 0)— REAL projected depth, DEPTHTEST_ALWAYS + z-write ON (pc:424584), invisible (vertex alpha 0, pc:424686), CULL_NONE, CPU-clipped to the current view (pc:424577). (Sibling mode arg2==1 = the far-Z PUNCH z≈0.999999, look-ins only; does not increment the counter.)
Then UNCONDITIONALLY (even when ov==0): 6. Lighting restore (pc:432811-12). 7. Cell shells far→near: DrawEnvCell dedupes per frame stamp — the shell draws ONCE, WHOLE; the per-view loop is vestigial for it; no view planes applied; correctness = pure z-test (pc:427885-88). 8. Cell objects far→near: PortalList = the cell's views → DrawObjCellForDummies → shadow parts sorted by CYpt (viewer distance to scaled sort_center, insertion_sort @pc:701156-75) → CPhysicsPart::Draw — statics, dynamics, AND particle-emitter parts through ONE machinery. 9. Final drain (pc:92687) vs interior depth + seal-stamped apertures.
Clip vs cull (the definitive table)
- CPU polygon-clipped: ONLY portal-chain polys during the flood (GetClip @0x005a4320) and seal/punch fans at draw (their own polyClipFinish vs the current view, pc:424577, + a ±12 local-XY trivial reject pc:424506-50).
- Sphere-vs-view CULLED, whole-drawn: all meshes (statics, dynamics, particles' parts) — DrawMesh per view: viewconeCheck(sphere) (pc:429295-96); PARTIAL draws WHOLE under that view; a mesh in N views submits N times. Building PORTAL pass is cone-exempt (punches always attempted; GetClip <3 points is the real stop, pc:429302-10).
- Cached-flag culled: terrain cells via in_view stamped by draw_check_blocks @0x00505f80 iterating EVERY view of PortalList (blocks wholly outside every exit view skip at pc:267936). Terrain polys then draw whole + immediate (landPolysDraw @0x006b7040 — no view clip, no alpha list).
- No test at all: EnvCell shell geometry; seal z-behavior (ALWAYS).
alwaysDrawObjects = 1(@0x00820ed4): DrawSortCell runs for EVERY cell of a drawn block — the per-part per-view cone check is the real object gate (no pixel leak, just CPU).
The alpha lists (retail)
TWO static FIFO lists, 3000 entries each (Clip @0x00871b10, Alpha @0x008af370). NO depth key stored, NO sort at flush — order = submission order; far-to-near correctness comes from the cell walk + per-cell CYpt insertion sort. Overflow DROPS the subset (pc:424983). Entry routing: s_AlphaDelayMask (default 0xE) vs the surface mask; bit3 → Clip list.
Flush sites (grep-complete):
| # | Site | Arg | Depth in place |
|---|---|---|---|
| 1 | DrawBuilding @0x0059f30b | 0f FULL | outdoor-so-far (pre-punch) |
| 2 | DrawBlock @0x005a1a07 | 0.75 valve, fires per outdoor cell (BN's flush>1 guard is a MISREAD — Ghidra: fires when flush<=1.0) |
mid-landscape |
| 3 | DrawCells @0x005a4872 | 0f FULL | complete outdoor depth (pre-clear) |
| 4 | RenderNormalMode @0x00453b8b | 0f FULL (final) | interior + seals |
| 5 | CreatureMode::Render (chargen only) | 0f | its own viewport |
Depth at final drain (interior root)
- Aperture/vista pixels: the SEAL's portal-plane depth (outdoor per-pixel depth NO LONGER EXISTS — cleared; outdoor COLOR survives under the stamp). Interior alpha fragments behind the portal plane FAIL there.
- Outdoor particles (falls): drawn in the landscape pass, drained at site #3 vs outdoor depth. Cone-PARTIAL spill outside the aperture is buried because EVERY interior pixel paints after every outdoor drain point — protection is pure ORDERING, not depth.
ShouldDrawParticles @0x0050fe60 — an UPDATE-time gate
Called per emitter from ParticleEmitter::UpdateParticles (physics-side),
NOT during rendering: ExaminationObject → 1; CYpt > degrade_distance → 0; cell NULL → 0; cell->IsInView()==0 → 0. On false: SetNoDraw(1),
degraded_out=1 (particles stop updating AND drawing).
- CLandCell::IsInView @0x00532cb0 = the PREVIOUS frame's stamped in_view (one-frame-stale gate).
- CEnvCell does NOT override it — the slot resolves to the ICF-folded
constant @0x005269f0
return 1(PARTIALLY_INSIDE, PDB-verified): for interior emitters the cell test is VACUOUS; distance is the only update-time gate; their draw is flood-gated instead.
Can content outside the flood/exit views draw? NO.
Interior: pure flood membership. Outdoor blocks/cells: in_view under ≥1 exit view. Outdoor parts: per-exit-view sphere-cone in DrawMesh. Building portal pass cone-exempt but clip-stopped. No distance bypass exists.
Corrections to campaign docs
- DrawBlock per-cell flush polarity RESOLVED (fires; 0.75 pressure valve) — BN misread.
- The m_nFrameStamp bump separates the landscape and interior passes of ONE interior-root frame; look-ins NEVER bump (outdoor_pview ov==0).
- NEW: CEnvCell::IsInView = constant PARTIALLY_INSIDE (ICF fold, symbols.json 19-name cluster) — not in any FW doc.
UNVERIFIED (the agent's own list)
The 0xF418 falls emitters' owner (land cell vs EnvCell static — dat question; note the DAT-geometry companion resolves this: landblock object-list stabs); z-write during alpha drains; insertion_sort direction byte-proof; the ±12 reject polarity; GameSky depth behavior.