From fde169970f859e0dc61ea3e1d98261e1d30157e4 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 31 May 2026 14:21:46 +0200 Subject: [PATCH] =?UTF-8?q?diag(render):=20Phase=20U.4c=20=E2=80=94=20flap?= =?UTF-8?q?=20probe=20logs=20projected=20NDC=20coords=20+=20clip=20result?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The [flap] line now reports, per root portal, the actual projected NDC vertices and the Intersect-against-FullScreen result count (clip=N), so a portal that PROJECTS (proj>=3) but still fails to ADD its neighbour (vis stays low) shows WHY: clip=0 with ndc inside [-1,1] = winding/self-intersection degeneracy; clip=0 with ndc outside [-1,1] = genuinely off-screen; the ndc coords expose a near-plane bowtie. Pins the exact clip-region failure before the root-cause fix. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Rendering/PortalVisibilityBuilder.cs | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs b/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs index e7f47d1..dd636a0 100644 --- a/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs +++ b/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs @@ -238,6 +238,10 @@ public static class PortalVisibilityBuilder return frame; } + // The NDC [-1,1] viewport quad (CCW), reused by the flap probe's clip recompute. + private static readonly Vector2[] FullScreenQuad = + { new Vector2(-1f, -1f), new Vector2(1f, -1f), new Vector2(1f, 1f), new Vector2(-1f, 1f) }; + // Phase U.4c flap probe. One [flap] line per Build: the root cell's per-portal // signed distance D (eye→portal plane), traverse/cull decision, and NDC projection // vertex count, plus the frame's OutsideView polygon count + visible-cell count. @@ -266,17 +270,34 @@ public static class PortalVisibilityBuilder d = Vector3.Dot(pl.Normal, localEye) + pl.D; side = CameraOnInteriorSide(cameraCell, i, cameraPos); } - int projN = -1; + // Replicate the walk's project → EnsureCcw → Intersect(FullScreen) exactly, so a + // portal that PROJECTS (proj>=3) but still fails to ADD its neighbour shows WHY: + // clip=0 with ndc inside [-1,1] ⇒ winding/self-intersection degeneracy; clip=0 with + // ndc outside [-1,1] ⇒ genuinely off-screen. The ndc coords expose a near-plane bowtie. + int projN = -1, clipN = -1; + string ndcText = ""; if (i < cameraCell.PortalPolygons.Count) { var poly = cameraCell.PortalPolygons[i]; if (poly != null && poly.Length >= 3) - projN = PortalProjection.ProjectToNdc(poly, cameraCell.WorldTransform, viewProj).Length; + { + var ndc = PortalProjection.ProjectToNdc(poly, cameraCell.WorldTransform, viewProj); + projN = ndc.Length; + if (ndc.Length >= 3) + { + EnsureCcw(ndc); + clipN = ScreenPolygonClip.Intersect(ndc, FullScreenQuad).Length; + var ns = new System.Text.StringBuilder(48); + foreach (var v in ndc) ns.Append('(').Append(v.X.ToString("F1")).Append(',').Append(v.Y.ToString("F1")).Append(')'); + ndcText = ns.ToString(); + } + } } sb.Append(" | p").Append(i).Append("->0x").Append(portal.OtherCellId.ToString("X4")); sb.Append(" D=").Append(float.IsNaN(d) ? "na" : d.ToString("F2")); sb.Append(side ? " TRV" : " CULL"); - sb.Append(" proj=").Append(projN); + sb.Append(" proj=").Append(projN).Append(" clip=").Append(clipN); + if (ndcText.Length > 0) sb.Append(" ndc=").Append(ndcText); } sb.Append(" || outPolys=").Append(frame.OutsideView.Polygons.Count); sb.Append(" vis=").Append(frame.OrderedVisibleCells.Count);