Adds EntitySet { All, IndoorOnly, OutdoorOnly } and a Draw parameter to
partition the per-entity walk by ParentCellId presence. EntitySet.All
preserves pre-A8 behavior; IndoorOnly drops null-ParentCellId entities;
OutdoorOnly drops ParentCellId.HasValue entities. The visibleCellIds
filter is still applied on top.
Used by Task 7 to split the render frame's single Draw call into two
(indoor stencil-OFF, outdoor stencil-gated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
110 lines
3.4 KiB
C#
110 lines
3.4 KiB
C#
// Phase A8 — verify the WbDrawDispatcher EntitySet partition.
|
|
//
|
|
// The pure-data WalkEntitiesForTest helper iterates a flat entity list and
|
|
// returns the IDs that survive the EntitySet filter + visibleCellIds gate.
|
|
// EntitySet.IndoorOnly should include only entities with ParentCellId,
|
|
// EntitySet.OutdoorOnly only entities with null ParentCellId, and
|
|
// EntitySet.All (the default) should match the pre-A8 behavior.
|
|
|
|
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using AcDream.App.Rendering.Wb;
|
|
using AcDream.Core.World;
|
|
using Xunit;
|
|
|
|
namespace AcDream.Core.Tests.Rendering.Wb;
|
|
|
|
public class WbDrawDispatcherEntitySetTests
|
|
{
|
|
private static WorldEntity Indoor(uint id, uint cellId) => new()
|
|
{
|
|
Id = id,
|
|
SourceGfxObjOrSetupId = 0x01000001u,
|
|
ParentCellId = cellId,
|
|
MeshRefs = new List<AcDream.Core.World.MeshRef>
|
|
{
|
|
new() { GfxObjId = 0x01000001u },
|
|
},
|
|
Position = Vector3.Zero,
|
|
Rotation = System.Numerics.Quaternion.Identity,
|
|
};
|
|
|
|
private static WorldEntity Outdoor(uint id) => new()
|
|
{
|
|
Id = id,
|
|
SourceGfxObjOrSetupId = 0x01000001u,
|
|
ParentCellId = null,
|
|
MeshRefs = new List<AcDream.Core.World.MeshRef>
|
|
{
|
|
new() { GfxObjId = 0x01000001u },
|
|
},
|
|
Position = Vector3.Zero,
|
|
Rotation = System.Numerics.Quaternion.Identity,
|
|
};
|
|
|
|
[Fact]
|
|
public void EntitySet_IndoorOnly_DropsOutdoorEntities()
|
|
{
|
|
var entities = new List<WorldEntity>
|
|
{
|
|
Indoor(0x10000001, 0xA9B40143),
|
|
Outdoor(0x10000002),
|
|
Indoor(0x10000003, 0xA9B40144),
|
|
};
|
|
|
|
var visible = new HashSet<uint> { 0xA9B40143u, 0xA9B40144u };
|
|
var result = WbDrawDispatcher.WalkEntitiesForTest(
|
|
entities,
|
|
visibleCellIds: visible,
|
|
set: WbDrawDispatcher.EntitySet.IndoorOnly);
|
|
|
|
Assert.Equal(2, result.Count);
|
|
Assert.Contains(0x10000001u, result);
|
|
Assert.Contains(0x10000003u, result);
|
|
Assert.DoesNotContain(0x10000002u, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void EntitySet_OutdoorOnly_KeepsOnlyNullParentCellId()
|
|
{
|
|
var entities = new List<WorldEntity>
|
|
{
|
|
Indoor(0x10000001, 0xA9B40143),
|
|
Outdoor(0x10000002),
|
|
Outdoor(0x10000003),
|
|
};
|
|
|
|
var result = WbDrawDispatcher.WalkEntitiesForTest(
|
|
entities,
|
|
visibleCellIds: null,
|
|
set: WbDrawDispatcher.EntitySet.OutdoorOnly);
|
|
|
|
Assert.Equal(2, result.Count);
|
|
Assert.Contains(0x10000002u, result);
|
|
Assert.Contains(0x10000003u, result);
|
|
Assert.DoesNotContain(0x10000001u, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void EntitySet_All_MatchesPreA8Behavior()
|
|
{
|
|
var entities = new List<WorldEntity>
|
|
{
|
|
Indoor(0x10000001, 0xA9B40143),
|
|
Outdoor(0x10000002),
|
|
Indoor(0x10000003, 0xA9B40999), // not in visibleCellIds
|
|
};
|
|
|
|
var visible = new HashSet<uint> { 0xA9B40143u };
|
|
var result = WbDrawDispatcher.WalkEntitiesForTest(
|
|
entities,
|
|
visibleCellIds: visible,
|
|
set: WbDrawDispatcher.EntitySet.All);
|
|
|
|
// Pre-A8: visibleCellIds gates indoor entities, outdoor entities pass.
|
|
Assert.Equal(2, result.Count);
|
|
Assert.Contains(0x10000001u, result);
|
|
Assert.Contains(0x10000002u, result);
|
|
Assert.DoesNotContain(0x10000003u, result);
|
|
}
|
|
}
|