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:
Erik 2026-06-11 21:44:23 +02:00
parent 2163308032
commit 987313aa54
7 changed files with 357 additions and 130 deletions

View file

@ -0,0 +1,98 @@
# ACRender::polyClipFinish — W=0 eye-plane clip pseudocode (the knife-edge port)
**Source:** `ACRender::polyClipFinish` at `0x006b6d00`,
`docs/research/named-retail/acclient_2013_pseudo_c.txt:702749-702988`.
Read 2026-06-11 for the knife-edge in-plane portal clip port
(handoff `docs/research/2026-06-11-tower-stairs-fundamental-handoff.md` §5).
## Signature (reconstructed)
```
polyClipFinish(view_vertex** inVerts, // arg1 — homogeneous clip-space verts (x,y,z,w)
int inCount, // arg2
Vec2Dscreen** outVerts, // arg3 — output vertex pointers
int* outCount, // arg4
int planeMask) // arg5 — per-edge skip mask (bit set = poly already
// fully inside that portal_view edge)
```
## Part 1 — the W=0 eye-plane pass (0x006b6d5d0x006b6f12)
```
scan = inCount - 1
while scan >= 0: # walk verts from the END
if inVerts[scan].w < 0: break # found a vertex BEHIND the eye plane must clip
scan -= 1
if scan < 0: goto edge_clips # all w >= 0 → skip the W pass entirely
# homogeneous Sutherland-Hodgman against w = 0, intersections EMITTED:
out = []
prev = inVerts[0]; prevIn = (prev.w >= 0)
for cur in inVerts[last..0]: # retail iterates indices descending
curIn = (cur.w >= 0)
if curIn != prevIn:
t = prev.w / (prev.w - cur.w) # 0x006b6ea0: w0 / (w0 - w1)
emit(prev + t*(cur - prev)) # interpolates x, y, z, w → lands at w == 0 exactly
if curIn: emit(cur)
prev, prevIn = cur, curIn
if len(out) < 3: return # 0x006b6f00: reject fewer than 3 survivors
inVerts = out # ping-pong to tempPtBuf
```
**x87 flag-decode note** (the BN polarity trap, [[feedback_bn_decomp_field_names]]):
the scan loop's `test ah, 0x5` (C0|C2) breaks on **w < 0**, NOT w ≥ 0. Decoded by
case analysis: the all-behind polygon must reach the W pass and clip to empty
(reject), and the common all-in-front polygon must skip the pass — only the
break-on-negative decode yields both. The inside predicate in the clip pass
(`test ah, 0x41`, C0|C3) is **inside ⇔ w ≥ 0** (emit-on-sign-change with
`t = w0/(w0w1)` confirms: t∈[0,1] requires opposite signs).
## Part 2 — portal_view edge clips (0x006b6d820x006b7030)
```
for each portal_view edge (vertex pair), mask-gated (planeMask bit set → skip):
# homogeneous 2D edge function for vertex P against edge (a → b):
# side(P) = (P.x a.x·P.w)·(b.y a.y) (P.y a.y·P.w)·(b.x a.x)
# (0x006b6e05) — linear in (x, y, w): valid for w = 0 verts (directions).
Sutherland-Hodgman with intersection emission (t = s0/(s0 s1), all 4 comps)
if survivors < 3: return # 0x006b6fe1
*outCount = survivors # 0x006b7006
```
## The load-bearing semantics for acdream
1. **Clip at w ≥ 0 EXACTLY** — boundary intersections land at w == 0. A w=0
vertex is a homogeneous DIRECTION; the polygon containing it represents the
unbounded screen region extending toward that direction. This is what makes
an eye-crossing portal (climbing through a stair opening) produce the
correct large half-region instead of a bounded sliver:
- At `w ≥ ε` (our old `EyePlaneW = 1e-4`), boundary verts are finite NDC
points ~1e4 units out along the portal-plane horizon line; the polygon's
screen intersection still APPROXIMATES the half-region, but the divide
and the dedup/merge operate on degenerate near-collinear coordinates.
- At `w = 0`, the edge functions stay exact (linear in homogeneous coords)
and no divide ever touches a w=0 vertex (see invariant below).
2. **A w=0 vertex can never survive the region clip into the divide** when the
clip region is BOUNDED: for a bounded convex CCW region the edge directions
wrap 360°, so a nonzero direction fails at least one edge's inside test.
Our regions are always bounded (FullScreenQuad and its descendants), so the
post-clip divide is safe by construction. The measure-zero exception
(direction exactly on a region corner) is guarded by a non-finite check
that returns empty — identical net behavior to retail's degenerate sliver.
3. **Empty is a verdict, not an error.** `< 3 survivors → reject` at every
stage; retail has NO eye-in-opening rescue anywhere in this path. The
acdream `EyeInsidePortalOpening` rescue was the documented compensation for
the `EyePlaneW = 1e-4` divergence (T2 ledger) and is deleted with this port.
## What is NOT ported here
- `cdstW = 0.000199999995` (pinned at `0x007247d5`) — consumed elsewhere
(its consumer is still unmapped; `landPolysDraw` 0x006b7040 uses the same
0.0002 inline for plane side tests). `PortalSideEpsilon = 0.01` stays as the
documented root-lag tolerance (T2 refutation: retail's 0.0002 needs
eye-exact viewer-cell tracking first).
- The `planeMask` per-edge skip — a perf short-circuit; our ClipToRegion
clips against every region edge unconditionally.
- Retail's descending vertex iteration order — Sutherland-Hodgman output is
order-invariant up to rotation; we keep ascending.