fix(physics): swept-sphere collision prevents penetration
Restructure FindObjCollisions to compute collision ALONG the movement path instead of at the final position: BSP: movement-aware SphereIntersectsPoly with front-face culling (dot(movement, normal) < 0). Only detects faces the sphere is approaching, matching retail Polygon.pos_hits_sphere. Cylinder: quadratic ray-cylinder intersection computes parametric contact time t. If t < 1.0, sphere is rewound to the contact point. Both: find the EARLIEST collision (minimum t), rewind sphere to contact point + small epsilon along normal, then SlideSphere. This prevents the "walking into walls" penetration (BUG-005). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3997839d1a
commit
8e1230c53b
2 changed files with 176 additions and 39 deletions
|
|
@ -1057,4 +1057,89 @@ public static class BSPQuery
|
|||
return SphereIntersectsPoly(node.NegNode, polygons, vertices,
|
||||
sphereCenter, sphereRadius, out hitPolyId, out hitNormal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Movement-aware sphere-BSP intersection with front-face culling.
|
||||
/// Only detects collisions against polygon faces the sphere is moving TOWARD
|
||||
/// (dot(movement, normal) < 0). This prevents detecting collisions after
|
||||
/// the sphere has already penetrated past a face.
|
||||
///
|
||||
/// Matches retail Polygon.pos_hits_sphere behavior.
|
||||
/// </summary>
|
||||
public static bool SphereIntersectsPoly(
|
||||
PhysicsBSPNode? node,
|
||||
Dictionary<ushort, Polygon> polygons,
|
||||
VertexArray vertices,
|
||||
Vector3 sphereCenter,
|
||||
float sphereRadius,
|
||||
Vector3 movement,
|
||||
out ushort hitPolyId,
|
||||
out Vector3 hitNormal)
|
||||
{
|
||||
hitPolyId = 0;
|
||||
hitNormal = Vector3.Zero;
|
||||
|
||||
if (node is null) return false;
|
||||
|
||||
// Broad phase: bounding sphere
|
||||
{
|
||||
float dist = Vector3.Distance(sphereCenter, node.BoundingSphere.Origin);
|
||||
if (dist > sphereRadius + node.BoundingSphere.Radius + movement.Length() + 0.1f)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node.Type == BSPNodeType.Leaf)
|
||||
{
|
||||
foreach (var polyIdx in node.Polygons)
|
||||
{
|
||||
if (!polygons.TryGetValue(polyIdx, out var poly)) continue;
|
||||
if (!TryGetPolyPlane(poly, vertices, out var polyPlane, out var polyVerts))
|
||||
continue;
|
||||
|
||||
// Front-face culling: only collide if moving TOWARD this face.
|
||||
// This is the critical retail behavior from Polygon.pos_hits_sphere.
|
||||
if (Vector3.Dot(movement, polyPlane.Normal) >= 0f)
|
||||
continue; // moving away from or parallel to face
|
||||
|
||||
if (CollisionPrimitives.SphereIntersectsPoly(
|
||||
polyPlane, polyVerts, sphereCenter, sphereRadius, out _))
|
||||
{
|
||||
hitPolyId = polyIdx;
|
||||
hitNormal = polyPlane.Normal;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Also check at the end-of-movement position.
|
||||
Vector3 endCenter = sphereCenter + movement;
|
||||
if (CollisionPrimitives.SphereIntersectsPoly(
|
||||
polyPlane, polyVerts, endCenter, sphereRadius, out _))
|
||||
{
|
||||
hitPolyId = polyIdx;
|
||||
hitNormal = polyPlane.Normal;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Internal node: same split logic
|
||||
float splitDist = Vector3.Dot(node.SplittingPlane.Normal, sphereCenter)
|
||||
+ node.SplittingPlane.D;
|
||||
float reach = sphereRadius + movement.Length();
|
||||
|
||||
if (splitDist >= reach)
|
||||
return SphereIntersectsPoly(node.PosNode, polygons, vertices,
|
||||
sphereCenter, sphereRadius, movement, out hitPolyId, out hitNormal);
|
||||
|
||||
if (splitDist <= -reach)
|
||||
return SphereIntersectsPoly(node.NegNode, polygons, vertices,
|
||||
sphereCenter, sphereRadius, movement, out hitPolyId, out hitNormal);
|
||||
|
||||
if (SphereIntersectsPoly(node.PosNode, polygons, vertices,
|
||||
sphereCenter, sphereRadius, movement, out hitPolyId, out hitNormal))
|
||||
return true;
|
||||
|
||||
return SphereIntersectsPoly(node.NegNode, polygons, vertices,
|
||||
sphereCenter, sphereRadius, movement, out hitPolyId, out hitNormal);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue