using System.Numerics; using AcDream.App.Rendering; using AcDream.Core.World; using Xunit; namespace AcDream.App.Tests.Rendering; /// /// Campaign CC gate round 1, Batch D (GF-7/GF-14): pins /// , the pure /// helper that decides which entities /// submits to WbDrawDispatcher. Exercised directly (no GPU device, no /// constructed WbDrawDispatcher) — the same interface-fake-first /// testing shape CreatureAppraisalPresentationTests already uses for /// this renderer family, since PrivateEntityViewportRenderer itself /// pulls in a live mesh-pipeline object graph too heavy to construct in a /// unit test. /// /// /// This is the paperdoll/creature-appraisal REGRESSION PIN the batch's test /// plan calls for: both renderers never configure a backdrop slot (see /// PaperdollViewportRenderer/CreatureAppraisalPresentation.cs — /// neither passes a backdropRenderId nor exposes SetBackdrop), /// so every one of their draws calls this helper with backdrop: null — /// exactly the first case below. /// /// public sealed class PrivateEntityViewportRendererDrawOrderTests { private static WorldEntity Entity(uint id, IReadOnlyList meshRefs) => new() { Id = id, ServerGuid = id, SourceGfxObjOrSetupId = 0x0200_0001u, Position = Vector3.Zero, Rotation = Quaternion.Identity, MeshRefs = meshRefs, }; private static readonly MeshRef[] OneMesh = [new MeshRef(0x0100_0001u, Matrix4x4.Identity)]; [Fact] public void NoBackdrop_ReturnsExactlyTheMainEntity() { WorldEntity main = Entity(1u, OneMesh); IReadOnlyList entities = PrivateEntityViewportRenderer.BuildDrawEntities(backdrop: null, main); Assert.Same(main, Assert.Single(entities)); } [Fact] public void BackdropPresent_ReturnsBackdropFirstThenMain() { // Decomp-cited: gmCG3DView::Update adds the backdrop object to // creature_mode_objects BEFORE the player object is re-added // (~0x004eed44 vs ~0x004ef199) — this ordering is retail-faithful, // not an arbitrary choice. WorldEntity backdrop = Entity(ChargenPreviewEntityBuilder.PreviewBackdropRenderId, OneMesh); WorldEntity main = Entity(ChargenPreviewEntityBuilder.PreviewRenderId, OneMesh); IReadOnlyList entities = PrivateEntityViewportRenderer.BuildDrawEntities(backdrop, main); Assert.Equal(2, entities.Count); Assert.Same(backdrop, entities[0]); Assert.Same(main, entities[1]); } [Fact] public void BackdropWithNoDrawableMeshes_DegradesToExactlyTheMainEntity() { WorldEntity emptyBackdrop = Entity( ChargenPreviewEntityBuilder.PreviewBackdropRenderId, []); WorldEntity main = Entity(ChargenPreviewEntityBuilder.PreviewRenderId, OneMesh); IReadOnlyList entities = PrivateEntityViewportRenderer.BuildDrawEntities(emptyBackdrop, main); Assert.Same(main, Assert.Single(entities)); } }