fix(render): R-A2b — cull back portal like retail (InitCell side test), kill the indoor flap cycle
Pinned (flap-sidechk.log): the indoor doorway flap is a 0171<->0173 flood cycle. Back portals show camInterior=False (our side test already agrees with retail) but were traversed when eyeIn=True because the side-cull had an bypass (added 2026-06-05 for the void). Within 1.75m of a doorway that bypass kept the BACK portal alive -> mutual re-contribution -> re-enqueue churn (maxPop=16) -> eye-sensitive flood depth -> grey flap + dropped floor. Fix (Option B1): drop the bypass from the side-cull in Build + BuildFromExterior so back portals cull by the side test alone, exactly like retail PView::InitCell (:432962, no eye-in-opening bypass). The forward-portal clip-empty void rescue is a SEPARATE branch and is untouched (Build_EyeStandingInInteriorPortal_FloodsNeighbour stays green). New RED->GREEN test Build_BackFacingPortal_EyeStandingInOpening_StillCulled; full App suite 218 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
89a2032c8e
commit
485e44d163
2 changed files with 37 additions and 7 deletions
|
|
@ -216,12 +216,15 @@ public static class PortalVisibilityBuilder
|
|||
|
||||
bool sideAllowed = true;
|
||||
|
||||
// Portal-side test: only traverse a portal the camera is on the interior side of
|
||||
// (mirrors CellVisibility.GetVisibleCells + retail's 'seen' flag). Culls back-facing
|
||||
// portals so we never feed a degenerate/wrong-facing projection downstream.
|
||||
// Portal-side test (retail PView::InitCell side test, decomp:432962): only traverse a portal
|
||||
// the camera is on the INTERIOR side of. Retail culls the back-facing portal (the doorway just
|
||||
// flooded through) by this test ALONE — there is NO eye-in-opening bypass. R-A2b: the old
|
||||
// `&& !eyeInsideOpening` bypass let a back portal within 1.75 m through, forming the
|
||||
// 0171<->0173 flood cycle -> re-enqueue churn -> the doorway flap (pinned in flap-sidechk.log:
|
||||
// back portals show camInterior=False eyeIn=True). The forward-portal clip-empty void rescue
|
||||
// (below, the `clippedRegion.Count == 0` branch) is a SEPARATE path and stays.
|
||||
if (i < cell.ClipPlanes.Count
|
||||
&& !CameraOnInteriorSide(cell, i, cameraPos)
|
||||
&& !eyeInsideOpening)
|
||||
&& !CameraOnInteriorSide(cell, i, cameraPos))
|
||||
{
|
||||
sideAllowed = false;
|
||||
trace?.Add($"portal cell=0x{cell.CellId:X8} p{i}->0x{portal.OtherCellId:X4} skip=side eyeIn={eyeInsideOpening}");
|
||||
|
|
@ -483,9 +486,9 @@ public static class PortalVisibilityBuilder
|
|||
continue; // already outdoors; exterior terrain was drawn by the caller.
|
||||
|
||||
bool eyeInsideOpening = EyeInsidePortalOpening(poly, cell.WorldTransform, cameraPos);
|
||||
// R-A2b: cull back portals by the side test alone (no eye-in-opening bypass) — see Build().
|
||||
if (i < cell.ClipPlanes.Count
|
||||
&& !CameraOnInteriorSide(cell, i, cameraPos)
|
||||
&& !eyeInsideOpening)
|
||||
&& !CameraOnInteriorSide(cell, i, cameraPos))
|
||||
continue;
|
||||
|
||||
var clippedRegion = ClipPortalAgainstView(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue