fix(portal): synchronize destination presentation state

This commit is contained in:
Erik 2026-07-16 21:17:13 +02:00
parent 4b1bceefbb
commit e95f55f25b
42 changed files with 2815 additions and 288 deletions

View file

@ -1,6 +1,7 @@
using System;
using System.Numerics;
using AcDream.Core.Physics;
using AcDream.Core.World.Cells;
using Xunit;
namespace AcDream.Core.Tests.Physics;
@ -31,4 +32,123 @@ public class PhysicsEngineResidencyTests
Assert.True(eng.IsLandblockTerrainResident(0xA9B40019u)); // cell-resolved id, same LB
Assert.False(eng.IsLandblockTerrainResident(0xC6A90019u)); // different LB
}
[Fact]
public void DemoteLandblockToTerrain_RemovesEnvCellsButPreservesTerrainResidency()
{
const uint landblockId = 0xA9B4FFFFu;
var cache = new PhysicsDataCache();
var engine = new PhysicsEngine { DataCache = cache };
engine.AddLandblock(
landblockId,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty<CellSurface>(),
Array.Empty<PortalPlane>(),
0f,
0f);
cache.CellGraph.Add(new EnvCell(
0xA9B40174u,
Matrix4x4.Identity,
Matrix4x4.Identity,
Vector3.Zero,
Vector3.One,
Array.Empty<CellPortal>(),
Array.Empty<uint>(),
false,
null));
engine.DemoteLandblockToTerrain(landblockId);
Assert.True(engine.IsLandblockTerrainResident(landblockId));
Assert.Null(cache.CellGraph.GetVisible(0xA9B40174u));
Assert.IsType<LandCell>(cache.CellGraph.GetVisible(0xA9B40001u));
}
[Fact]
public void DemoteLandblockToTerrain_DeregistersCrossBoundaryStaticButRetainsDynamic()
{
const uint landblockId = 0xA9B4FFFFu;
const uint adjacentCell = 0xAAB40001u;
const uint staticId = 100u;
const uint dynamicId = 200u;
var engine = new PhysicsEngine();
engine.AddLandblock(
landblockId,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty<CellSurface>(),
Array.Empty<PortalPlane>(),
0f,
0f);
var seamPosition = new Vector3(191.5f, 12f, 1f);
engine.ShadowObjects.Register(
staticId,
0x01000001u,
seamPosition,
Quaternion.Identity,
2f,
0f,
0f,
landblockId,
isStatic: true);
engine.ShadowObjects.Register(
dynamicId,
0x01000002u,
seamPosition,
Quaternion.Identity,
2f,
0f,
0f,
landblockId,
isStatic: false);
Assert.Contains(
engine.ShadowObjects.GetObjectsInCell(adjacentCell),
entry => entry.EntityId == staticId);
Assert.Contains(
engine.ShadowObjects.GetObjectsInCell(adjacentCell),
entry => entry.EntityId == dynamicId);
engine.DemoteLandblockToTerrain(landblockId);
Assert.DoesNotContain(
engine.ShadowObjects.AllEntriesForDebug(),
entry => entry.EntityId == staticId);
Assert.Contains(
engine.ShadowObjects.GetObjectsInCell(adjacentCell),
entry => entry.EntityId == dynamicId);
Assert.Equal(1, engine.ShadowObjects.RetainedRegistrationCount);
}
[Fact]
public void RemoveLandblock_DeregistersCrossBoundaryStaticButRetainsDynamic()
{
const uint landblockId = 0xA9B4FFFFu;
const uint adjacentCell = 0xAAB40001u;
const uint staticId = 300u;
const uint dynamicId = 400u;
var engine = new PhysicsEngine();
engine.AddLandblock(
landblockId,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty<CellSurface>(),
Array.Empty<PortalPlane>(),
0f,
0f);
var seamPosition = new Vector3(191.5f, 12f, 1f);
engine.ShadowObjects.Register(
staticId, 0x01000003u, seamPosition, Quaternion.Identity, 2f,
0f, 0f, landblockId, isStatic: true);
engine.ShadowObjects.Register(
dynamicId, 0x01000004u, seamPosition, Quaternion.Identity, 2f,
0f, 0f, landblockId, isStatic: false);
engine.RemoveLandblock(landblockId);
Assert.DoesNotContain(
engine.ShadowObjects.AllEntriesForDebug(),
entry => entry.EntityId == staticId);
Assert.Contains(
engine.ShadowObjects.GetObjectsInCell(adjacentCell),
entry => entry.EntityId == dynamicId);
Assert.Equal(1, engine.ShadowObjects.RetainedRegistrationCount);
}
}