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

5.2 KiB
Raw Blame History

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.