Revert "fix(render): Phase A8 — animated entities exempt from stencil-gated outdoor pass"
This reverts commit a2ad5c1ac4.
This commit is contained in:
parent
c897a179fa
commit
96f8bd2bd7
2 changed files with 10 additions and 87 deletions
|
|
@ -357,12 +357,9 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
foreach (var animatedId in animatedEntityIds)
|
foreach (var animatedId in animatedEntityIds)
|
||||||
{
|
{
|
||||||
if (!entry.AnimatedById.TryGetValue(animatedId, out var entity)) continue;
|
if (!entry.AnimatedById.TryGetValue(animatedId, out var entity)) continue;
|
||||||
// Phase A8 fix: every entity in this loop IS animated (we're iterating
|
// Phase A8: EntitySet partition for indoor/outdoor split passes.
|
||||||
// animatedEntityIds). Animated entities (player, NPCs, monsters) are live
|
if (set == EntitySet.IndoorOnly && !entity.ParentCellId.HasValue) continue;
|
||||||
// server-spawned objects that have ParentCellId == null but must ALWAYS
|
if (set == EntitySet.OutdoorOnly && entity.ParentCellId.HasValue) continue;
|
||||||
// render in the indoor pass — never stencil-gated by OutdoorOnly.
|
|
||||||
// Otherwise the character disappears when the camera enters a building.
|
|
||||||
if (set == EntitySet.OutdoorOnly) continue;
|
|
||||||
if (entity.MeshRefs.Count == 0) continue;
|
if (entity.MeshRefs.Count == 0) continue;
|
||||||
if (entity.ParentCellId.HasValue && visibleCellIds is not null
|
if (entity.ParentCellId.HasValue && visibleCellIds is not null
|
||||||
&& !visibleCellIds.Contains(entity.ParentCellId.Value)) continue;
|
&& !visibleCellIds.Contains(entity.ParentCellId.Value)) continue;
|
||||||
|
|
@ -375,13 +372,9 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
|
|
||||||
foreach (var entity in entry.Entities)
|
foreach (var entity in entry.Entities)
|
||||||
{
|
{
|
||||||
// Phase A8 fix: animated entities (player, NPCs, monsters) are live
|
// Phase A8: EntitySet partition for indoor/outdoor split passes.
|
||||||
// server-spawned objects with ParentCellId == null. They must ALWAYS render
|
if (set == EntitySet.IndoorOnly && !entity.ParentCellId.HasValue) continue;
|
||||||
// in the indoor pass — never stencil-gated by OutdoorOnly — or the character
|
if (set == EntitySet.OutdoorOnly && entity.ParentCellId.HasValue) continue;
|
||||||
// disappears when the camera enters a building.
|
|
||||||
bool isAnimated = animatedEntityIds is not null && animatedEntityIds.Contains(entity.Id);
|
|
||||||
if (set == EntitySet.IndoorOnly && !entity.ParentCellId.HasValue && !isAnimated) continue;
|
|
||||||
if (set == EntitySet.OutdoorOnly && (entity.ParentCellId.HasValue || isAnimated)) continue;
|
|
||||||
if (entity.MeshRefs.Count == 0) continue;
|
if (entity.MeshRefs.Count == 0) continue;
|
||||||
|
|
||||||
// Detect cell entity for indoor probes — first MeshRef.GfxObjId
|
// Detect cell entity for indoor probes — first MeshRef.GfxObjId
|
||||||
|
|
@ -410,7 +403,7 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
// Per-entity AABB frustum cull (perf #3). Animated entities bypass —
|
// Per-entity AABB frustum cull (perf #3). Animated entities bypass —
|
||||||
// they're tracked at landblock level + need per-frame work regardless.
|
// they're tracked at landblock level + need per-frame work regardless.
|
||||||
// A.5 T18 Change #2: read cached AABB, refresh lazily on AabbDirty.
|
// A.5 T18 Change #2: read cached AABB, refresh lazily on AabbDirty.
|
||||||
// Note: isAnimated already computed above for the EntitySet partition.
|
bool isAnimated = animatedEntityIds?.Contains(entity.Id) == true;
|
||||||
bool aabbVisible = true;
|
bool aabbVisible = true;
|
||||||
if (frustum is not null && !isAnimated && entry.LandblockId != neverCullLandblockId)
|
if (frustum is not null && !isAnimated && entry.LandblockId != neverCullLandblockId)
|
||||||
{
|
{
|
||||||
|
|
@ -1357,18 +1350,13 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
public static List<uint> WalkEntitiesForTest(
|
public static List<uint> WalkEntitiesForTest(
|
||||||
IReadOnlyList<AcDream.Core.World.WorldEntity> entities,
|
IReadOnlyList<AcDream.Core.World.WorldEntity> entities,
|
||||||
HashSet<uint>? visibleCellIds,
|
HashSet<uint>? visibleCellIds,
|
||||||
EntitySet set,
|
EntitySet set)
|
||||||
HashSet<uint>? animatedEntityIds = null)
|
|
||||||
{
|
{
|
||||||
var output = new List<uint>();
|
var output = new List<uint>();
|
||||||
foreach (var entity in entities)
|
foreach (var entity in entities)
|
||||||
{
|
{
|
||||||
// Phase A8 fix: animated entities (player, NPCs, monsters) are live
|
if (set == EntitySet.IndoorOnly && !entity.ParentCellId.HasValue) continue;
|
||||||
// server-spawned objects with ParentCellId == null. They must ALWAYS render
|
if (set == EntitySet.OutdoorOnly && entity.ParentCellId.HasValue) continue;
|
||||||
// in the indoor pass — never stencil-gated by OutdoorOnly.
|
|
||||||
bool isAnimated = animatedEntityIds is not null && animatedEntityIds.Contains(entity.Id);
|
|
||||||
if (set == EntitySet.IndoorOnly && !entity.ParentCellId.HasValue && !isAnimated) continue;
|
|
||||||
if (set == EntitySet.OutdoorOnly && (entity.ParentCellId.HasValue || isAnimated)) continue;
|
|
||||||
if (entity.MeshRefs.Count == 0) continue;
|
if (entity.MeshRefs.Count == 0) continue;
|
||||||
|
|
||||||
bool cellInVis = !(entity.ParentCellId.HasValue
|
bool cellInVis = !(entity.ParentCellId.HasValue
|
||||||
|
|
|
||||||
|
|
@ -107,69 +107,4 @@ public class WbDrawDispatcherEntitySetTests
|
||||||
Assert.Contains(0x10000002u, result);
|
Assert.Contains(0x10000002u, result);
|
||||||
Assert.DoesNotContain(0x10000003u, result);
|
Assert.DoesNotContain(0x10000003u, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Phase A8 fix (post-visual-verification): animated entities (player,
|
|
||||||
// NPCs, monsters) are live server-spawned objects with ParentCellId == null.
|
|
||||||
// Without these tests, they would be classified as outdoor scenery and
|
|
||||||
// stencil-gated by the OutdoorOnly pass — causing the character to
|
|
||||||
// disappear when the camera enters a building. Fix: animatedEntityIds
|
|
||||||
// overrides the ParentCellId-based partition. Animated entities always
|
|
||||||
// belong in the IndoorOnly pass, never in OutdoorOnly.
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void EntitySet_IndoorOnly_IncludesAnimatedEntitiesEvenWithNullParentCellId()
|
|
||||||
{
|
|
||||||
var entities = new List<WorldEntity>
|
|
||||||
{
|
|
||||||
Indoor(0x10000001, 0xA9B40143),
|
|
||||||
Outdoor(0x10000002), // static outdoor scenery
|
|
||||||
Outdoor(0x40000005), // animated (player/NPC)
|
|
||||||
};
|
|
||||||
|
|
||||||
var visible = new HashSet<uint> { 0xA9B40143u };
|
|
||||||
var animated = new HashSet<uint> { 0x40000005u };
|
|
||||||
|
|
||||||
var result = WbDrawDispatcher.WalkEntitiesForTest(
|
|
||||||
entities,
|
|
||||||
visibleCellIds: visible,
|
|
||||||
set: WbDrawDispatcher.EntitySet.IndoorOnly,
|
|
||||||
animatedEntityIds: animated);
|
|
||||||
|
|
||||||
// Indoor entity passes via ParentCellId.HasValue.
|
|
||||||
// Outdoor scenery (0x10000002) fails — not animated, no ParentCellId.
|
|
||||||
// Animated entity (0x40000005) passes via animatedEntityIds override.
|
|
||||||
Assert.Equal(2, result.Count);
|
|
||||||
Assert.Contains(0x10000001u, result);
|
|
||||||
Assert.Contains(0x40000005u, result);
|
|
||||||
Assert.DoesNotContain(0x10000002u, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void EntitySet_OutdoorOnly_ExcludesAnimatedEntities()
|
|
||||||
{
|
|
||||||
var entities = new List<WorldEntity>
|
|
||||||
{
|
|
||||||
Outdoor(0x10000002), // static outdoor scenery
|
|
||||||
Outdoor(0x40000005), // animated (player/NPC)
|
|
||||||
Outdoor(0x40000006), // animated (NPC)
|
|
||||||
};
|
|
||||||
|
|
||||||
var animated = new HashSet<uint> { 0x40000005u, 0x40000006u };
|
|
||||||
|
|
||||||
var result = WbDrawDispatcher.WalkEntitiesForTest(
|
|
||||||
entities,
|
|
||||||
visibleCellIds: null,
|
|
||||||
set: WbDrawDispatcher.EntitySet.OutdoorOnly,
|
|
||||||
animatedEntityIds: animated);
|
|
||||||
|
|
||||||
// Only static outdoor scenery passes the OutdoorOnly partition.
|
|
||||||
// Animated entities are explicitly excluded so they don't get
|
|
||||||
// stencil-gated (they're drawn in the IndoorOnly pass instead).
|
|
||||||
Assert.Single(result);
|
|
||||||
Assert.Contains(0x10000002u, result);
|
|
||||||
Assert.DoesNotContain(0x40000005u, result);
|
|
||||||
Assert.DoesNotContain(0x40000006u, result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue