some more collision fast paths

main
cosmonaut 2022-04-05 17:31:27 -07:00
parent f8146b799a
commit 35ded250ed
2 changed files with 63 additions and 4 deletions

View File

@ -13,23 +13,41 @@ namespace MoonWorks.Collision
public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)
{ {
if (shapeA is Rectangle rectangleA && shapeB is Rectangle rectangleB && transformA.Rotation == 0 && transformB.Rotation == 0) // If we can use a fast path check, let's do that!
if (shapeA is Rectangle rectangleA && shapeB is Rectangle rectangleB && transformA.IsAxisAligned && transformB.IsAxisAligned)
{ {
return TestRectangleOverlap(rectangleA, transformA, rectangleB, transformB); return TestRectangleOverlap(rectangleA, transformA, rectangleB, transformB);
} }
else if (shapeA is Point && shapeB is Rectangle && transformB.Rotation == 0) else if (shapeA is Point && shapeB is Rectangle && transformB.IsAxisAligned)
{ {
return TestPointRectangleOverlap((Point) shapeA, transformA, (Rectangle) shapeB, transformB); return TestPointRectangleOverlap((Point) shapeA, transformA, (Rectangle) shapeB, transformB);
} }
else if (shapeA is Rectangle && shapeB is Point && transformA.Rotation == 0) else if (shapeA is Rectangle && shapeB is Point && transformA.IsAxisAligned)
{ {
return TestPointRectangleOverlap((Point) shapeB, transformB, (Rectangle) shapeA, transformA); return TestPointRectangleOverlap((Point) shapeB, transformB, (Rectangle) shapeA, transformA);
} }
else if (shapeA is Circle circleA && shapeB is Circle circleB && transformA.Scale.X == transformA.Scale.Y && transformB.Scale.X == transformB.Scale.Y) else if (shapeA is Rectangle && shapeB is Circle && transformA.IsAxisAligned && transformB.IsUniformScale)
{
return TestCircleRectangleOverlap((Circle) shapeB, transformB, (Rectangle) shapeA, transformA);
}
else if (shapeA is Circle && shapeB is Rectangle && transformA.IsUniformScale && transformB.IsAxisAligned)
{
return TestCircleRectangleOverlap((Circle) shapeA, transformA, (Rectangle) shapeB, transformB);
}
else if (shapeA is Circle && shapeB is Point && transformA.IsUniformScale)
{
return TestCirclePointOverlap((Circle) shapeA, transformA, (Point) shapeB, transformB);
}
else if (shapeA is Point && shapeB is Circle && transformB.IsUniformScale)
{
return TestCirclePointOverlap((Circle) shapeB, transformB, (Point) shapeA, transformA);
}
else if (shapeA is Circle circleA && shapeB is Circle circleB && transformA.IsUniformScale && transformB.IsUniformScale)
{ {
return TestCircleOverlap(circleA, transformA, circleB, transformB); return TestCircleOverlap(circleA, transformA, circleB, transformB);
} }
// Sad, we can't do a fast path optimization. Time for a simplex reduction.
return FindCollisionSimplex(shapeA, transformA, shapeB, transformB).Item1; return FindCollisionSimplex(shapeA, transformA, shapeB, transformB).Item1;
} }
@ -49,6 +67,36 @@ namespace MoonWorks.Collision
return transformedPoint.X > AABB.Left && transformedPoint.X < AABB.Right && transformedPoint.Y < AABB.Bottom && transformedPoint.Y > AABB.Top; return transformedPoint.X > AABB.Left && transformedPoint.X < AABB.Right && transformedPoint.Y < AABB.Bottom && transformedPoint.Y > AABB.Top;
} }
public static bool TestCirclePointOverlap(Circle circle, Transform2D circleTransform, Point point, Transform2D pointTransform)
{
var circleCenter = circleTransform.Position;
var circleRadius = circle.Radius * circleTransform.Scale.X;
var distanceX = circleCenter.X - pointTransform.Position.X;
var distanceY = circleCenter.Y - pointTransform.Position.Y;
return (distanceX * distanceX) + (distanceY * distanceY) < (circleRadius * circleRadius);
}
/// <summary>
/// NOTE: The rectangle must be axis aligned, and the scaling of the circle must be uniform.
/// </summary>
public static bool TestCircleRectangleOverlap(Circle circle, Transform2D circleTransform, Rectangle rectangle, Transform2D rectangleTransform)
{
var circleCenter = circleTransform.Position;
var circleRadius = circle.Radius * circleTransform.Scale.X;
var AABB = rectangle.TransformedAABB(rectangleTransform);
var closestX = Math.MathHelper.Clamp(circleCenter.X, AABB.Left, AABB.Right);
var closestY = Math.MathHelper.Clamp(circleCenter.Y, AABB.Top, AABB.Bottom);
var distanceX = circleCenter.X - closestX;
var distanceY = circleCenter.Y - closestY;
var distanceSquared = (distanceX * distanceX) + (distanceY * distanceY);
return distanceSquared < (circleRadius * circleRadius);
}
public static bool TestCircleOverlap(Circle circleA, Transform2D transformA, Circle circleB, Transform2D transformB) public static bool TestCircleOverlap(Circle circleA, Transform2D transformA, Circle circleB, Transform2D transformB)
{ {
var radiusA = circleA.Radius * transformA.Scale.X; var radiusA = circleA.Radius * transformA.Scale.X;

View File

@ -8,6 +8,17 @@ namespace MoonWorks.Math
public Matrix3x2 TransformMatrix { get; } public Matrix3x2 TransformMatrix { get; }
public bool IsAxisAligned => Rotation % MathHelper.PiOver2 == 0;
public bool IsUniformScale => Scale.X == Scale.Y;
public Transform2D()
{
Position = Vector2.Zero;
Rotation = 0;
Scale = Vector2.One;
TransformMatrix = CreateTransformMatrix(Position, Rotation, Scale);
}
public Transform2D(Vector2 position) public Transform2D(Vector2 position)
{ {
Position = position; Position = position;