feat(physics): Phase 2 — wire CellBSP + Portals into CellPhysics
Adds PortalInfo struct and extends CellPhysics with CellBSP (third BSP for point-in-cell tests, typed CellBSPTree from DatReaderWriter), Portals (from envCell.CellPortals), PortalPolygons (resolved cellStruct.Polygons — portals reference visible polys, not PhysicsPolygons), and VisibleCellIds (populated for future use; envCell.VisibleCells is List<UInt16>, not Dictionary). Deletes CellPhysics.LocalAabbMin/Max and PhysicsDataCache.TryFindContainingCell — Phase D's AABB shortcut is gone. CacheCellStruct's AABB compute removed; the [cell-cache] diagnostic updated with portal/visible counts instead. CacheCellStruct signature gains an EnvCell parameter (one call site in GameWindow.cs:5384 updated). ResolveOutdoorCellId drops the TryFindContainingCell call; portal-graph CellTransit replaces it next. ResolveOutdoorCellIdTests object initializers had the deleted AABB properties stripped temporarily so the build stays green; the file gets replaced wholesale in the next commit (CellTransit integration). Those 2 AABB-containment tests continue to fail (they were pre-broken on this branch); no new failures introduced. Spec: docs/superpowers/specs/2026-05-19-indoor-portal-cell-tracking-design.md Plan: docs/superpowers/plans/2026-05-19-indoor-portal-cell-tracking.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b282c69f28
commit
1969c55823
7 changed files with 215 additions and 113 deletions
|
|
@ -0,0 +1,67 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Physics;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Tests.Physics;
|
||||
|
||||
public class CellPhysicsPortalWiringTests
|
||||
{
|
||||
[Fact]
|
||||
public void NewFields_HaveSensibleDefaults()
|
||||
{
|
||||
var cp = new CellPhysics
|
||||
{
|
||||
WorldTransform = Matrix4x4.Identity,
|
||||
InverseWorldTransform = Matrix4x4.Identity,
|
||||
Resolved = new System.Collections.Generic.Dictionary<ushort, ResolvedPolygon>(),
|
||||
};
|
||||
|
||||
Assert.Null(cp.CellBSP);
|
||||
Assert.Empty(cp.Portals);
|
||||
Assert.Null(cp.PortalPolygons);
|
||||
Assert.Empty(cp.VisibleCellIds);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewFields_AcceptInitValues()
|
||||
{
|
||||
var portal = new PortalInfo(otherCellId: 0x0101, polygonId: 5, flags: 0);
|
||||
|
||||
var cp = new CellPhysics
|
||||
{
|
||||
WorldTransform = Matrix4x4.Identity,
|
||||
InverseWorldTransform = Matrix4x4.Identity,
|
||||
Resolved = new System.Collections.Generic.Dictionary<ushort, ResolvedPolygon>(),
|
||||
Portals = new[] { portal },
|
||||
VisibleCellIds = new System.Collections.Generic.HashSet<uint> { 0xA9B40101 },
|
||||
};
|
||||
|
||||
Assert.Single(cp.Portals);
|
||||
Assert.Equal((ushort)0x0101, cp.Portals[0].OtherCellId);
|
||||
Assert.Contains(0xA9B40101u, cp.VisibleCellIds);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellPhysics_PortalsRoundTrip()
|
||||
{
|
||||
var portals = new[]
|
||||
{
|
||||
new PortalInfo(otherCellId: 0x0101, polygonId: 7, flags: 0),
|
||||
new PortalInfo(otherCellId: 0xFFFF, polygonId: 8, flags: 2),
|
||||
};
|
||||
|
||||
var cp = new CellPhysics
|
||||
{
|
||||
WorldTransform = Matrix4x4.Identity,
|
||||
InverseWorldTransform = Matrix4x4.Identity,
|
||||
Resolved = new System.Collections.Generic.Dictionary<ushort, ResolvedPolygon>(),
|
||||
Portals = portals,
|
||||
};
|
||||
|
||||
Assert.Equal(2, cp.Portals.Count);
|
||||
Assert.Equal((ushort)0x0101, cp.Portals[0].OtherCellId);
|
||||
Assert.True(cp.Portals[0].PortalSide);
|
||||
Assert.Equal((ushort)0xFFFF, cp.Portals[1].OtherCellId);
|
||||
Assert.False(cp.Portals[1].PortalSide);
|
||||
}
|
||||
}
|
||||
35
tests/AcDream.Core.Tests/Physics/PortalInfoTests.cs
Normal file
35
tests/AcDream.Core.Tests/Physics/PortalInfoTests.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using AcDream.Core.Physics;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Tests.Physics;
|
||||
|
||||
public class PortalInfoTests
|
||||
{
|
||||
[Fact]
|
||||
public void PortalSide_FlagsBit2Clear_ReturnsTrue()
|
||||
{
|
||||
var portal = new PortalInfo(otherCellId: 0x0101, polygonId: 5, flags: 0);
|
||||
Assert.True(portal.PortalSide);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PortalSide_FlagsBit2Set_ReturnsFalse()
|
||||
{
|
||||
var portal = new PortalInfo(otherCellId: 0x0101, polygonId: 5, flags: 2);
|
||||
Assert.False(portal.PortalSide);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PortalSide_OtherBitsSet_FollowsOnlyBit2()
|
||||
{
|
||||
var portal = new PortalInfo(otherCellId: 0x0101, polygonId: 5, flags: 0xFF & ~2);
|
||||
Assert.True(portal.PortalSide);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OtherCellId_StoredAsLowSixteenBits()
|
||||
{
|
||||
var portal = new PortalInfo(otherCellId: 0xFFFF, polygonId: 5, flags: 0);
|
||||
Assert.Equal((ushort)0xFFFF, portal.OtherCellId);
|
||||
}
|
||||
}
|
||||
|
|
@ -46,8 +46,6 @@ public class ResolveOutdoorCellIdIndoorContainmentTests
|
|||
Resolved = new Dictionary<ushort, ResolvedPolygon> { [0] = poly },
|
||||
WorldTransform = world,
|
||||
InverseWorldTransform = inv,
|
||||
LocalAabbMin = min,
|
||||
LocalAabbMax = max,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -121,8 +119,6 @@ public class ResolveOutdoorCellIdIndoorContainmentTests
|
|||
Resolved = new Dictionary<ushort, ResolvedPolygon> { [0] = poly },
|
||||
WorldTransform = rotation,
|
||||
InverseWorldTransform = inv,
|
||||
LocalAabbMin = -halfExtent,
|
||||
LocalAabbMax = halfExtent,
|
||||
};
|
||||
|
||||
var engine = new PhysicsEngine();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue