using System.Numerics; using AcDream.App.Rendering; using AcDream.App.Rendering.Walk; namespace AcDream.App.Tests.Rendering.Walk; /// /// S3 review fix round 1 (F2, major): the exit-seal CPU/GPU equivalence pin, /// rewritten to drive the PRODUCER that actually runs. The prior pin (still /// at ClipFrameLayoutTests.GetSlotPlanes_ExitSealPath_... before this /// commit) drove ClipFrame.AppendSlot(ClipPlaneSet) directly — an /// overload with ZERO production callers. Production seal planes come from /// , which resolves /// through the driver's private CaptureViews/AppendClipSlot (the /// SAME pixel-space capture /// runs during a real interior flood — RetailPViewPassExecutor. /// DrawExitPortalMask reads it back through exactly this accessor). This /// file drives that real path directly ( /// + the cast, the same minimal-harness pattern /// WalkFrameDriverTranscriptTests uses) — reusing 's own private fixture types via the shared /// partial class. /// public sealed partial class WalkFrameDriverTests { private static WalkScreenPoint[] NdcToPixelPoints(Vector2[] ndcVerts, float width, float height) { var points = new WalkScreenPoint[ndcVerts.Length]; for (int i = 0; i < ndcVerts.Length; i++) { float px = (ndcVerts[i].X + 1f) * width / 2f; float py = (1f - ndcVerts[i].Y) * height / 2f; points[i] = new WalkScreenPoint(px, py, 0f, 1f); } return points; } /// /// KEEP item 1 — exit seals, rewritten through the real producer. /// AppendClipSlot's per-edge plane formula (normalize + inward /// perpendicular, winding-corrected) is IDENTICAL to 's own — two independent /// implementations of the same retail idea — so an exact plane-for-plane /// match against ClipPlaneSet.From's output proves the walk's own /// capture reproduces the CPU polygon's true edge planes, not merely /// bytes that happen to satisfy a looser geometric check. /// MUTATION: flip the winding selection (ccw = area2 < 0f /// instead of >= 0f) — every plane's sign inverts, failing the /// exact-match assertion. Drop the normalize (Vector2.Normalize /// removed from the per-edge formula) — the plane magnitude scales by /// the (unnormalized) edge length, failing the exact-match assertion. /// See /// for the third mutation (deleting the AABB overflow branch). /// [Fact] public void ExitSealPath_PlanesMatchClipPlaneSetsIndependentComputation_ThroughCaptureViewsAndAppendClipSlot() { using var fx = new DispatcherFixture(); var ctx = new TestContext(); using ClipFrame clipFrame = ClipFrame.NoClip(); var driver = new WalkFrameDriver( fx.Dispatcher, new RecordingLeafRenderer(new List()), new FakeWorldData(), clipFrame: clipFrame); const uint cellId = 0x100u; var cell = new WalkCell { CellId = cellId }; cell.PushView(); // A non-axis-aligned convex quad, CCW in NDC — distinct from the // punch-fan KEEP-2 pin's own verts (ClipFrameLayoutTests). Vector2[] verts = [ new(-0.3f, 0.5f), new(-0.6f, -0.2f), new(0.2f, -0.5f), new(0.6f, 0.3f), ]; Assert.True(WalkCopyView.Append( cell.TopView, NdcToPixelPoints(verts, ctx.ViewportWidth, ctx.ViewportHeight), ctx.Rays, ctx.WorldViewpoint)); ctx.Cells[cellId] = cell; driver.BeginFrame(ctx, Matrix4x4.Identity, Vector3.Zero); ((IWalkEventSink)driver).OnInteriorFloodDrawTurn(new[] { cellId }, outsideViewCount: 1); driver.EndFrame(); Assert.Equal(1, driver.InteriorFloodViewSliceCountAt(0)); ReadOnlySpan planes = driver.InteriorFloodViewClipPlanesAt(0, 0); Vector4[] expected = ClipPlaneSet.From(new ViewPolygon(verts)).PlaneArray; Assert.Equal(expected.Length, planes.Length); for (int i = 0; i < expected.Length; i++) { Assert.Equal(expected[i].X, planes[i].X, 4); Assert.Equal(expected[i].Y, planes[i].Y, 4); Assert.Equal(expected[i].Z, planes[i].Z, 4); Assert.Equal(expected[i].W, planes[i].W, 4); } } /// /// F2's first overflow case: a 9-vertex portal view (over = 8) exit-seals to exactly the 4 AABB /// planes that CONTAIN every one of the 9 source vertices (over-include, /// never under-include) — AppendClipSlot's conservative fallback /// for a view too complex for the convex half-plane budget, rather than /// falling back to slot 0 (which would erase the aperture entirely). /// MUTATION: delete the if (ndc.Length > ClipFrame.MaxPlanes) /// branch — the per-edge loop below stack-allocates exactly /// (8) plane slots, so a 9-edge polygon /// indexes past the end and throws, failing this test loudly. /// [Fact] public void ExitSealPath_NineVertexView_UsesFourAabbPlanesContainingEveryVertex() { using var fx = new DispatcherFixture(); var ctx = new TestContext(); using ClipFrame clipFrame = ClipFrame.NoClip(); var driver = new WalkFrameDriver( fx.Dispatcher, new RecordingLeafRenderer(new List()), new FakeWorldData(), clipFrame: clipFrame); const uint cellId = 0x100u; var cell = new WalkCell { CellId = cellId }; cell.PushView(); const int n = 9; var verts = new Vector2[n]; for (int i = 0; i < n; i++) { float angle = i * MathF.Tau / n; verts[i] = new Vector2(0.7f * MathF.Cos(angle), 0.7f * MathF.Sin(angle)); } Assert.True(WalkCopyView.Append( cell.TopView, NdcToPixelPoints(verts, ctx.ViewportWidth, ctx.ViewportHeight), ctx.Rays, ctx.WorldViewpoint)); // No collinear-merge stole a vertex — a genuine 9-gon reached AppendClipSlot. Assert.Equal(n, cell.TopView.View.Polys[0].VertexCount); ctx.Cells[cellId] = cell; driver.BeginFrame(ctx, Matrix4x4.Identity, Vector3.Zero); ((IWalkEventSink)driver).OnInteriorFloodDrawTurn(new[] { cellId }, outsideViewCount: 1); driver.EndFrame(); ReadOnlySpan planes = driver.InteriorFloodViewClipPlanesAt(0, 0); Assert.Equal(4, planes.Length); float minX = verts.Min(v => v.X), maxX = verts.Max(v => v.X); float minY = verts.Min(v => v.Y), maxY = verts.Max(v => v.Y); // Over-include: every source vertex satisfies every plane. foreach (Vector2 v in verts) { var clip = new Vector4(v.X, v.Y, 0f, 1f); for (int p = 0; p < planes.Length; p++) Assert.True(Vector4.Dot(planes[p], clip) >= -1e-3f); } // Exactly the four axis-aligned NDC bounds, matching AppendClipSlot's own formula. Vector4[] expected = [ new(1f, 0f, 0f, -minX), new(-1f, 0f, 0f, maxX), new(0f, 1f, 0f, -minY), new(0f, -1f, 0f, maxY), ]; for (int i = 0; i < 4; i++) { Assert.Equal(expected[i].X, planes[i].X, 3); Assert.Equal(expected[i].Y, planes[i].Y, 3); Assert.Equal(expected[i].Z, planes[i].Z, 3); Assert.Equal(expected[i].W, planes[i].W, 2); } } }