using System.Numerics; using AcDream.App.Rendering; using AcDream.App.Rendering.Scene; using AcDream.Core.World; namespace AcDream.App.Tests.Rendering; public sealed class CurrentRenderSceneOracleTests { private const uint LandblockA = 0xA9B4FFFF; private const uint LandblockB = 0xA9B5FFFF; private const uint CellA = 0xA9B40170; private const uint CellB = 0xA9B50170; [Fact] public void DigestIsStableAcrossLandblockEnumerationOrder() { WorldEntity outdoorA = Entity(id: 7, serverGuid: 0, parentCell: null); WorldEntity cellStatic = Entity(id: 8, serverGuid: 0, parentCell: CellA); WorldEntity live = Entity( id: 1_000_001, serverGuid: 0x8000_0001, parentCell: CellA); // Static IDs are landblock-local. The same id in another landblock is // a distinct projection and must survive the referee sort. WorldEntity outdoorB = Entity(id: 7, serverGuid: 0, parentCell: null); var firstEntries = new[] { Entry(LandblockA, outdoorA, cellStatic, live), Entry(LandblockB, outdoorB), }; var secondEntries = new[] { Entry(LandblockB, outdoorB), Entry(LandblockA, outdoorA, cellStatic, live), }; var visible = new HashSet { CellA }; var result = new InteriorEntityPartition.Result(); var oracle = new CurrentRenderSceneOracle(); InteriorEntityPartition.Partition( result, visible, firstEntries, oracle); CurrentRenderSceneOracleSnapshot first = oracle.Snapshot; InteriorEntityPartition.Partition( result, visible, secondEntries, oracle); CurrentRenderSceneOracleSnapshot second = oracle.Snapshot; Assert.True(first.Enabled); Assert.Equal(4, first.ProjectionCount); Assert.Equal(2, first.OutdoorStaticCount); Assert.Equal(1, first.CellStaticCount); Assert.Equal(1, first.DynamicCount); Assert.Equal(1, first.CellBucketCount); Assert.Equal(first.Digest, second.Digest); Assert.Equal(first.CompletedFrameSequence + 1, second.CompletedFrameSequence); Assert.Equal(2, oracle.Projections.Count( projection => projection.EntityId == 7)); Assert.Contains( oracle.Projections, projection => projection.EntityId == 7 && projection.LandblockId == LandblockA); Assert.Contains( oracle.Projections, projection => projection.EntityId == 7 && projection.LandblockId == LandblockB); } [Fact] public void ObservedPartitionMatchesUninstrumentedProductionPath() { WorldEntity outdoor = Entity(id: 10, serverGuid: 0, parentCell: null); WorldEntity cellStatic = Entity(id: 11, serverGuid: 0, parentCell: CellA); WorldEntity dynamic = Entity( id: 1_000_011, serverGuid: 0x8000_0011, parentCell: CellB); WorldEntity hiddenStatic = Entity( id: 12, serverGuid: 0, parentCell: CellB); var entries = new[] { Entry(LandblockA, outdoor, cellStatic), Entry(LandblockB, dynamic, hiddenStatic), }; var visible = new HashSet { CellA }; var production = new InteriorEntityPartition.Result(); var observed = new InteriorEntityPartition.Result(); var oracle = new CurrentRenderSceneOracle(); InteriorEntityPartition.Partition(production, visible, entries); InteriorEntityPartition.Partition(observed, visible, entries, oracle); Assert.Equal(production.ByCell.Keys, observed.ByCell.Keys); foreach (uint cell in production.ByCell.Keys) Assert.Equal(production.ByCell[cell], observed.ByCell[cell]); Assert.Equal(production.OutdoorStatic, observed.OutdoorStatic); Assert.Equal(production.Dynamics, observed.Dynamics); } [Fact] public void FingerprintsNameTransformGeometryAppearanceAndFlagChanges() { var visible = new HashSet { CellA, CellB }; CurrentRenderProjectionFingerprint baseline = CaptureSingle( Entity(id: 20, serverGuid: 0, parentCell: CellA), LandblockA, visible); CurrentRenderProjectionFingerprint transform = CaptureSingle( Entity( id: 20, serverGuid: 0, parentCell: CellA, position: new Vector3(1, 2, 3)), LandblockA, visible); CurrentRenderProjectionFingerprint geometry = CaptureSingle( Entity( id: 20, serverGuid: 0, parentCell: CellA, meshId: 0x0100_2222), LandblockA, visible); CurrentRenderProjectionFingerprint appearance = CaptureSingle( Entity( id: 20, serverGuid: 0, parentCell: CellA, palette: new PaletteOverride( 0x0400_0001, [new PaletteOverride.SubPaletteRange(0x0400_0002, 3, 4)])), LandblockA, visible); WorldEntity hidden = Entity(id: 20, serverGuid: 0, parentCell: CellA); hidden.IsDrawVisible = false; CurrentRenderProjectionFingerprint flags = CaptureSingle( hidden, LandblockA, visible); CurrentRenderProjectionFingerprint cell = CaptureSingle( Entity(id: 20, serverGuid: 0, parentCell: CellB), LandblockB, visible); Assert.NotEqual(baseline.Transform, transform.Transform); Assert.Equal(baseline.Geometry, transform.Geometry); Assert.Equal(baseline.Appearance, transform.Appearance); Assert.NotEqual(baseline.Geometry, geometry.Geometry); Assert.Equal(baseline.Transform, geometry.Transform); Assert.NotEqual(baseline.Appearance, appearance.Appearance); Assert.NotEqual(baseline.Flags, flags.Flags); Assert.NotEqual(baseline.ParentCellId, cell.ParentCellId); Assert.NotEqual(baseline.LandblockId, cell.LandblockId); } [Fact] public void NonFloodedStaticIsAbsentButNonFloodedDynamicRemains() { WorldEntity hiddenStatic = Entity( id: 30, serverGuid: 0, parentCell: CellB); WorldEntity hiddenDynamic = Entity( id: 1_000_030, serverGuid: 0x8000_0030, parentCell: CellB); var oracle = new CurrentRenderSceneOracle(); var result = new InteriorEntityPartition.Result(); InteriorEntityPartition.Partition( result, new HashSet { CellA }, new[] { Entry(LandblockB, hiddenStatic, hiddenDynamic) }, oracle); Assert.Equal(1, oracle.Snapshot.ProjectionCount); Assert.Equal(0, oracle.Snapshot.CellStaticCount); Assert.Equal(1, oracle.Snapshot.DynamicCount); CurrentRenderProjectionFingerprint fingerprint = Assert.Single(oracle.Projections); Assert.Equal(hiddenDynamic.Id, fingerprint.EntityId); Assert.Equal( InteriorEntityPartition.ProjectionClass.Dynamic, fingerprint.ProjectionClass); } [Fact] public void AbortPreservesLastCompleteDigestAndRecordsFailure() { var oracle = new CurrentRenderSceneOracle(); var result = new InteriorEntityPartition.Result(); WorldEntity entity = Entity(id: 40, serverGuid: 0, parentCell: null); InteriorEntityPartition.Partition( result, [], new[] { Entry(LandblockA, entity) }, oracle); CurrentRenderSceneOracleSnapshot complete = oracle.Snapshot; oracle.BeginFrame(); oracle.Observe( LandblockA, entity, InteriorEntityPartition.ProjectionClass.OutdoorStatic); oracle.AbortFrame(); Assert.Equal(complete.Digest, oracle.Snapshot.Digest); Assert.Equal( complete.CompletedFrameSequence, oracle.Snapshot.CompletedFrameSequence); Assert.Equal(complete.AbortedFrames + 1, oracle.Snapshot.AbortedFrames); Assert.Empty(oracle.Projections); } private static CurrentRenderProjectionFingerprint CaptureSingle( WorldEntity entity, uint landblockId, HashSet visible) { var oracle = new CurrentRenderSceneOracle(); var result = new InteriorEntityPartition.Result(); InteriorEntityPartition.Partition( result, visible, new[] { Entry(landblockId, entity) }, oracle); return Assert.Single(oracle.Projections); } private static WorldEntity Entity( uint id, uint serverGuid, uint? parentCell, Vector3? position = null, uint meshId = 0x0100_0001, PaletteOverride? palette = null) => new() { Id = id, ServerGuid = serverGuid, SourceGfxObjOrSetupId = meshId, Position = position ?? Vector3.Zero, Rotation = Quaternion.Identity, MeshRefs = [ new MeshRef( meshId, Matrix4x4.CreateTranslation(0.25f, 0.5f, 0.75f)), ], ParentCellId = parentCell, PaletteOverride = palette, }; private static ( uint LandblockId, Vector3 AabbMin, Vector3 AabbMax, IReadOnlyList Entities, IReadOnlyDictionary? AnimatedById) Entry(uint landblockId, params WorldEntity[] entities) => ( landblockId, Vector3.Zero, Vector3.One, entities, null); }