fix #108-residual (root cause): terrain drew DOUBLE-SIDED - port retail landPolysDraw eye-side gate as terrain backface cull

The cellar-ascent grass window was the UNDERSIDE of the z~94 grade
sheet. Retail terrain is single-sided: ACRender::landPolysDraw
(0x006b7040) draws each land triangle ONLY when the camera is on the
POSITIVE (upper) side of its plane (Plane::which_side2 vs
Render::FrameCurrent, zFightTerrainAdjust bias) - a below-grade eye
gets NO terrain, so retail shows sky through the cellar door.

We inherited WB's frame-global cull DISABLE (WB GameScene.cs:841 - an
editor camera goes underground by design) and TerrainModernRenderer.Draw
set no cull state of its own -> terrain rasterized both sides. From a
below-grade eye every aperture sight-ray RISES, so the only 'terrain'
it can see is the grade sheet's underside - which painted the exit-door
aperture (the landscape slice's 2D NDC clip planes (nx,ny,0,dw) have no
depth axis and cannot exclude between-eye-and-portal geometry) and slid
off the door exactly as the eye crossed grade. Membership/viewer was
exonerated by the harness in the previous commit.

Fix: TerrainModernRenderer.Draw owns its cull state (the 7th
self-contained-GL-state instance): Enable(CullFace) + CullFace(Back) +
FrontFace(Ccw), set -> draw -> restore the frame-global CW + cull-off
baseline. GL backface culling evaluates retail's per-triangle eye-side
predicate at rasterization; no shader change.

Pins:
- LandblockMeshTests.Build_AllTriangles_WindCounterClockwiseInWorldXY:
  every emitted triangle CCW in world XY across both FSplitNESW split
  directions - the winding invariant culling depends on.
- TerrainCullOrientationTests: under the production camera convention
  (LookAt up=+Z, Numerics perspective) an up-facing triangle winds CCW
  in window space from above (kept) and CW from below (culled) - guards
  FrontFace inversion, which would blank terrain from above.

Oracle note: retail's through-portal clip has NO portal-face near plane
(PView::GetClip / Render::set_view install edge planes only); nearer-
than-portal exclusion comes from the eye-side cull + cell-level
admission. No register row: this PORTS the retail mechanism, retiring
an undocumented WB-heritage deviation.

Gate pending: cellar climb (grass window gone) + outdoor sanity glance
(terrain intact from above).

Suites: App 263+1skip / Core 1443+2skip / UI 420 / Net 294.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-12 22:05:31 +02:00
parent 007af1391c
commit 96a425a9a5
4 changed files with 180 additions and 19 deletions

View file

@ -3701,27 +3701,47 @@ Unverified. The likely culprits, ranked by suspected probability:
---
## #108 — Cellar↔main-floor transition: terrain (grass) sweeps across the upstairs door opening — [REOPENED 2026-06-11 · narrowed residual]
## #108 — Cellar↔main-floor transition: terrain (grass) sweeps across the upstairs door opening — [FIX SHIPPED 2026-06-12 · awaiting cellar visual gate]
**Status:** REOPENED (narrowed) — the broad symptom is GONE (T5 +
re-gate #2: "Yes, but…"), but a residual remains in ONE window: during
the cellar ASCENT, while the eye is still below ground level, the
upstairs exit-door opening is covered with grass — "like the ground
level rose to the top of the door … as soon as my head pops up it falls
back to ground level" (user, re-gate 2026-06-11). The original
BR-2-era diagnosis stands: grass-sweep frames render through the
OUTDOOR root (membership/viewer-cell flips outdoor mid-cellar), and the
#117 depth-gated punch then correctly refuses to punch the aperture
where terrain depth is NEARER than the door fan (eye below grade ⇒ the
visible front-facing terrain can sit between the eye and the door in
depth). The punch must STAY depth-gated (DO-NOT-RETRY) — the fix is on
the membership/viewer side (why is the root outdoor while the eye is in
the cellar stairwell below grade?). Apparatus shape: a vertical
cellar-ascent variant of the #118 exit-walk harness (drive the eye up
the stair path; log root resolution + the punch's mark-pass outcome per
step). Prior history below.
**Status:** FIX SHIPPED (desk-pinned) — root cause found 2026-06-12; the
cellar-ascent visual gate is pending.
**ROOT CAUSE (2026-06-12): terrain was drawn DOUBLE-SIDED — the grass was
the UNDERSIDE of the grade sheet.** Two steps:
1. The membership/viewer re-diagnosis below is **REFUTED** by the vertical
cellar-ascent harness (`Issue108CellarAscentViewerReplayTests`, dat-backed
A9B4 corner-building cellar 0x0174→0x0175→0x0171, production
FindCellList pick + the camera probe chain mirrored verbatim): 0
outdoor/null viewer resolutions while the eye is below grade, 0 sweep
failures, 0 fallback branches across boom distance {2.61, 5} × damping
lag {0, 0.3}. The viewer enters 0x0171 at eye z 94.01 — exactly as the
head pops above grade (the stairwell portal sits at grade), matching the
user's wording. The root is INTERIOR the whole window.
2. Retail terrain is SINGLE-SIDED: `ACRender::landPolysDraw` (0x006b7040)
draws each land triangle ONLY when the camera is on the POSITIVE (upper)
side of its plane (`Plane::which_side2` vs `Render::FrameCurrent`). A
below-grade eye gets NO terrain — through the door retail shows sky.
WB renders the world with face culling DISABLED frame-globally (WB
`GameScene.cs:841` — editor heritage), and `TerrainModernRenderer.Draw`
set no cull state of its own → terrain drew double-sided. From a
below-grade eye every aperture sight-ray RISES, so the only "terrain" it
can see is the underside of the z≈94 grade sheet — which painted the
whole exit-door aperture (the landscape slice's 2D NDC clip planes
`(nx,ny,0,dw)` have no depth axis and cannot exclude it) and slid down
off the door exactly as the eye crossed grade.
**Fix: port the landPolysDraw eye-side gate as terrain backface culling**
`TerrainModernRenderer.Draw` now owns Enable(CullFace) + Cull(Back) +
FrontFace(Ccw) (set→draw→restore; 7th instance of the self-contained-GL-
state rule). Pins: `LandblockMeshTests.Build_AllTriangles_WindCounter-
ClockwiseInWorldXY` (every emitted triangle CCW in world XY — cull-safe
winding) + `TerrainCullOrientationTests` (above-eye ⇒ CCW window winding
kept / below-eye ⇒ CW culled under the production camera convention).
**Gate:** climb out of the corner-building cellar — the grass window over
the exit door must be gone (sky/world through the door instead); plus a
general outdoor sanity glance (terrain intact from above — a wrong
FrontFace would blank it).
**Severity:** MEDIUM
**Component:** ~~render / indoor PView~~**physics / membership** (cellar-transition root flip)
**Component:** render / terrain (single-sidedness) — membership/viewer EXONERATED
During the cellar→main-floor ascent (Holtburg), the door opening visible on the main floor
shows the outdoor GRASS texture sweeping over it — "like outdoor ground rising up from the