feat(physics): add differential flat BSP traversal
Port every current containment, overlap, walkable, and six-path moving-collision query to immutable integer-indexed assets behind a graph-authoritative referee. Exact synthetic, installed-DAT, complete-resolver, and zero-allocation gates prove bit-identical behavior before connected dual publication. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
9cd42417a8
commit
f7ff9f4eea
13 changed files with 3907 additions and 46 deletions
|
|
@ -159,15 +159,19 @@ public static class CellTransit
|
|||
// neighbour cell whether the sphere intersects its CellBSP.
|
||||
// The portal-plane side test is only the unloaded-cell load hint.
|
||||
var otherCell = cache.GetCellStruct(otherId);
|
||||
if (otherCell?.CellBSP?.Root is not null)
|
||||
if (otherCell is not null &&
|
||||
CollisionTraversal.HasCellContainment(cache, otherCell))
|
||||
{
|
||||
for (int i = 0; i < sphereCount; i++)
|
||||
{
|
||||
var sphere = worldSpheres[i];
|
||||
var otherLocalCenter = Vector3.Transform(
|
||||
sphere.Origin, otherCell.InverseWorldTransform);
|
||||
bool hit = BSPQuery.SphereIntersectsCellBsp(
|
||||
otherCell.CellBSP.Root, otherLocalCenter, sphere.Radius);
|
||||
bool hit = CollisionTraversal.SphereIntersectsCell(
|
||||
cache,
|
||||
otherCell,
|
||||
otherLocalCenter,
|
||||
sphere.Radius);
|
||||
if (hit)
|
||||
{
|
||||
candidates.Add(otherId);
|
||||
|
|
@ -372,7 +376,8 @@ public static class CellTransit
|
|||
continue;
|
||||
|
||||
var otherCell = cache.GetCellStruct(portal.OtherCellId);
|
||||
if (otherCell?.CellBSP?.Root is null)
|
||||
if (otherCell is null ||
|
||||
!CollisionTraversal.HasCellContainment(cache, otherCell))
|
||||
{
|
||||
if (PhysicsDiagnostics.ProbeIndoorBspEnabled)
|
||||
{
|
||||
|
|
@ -398,8 +403,11 @@ public static class CellTransit
|
|||
{
|
||||
var sphere = worldSpheres[i];
|
||||
var localCenter = Vector3.Transform(sphere.Origin, otherCell.InverseWorldTransform);
|
||||
inside = BSPQuery.SphereIntersectsCellBsp(
|
||||
otherCell.CellBSP.Root, localCenter, sphere.Radius);
|
||||
inside = CollisionTraversal.SphereIntersectsCell(
|
||||
cache,
|
||||
otherCell,
|
||||
localCenter,
|
||||
sphere.Radius);
|
||||
|
||||
if (PhysicsDiagnostics.ProbeIndoorBspEnabled)
|
||||
{
|
||||
|
|
@ -599,19 +607,25 @@ public static class CellTransit
|
|||
if (start is null) return 0u;
|
||||
|
||||
// this->point_in_cell(point) → return this (:311402-311405)
|
||||
if (PointInCell(start, worldPoint)) return startCellId;
|
||||
if (PointInCell(cache, start, worldPoint)) return startCellId;
|
||||
|
||||
if (useStabList)
|
||||
{
|
||||
// arg3 != 0 → iterate stab_list, GetVisible + point_in_cell (:311444-311465)
|
||||
foreach (uint id in start.VisibleCellIds)
|
||||
if (PointInCell(cache.GetCellStruct(id), worldPoint)) return id;
|
||||
if (PointInCell(cache, cache.GetCellStruct(id), worldPoint)) return id;
|
||||
}
|
||||
else
|
||||
{
|
||||
// arg3 == 0 → iterate direct portals, GetOtherCell + point_in_cell (:311411-311434)
|
||||
foreach (var portal in start.Portals)
|
||||
if (PointInCell(cache.GetCellStruct(portal.OtherCellId), worldPoint)) return portal.OtherCellId;
|
||||
if (PointInCell(
|
||||
cache,
|
||||
cache.GetCellStruct(portal.OtherCellId),
|
||||
worldPoint))
|
||||
{
|
||||
return portal.OtherCellId;
|
||||
}
|
||||
}
|
||||
|
||||
return 0u;
|
||||
|
|
@ -623,11 +637,19 @@ public static class CellTransit
|
|||
/// A cell with no hydrated <see cref="CellPhysics.CellBSP"/> returns false (see
|
||||
/// <see cref="FindVisibleChildCell"/>'s adaptation note).
|
||||
/// </summary>
|
||||
private static bool PointInCell(CellPhysics? cell, Vector3 worldPoint)
|
||||
private static bool PointInCell(
|
||||
PhysicsDataCache cache,
|
||||
CellPhysics? cell,
|
||||
Vector3 worldPoint)
|
||||
{
|
||||
if (cell?.CellBSP?.Root is null) return false;
|
||||
if (cell is null ||
|
||||
!CollisionTraversal.HasCellContainment(cache, cell))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var local = Vector3.Transform(worldPoint, cell.InverseWorldTransform);
|
||||
return BSPQuery.PointInsideCellBsp(cell.CellBSP.Root, local);
|
||||
return CollisionTraversal.PointInsideCell(cache, cell, local);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -901,9 +923,14 @@ public static class CellTransit
|
|||
{
|
||||
// Interior candidate — point_in_cell via the cell BSP (vtable[0x84]).
|
||||
var cand = cache.GetCellStruct(candId);
|
||||
if (cand?.CellBSP?.Root is null) continue;
|
||||
if (cand is null ||
|
||||
!CollisionTraversal.HasCellContainment(cache, cand))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var local = Vector3.Transform(worldSphereCenter, cand.InverseWorldTransform);
|
||||
if (BSPQuery.PointInsideCellBsp(cand.CellBSP.Root, local))
|
||||
if (CollisionTraversal.PointInsideCell(cache, cand, local))
|
||||
return candId; // interior-wins, stop (pseudo_c:308819)
|
||||
}
|
||||
else if (outdoorResult == 0u && containingOutdoorId != 0u && outdoorPickAllowed)
|
||||
|
|
@ -950,10 +977,15 @@ public static class CellTransit
|
|||
if (currentLow >= 0x0100u)
|
||||
{
|
||||
var cur = cache.GetCellStruct(currentCellId);
|
||||
if (cur?.CellBSP?.Root is not null)
|
||||
if (cur is not null &&
|
||||
CollisionTraversal.HasCellContainment(cache, cur))
|
||||
{
|
||||
var curLocal = Vector3.Transform(worldSphereCenter, cur.InverseWorldTransform);
|
||||
if (!BSPQuery.SphereIntersectsCellBsp(cur.CellBSP.Root, curLocal, sphereRadius))
|
||||
if (!CollisionTraversal.SphereIntersectsCell(
|
||||
cache,
|
||||
cur,
|
||||
curLocal,
|
||||
sphereRadius))
|
||||
{
|
||||
uint recovered = FindVisibleChildCell(
|
||||
cache, currentCellId, worldSphereCenter, useStabList: true);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue