fix #129: cap the punch mark bias's eye-space reach (was unbounded at distance)
The user's "doors/doorways leak through terrain and houses over a
landblock" is the #117 mark-pass bias evaluated in the wrong space.
Mechanism (confirmed analytically, Issue129PunchBiasTests): the punch's
pass-A stencil mark biased the aperture fan toward the viewer by a
CONSTANT 0.0005 NDC. NDC depth is non-linear - a constant NDC bias b
spans ~= b*d^2*(f-n)/(f*n) meters of eye depth at eye distance d. With
retail's znear 0.1 (d4b5c71) that is 0.125 m at 5 m but ~190 m at one
landblock: every hill/house in front of a distant aperture passed the
LEQUAL mark and was far-Z punched -> door-shaped leak through the
occluder. This is exactly the risk AD-18's register row recorded
("an occluder within ~bias in front of a distant aperture gets punched
through") - the symptom-scan rule found it before instrumentation.
Fix: cap the bias's EYE-SPACE span at 0.5 m -
biasNdc(d) = min(0.0005, capMeters * near / d^2)
in the mark-pass vertex shader (clipPos.w = eye depth), CPU-mirrored as
PortalDepthMaskRenderer.MarkBiasNdc for tests. Below the ~10 m
crossover the constant-NDC term is smaller and wins - bit-identical to
the T5-validated close-range behavior, so the #108 grass coverage that
justified the bias is untouched. Beyond it the punch can never reach an
occluder more than 0.5 m in front of the aperture plane.
Pins (Issue129PunchBiasTests): the old form spans >100 m of eye depth
at a landblock (the leak, kept as documentation of the refuted shape);
the capped form stays <= 0.5 m at every distance 1-400 m and matches
the validated constant bit-for-bit below 10 m.
AD-18 register row updated in the same commit (bias description + the
#129 closure + the residual risk note: door-hugging geometry beyond the
0.5 m cap at >10 m viewing range re-occludes - the cap constant is the
tuning knob if the gate shows residue).
Suites: App 256+1skip / Core 1439+2skip / UI 420 / Net 294 green.
Awaiting the user visual gate at the original spot (+ #108 cellar
re-check up close).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6c4b6d64d9
commit
4ba714835d
4 changed files with 134 additions and 25 deletions
|
|
@ -4477,36 +4477,38 @@ staircase entity's per-frame draw decision.
|
|||
|
||||
## #129 — Doors/doorways leak through terrain and houses from over a landblock away
|
||||
|
||||
**Status:** OPEN
|
||||
**Status:** FIX SHIPPED — awaiting user visual gate
|
||||
**Severity:** MEDIUM (visible at distance during normal outdoor play)
|
||||
**Filed:** 2026-06-12 (user report, post-#119-close session)
|
||||
**Component:** render — aperture depth punch at distance (#117 family)
|
||||
**Component:** render — aperture depth punch at distance (#117 family, AD-18)
|
||||
|
||||
**Symptom (user):** "leakage of like doors and doorways through the
|
||||
terrain and houses over a landblock" — door/doorway-shaped patches
|
||||
visible THROUGH intervening terrain and nearer buildings when the
|
||||
source building is roughly a landblock (~192 m) or more away.
|
||||
|
||||
**Leads:**
|
||||
1. **The #117 stencil depth-gate bias at long range (top suspect).**
|
||||
#117's fix (`478c549`) marks aperture pixels at biased true depth
|
||||
(LEQUAL, bias 0.0005 NDC) then far-Z punches only marked pixels. With
|
||||
a non-linear depth buffer, 0.0005 NDC at ~200 m spans many METERS of
|
||||
view depth — the bias can exceed the separation between the aperture
|
||||
and a hill/house in front of it, marking occluder pixels and punching
|
||||
them → the occluder shows the interior/background behind. The #108
|
||||
coverage constraint pulls the bias up; distance pulls it wrong —
|
||||
re-derive the bias in eye-space (or scale by w) instead of constant
|
||||
NDC.
|
||||
2. Per-building look-in floods admitting distant buildings (the #127
|
||||
churn family) — would gate WHICH buildings punch, not the
|
||||
through-occluder leak itself.
|
||||
**Root cause (lead 1 confirmed analytically, `Issue129PunchBiasTests`):**
|
||||
the #117 mark-pass bias was a CONSTANT 0.0005 NDC. NDC depth is
|
||||
non-linear — a constant NDC bias `b` spans ≈ `b·d²/near` meters of eye
|
||||
depth at distance `d`. With retail's znear 0.1 that is 0.125 m at 5 m
|
||||
but **~190 m at a landblock**: every hill/house in front of a distant
|
||||
aperture passed the LEQUAL mark and was far-Z punched → the door-shaped
|
||||
leak. Exactly AD-18's recorded "Risk if assumption breaks".
|
||||
|
||||
**Next:** capture at the spot (ACDREAM_PROBE_VIEWER=1 + a screenshot +
|
||||
player/eye position from [snap]/[viewer]); confirm whether the leak
|
||||
patch matches an aperture polygon of the distant building; then test
|
||||
the eye-space-bias hypothesis headlessly (the #117 commit has the bias
|
||||
math).
|
||||
**Fix (2026-06-12):** cap the bias's EYE-SPACE span —
|
||||
`biasNdc(d) = min(0.0005, 0.5 m × near / d²)`
|
||||
(`PortalDepthMaskRenderer.MarkBiasNdc`, mirrored in the vertex shader).
|
||||
Below the ~10 m crossover the constant term wins, bit-identical to the
|
||||
T5-validated behavior (#108 grass coverage untouched); beyond it the
|
||||
punch can never reach an occluder more than 0.5 m in front of the
|
||||
aperture plane. Pins: `Issue129PunchBiasTests` (old form spans >100 m
|
||||
at a landblock; capped form ≤0.5 m at all distances; close range
|
||||
unchanged).
|
||||
|
||||
**Gate:** the original spot — distant building doors no longer show
|
||||
through terrain/houses at ~a landblock; AND the #108 cellar grass-sweep
|
||||
stays gone up close. If a >10 m-range #108-class residue appears, the
|
||||
cap constant (0.5 m) is the tuning knob — see AD-18.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ accepted-divergence entries (#96, #49, #50).
|
|||
| AD-15 | `IsEnv` masks low-16 of the cell id (`(Id & 0xFFFF) >= 0x100`) where retail tests the full id | `src/AcDream.Core/World/Cells/ObjCell.cs:25` | Every real prefixed EnvCell id has low-16 ≥ 0x100 and every outdoor cell ≤ 0x40 — identical answers for all real dat ids, works for both bare and prefixed forms | None for real dat data; a hypothetical convention-violating id would route to the wrong (BSP vs terrain) point-in-cell logic | `CObjCell::GetVisible` pc:308215 |
|
||||
| AD-16 | Building-flood gate is a CPU frustum test on each building's `PortalBounds` AABB; retail floods exactly when the shell draws and an aperture survives (no bounds constant anywhere) | `src/AcDream.App/Rendering/GameWindow.cs:7634` | Documented as the tight equivalent of the shell viewconeCheck for flood purposes (the FPS fix the Chebyshev≤1 hack approximated); per-portal admission still goes through BuildFromExterior's screen clip; missing-bounds buildings always flood (safe over-include) | A too-small/stale PortalBounds AABB means the interior never floods — doorway shows a hole/black aperture from outside (inverse of the vanishing-staircase class) | `DrawBuilding` 0x0059f2a0; `BSPPORTAL::portal_draw_portals_only` 0x53d870 |
|
||||
| AD-17 | ≤8 GPU `gl_ClipDistance` half-planes per view region, degrading to a union-AABB scissor (over-include) on multi-polygon / >8-edge views; particles always scissor; scissor slices disable per-object viewcone culling. Retail CPU-clips against the exact portal polygon | `src/AcDream.App/Rendering/ClipPlaneSet.cs:23` | GL guarantees only 8 simultaneous clip planes; invariant documented: over-inclusion is safe, under-inclusion is the bug class | Fallback on complex multi-aperture views draws terrain/sky/particles/objects outside the true aperture but inside its AABB — background/interior bleed strips at doorways (the **#130** family) | `ACRender::polyClipFinish` decomp:702749; PView portal_view slices |
|
||||
| AD-18 | Aperture far-Z punch is two-pass stencil-gated with invented `PunchMarkDepthBias = 0.0005` NDC; retail's single DEPTHTEST_ALWAYS punch is safe only under painter's far→near order we don't have | `src/AcDream.App/Rendering/PortalDepthMaskRenderer.cs:149` | **#117** (2026-06-11): the unconditional punch erased nearer occluders (hills, closer buildings), painting interiors through them; the two-pass form is the z-buffered equivalent of retail's ordering safety. DO-NOT-RETRY: punch must stay depth-gated (ISSUES #108) | Bias is depth-dependent: an occluder within ~bias in front of a distant aperture gets punched through; door-plane-hugging geometry just beyond it re-occludes the aperture (a **#108**-class regression) | `D3DPolyRender::DrawPortalPolyInternal` 0x0059bc90 (maxZ1=7 / maxZ2=6) |
|
||||
| AD-18 | Aperture far-Z punch is two-pass stencil-gated with an invented mark bias: 0.0005 NDC capped to a 0.5 m EYE-SPACE span (`MarkBiasNdc`); retail's single DEPTHTEST_ALWAYS punch is safe only under painter's far→near order we don't have | `src/AcDream.App/Rendering/PortalDepthMaskRenderer.cs:149` | **#117** (2026-06-11): the unconditional punch erased nearer occluders, painting interiors through them; the two-pass form is the z-buffered equivalent of retail's ordering safety. **#129** (2026-06-12): the constant-NDC bias spanned ~190 m of eye depth at a landblock (non-linear depth) → distant occluders punched; the eye-space cap bounds the reach (`Issue129PunchBiasTests`). DO-NOT-RETRY: punch must stay depth-gated (ISSUES #108) | Door-plane-hugging geometry beyond the 0.5 m cap re-occludes the aperture (a **#108**-class regression at >10 m viewing range); an occluder within the cap in front of a distant aperture still punches through | `D3DPolyRender::DrawPortalPolyInternal` 0x0059bc90 (maxZ1=7 / maxZ2=6) |
|
||||
| AD-19 | Under outdoor roots, ALL dynamics draw in one z-buffered final pass; retail draws objects painter-ordered per landcell inside the landscape pass (interior roots route per **#118**) | `src/AcDream.App/Rendering/RetailPViewRenderer.cs:126` | The dynamics-drawn-LAST invariant is what makes the aperture depth punch safe (first BR-2 attempt punched after dynamics and erased the player, reverted `88be519`); z-buffer substitutes for painter's order on opaque geometry | Punch/seal correctness hinges on an ordering invariant — any pass added after DrawDynamicsLast, or alpha content needing painter order, gets erased inside apertures or composites wrong | `LScape::draw` → `DrawBlock` 0x005a17c0 → DrawSortCell pc:430124; `PView::DrawCells` 0x005a4840 |
|
||||
| AD-20 | Camera sweep fallback seeds the eye's `AdjustPosition` from the PLAYER's cell; retail re-seats at the sought eye's own tracked cell (rest of function is a verbatim `update_viewer` port) | `src/AcDream.App/Rendering/PhysicsCameraCollisionProbe.cs:97` | acdream's camera doesn't track the sought-eye's cell separately; the eye is near the player so the player-cell stab list is assumed to cover it | An eye outside the player cell's stab-list coverage (boundary corners, cross-landblock pull-back) seats in the wrong cell — and the viewer cell roots the whole render: one-frame wrong root (flap-class flash) | `SmartBox::update_viewer` 0x00453ce0, pc:92878-92883 |
|
||||
| AD-21 | Null-clipRoot legacy outdoor safety path (no portal visibility, no punches/seals, no-clip terrain) for pre-spawn / login / legacy cameras; in-world retail always has a viewer_cell root | `src/AcDream.App/Rendering/GameWindow.cs:7671` | Result is null ONLY when neither an interior root nor the synthetic outdoor node exists; kept so the login screen shows the live sky | If viewer-root resolution ever returns null in-world (membership bug, fly-camera edge), the frame silently degrades — interiors stop drawing through doorways; the old two-branch FLAP reappears for those frames | `SmartBox::RenderNormalMode` decomp:92635 |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue