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
226
src/AcDream.Core/Physics/CollisionTraversal.cs
Normal file
226
src/AcDream.Core/Physics/CollisionTraversal.cs
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace AcDream.Core.Physics;
|
||||
|
||||
/// <summary>
|
||||
/// Temporary Slice I referee mode. Graph remains production-authoritative;
|
||||
/// Flat is enabled only by differential tests until dual publication and the
|
||||
/// connected shadow gate complete.
|
||||
/// </summary>
|
||||
internal enum CollisionTraversalMode
|
||||
{
|
||||
Graph,
|
||||
Flat,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Narrow representation switch used while the graph and flat collision
|
||||
/// worlds coexist. I6 removes the graph branch after acceptance.
|
||||
/// </summary>
|
||||
internal static class CollisionTraversal
|
||||
{
|
||||
internal static bool HasCellContainment(
|
||||
PhysicsDataCache cache,
|
||||
CellPhysics cell)
|
||||
{
|
||||
if (cache.CollisionTraversalMode == CollisionTraversalMode.Flat)
|
||||
{
|
||||
FlatCellContainmentBsp flat = cell.FlatContainmentBsp ??
|
||||
throw MissingFlat("cell containment");
|
||||
return flat.RootIndex >= 0;
|
||||
}
|
||||
|
||||
return cell.CellBSP?.Root is not null;
|
||||
}
|
||||
|
||||
internal static bool HasPhysics(
|
||||
PhysicsDataCache cache,
|
||||
CellPhysics cell)
|
||||
{
|
||||
if (cache.CollisionTraversalMode == CollisionTraversalMode.Flat)
|
||||
{
|
||||
FlatPhysicsBsp flat = cell.FlatPhysicsBsp ??
|
||||
throw MissingFlat("cell physics");
|
||||
return flat.RootIndex >= 0;
|
||||
}
|
||||
|
||||
return cell.BSP?.Root is not null;
|
||||
}
|
||||
|
||||
internal static bool HasPhysics(
|
||||
PhysicsDataCache cache,
|
||||
GfxObjPhysics gfxObject)
|
||||
{
|
||||
if (cache.CollisionTraversalMode == CollisionTraversalMode.Flat)
|
||||
{
|
||||
FlatPhysicsBsp flat = gfxObject.FlatPhysicsBsp ??
|
||||
throw MissingFlat("GfxObj physics");
|
||||
return flat.RootIndex >= 0;
|
||||
}
|
||||
|
||||
return gfxObject.BSP.Root is not null;
|
||||
}
|
||||
|
||||
internal static FlatCollisionSphere RootBoundingSphere(
|
||||
PhysicsDataCache cache,
|
||||
CellPhysics cell)
|
||||
{
|
||||
if (cache.CollisionTraversalMode == CollisionTraversalMode.Flat)
|
||||
{
|
||||
FlatPhysicsBsp flat = cell.FlatPhysicsBsp ??
|
||||
throw MissingFlat("cell physics");
|
||||
return flat.Nodes[flat.RootIndex].BoundingSphere;
|
||||
}
|
||||
|
||||
DatReaderWriter.Types.Sphere sphere = cell.BSP!.Root!.BoundingSphere;
|
||||
return new FlatCollisionSphere(sphere.Origin, sphere.Radius);
|
||||
}
|
||||
|
||||
internal static bool PointInsideCell(
|
||||
PhysicsDataCache cache,
|
||||
CellPhysics cell,
|
||||
Vector3 localPoint)
|
||||
{
|
||||
if (cache.CollisionTraversalMode == CollisionTraversalMode.Flat)
|
||||
{
|
||||
FlatCellContainmentBsp flat = cell.FlatContainmentBsp ??
|
||||
throw MissingFlat("cell containment");
|
||||
return FlatBspQuery.PointInsideCellBsp(flat, localPoint);
|
||||
}
|
||||
|
||||
return BSPQuery.PointInsideCellBsp(cell.CellBSP?.Root, localPoint);
|
||||
}
|
||||
|
||||
internal static bool SphereIntersectsCell(
|
||||
PhysicsDataCache cache,
|
||||
CellPhysics cell,
|
||||
Vector3 localCenter,
|
||||
float radius)
|
||||
{
|
||||
if (cache.CollisionTraversalMode == CollisionTraversalMode.Flat)
|
||||
{
|
||||
FlatCellContainmentBsp flat = cell.FlatContainmentBsp ??
|
||||
throw MissingFlat("cell containment");
|
||||
return FlatBspQuery.SphereIntersectsCellBsp(
|
||||
flat,
|
||||
localCenter,
|
||||
radius);
|
||||
}
|
||||
|
||||
return BSPQuery.SphereIntersectsCellBsp(
|
||||
cell.CellBSP?.Root,
|
||||
localCenter,
|
||||
radius);
|
||||
}
|
||||
|
||||
internal static TransitionState FindCollisions(
|
||||
PhysicsDataCache cache,
|
||||
CellPhysics cell,
|
||||
Transition transition,
|
||||
Vector3 localSphereCenter,
|
||||
float localSphereRadius,
|
||||
bool hasLocalSphere1,
|
||||
Vector3 localSphere1Center,
|
||||
float localSphere1Radius,
|
||||
Vector3 localCurrentCenter,
|
||||
Vector3 localSpaceZ,
|
||||
float scale,
|
||||
Quaternion localToWorld,
|
||||
PhysicsEngine? engine,
|
||||
Vector3 worldOrigin)
|
||||
{
|
||||
if (cache.CollisionTraversalMode == CollisionTraversalMode.Flat)
|
||||
{
|
||||
FlatPhysicsBsp flat = cell.FlatPhysicsBsp ??
|
||||
throw MissingFlat("cell physics");
|
||||
return FlatBspQuery.FindCollisions(
|
||||
flat,
|
||||
transition,
|
||||
localSphereCenter,
|
||||
localSphereRadius,
|
||||
hasLocalSphere1,
|
||||
localSphere1Center,
|
||||
localSphere1Radius,
|
||||
localCurrentCenter,
|
||||
localSpaceZ,
|
||||
scale,
|
||||
localToWorld,
|
||||
engine,
|
||||
worldOrigin);
|
||||
}
|
||||
|
||||
return BSPQuery.FindCollisions(
|
||||
cell.BSP?.Root,
|
||||
cell.Resolved,
|
||||
transition,
|
||||
localSphereCenter,
|
||||
localSphereRadius,
|
||||
hasLocalSphere1,
|
||||
localSphere1Center,
|
||||
localSphere1Radius,
|
||||
localCurrentCenter,
|
||||
localSpaceZ,
|
||||
scale,
|
||||
localToWorld,
|
||||
engine,
|
||||
worldOrigin);
|
||||
}
|
||||
|
||||
internal static TransitionState FindCollisions(
|
||||
PhysicsDataCache cache,
|
||||
GfxObjPhysics gfxObject,
|
||||
Transition transition,
|
||||
Vector3 localSphereCenter,
|
||||
float localSphereRadius,
|
||||
bool hasLocalSphere1,
|
||||
Vector3 localSphere1Center,
|
||||
float localSphere1Radius,
|
||||
Vector3 localCurrentCenter,
|
||||
Vector3 localSpaceZ,
|
||||
float scale,
|
||||
Quaternion localToWorld,
|
||||
PhysicsEngine? engine,
|
||||
Vector3 worldOrigin)
|
||||
{
|
||||
if (cache.CollisionTraversalMode == CollisionTraversalMode.Flat)
|
||||
{
|
||||
FlatPhysicsBsp flat = gfxObject.FlatPhysicsBsp ??
|
||||
throw MissingFlat("GfxObj physics");
|
||||
return FlatBspQuery.FindCollisions(
|
||||
flat,
|
||||
transition,
|
||||
localSphereCenter,
|
||||
localSphereRadius,
|
||||
hasLocalSphere1,
|
||||
localSphere1Center,
|
||||
localSphere1Radius,
|
||||
localCurrentCenter,
|
||||
localSpaceZ,
|
||||
scale,
|
||||
localToWorld,
|
||||
engine,
|
||||
worldOrigin);
|
||||
}
|
||||
|
||||
return BSPQuery.FindCollisions(
|
||||
gfxObject.BSP.Root,
|
||||
gfxObject.Resolved,
|
||||
transition,
|
||||
localSphereCenter,
|
||||
localSphereRadius,
|
||||
hasLocalSphere1,
|
||||
localSphere1Center,
|
||||
localSphere1Radius,
|
||||
localCurrentCenter,
|
||||
localSpaceZ,
|
||||
scale,
|
||||
localToWorld,
|
||||
engine,
|
||||
worldOrigin);
|
||||
}
|
||||
|
||||
private static InvalidOperationException MissingFlat(string kind) =>
|
||||
new(
|
||||
$"Flat collision traversal requires a prepared {kind} asset. " +
|
||||
"Gameplay must not fall back silently.");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue