acdream/docs/research/2026-06-11-polyclipfinish-w0-clip-pseudocode.md
Erik 987313aa54 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>
2026-06-11 21:44:23 +02:00

98 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.