using System.Numerics; using AcDream.App.Rendering; using AcDream.App.Rendering.Selection; using AcDream.Core.Selection; using AcDream.Core.World; namespace AcDream.App.Tests.Rendering; public sealed class RetailSelectionSceneTests { private sealed class GeometrySource(RetailSelectionMesh mesh) : IRetailSelectionGeometrySource { public RetailSelectionMesh? Resolve(uint gfxObjId) => gfxObjId == 0x0100_0001u ? mesh : null; } [Fact] public void PublishedHitCarriesLogicalEntityIdentity() { var scene = CreateScene(); var entity = Entity(localId: 44u, serverGuid: 0x5000_0044u); Publish(scene, entity); RetailSelectionHit? hit = PickCenter(scene); Assert.NotNull(hit); Assert.Equal(entity.ServerGuid, hit.Value.ServerGuid); Assert.Equal(entity.Id, hit.Value.LocalEntityId); } [Fact] public void ResetClearsPublishedFrameAndLightingPulse() { double now = 1d; var pulse = new RetailSelectionLightingPulse(() => now); var scene = CreateScene(pulse); var entity = Entity(localId: 45u, serverGuid: 0x5000_0045u); Publish(scene, entity); scene.BeginLightingPulse(entity.ServerGuid, entity.Id); scene.Reset(); Assert.Null(PickCenter(scene)); Assert.False(scene.TryGetLighting(entity.ServerGuid, entity.Id, out _)); } [Fact] public void AbortFrame_DiscardsBuildingSelectionAndPreservesPublishedFrame() { var scene = CreateScene(); var published = Entity(localId: 46u, serverGuid: 0x5000_0046u); Publish(scene, published); var rejected = Entity(localId: 47u, serverGuid: 0x5000_0047u); scene.BeginFrame(); (Matrix4x4 view, Matrix4x4 projection) = Camera(); scene.SetViewFrustum(FrustumPlanes.FromViewProjection(view * projection)); scene.AddVisiblePart( rejected, partIndex: 0, gfxObjId: 0x0100_0001u, Matrix4x4.CreateTranslation(0f, 0f, -5f)); scene.AbortFrame(); RetailSelectionHit? hit = PickCenter(scene); Assert.NotNull(hit); Assert.Equal(published.Id, hit.Value.LocalEntityId); } private static RetailSelectionScene CreateScene( RetailSelectionLightingPulse? pulse = null) { var mesh = new RetailSelectionMesh( Vector3.Zero, 2f, [new RetailSelectionPolygon( [ new(-1f, -1f, 0f), new( 1f, -1f, 0f), new( 1f, 1f, 0f), new(-1f, 1f, 0f), ], SingleSided: false)]); return new RetailSelectionScene(new GeometrySource(mesh), pulse); } private static WorldEntity Entity(uint localId, uint serverGuid) => new() { Id = localId, ServerGuid = serverGuid, SourceGfxObjOrSetupId = 0x0200_0001u, Position = Vector3.Zero, Rotation = Quaternion.Identity, MeshRefs = [], }; private static void Publish(RetailSelectionScene scene, WorldEntity entity) { scene.BeginFrame(); (Matrix4x4 view, Matrix4x4 projection) = Camera(); scene.SetViewFrustum(FrustumPlanes.FromViewProjection(view * projection)); scene.AddVisiblePart( entity, partIndex: 0, gfxObjId: 0x0100_0001u, Matrix4x4.CreateTranslation(0f, 0f, -5f)); scene.CompleteFrame(); } private static RetailSelectionHit? PickCenter(RetailSelectionScene scene) { (Matrix4x4 view, Matrix4x4 projection) = Camera(); return scene.Pick( 400f, 300f, new Vector2(800f, 600f), view, projection, skipServerGuid: 0u); } private static (Matrix4x4 View, Matrix4x4 Projection) Camera() { Matrix4x4 view = Matrix4x4.CreateLookAt( Vector3.Zero, -Vector3.UnitZ, Vector3.UnitY); Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView( MathF.PI / 3f, 4f / 3f, 0.1f, 100f); return (view, projection); } }