using System.Collections.Generic; using System.Numerics; using AcDream.App.Rendering; using Xunit; namespace AcDream.App.Tests.Rendering; public class ClipFrameAssemblerTests { private static ViewPolygon Square(float cx, float cy, float half) => new(new[] { new Vector2(cx - half, cy - half), new Vector2(cx + half, cy - half), new Vector2(cx + half, cy + half), new Vector2(cx - half, cy + half), }); private static CellView ViewOf(params ViewPolygon[] polys) { var view = new CellView(); foreach (var p in polys) view.Add(p); return view; } [Fact] public void TwoVisibleCells_PlusOutsideView_ProducesCorrectSlotMapAndCounts() { const uint cellA = 0xA9B40100; const uint cellB = 0xA9B40101; var pv = new PortalVisibilityFrame(); pv.CellViews[cellA] = ViewOf(Square(-0.3f, 0f, 0.3f)); pv.CellViews[cellB] = ViewOf(Square(0.3f, 0f, 0.2f)); pv.OrderedVisibleCells.Add(cellA); pv.OrderedVisibleCells.Add(cellB); pv.OutsideView.Add(Square(0f, 0.5f, 0.25f)); var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv); Assert.Equal(4, asm.Frame.SlotCount); // slot 0 + two cells + one outside slice Assert.Contains(cellA, asm.CellIdToSlot.Keys); Assert.Contains(cellB, asm.CellIdToSlot.Keys); Assert.NotEqual(0, asm.CellIdToSlot[cellA]); Assert.NotEqual(0, asm.CellIdToSlot[cellB]); Assert.NotEqual(asm.CellIdToSlot[cellA], asm.CellIdToSlot[cellB]); Assert.Equal(4, asm.PerCellPlaneCounts[cellA]); Assert.Equal(4, asm.PerCellPlaneCounts[cellB]); Assert.True(asm.OutdoorVisible); Assert.NotEqual(0, asm.OutdoorSlot); Assert.Single(asm.OutsideViewSlices); Assert.Equal(asm.OutdoorSlot, asm.OutsideViewSlices[0].Slot); Assert.Equal(TerrainClipMode.Planes, asm.TerrainMode); Assert.Equal(4, asm.OutsidePlaneCount); Assert.Equal(0, asm.ScissorFallbacks); } [Fact] public void NothingVisibleCell_IsExcludedFromSlotMap_AndNotAppended() { const uint cellA = 0xA9B40100; const uint cellB = 0xA9B40101; var pv = new PortalVisibilityFrame(); pv.CellViews[cellA] = ViewOf(Square(0f, 0f, 0.3f)); pv.CellViews[cellB] = new CellView(); pv.OrderedVisibleCells.Add(cellA); pv.OrderedVisibleCells.Add(cellB); var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv); Assert.Equal(2, asm.Frame.SlotCount); Assert.Contains(cellA, asm.CellIdToSlot.Keys); Assert.DoesNotContain(cellB, asm.CellIdToSlot.Keys); Assert.False(asm.OutdoorVisible); Assert.Empty(asm.OutsideViewSlices); Assert.Equal(TerrainClipMode.Skip, asm.TerrainMode); Assert.Equal(0, asm.OutsidePlaneCount); } [Fact] public void OutsideViewMultiPolygon_PreservesRetailSlices() { const uint cellA = 0xA9B40100; var pv = new PortalVisibilityFrame(); pv.CellViews[cellA] = ViewOf(Square(0f, 0f, 0.3f)); pv.OrderedVisibleCells.Add(cellA); pv.OutsideView.Add(Square(-0.5f, 0f, 0.1f)); pv.OutsideView.Add(Square(0.5f, 0f, 0.1f)); var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv); Assert.True(asm.OutdoorVisible); Assert.NotEqual(0, asm.OutdoorSlot); Assert.Equal(2, asm.OutsideViewSlices.Length); Assert.NotEqual(asm.OutsideViewSlices[0].Slot, asm.OutsideViewSlices[1].Slot); Assert.Equal(TerrainClipMode.Planes, asm.TerrainMode); Assert.Equal(4, asm.OutsidePlaneCount); Assert.Equal(0, asm.ScissorFallbacks); Assert.Equal(4, asm.Frame.SlotCount); // slot 0 + cell + two outside slices Assert.Equal(Vector4.Zero, asm.TerrainScissorNdcAabb); } [Fact] public void CellMultiPolygonView_PreservesRetailViewSlices() { const uint cellA = 0xA9B40100; var pv = new PortalVisibilityFrame(); pv.CellViews[cellA] = ViewOf( Square(-0.4f, 0f, 0.1f), Square(0.4f, 0f, 0.1f)); pv.OrderedVisibleCells.Add(cellA); pv.OutsideView.Add(Square(0f, 0f, 0.3f)); var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv); Assert.True(asm.CellIdToSlot[cellA] > 0); Assert.Equal(2, asm.CellIdToViewSlots[cellA].Length); Assert.Equal(2, asm.CellIdToViewSlices[cellA].Length); Assert.NotEqual(asm.CellIdToViewSlots[cellA][0], asm.CellIdToViewSlots[cellA][1]); Assert.Equal(4, asm.PerCellPlaneCounts[cellA]); Assert.Single(asm.OutsideViewSlices); Assert.Equal(4, asm.Frame.SlotCount); // slot 0 + two cell slices + outside slice Assert.Equal(0, asm.ScissorFallbacks); Assert.Equal(TerrainClipMode.Planes, asm.TerrainMode); } [Fact] public void Assemble_OutsideViewWithExitPortal_HasOutsideViewTrue_AabbMatchesBounds() { var pv = new PortalVisibilityFrame(); pv.OutsideView.Add(Square(-0.3f, 0.2f, 0.25f)); var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv); Assert.True(asm.HasOutsideView); Assert.Equal(TerrainClipMode.Planes, asm.TerrainMode); Assert.Single(asm.OutsideViewSlices); var expected = new Vector4( pv.OutsideView.MinX, pv.OutsideView.MinY, pv.OutsideView.MaxX, pv.OutsideView.MaxY); Assert.Equal(expected, asm.OutsideViewNdcAabb); } [Fact] public void Assemble_OutsideViewMultiPolygon_PreservesSlicesAndUnionAabb() { var pv = new PortalVisibilityFrame(); pv.OutsideView.Add(Square(-0.6f, 0f, 0.15f)); pv.OutsideView.Add(Square(0.6f, 0f, 0.15f)); var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv); Assert.True(asm.HasOutsideView); Assert.Equal(TerrainClipMode.Planes, asm.TerrainMode); Assert.Equal(2, asm.OutsideViewSlices.Length); var expected = new Vector4( pv.OutsideView.MinX, pv.OutsideView.MinY, pv.OutsideView.MaxX, pv.OutsideView.MaxY); Assert.Equal(expected, asm.OutsideViewNdcAabb); Assert.Equal(Vector4.Zero, asm.TerrainScissorNdcAabb); } [Fact] public void Assemble_EmptyOutsideView_HasOutsideViewFalse_AabbZero() { const uint cellA = 0xA9B40100; var pv = new PortalVisibilityFrame(); pv.CellViews[cellA] = ViewOf(Square(0f, 0f, 0.3f)); pv.OrderedVisibleCells.Add(cellA); var asm = ClipFrameAssembler.Assemble(ClipFrame.NoClip(), pv); Assert.False(asm.HasOutsideView); Assert.Equal(TerrainClipMode.Skip, asm.TerrainMode); Assert.Equal(Vector4.Zero, asm.OutsideViewNdcAabb); } [Fact] public void Reset_ReusesFrame_NoSlotLeakAcrossAssemblies() { var frame = ClipFrame.NoClip(); var pv1 = new PortalVisibilityFrame(); pv1.CellViews[0xA9B40100] = ViewOf(Square(-0.3f, 0f, 0.2f)); pv1.CellViews[0xA9B40101] = ViewOf(Square(0.3f, 0f, 0.2f)); pv1.OrderedVisibleCells.Add(0xA9B40100); pv1.OrderedVisibleCells.Add(0xA9B40101); pv1.OutsideView.Add(Square(0f, 0.4f, 0.2f)); var asm1 = ClipFrameAssembler.Assemble(frame, pv1); Assert.Equal(4, asm1.Frame.SlotCount); var pv2 = new PortalVisibilityFrame(); pv2.CellViews[0xA9B40200] = ViewOf(Square(0f, 0f, 0.25f)); pv2.OrderedVisibleCells.Add(0xA9B40200); var asm2 = ClipFrameAssembler.Assemble(frame, pv2); Assert.Equal(2, asm2.Frame.SlotCount); Assert.Contains(0xA9B40200, asm2.CellIdToSlot.Keys); Assert.DoesNotContain(0xA9B40100, asm2.CellIdToSlot.Keys); Assert.False(asm2.OutdoorVisible); Assert.Equal(TerrainClipMode.Skip, asm2.TerrainMode); } }