knife-edge port: polyClipFinish W=0 eye-plane clip + degenerate-view propagation; EyeInsidePortalOpening rescue DELETED
Ports retail ACRender::polyClipFinish (0x006b6d00, pc:702749) near-eye
semantics into PortalProjection.ProjectToClip - the fundamental fix for
the in-plane portal clip family (climb strobes, tower-top roof/floor
flap while turning; live-corroborated this session: [viewer-diff]
0xAAB30108 strobing 27x mid-climb, whole interior dropping at the top).
Pseudocode: docs/research/2026-06-11-polyclipfinish-w0-clip-pseudocode.md.
Three legs, all decomp-driven:
1. ProjectToClip clips at w >= 0 EXACTLY (was EyePlaneW=1e-4), with
retail's any-negative-w gate. Boundary intersections land at w == 0
(homogeneous directions), so a portal the eye is CROSSING yields the
correct unbounded half-region that the bounded view-region clip cuts
to the screen. A w=0 vertex cannot survive a bounded region clip
into the divide (direction fails some edge of any bounded convex
region); the measure-zero corner case is guarded non-finite->empty.
2. CellView.CanonicalKey keys ALL-COLLINEAR (zero-area) views as their
snapped segment ("L:" + extremes) instead of rejecting them - retail
PROPAGATES degenerate views (ClipPortals decomp:433651-433711
forwards any count!=0 GetClip output, no area gate anywhere), keeping
the cell behind an exactly-in-plane portal in the draw list (cells
draw whole; onward floods die naturally). Rejection dropped the
whole chain for the frame - the parked-eye knife-edge band. Finite
key space unchanged -> dedup + strict-growth convergence intact.
3. The EyeInsidePortalOpening rescue is DELETED (the T2-documented
compensation for the 1e-4 divergence) along with EyeStandingPerpDist
+ PointInPoly2D. Empty clip = no flood, period (retail's rule).
CornerFloodReplay - the gate that REFUTED the previous deletion
attempt - passes WITHOUT the rescue under the W=0 port.
Harness criterion corrected to retail's rules (it codified the rescue):
cells fully BEHIND the camera are not required (all-behind portals clip
empty in retail); monotone area holds per root regime; the two
manufactured exact-on-plane steps assert root-only (boundary root pick
is ambiguous; the in-plane portal there is ~perpendicular to the gaze =
genuinely off-screen). Build_CollapsedInteriorPortalNearEye test
inverted to pin the retail empty-clip rule (it pinned the rescue).
New pins: eye-crossing portal -> w==0 boundary verts + half-region (not
sliver); gaze-along-plane degenerate view accepted + segment-key dedup;
non-finite guard. Replay harnesses (CornerFloodReplay, Issue120,
TowerAscent, HouseExit, Issue127) all green.
Suites: App 246+1skip / Core 1430+2skip / UI 420 / Net 294.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2163308032
commit
987313aa54
7 changed files with 357 additions and 130 deletions
|
|
@ -340,4 +340,119 @@ public class PortalProjectionTests
|
|||
Assert.True(ndc.Length >= 3);
|
||||
foreach (var v in ndc) { Assert.InRange(v.X, -0.301f, 0.301f); Assert.InRange(v.Y, -0.301f, 0.301f); }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The W=0 knife-edge port (2026-06-11) — retail ACRender::polyClipFinish part 1
|
||||
// (0x006b6d00, pc:702749; pseudocode at docs/research/2026-06-11-polyclipfinish-
|
||||
// w0-clip-pseudocode.md). The eye-plane clip is at w >= 0 EXACTLY: boundary
|
||||
// intersections land at w == 0 (homogeneous directions), so a portal the eye is
|
||||
// CROSSING (stair openings on a spiral climb, the tower deck) yields the correct
|
||||
// unbounded half-region that the bounded view-region clip then cuts to the
|
||||
// screen. The previous EyePlaneW = 1e-4 made the boundary verts finite ~1e4-NDC
|
||||
// points and the resulting regions sat at the merge/dedup degeneracy threshold —
|
||||
// the climb-strobe class that the (now deleted) EyeInsidePortalOpening rescue
|
||||
// compensated for.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void ProjectToClip_EyeCrossingPortal_BoundaryVertsLandAtWZero()
|
||||
{
|
||||
// A horizontal floor opening 5 mm BELOW the eye, spanning from 1.5 m ahead to
|
||||
// 0.5 m behind — the spiral-climb crossing frame. The two edges crossing the
|
||||
// eye plane must emit intersections at exactly w == 0 (retail polyClipFinish
|
||||
// t = w0/(w0-w1)), not at an epsilon offset.
|
||||
var opening = new[]
|
||||
{
|
||||
new Vector3(-1f, -0.005f, -1.5f), new Vector3(1f, -0.005f, -1.5f),
|
||||
new Vector3(1f, -0.005f, 0.5f), new Vector3(-1f, -0.005f, 0.5f),
|
||||
};
|
||||
var clip = PortalProjection.ProjectToClip(opening, Matrix4x4.Identity, ViewProj());
|
||||
Assert.True(clip.Length >= 3, "an eye-crossing portal must keep its forward half");
|
||||
int atZero = 0;
|
||||
foreach (var v in clip)
|
||||
{
|
||||
Assert.True(v.W >= 0f, $"no survivor may sit behind the eye plane, got w={v.W}");
|
||||
if (v.W == 0f) atZero++;
|
||||
}
|
||||
Assert.True(atZero >= 2, $"the two eye-plane crossings must land at exactly w==0, got {atZero}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClipToRegion_EyeCrossingFloorOpening_YieldsHalfRegionNotSliver()
|
||||
{
|
||||
// Same crossing frame: the visible set through an opening the eye is inside is
|
||||
// the half-screen below the opening's plane horizon — NOT the degenerate sliver
|
||||
// the epsilon clip produced. Full screen area is 4.0; the half-region must hold
|
||||
// a substantial part of it.
|
||||
var opening = new[]
|
||||
{
|
||||
new Vector3(-1f, -0.005f, -1.5f), new Vector3(1f, -0.005f, -1.5f),
|
||||
new Vector3(1f, -0.005f, 0.5f), new Vector3(-1f, -0.005f, 0.5f),
|
||||
};
|
||||
var clip = PortalProjection.ProjectToClip(opening, Matrix4x4.Identity, ViewProj());
|
||||
var ndc = PortalProjection.ClipToRegion(clip, FullScreenCcw());
|
||||
Assert.True(ndc.Length >= 3, "the crossing frame must produce a region, not empty (the climb strobe)");
|
||||
foreach (var v in ndc)
|
||||
{
|
||||
Assert.True(float.IsFinite(v.X) && float.IsFinite(v.Y), $"region verts must be finite, got ({v.X},{v.Y})");
|
||||
Assert.InRange(v.X, -1.001f, 1.001f);
|
||||
Assert.InRange(v.Y, -1.001f, 1.001f);
|
||||
}
|
||||
float area = AbsArea(ndc);
|
||||
Assert.True(area > 1.5f,
|
||||
$"the region must approximate the lower half-screen (area ~2.0 of 4.0), got {area} (sliver = the strobe bug)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EyeInPortalPlane_GazeAlongPlane_DegenerateViewPropagates()
|
||||
{
|
||||
// The spiral-climb knife edge: the eye sits IN a horizontal portal's plane with
|
||||
// the gaze ALONG the plane (climbing stairs through the opening). The opening is
|
||||
// visibly edge-on ON screen: ProjectToClip + ClipToRegion yield a zero-area
|
||||
// collinear region — and retail PROPAGATES it (ClipPortals forwards any count!=0
|
||||
// clip; no area gate), keeping the cell behind in the draw list. CellView.Add
|
||||
// must therefore ACCEPT the collinear polygon (the "L:" segment key) instead of
|
||||
// rejecting it as degenerate — rejection dropped the whole chain for the frame.
|
||||
var view = Matrix4x4.CreateLookAt(Vector3.Zero, new Vector3(0, 0, -1), Vector3.UnitY);
|
||||
var proj = Matrix4x4.CreatePerspectiveFieldOfView(MathF.PI / 3f, 16f / 9f, 1.0f, 5000f);
|
||||
var vp = view * proj;
|
||||
// Horizontal opening in the y=0 plane (contains the eye), ahead of the camera.
|
||||
var opening = new[]
|
||||
{
|
||||
new Vector3(-1f, 0f, -1f), new Vector3(1f, 0f, -1f),
|
||||
new Vector3(1f, 0f, -4f), new Vector3(-1f, 0f, -4f),
|
||||
};
|
||||
var clip = PortalProjection.ProjectToClip(opening, Matrix4x4.Identity, vp);
|
||||
Assert.True(clip.Length >= 3, "the in-plane opening's forward part must survive the W clip");
|
||||
var ndc = PortalProjection.ClipToRegion(clip, FullScreenCcw());
|
||||
Assert.True(ndc.Length >= 3, "the edge-on opening must yield its (zero-area) collinear region");
|
||||
|
||||
var cellView = new CellView();
|
||||
Assert.True(cellView.Add(new ViewPolygon(ndc)),
|
||||
"a zero-area collinear view must be ACCEPTED (retail propagates degenerate views; " +
|
||||
"rejecting it drops the cell chain at the knife edge)");
|
||||
// Re-emission of the same degenerate view dedups (finite segment-key space = convergence).
|
||||
Assert.False(cellView.Add(new ViewPolygon(ndc)),
|
||||
"a re-emitted degenerate view must dedup via its segment key");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClipToRegion_NeverReturnsNonFiniteVerts()
|
||||
{
|
||||
// The measure-zero guard: whatever survives the bounded region clip must divide
|
||||
// to finite NDC. Exercise with a portal whose vertices sit ON the eye plane
|
||||
// (w == 0 inputs) plus one in front — degenerate input, must yield empty or finite.
|
||||
var degenerate = new[]
|
||||
{
|
||||
new Vector3(-1f, 0f, 0f), new Vector3(1f, 0f, 0f), new Vector3(0f, 1f, -2f),
|
||||
};
|
||||
var clip = PortalProjection.ProjectToClip(degenerate, Matrix4x4.Identity, ViewProj());
|
||||
if (clip.Length >= 3)
|
||||
{
|
||||
var ndc = PortalProjection.ClipToRegion(clip, FullScreenCcw());
|
||||
foreach (var v in ndc)
|
||||
Assert.True(float.IsFinite(v.X) && float.IsFinite(v.Y),
|
||||
$"non-finite NDC vert leaked from the divide: ({v.X},{v.Y})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue