diag(render): Phase U.4c — flap probe logs projected NDC coords + clip result

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) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-31 14:21:46 +02:00
parent 1d47ede007
commit fde169970f

View file

@ -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);