fix(physics): restore retail path-6 collision response
This commit is contained in:
parent
acec33eca8
commit
75b6f6b6c9
8 changed files with 523 additions and 223 deletions
|
|
@ -231,16 +231,18 @@ public sealed class RetailEdgeResponseOrderingTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiFrameSteepRoof_GraphAndFlatTraversalRemainExactAndDoNotWedge()
|
||||
public void MultiFrameSteepRoof_PureVertical_GraphAndFlatSlideDownhillWithoutWedge()
|
||||
{
|
||||
TraceRun graph = RunSteepRoofTrace(preparedFlat: false);
|
||||
TraceRun flat = RunSteepRoofTrace(preparedFlat: true);
|
||||
TraceRun graph = RunSteepRoofTrace(preparedFlat: false, Vector2.Zero);
|
||||
TraceRun flat = RunSteepRoofTrace(preparedFlat: true, Vector2.Zero);
|
||||
|
||||
AssertTraceParity(graph, flat);
|
||||
Assert.Contains(graph.Frames, frame =>
|
||||
frame.Result.Position.X < 0f
|
||||
&& frame.Result.Position.Z <= BSPStepUpFixtures.SphereRadius + 0.05f);
|
||||
Assert.Contains(graph.Frames, frame => frame.Result.InContact);
|
||||
AssertNoLongFrozenStreak(graph.Frames, maximumTicks: 15);
|
||||
Assert.True(graph.Frames[^1].Result.Position.X
|
||||
< graph.Frames[0].Result.Position.X - 0.20f,
|
||||
$"The vertical trace did not descend the roof: " +
|
||||
$"{graph.Frames[0].Result.Position} -> {graph.Frames[^1].Result.Position}.");
|
||||
|
||||
Plane slope = BSPStepUpFixtures.SlopedUnwalkable().Resolved[
|
||||
BSPStepUpFixtures.SlopedUnwalkable_SlopeId].Plane;
|
||||
|
|
@ -272,6 +274,69 @@ public sealed class RetailEdgeResponseOrderingTests
|
|||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-0.30f, 0f, "downhill")]
|
||||
[InlineData( 0.30f, 0f, "uphill")]
|
||||
[InlineData( 0f, 0.30f, "tangential")]
|
||||
public void MultiFrameSteepRoof_DirectionalMotion_RemainsExactAndPreservesRetailResponse(
|
||||
float velocityX,
|
||||
float velocityY,
|
||||
string direction)
|
||||
{
|
||||
Vector2 horizontalVelocity = new(velocityX, velocityY);
|
||||
TraceRun graph = RunSteepRoofTrace(preparedFlat: false, horizontalVelocity);
|
||||
TraceRun flat = RunSteepRoofTrace(preparedFlat: true, horizontalVelocity);
|
||||
|
||||
AssertTraceParity(graph, flat);
|
||||
Assert.Contains(graph.Frames, frame => frame.Result.InContact);
|
||||
AssertNoLongFrozenStreak(graph.Frames, maximumTicks: 15);
|
||||
|
||||
Vector3 first = graph.Frames[0].Result.Position;
|
||||
Vector3 last = graph.Frames[^1].Result.Position;
|
||||
Vector2 progress = new(last.X - first.X, last.Y - first.Y);
|
||||
if (direction == "uphill")
|
||||
{
|
||||
int contactFrame = graph.Frames.FindIndex(frame => frame.Result.InContact);
|
||||
Assert.True(contactFrame >= 0);
|
||||
float peakAfterContact = graph.Frames
|
||||
.GetRange(contactFrame, graph.Frames.Count - contactFrame)
|
||||
.Max(frame => frame.Result.Position.Z);
|
||||
Assert.True(peakAfterContact <= graph.Frames[contactFrame].Result.Position.Z + 0.001f,
|
||||
$"The uphill trace launched/bounced from the roof: " +
|
||||
$"contactZ={graph.Frames[contactFrame].Result.Position.Z}, peak={peakAfterContact}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True(Vector2.Dot(progress, Vector2.Normalize(horizontalVelocity)) > 0.10f,
|
||||
$"The {direction} trace lost requested progress: {first} -> {last}.");
|
||||
}
|
||||
|
||||
Plane slope = BSPStepUpFixtures.SlopedUnwalkable().Resolved[
|
||||
BSPStepUpFixtures.SlopedUnwalkable_SlopeId].Plane;
|
||||
float radius = BSPStepUpFixtures.SphereRadius;
|
||||
for (int i = 0; i < graph.Frames.Count; i++)
|
||||
{
|
||||
Vector3 position = graph.Frames[i].Result.Position;
|
||||
AssertFinite(position, $"steep-roof {direction} frame {i}");
|
||||
if (i > 0)
|
||||
{
|
||||
float distance = Vector3.Distance(
|
||||
graph.Frames[i - 1].Result.Position,
|
||||
position);
|
||||
Assert.InRange(distance, 0f, 1.1f);
|
||||
}
|
||||
|
||||
if (position.X is >= 0f and <= 1f && MathF.Abs(position.Y) <= 1f)
|
||||
{
|
||||
Vector3 footCenter = position + new Vector3(0f, 0f, radius);
|
||||
float signedDistance = Vector3.Dot(slope.Normal, footCenter) + slope.D;
|
||||
Assert.True(signedDistance >= radius - 0.015f,
|
||||
$"Steep-roof {direction} penetration at frame {i}: " +
|
||||
$"distance={signedDistance}, radius={radius}, position={position}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiFrameFlatRoofLedge_GraphAndFlatTraversalRemainExactAndSlideAlongEdge()
|
||||
{
|
||||
|
|
@ -325,7 +390,7 @@ public sealed class RetailEdgeResponseOrderingTests
|
|||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void MultiFrameGroundedFloorWallSlide_FinalPlacementIsMandatoryAndGraphFlatExact(
|
||||
public void MultiFrameGroundedFloorWallSlide_InwardTangentialMotionIsGraphFlatExact(
|
||||
bool twoSpheres)
|
||||
{
|
||||
WallMaintenanceTrace graph = RunGroundedFloorWallSlide(
|
||||
|
|
@ -391,14 +456,22 @@ public sealed class RetailEdgeResponseOrderingTests
|
|||
new(-2f, 2f, 0f),
|
||||
];
|
||||
|
||||
private static TraceRun RunSteepRoofTrace(bool preparedFlat)
|
||||
private static TraceRun RunSteepRoofTrace(
|
||||
bool preparedFlat,
|
||||
Vector2 horizontalVelocity)
|
||||
{
|
||||
var fixture = BSPStepUpFixtures.SlopedUnwalkable();
|
||||
PhysicsEngine engine = BuildCollisionEngine(fixture, preparedFlat, 0x0100E101u);
|
||||
float radius = BSPStepUpFixtures.SphereRadius;
|
||||
const float dt = 1f / 30f;
|
||||
const float gravity = -9.8f;
|
||||
var body = new PhysicsBody { TransientState = TransientStateFlags.Active };
|
||||
var body = new PhysicsBody
|
||||
{
|
||||
Position = new Vector3(0.5f, 0f, 3f),
|
||||
Orientation = Quaternion.Identity,
|
||||
State = PhysicsStateFlags.Gravity | PhysicsStateFlags.ReportCollisions,
|
||||
TransientState = TransientStateFlags.Active,
|
||||
};
|
||||
Vector3 position = new(0.5f, 0f, 3f);
|
||||
float velocityZ = 0f;
|
||||
var trace = new List<TraceFrame>(90);
|
||||
|
|
@ -406,15 +479,19 @@ public sealed class RetailEdgeResponseOrderingTests
|
|||
for (int tick = 0; tick < 90; tick++)
|
||||
{
|
||||
velocityZ += gravity * dt;
|
||||
body.Velocity = new Vector3(
|
||||
horizontalVelocity.X,
|
||||
horizontalVelocity.Y,
|
||||
velocityZ);
|
||||
ResolveResult result = engine.ResolveWithTransition(
|
||||
position,
|
||||
position + new Vector3(0f, 0f, velocityZ * dt),
|
||||
position + body.Velocity * dt,
|
||||
Cell,
|
||||
radius,
|
||||
radius * 2f,
|
||||
stepUpHeight: 0.30f,
|
||||
stepDownHeight: 0.04f,
|
||||
isOnGround: false,
|
||||
isOnGround: body.OnWalkable,
|
||||
body,
|
||||
ObjectInfoState.IsPlayer | ObjectInfoState.EdgeSlide,
|
||||
movingEntityId: 0x01000000u);
|
||||
|
|
@ -422,7 +499,13 @@ public sealed class RetailEdgeResponseOrderingTests
|
|||
position = result.Position;
|
||||
body.Position = position;
|
||||
if (result.IsOnGround)
|
||||
{
|
||||
velocityZ = 0f;
|
||||
body.Velocity = new Vector3(
|
||||
horizontalVelocity.X,
|
||||
horizontalVelocity.Y,
|
||||
0f);
|
||||
}
|
||||
ApplyContactResult(body, result);
|
||||
trace.Add(CaptureFrame(result, body));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue