Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
CellTransit.FindTransitCellsBox ports CEnvCell::find_transit_cells'
part-array overload @0x0052cae0 line-for-line: per-portal x per-part
order, the sphere cheap-reject at F_EPSILON+radius, the box admit whose
"Straddle or crossing-side" rule is exactly retail's `eax != side` under
the PDB Sidedness enum, leads-outside placed AFTER the admit, the
unconditional unloaded-neighbour hint without the sphere overload's
re-test, the destination box_intersects_cell gate with its deliberate
no-break, and add_all_outside_cells after the loop. The box-vs-cell BSP
traversal lands in BOTH representations behind the flat-authoritative
dispatcher with a graph referee whose 20,000 installed comparisons are
pinned by assertion (review F5), zero mismatch.
Dual Opus review: PASS on both lenses. The mandatory D0 pseudocode pass
caught that the contract's own supplementary note misattributed the box
block to the sphere overload — it belongs to a SECOND
check_building_transit overload @0x0052c680, whose portal-side
convention is INVERTED and whose admit differs; the pseudocode doc now
records that trap plus two byte confirmations made at review:
which_side @0x00444720 is strictly > eps for POSITIVE, and
intersect_box's in-plane early exit returns CROSSING(3)
(jp @0x005aa1bc -> mov eax,3), settling review items b1/b2 for the
future bridge porter. The bridge itself stays unported as AP-159's
explicit remainder.
The review also retired #335's severity premise honestly: "over-
inclusive only, never a missed one" is wrong at production shape ratios,
where the box (whole-vertex AABB) legitimately exceeds the sphere
(physics-polygon root sphere). Measured, both populations: rigged
(box << sphere) — 1,520 placements, 978 cells removed, 0 added;
production-ratio (box >= sphere) — 950 placements, 20 removed, 1 ADDED
through the loaded-neighbour gate, which is retail's direction, not a
defect. The no-op guard (review F4) asserts removal is nonzero so an
unwired admit cannot pass silently.
Process note: the implementer authored against this session's worktree
at bec5c69d, 25 commits stale — the recorded worktree-base class. All
six files were byte-identical between bases, the diff transplanted
losslessly, and every verdict-bearing run (referee, direction sweeps,
this clean-room) was re-executed on current main. S2's uncommitted
phase-1 edits were stashed for this landing so the suite verdicts
exactly one changeset.
Also untracks 341-slope-capture.jsonl (an accidental add) and
gitignores it.
Clean-room suite: 11,248 passed / 6 skipped / 0 failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1193 lines
36 KiB
C#
1193 lines
36 KiB
C#
using System.Numerics;
|
|
|
|
namespace AcDream.Core.Physics;
|
|
|
|
/// <summary>
|
|
/// Temporary Slice I6 authority/referee selector. Gameplay uses
|
|
/// <see cref="Flat"/>; <see cref="Graph"/> remains only for the old-oracle
|
|
/// differential fixtures until the accepted cutover deletes it.
|
|
/// </summary>
|
|
internal enum CollisionTraversalMode
|
|
{
|
|
Graph,
|
|
Flat,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Narrow representation switch used while flat production and the graph
|
|
/// referee coexist. I6 removes the graph branch after acceptance.
|
|
/// </summary>
|
|
internal static class CollisionTraversal
|
|
{
|
|
internal static bool HasCellContainment(
|
|
PhysicsDataCache cache,
|
|
CellPhysics cell)
|
|
{
|
|
if (UseFlat(cache))
|
|
{
|
|
FlatCellContainmentBsp flat = cell.FlatContainmentBsp ??
|
|
throw MissingFlat("cell containment");
|
|
bool flatAuthorityResult = flat.RootIndex >= 0;
|
|
CollisionShadowVerifier? flatShadow = cache.CollisionShadow;
|
|
if (flatShadow is null ||
|
|
!flatShadow.TrySample(out long flatAuthoritySample))
|
|
return flatAuthorityResult;
|
|
|
|
bool graphRefereeResult = false;
|
|
Exception? graphRefereeFault = null;
|
|
flatShadow.BeginGraphPass();
|
|
try
|
|
{
|
|
graphRefereeResult = cell.CellBSP?.Root is not null;
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
graphRefereeFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
flatShadow.EndGraphPass();
|
|
}
|
|
|
|
if (graphRefereeFault is null)
|
|
{
|
|
flatShadow.RecordBoolean(
|
|
flatAuthoritySample,
|
|
"HasCellContainment",
|
|
cell.SourceId,
|
|
graphRefereeResult,
|
|
flatAuthorityResult,
|
|
string.Empty);
|
|
}
|
|
else
|
|
{
|
|
flatShadow.RecordFault(
|
|
flatAuthoritySample,
|
|
"HasCellContainment",
|
|
cell.SourceId,
|
|
graphRefereeFault,
|
|
string.Empty,
|
|
"flat");
|
|
}
|
|
return flatAuthorityResult;
|
|
}
|
|
|
|
CollisionShadowVerifier? shadow = cache.CollisionShadow;
|
|
if (shadow is null || !shadow.TrySample(out long sample))
|
|
return cell.CellBSP?.Root is not null;
|
|
|
|
bool flatResult = false;
|
|
Exception? flatFault = null;
|
|
shadow.BeginFlatPass();
|
|
try
|
|
{
|
|
flatResult = (cell.FlatContainmentBsp ??
|
|
throw MissingFlat("cell containment")).RootIndex >= 0;
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
flatFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
shadow.EndFlatPass();
|
|
}
|
|
|
|
bool graphResult = cell.CellBSP?.Root is not null;
|
|
if (flatFault is null)
|
|
{
|
|
shadow.RecordBoolean(
|
|
sample,
|
|
"HasCellContainment",
|
|
cell.SourceId,
|
|
graphResult,
|
|
flatResult,
|
|
string.Empty);
|
|
}
|
|
else
|
|
{
|
|
shadow.RecordFault(
|
|
sample,
|
|
"HasCellContainment",
|
|
cell.SourceId,
|
|
flatFault,
|
|
string.Empty);
|
|
}
|
|
return graphResult;
|
|
}
|
|
|
|
internal static bool HasPhysics(
|
|
PhysicsDataCache cache,
|
|
CellPhysics cell)
|
|
{
|
|
if (UseFlat(cache))
|
|
{
|
|
FlatPhysicsBsp flat = cell.FlatPhysicsBsp ??
|
|
throw MissingFlat("cell physics");
|
|
bool flatAuthorityResult = flat.RootIndex >= 0;
|
|
CollisionShadowVerifier? flatShadow = cache.CollisionShadow;
|
|
if (flatShadow is null ||
|
|
!flatShadow.TrySample(out long flatAuthoritySample))
|
|
return flatAuthorityResult;
|
|
|
|
bool graphRefereeResult = false;
|
|
Exception? graphRefereeFault = null;
|
|
flatShadow.BeginGraphPass();
|
|
try
|
|
{
|
|
graphRefereeResult = cell.BSP?.Root is not null;
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
graphRefereeFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
flatShadow.EndGraphPass();
|
|
}
|
|
|
|
if (graphRefereeFault is null)
|
|
{
|
|
flatShadow.RecordBoolean(
|
|
flatAuthoritySample,
|
|
"HasCellPhysics",
|
|
cell.SourceId,
|
|
graphRefereeResult,
|
|
flatAuthorityResult,
|
|
string.Empty);
|
|
}
|
|
else
|
|
{
|
|
flatShadow.RecordFault(
|
|
flatAuthoritySample,
|
|
"HasCellPhysics",
|
|
cell.SourceId,
|
|
graphRefereeFault,
|
|
string.Empty,
|
|
"flat");
|
|
}
|
|
return flatAuthorityResult;
|
|
}
|
|
|
|
CollisionShadowVerifier? shadow = cache.CollisionShadow;
|
|
if (shadow is null || !shadow.TrySample(out long sample))
|
|
return cell.BSP?.Root is not null;
|
|
|
|
bool flatResult = false;
|
|
Exception? flatFault = null;
|
|
shadow.BeginFlatPass();
|
|
try
|
|
{
|
|
flatResult = (cell.FlatPhysicsBsp ??
|
|
throw MissingFlat("cell physics")).RootIndex >= 0;
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
flatFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
shadow.EndFlatPass();
|
|
}
|
|
|
|
bool graphResult = cell.BSP?.Root is not null;
|
|
if (flatFault is null)
|
|
{
|
|
shadow.RecordBoolean(
|
|
sample,
|
|
"HasCellPhysics",
|
|
cell.SourceId,
|
|
graphResult,
|
|
flatResult,
|
|
string.Empty);
|
|
}
|
|
else
|
|
{
|
|
shadow.RecordFault(
|
|
sample,
|
|
"HasCellPhysics",
|
|
cell.SourceId,
|
|
flatFault,
|
|
string.Empty);
|
|
}
|
|
return graphResult;
|
|
}
|
|
|
|
internal static bool HasPhysics(
|
|
PhysicsDataCache cache,
|
|
GfxObjPhysics gfxObject)
|
|
{
|
|
if (UseFlat(cache))
|
|
{
|
|
FlatPhysicsBsp flat = gfxObject.FlatPhysicsBsp ??
|
|
throw MissingFlat("GfxObj physics");
|
|
bool flatAuthorityResult = flat.RootIndex >= 0;
|
|
CollisionShadowVerifier? flatShadow = cache.CollisionShadow;
|
|
if (flatShadow is null ||
|
|
!flatShadow.TrySample(out long flatAuthoritySample))
|
|
return flatAuthorityResult;
|
|
|
|
bool graphRefereeResult = false;
|
|
Exception? graphRefereeFault = null;
|
|
flatShadow.BeginGraphPass();
|
|
try
|
|
{
|
|
graphRefereeResult = gfxObject.BSP?.Root is not null;
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
graphRefereeFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
flatShadow.EndGraphPass();
|
|
}
|
|
|
|
if (graphRefereeFault is null)
|
|
{
|
|
flatShadow.RecordBoolean(
|
|
flatAuthoritySample,
|
|
"HasGfxObjPhysics",
|
|
gfxObject.SourceId,
|
|
graphRefereeResult,
|
|
flatAuthorityResult,
|
|
string.Empty);
|
|
}
|
|
else
|
|
{
|
|
flatShadow.RecordFault(
|
|
flatAuthoritySample,
|
|
"HasGfxObjPhysics",
|
|
gfxObject.SourceId,
|
|
graphRefereeFault,
|
|
string.Empty,
|
|
"flat");
|
|
}
|
|
return flatAuthorityResult;
|
|
}
|
|
|
|
CollisionShadowVerifier? shadow = cache.CollisionShadow;
|
|
if (shadow is null || !shadow.TrySample(out long sample))
|
|
return gfxObject.BSP?.Root is not null;
|
|
|
|
bool flatResult = false;
|
|
Exception? flatFault = null;
|
|
shadow.BeginFlatPass();
|
|
try
|
|
{
|
|
flatResult = (gfxObject.FlatPhysicsBsp ??
|
|
throw MissingFlat("GfxObj physics")).RootIndex >= 0;
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
flatFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
shadow.EndFlatPass();
|
|
}
|
|
|
|
bool graphResult = gfxObject.BSP?.Root is not null;
|
|
if (flatFault is null)
|
|
{
|
|
shadow.RecordBoolean(
|
|
sample,
|
|
"HasGfxObjPhysics",
|
|
gfxObject.SourceId,
|
|
graphResult,
|
|
flatResult,
|
|
string.Empty);
|
|
}
|
|
else
|
|
{
|
|
shadow.RecordFault(
|
|
sample,
|
|
"HasGfxObjPhysics",
|
|
gfxObject.SourceId,
|
|
flatFault,
|
|
string.Empty);
|
|
}
|
|
return graphResult;
|
|
}
|
|
|
|
internal static FlatCollisionSphere RootBoundingSphere(
|
|
PhysicsDataCache cache,
|
|
CellPhysics cell)
|
|
{
|
|
if (UseFlat(cache))
|
|
{
|
|
FlatPhysicsBsp flat = cell.FlatPhysicsBsp ??
|
|
throw MissingFlat("cell physics");
|
|
FlatCollisionSphere flatAuthorityResult =
|
|
flat.Nodes[flat.RootIndex].BoundingSphere;
|
|
CollisionShadowVerifier? flatShadow = cache.CollisionShadow;
|
|
if (flatShadow is null ||
|
|
!flatShadow.TrySample(out long flatAuthoritySample))
|
|
return flatAuthorityResult;
|
|
|
|
try
|
|
{
|
|
flatShadow.BeginGraphPass();
|
|
DatReaderWriter.Types.Sphere graphSphere =
|
|
cell.BSP!.Root!.BoundingSphere;
|
|
var graphRefereeResult =
|
|
new FlatCollisionSphere(
|
|
graphSphere.Origin,
|
|
graphSphere.Radius);
|
|
flatShadow.EndGraphPass();
|
|
flatShadow.RecordSphere(
|
|
flatAuthoritySample,
|
|
"RootBoundingSphere",
|
|
cell.SourceId,
|
|
graphRefereeResult,
|
|
flatAuthorityResult);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
if (flatShadow.IsGraphPass)
|
|
flatShadow.EndGraphPass();
|
|
flatShadow.RecordFault(
|
|
flatAuthoritySample,
|
|
"RootBoundingSphere",
|
|
cell.SourceId,
|
|
fault,
|
|
string.Empty,
|
|
"flat");
|
|
}
|
|
return flatAuthorityResult;
|
|
}
|
|
|
|
DatReaderWriter.Types.Sphere sphere = cell.BSP!.Root!.BoundingSphere;
|
|
var graphResult =
|
|
new FlatCollisionSphere(sphere.Origin, sphere.Radius);
|
|
CollisionShadowVerifier? shadow = cache.CollisionShadow;
|
|
if (shadow is null || !shadow.TrySample(out long sample))
|
|
return graphResult;
|
|
|
|
try
|
|
{
|
|
shadow.BeginFlatPass();
|
|
FlatPhysicsBsp flat = cell.FlatPhysicsBsp ??
|
|
throw MissingFlat("cell physics");
|
|
FlatCollisionSphere flatResult =
|
|
flat.Nodes[flat.RootIndex].BoundingSphere;
|
|
shadow.EndFlatPass();
|
|
shadow.RecordSphere(
|
|
sample,
|
|
"RootBoundingSphere",
|
|
cell.SourceId,
|
|
graphResult,
|
|
flatResult);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
if (shadow.IsFlatPass)
|
|
shadow.EndFlatPass();
|
|
shadow.RecordFault(
|
|
sample,
|
|
"RootBoundingSphere",
|
|
cell.SourceId,
|
|
fault,
|
|
string.Empty);
|
|
}
|
|
return graphResult;
|
|
}
|
|
|
|
internal static bool PointInsideCell(
|
|
PhysicsDataCache cache,
|
|
CellPhysics cell,
|
|
Vector3 localPoint)
|
|
{
|
|
if (UseFlat(cache))
|
|
{
|
|
FlatCellContainmentBsp flat = cell.FlatContainmentBsp ??
|
|
throw MissingFlat("cell containment");
|
|
bool flatAuthorityResult =
|
|
FlatBspQuery.PointInsideCellBsp(flat, localPoint);
|
|
CollisionShadowVerifier? flatShadow = cache.CollisionShadow;
|
|
if (flatShadow is null ||
|
|
!flatShadow.TrySample(out long flatAuthoritySample))
|
|
return flatAuthorityResult;
|
|
|
|
bool graphRefereeResult = false;
|
|
Exception? graphRefereeFault = null;
|
|
flatShadow.BeginGraphPass();
|
|
try
|
|
{
|
|
graphRefereeResult = BSPQuery.PointInsideCellBsp(
|
|
cell.CellBSP?.Root,
|
|
localPoint);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
graphRefereeFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
flatShadow.EndGraphPass();
|
|
}
|
|
string flatAuthorityInput = CollisionShadowVerifier.FormatInput(
|
|
localPoint,
|
|
0f);
|
|
if (graphRefereeFault is null)
|
|
{
|
|
flatShadow.RecordBoolean(
|
|
flatAuthoritySample,
|
|
"PointInsideCell",
|
|
cell.SourceId,
|
|
graphRefereeResult,
|
|
flatAuthorityResult,
|
|
flatAuthorityInput);
|
|
}
|
|
else
|
|
{
|
|
flatShadow.RecordFault(
|
|
flatAuthoritySample,
|
|
"PointInsideCell",
|
|
cell.SourceId,
|
|
graphRefereeFault,
|
|
flatAuthorityInput,
|
|
"flat");
|
|
}
|
|
return flatAuthorityResult;
|
|
}
|
|
|
|
CollisionShadowVerifier? shadow = cache.CollisionShadow;
|
|
if (shadow is null || !shadow.TrySample(out long sample))
|
|
return BSPQuery.PointInsideCellBsp(
|
|
cell.CellBSP?.Root,
|
|
localPoint);
|
|
|
|
bool flatResult = false;
|
|
Exception? flatFault = null;
|
|
shadow.BeginFlatPass();
|
|
try
|
|
{
|
|
flatResult = FlatBspQuery.PointInsideCellBsp(
|
|
cell.FlatContainmentBsp ??
|
|
throw MissingFlat("cell containment"),
|
|
localPoint);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
flatFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
shadow.EndFlatPass();
|
|
}
|
|
bool graphResult = BSPQuery.PointInsideCellBsp(
|
|
cell.CellBSP?.Root,
|
|
localPoint);
|
|
string input = CollisionShadowVerifier.FormatInput(
|
|
localPoint,
|
|
0f);
|
|
if (flatFault is null)
|
|
{
|
|
shadow.RecordBoolean(
|
|
sample,
|
|
"PointInsideCell",
|
|
cell.SourceId,
|
|
graphResult,
|
|
flatResult,
|
|
input);
|
|
}
|
|
else
|
|
{
|
|
shadow.RecordFault(
|
|
sample,
|
|
"PointInsideCell",
|
|
cell.SourceId,
|
|
flatFault,
|
|
input);
|
|
}
|
|
return graphResult;
|
|
}
|
|
|
|
internal static bool SphereIntersectsCell(
|
|
PhysicsDataCache cache,
|
|
CellPhysics cell,
|
|
Vector3 localCenter,
|
|
float radius)
|
|
{
|
|
if (UseFlat(cache))
|
|
{
|
|
FlatCellContainmentBsp flat = cell.FlatContainmentBsp ??
|
|
throw MissingFlat("cell containment");
|
|
bool flatAuthorityResult = FlatBspQuery.SphereIntersectsCellBsp(
|
|
flat,
|
|
localCenter,
|
|
radius);
|
|
CollisionShadowVerifier? flatShadow = cache.CollisionShadow;
|
|
if (flatShadow is null ||
|
|
!flatShadow.TrySample(out long flatAuthoritySample))
|
|
return flatAuthorityResult;
|
|
|
|
bool graphRefereeResult = false;
|
|
Exception? graphRefereeFault = null;
|
|
flatShadow.BeginGraphPass();
|
|
try
|
|
{
|
|
graphRefereeResult = BSPQuery.SphereIntersectsCellBsp(
|
|
cell.CellBSP?.Root,
|
|
localCenter,
|
|
radius);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
graphRefereeFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
flatShadow.EndGraphPass();
|
|
}
|
|
string flatAuthorityInput = CollisionShadowVerifier.FormatInput(
|
|
localCenter,
|
|
radius);
|
|
if (graphRefereeFault is null)
|
|
{
|
|
flatShadow.RecordBoolean(
|
|
flatAuthoritySample,
|
|
"SphereIntersectsCell",
|
|
cell.SourceId,
|
|
graphRefereeResult,
|
|
flatAuthorityResult,
|
|
flatAuthorityInput);
|
|
}
|
|
else
|
|
{
|
|
flatShadow.RecordFault(
|
|
flatAuthoritySample,
|
|
"SphereIntersectsCell",
|
|
cell.SourceId,
|
|
graphRefereeFault,
|
|
flatAuthorityInput,
|
|
"flat");
|
|
}
|
|
return flatAuthorityResult;
|
|
}
|
|
|
|
CollisionShadowVerifier? shadow = cache.CollisionShadow;
|
|
if (shadow is null || !shadow.TrySample(out long sample))
|
|
{
|
|
return BSPQuery.SphereIntersectsCellBsp(
|
|
cell.CellBSP?.Root,
|
|
localCenter,
|
|
radius);
|
|
}
|
|
|
|
bool flatResult = false;
|
|
Exception? flatFault = null;
|
|
shadow.BeginFlatPass();
|
|
try
|
|
{
|
|
flatResult = FlatBspQuery.SphereIntersectsCellBsp(
|
|
cell.FlatContainmentBsp ??
|
|
throw MissingFlat("cell containment"),
|
|
localCenter,
|
|
radius);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
flatFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
shadow.EndFlatPass();
|
|
}
|
|
bool graphResult = BSPQuery.SphereIntersectsCellBsp(
|
|
cell.CellBSP?.Root,
|
|
localCenter,
|
|
radius);
|
|
string input = CollisionShadowVerifier.FormatInput(
|
|
localCenter,
|
|
radius);
|
|
if (flatFault is null)
|
|
{
|
|
shadow.RecordBoolean(
|
|
sample,
|
|
"SphereIntersectsCell",
|
|
cell.SourceId,
|
|
graphResult,
|
|
flatResult,
|
|
input);
|
|
}
|
|
else
|
|
{
|
|
shadow.RecordFault(
|
|
sample,
|
|
"SphereIntersectsCell",
|
|
cell.SourceId,
|
|
flatFault,
|
|
input);
|
|
}
|
|
return graphResult;
|
|
}
|
|
|
|
/// <summary>
|
|
/// AP-159 / #335 (2026-08-07). Box-shaped sibling of
|
|
/// <see cref="SphereIntersectsCell"/> — same flat-authority /
|
|
/// graph-referee shadow-sample dispatch shape, for the new
|
|
/// <c>CCellStruct::box_intersects_cell</c> port
|
|
/// (<see cref="BSPQuery.BoxIntersectsCellBsp"/> /
|
|
/// <see cref="FlatBspQuery.BoxIntersectsCellBsp"/>) that
|
|
/// <c>CellTransit.FindTransitCellsBox</c> uses for the destination-cell
|
|
/// gate in retail's part-array <c>find_transit_cells</c>.
|
|
/// </summary>
|
|
internal static bool BoxIntersectsCell(
|
|
PhysicsDataCache cache,
|
|
CellPhysics cell,
|
|
Vector3 localMin,
|
|
Vector3 localMax)
|
|
{
|
|
if (UseFlat(cache))
|
|
{
|
|
FlatCellContainmentBsp flat = cell.FlatContainmentBsp ??
|
|
throw MissingFlat("cell containment");
|
|
bool flatAuthorityResult = FlatBspQuery.BoxIntersectsCellBsp(
|
|
flat,
|
|
localMin,
|
|
localMax);
|
|
CollisionShadowVerifier? flatShadow = cache.CollisionShadow;
|
|
if (flatShadow is null ||
|
|
!flatShadow.TrySample(out long flatAuthoritySample))
|
|
return flatAuthorityResult;
|
|
|
|
bool graphRefereeResult = false;
|
|
Exception? graphRefereeFault = null;
|
|
flatShadow.BeginGraphPass();
|
|
try
|
|
{
|
|
graphRefereeResult = BSPQuery.BoxIntersectsCellBsp(
|
|
cell.CellBSP?.Root,
|
|
localMin,
|
|
localMax);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
graphRefereeFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
flatShadow.EndGraphPass();
|
|
}
|
|
string flatAuthorityInput = CollisionShadowVerifier.FormatInput(
|
|
localMin,
|
|
localMax);
|
|
if (graphRefereeFault is null)
|
|
{
|
|
flatShadow.RecordBoolean(
|
|
flatAuthoritySample,
|
|
"BoxIntersectsCell",
|
|
cell.SourceId,
|
|
graphRefereeResult,
|
|
flatAuthorityResult,
|
|
flatAuthorityInput);
|
|
}
|
|
else
|
|
{
|
|
flatShadow.RecordFault(
|
|
flatAuthoritySample,
|
|
"BoxIntersectsCell",
|
|
cell.SourceId,
|
|
graphRefereeFault,
|
|
flatAuthorityInput,
|
|
"flat");
|
|
}
|
|
return flatAuthorityResult;
|
|
}
|
|
|
|
CollisionShadowVerifier? shadow = cache.CollisionShadow;
|
|
if (shadow is null || !shadow.TrySample(out long sample))
|
|
{
|
|
return BSPQuery.BoxIntersectsCellBsp(
|
|
cell.CellBSP?.Root,
|
|
localMin,
|
|
localMax);
|
|
}
|
|
|
|
bool flatResult = false;
|
|
Exception? flatFault = null;
|
|
shadow.BeginFlatPass();
|
|
try
|
|
{
|
|
flatResult = FlatBspQuery.BoxIntersectsCellBsp(
|
|
cell.FlatContainmentBsp ??
|
|
throw MissingFlat("cell containment"),
|
|
localMin,
|
|
localMax);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
flatFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
shadow.EndFlatPass();
|
|
}
|
|
bool graphResult = BSPQuery.BoxIntersectsCellBsp(
|
|
cell.CellBSP?.Root,
|
|
localMin,
|
|
localMax);
|
|
string input = CollisionShadowVerifier.FormatInput(localMin, localMax);
|
|
if (flatFault is null)
|
|
{
|
|
shadow.RecordBoolean(
|
|
sample,
|
|
"BoxIntersectsCell",
|
|
cell.SourceId,
|
|
graphResult,
|
|
flatResult,
|
|
input);
|
|
}
|
|
else
|
|
{
|
|
shadow.RecordFault(
|
|
sample,
|
|
"BoxIntersectsCell",
|
|
cell.SourceId,
|
|
flatFault,
|
|
input);
|
|
}
|
|
return graphResult;
|
|
}
|
|
|
|
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 (UseFlat(cache))
|
|
{
|
|
CollisionShadowVerifier? flatShadow = cache.CollisionShadow;
|
|
long flatAuthoritySample = 0;
|
|
bool sampled =
|
|
flatShadow is not null &&
|
|
flatShadow.TrySample(out flatAuthoritySample);
|
|
Transition? graphTransition = sampled
|
|
? flatShadow!.PrepareShadow(transition)
|
|
: null;
|
|
FlatPhysicsBsp flat = cell.FlatPhysicsBsp ??
|
|
throw MissingFlat("cell physics");
|
|
TransitionState flatAuthorityState = FlatBspQuery.FindCollisions(
|
|
flat,
|
|
transition,
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
engine,
|
|
worldOrigin);
|
|
if (!sampled)
|
|
return flatAuthorityState;
|
|
|
|
TransitionState graphRefereeState = TransitionState.Invalid;
|
|
Exception? graphRefereeFault = null;
|
|
flatShadow!.BeginGraphPass();
|
|
try
|
|
{
|
|
graphRefereeState = BSPQuery.FindCollisions(
|
|
cell.BSP?.Root,
|
|
cell.Resolved,
|
|
graphTransition!,
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
engine,
|
|
worldOrigin);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
graphRefereeFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
flatShadow.EndGraphPass();
|
|
}
|
|
|
|
string flatAuthorityInput = CollisionShadowVerifier.FormatInput(
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
worldOrigin);
|
|
if (graphRefereeFault is null)
|
|
{
|
|
flatShadow.RecordTransition(
|
|
flatAuthoritySample,
|
|
"FindCellCollisions",
|
|
cell.SourceId,
|
|
graphRefereeState,
|
|
flatAuthorityState,
|
|
graphTransition!,
|
|
transition,
|
|
flatAuthorityInput);
|
|
}
|
|
else
|
|
{
|
|
flatShadow.RecordFault(
|
|
flatAuthoritySample,
|
|
"FindCellCollisions",
|
|
cell.SourceId,
|
|
graphRefereeFault,
|
|
flatAuthorityInput,
|
|
"flat");
|
|
}
|
|
return flatAuthorityState;
|
|
}
|
|
|
|
CollisionShadowVerifier? shadow = cache.CollisionShadow;
|
|
if (shadow is null || !shadow.TrySample(out long sample))
|
|
{
|
|
return BSPQuery.FindCollisions(
|
|
cell.BSP?.Root,
|
|
cell.Resolved,
|
|
transition,
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
engine,
|
|
worldOrigin);
|
|
}
|
|
|
|
Transition flatTransition = shadow.PrepareShadow(transition);
|
|
TransitionState flatState = TransitionState.Invalid;
|
|
Exception? flatFault = null;
|
|
shadow.BeginFlatPass();
|
|
try
|
|
{
|
|
flatState = FlatBspQuery.FindCollisions(
|
|
cell.FlatPhysicsBsp ??
|
|
throw MissingFlat("cell physics"),
|
|
flatTransition,
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
engine,
|
|
worldOrigin);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
flatFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
shadow.EndFlatPass();
|
|
}
|
|
|
|
TransitionState graphState = BSPQuery.FindCollisions(
|
|
cell.BSP?.Root,
|
|
cell.Resolved,
|
|
transition,
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
engine,
|
|
worldOrigin);
|
|
string input = CollisionShadowVerifier.FormatInput(
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
worldOrigin);
|
|
if (flatFault is null)
|
|
{
|
|
shadow.RecordTransition(
|
|
sample,
|
|
"FindCellCollisions",
|
|
cell.SourceId,
|
|
graphState,
|
|
flatState,
|
|
transition,
|
|
flatTransition,
|
|
input);
|
|
}
|
|
else
|
|
{
|
|
shadow.RecordFault(
|
|
sample,
|
|
"FindCellCollisions",
|
|
cell.SourceId,
|
|
flatFault,
|
|
input);
|
|
}
|
|
return graphState;
|
|
}
|
|
|
|
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 (UseFlat(cache))
|
|
{
|
|
CollisionShadowVerifier? flatShadow = cache.CollisionShadow;
|
|
long flatAuthoritySample = 0;
|
|
bool sampled =
|
|
flatShadow is not null &&
|
|
flatShadow.TrySample(out flatAuthoritySample);
|
|
Transition? graphTransition = sampled
|
|
? flatShadow!.PrepareShadow(transition)
|
|
: null;
|
|
FlatPhysicsBsp flat = gfxObject.FlatPhysicsBsp ??
|
|
throw MissingFlat("GfxObj physics");
|
|
TransitionState flatAuthorityState = FlatBspQuery.FindCollisions(
|
|
flat,
|
|
transition,
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
engine,
|
|
worldOrigin);
|
|
if (!sampled)
|
|
return flatAuthorityState;
|
|
|
|
TransitionState graphRefereeState = TransitionState.Invalid;
|
|
Exception? graphRefereeFault = null;
|
|
flatShadow!.BeginGraphPass();
|
|
try
|
|
{
|
|
graphRefereeState = BSPQuery.FindCollisions(
|
|
gfxObject.BSP!.Root!,
|
|
gfxObject.Resolved,
|
|
graphTransition!,
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
engine,
|
|
worldOrigin);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
graphRefereeFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
flatShadow.EndGraphPass();
|
|
}
|
|
|
|
string flatAuthorityInput = CollisionShadowVerifier.FormatInput(
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
worldOrigin);
|
|
if (graphRefereeFault is null)
|
|
{
|
|
flatShadow.RecordTransition(
|
|
flatAuthoritySample,
|
|
"FindGfxObjCollisions",
|
|
gfxObject.SourceId,
|
|
graphRefereeState,
|
|
flatAuthorityState,
|
|
graphTransition!,
|
|
transition,
|
|
flatAuthorityInput);
|
|
}
|
|
else
|
|
{
|
|
flatShadow.RecordFault(
|
|
flatAuthoritySample,
|
|
"FindGfxObjCollisions",
|
|
gfxObject.SourceId,
|
|
graphRefereeFault,
|
|
flatAuthorityInput,
|
|
"flat");
|
|
}
|
|
return flatAuthorityState;
|
|
}
|
|
|
|
CollisionShadowVerifier? shadow = cache.CollisionShadow;
|
|
if (shadow is null || !shadow.TrySample(out long sample))
|
|
{
|
|
return BSPQuery.FindCollisions(
|
|
gfxObject.BSP!.Root!,
|
|
gfxObject.Resolved,
|
|
transition,
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
engine,
|
|
worldOrigin);
|
|
}
|
|
|
|
Transition flatTransition = shadow.PrepareShadow(transition);
|
|
TransitionState flatState = TransitionState.Invalid;
|
|
Exception? flatFault = null;
|
|
shadow.BeginFlatPass();
|
|
try
|
|
{
|
|
flatState = FlatBspQuery.FindCollisions(
|
|
gfxObject.FlatPhysicsBsp ??
|
|
throw MissingFlat("GfxObj physics"),
|
|
flatTransition,
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
engine,
|
|
worldOrigin);
|
|
}
|
|
catch (Exception fault)
|
|
{
|
|
flatFault = fault;
|
|
}
|
|
finally
|
|
{
|
|
shadow.EndFlatPass();
|
|
}
|
|
|
|
TransitionState graphState = BSPQuery.FindCollisions(
|
|
gfxObject.BSP!.Root!,
|
|
gfxObject.Resolved,
|
|
transition,
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
engine,
|
|
worldOrigin);
|
|
string input = CollisionShadowVerifier.FormatInput(
|
|
localSphereCenter,
|
|
localSphereRadius,
|
|
hasLocalSphere1,
|
|
localSphere1Center,
|
|
localSphere1Radius,
|
|
localCurrentCenter,
|
|
localSpaceZ,
|
|
scale,
|
|
localToWorld,
|
|
worldOrigin);
|
|
if (flatFault is null)
|
|
{
|
|
shadow.RecordTransition(
|
|
sample,
|
|
"FindGfxObjCollisions",
|
|
gfxObject.SourceId,
|
|
graphState,
|
|
flatState,
|
|
transition,
|
|
flatTransition,
|
|
input);
|
|
}
|
|
else
|
|
{
|
|
shadow.RecordFault(
|
|
sample,
|
|
"FindGfxObjCollisions",
|
|
gfxObject.SourceId,
|
|
flatFault,
|
|
input);
|
|
}
|
|
return graphState;
|
|
}
|
|
|
|
private static bool UseFlat(PhysicsDataCache cache)
|
|
{
|
|
CollisionShadowVerifier? shadow = cache.CollisionShadow;
|
|
if (shadow?.IsGraphPass == true)
|
|
return false;
|
|
if (shadow?.IsFlatPass == true)
|
|
return true;
|
|
return cache.CollisionTraversalMode == CollisionTraversalMode.Flat;
|
|
}
|
|
|
|
private static InvalidOperationException MissingFlat(string kind) =>
|
|
new(
|
|
$"Flat collision traversal requires a prepared {kind} asset. " +
|
|
"Gameplay must not fall back silently.");
|
|
}
|