diff --git a/docs/superpowers/plans/2026-05-29-phase-a8f-portal-frame-visibility.md b/docs/superpowers/plans/2026-05-29-phase-a8f-portal-frame-visibility.md index 3d0f554..b20fef9 100644 --- a/docs/superpowers/plans/2026-05-29-phase-a8f-portal-frame-visibility.md +++ b/docs/superpowers/plans/2026-05-29-phase-a8f-portal-frame-visibility.md @@ -410,6 +410,19 @@ git commit -m "feat(render): Phase A8.F — ScreenPolygonClip 2D convex-polygon ## Task 3: `PortalProjection` — project portal polygon to NDC with near-clip +> **Correction (2026-05-29, during execution):** the `w >= WEps` clip in the Step-3 code +> below was a bug — it leaves a clipped vertex at the eye singularity (w≈1e-4) so `x/w` blows +> up (~±7852), relocating the inversion instead of preventing it. The shipped code clips +> against the in-front-of-camera half-space `w + z >= 0` (commit `a28a176`; comments corrected +> in `9ec8330`). That predicate is convention-agnostic: acdream's cameras use +> `Matrix4x4.CreatePerspectiveFieldOfView` (NDC z ∈ **[0,1]**, not GL [-1,1]); the eye (w=0) +> is always excluded so the divide is safe — and **Task 6 needs no projection-convention fix** +> (verified: no `glClipControl`/`glDepthRange` remap anywhere in the codebase). The straddle +> test bound was relaxed `[-10,10]`→`[-50,50]` and a 4th downstream-intersection test added. +> **Task 4 requirement:** `ProjectToNdc` preserves input winding (NOT normalized CCW) — the +> builder MUST apply the portal-side test and feed camera-facing (CCW) portals to the CCW-only +> `ScreenPolygonClip`, and Task 4's conformance tests should assert a winding/back-face case. + Transform a cell-local portal polygon to clip space, clip against the in-front-of-camera plane (`w >= eps`) to prevent inversion when a portal straddles the camera, then perspective-divide to NDC. Ports the projection in retail `PView::GetClip` (decomp:432344); the `w` clip is the homogeneous form of retail's near-plane sidedness math (`PView::ConstructView(CBldPortal)` decomp:433832-433845). **Files:**