diff --git a/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs b/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs index 56206d13..4193d674 100644 --- a/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs +++ b/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs @@ -311,8 +311,7 @@ namespace MonoGame.Extended.Collisions private static Vector2 PenetrationVector(RectangleF rectangleA, OrientedRectangle orientedRectangleB) { - return new Vector2(); - throw new NotImplementedException(); + return PenetrationVector((OrientedRectangle)rectangleA, orientedRectangleB); } private static Vector2 PenetrationVector(OrientedRectangle orientedRectangleA, CircleF circleB) @@ -327,8 +326,8 @@ namespace MonoGame.Extended.Collisions private static Vector2 PenetrationVector(OrientedRectangle orientedRectangleA, OrientedRectangle orientedRectangleB) { - return new Vector2(); - throw new NotImplementedException(); + return OrientedRectangle.Intersects(orientedRectangleA, orientedRectangleB) + .MinimumTranslationVector; } #endregion diff --git a/src/cs/MonoGame.Extended/Math/OrientedRectangle.cs b/src/cs/MonoGame.Extended/Math/OrientedRectangle.cs index d4057704..1439e9f7 100644 --- a/src/cs/MonoGame.Extended/Math/OrientedRectangle.cs +++ b/src/cs/MonoGame.Extended/Math/OrientedRectangle.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using Microsoft.Xna.Framework; +using SharpDX.Direct3D; namespace MonoGame.Extended { @@ -180,71 +181,108 @@ namespace MonoGame.Extended } /// - /// See https://www.flipcode.com/archives/2D_OBB_Intersection.shtml + /// See: + /// https://www.flipcode.com/archives/2D_OBB_Intersection.shtml + /// https://dyn4j.org/2010/01/sat /// /// /// /// /// - public static bool Intersects(OrientedRectangle rectangle, OrientedRectangle other) + public static (bool Intersects, Vector2 MinimumTranslationVector) Intersects( + OrientedRectangle rectangle, OrientedRectangle other) { var corners = rectangle.Points; var otherCorners = other.Points; - return IntersectsOneWay(corners, otherCorners) && IntersectsOneWay(otherCorners, corners); - bool IntersectsOneWay(IReadOnlyList source, IReadOnlyList target) + var allAxis = new[] { - var axis = new[] - { - source[1] - source[0], - source[3] - source[0] - }; - var origin = new float[2]; + corners[1] - corners[0], + corners[3] - corners[0], + otherCorners[1] - otherCorners[0], + otherCorners[3] - otherCorners[0], + }; + var normalizedAxis = new[] + { + allAxis[0], + allAxis[1], + allAxis[2], + allAxis[3] + }; + var overlap = 0f; + var minimumTranslationVector = Vector2.Zero; - // Make the length of each axis 1/edge length so we know any - // dot product must be less than 1 to fall within the edge. - for (var a = 0; a < 2; a++) + // Make the length of each axis 1/edge length, so we know any + // dot product must be less than 1 to fall within the edge. + for (var a = 0; a < normalizedAxis.Length; a++) + { + normalizedAxis[a] /= normalizedAxis[a].LengthSquared(); + } + + for (var a = 0; a < normalizedAxis.Length; a++) + { + var axisProjectedOnto = normalizedAxis[a]; + var originalAxis = allAxis[a]; + + var p1 = Project(corners, axisProjectedOnto); + var p2 = Project(otherCorners, axisProjectedOnto); + + if (!IsOverlapping(p1, p2)) { - axis[a] /= axis[a].LengthSquared(); - origin[a] = source[0].Dot(axis[a]); + // There was no intersection along this dimension; + // the boxes cannot possibly overlap. + return (false, Vector2.Zero); } - for (var a = 0; a < 2; a++) { - - var t = target[0].Dot(axis[a]); - - // Find the extent of box 2 on axis a - var tMin = t; - var tMax = t; - - for (var c = 1; c < 4; c++) + var o = GetOverlap(p1, p2); + if (o < overlap || overlap == 0f) + { + overlap = o; + minimumTranslationVector = originalAxis * overlap; + if (p1.Min > p2.Min) { - t = target[c].Dot(axis[a]); - - if (t < tMin) - { - tMin = t; - } - else if (t > tMax) - { - tMax = t; - } + minimumTranslationVector = -minimumTranslationVector; } + } + } - // We have to subtract off the origin + // There was no dimension along which there is no intersection. + // Therefore, the boxes overlap. + return (true, minimumTranslationVector); - // See if [tMin, tMax] intersects [0, 1] - if (tMin > 1 + origin[a] || tMax < origin[a]) + (float Min, float Max) Project(IReadOnlyList vertices, Vector2 axis) + { + var t = vertices[0].Dot(axis); + + // Find the extent of box 2 on axis a + var min = t; + var max = t; + + for (var c = 1; c < 4; c++) + { + t = vertices[c].Dot(axis); + + if (t < min) { - // There was no intersection along this dimension; - // the boxes cannot possibly overlap. - return false; + min = t; + } + else if (t > max) + { + max = t; } } - // There was no dimension along which there is no intersection. - // Therefore the boxes overlap. - return true; + return (min, max); + } + + bool IsOverlapping((float Min, float Max) p1, (float Min, float Max) p2) + { + return p1.Min <= p2.Max && p1.Max >= p2.Min; + } + + float GetOverlap((float Min, float Max) p1, (float Min, float Max) p2) + { + return Math.Min(p1.Max, p2.Max) - Math.Max(p1.Min, p2.Min); } } diff --git a/src/cs/MonoGame.Extended/Math/ShapeF.cs b/src/cs/MonoGame.Extended/Math/ShapeF.cs index 274a0a40..4ffe452a 100644 --- a/src/cs/MonoGame.Extended/Math/ShapeF.cs +++ b/src/cs/MonoGame.Extended/Math/ShapeF.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.Xna.Framework; namespace MonoGame.Extended { @@ -42,12 +43,12 @@ namespace MonoGame.Extended RectangleF rectangleA when b is CircleF circleB => Intersects(circleB, rectangleA), RectangleF rectangleA when b is RectangleF rectangleB => Intersects(rectangleA, rectangleB), - RectangleF rectangleA when b is OrientedRectangle orientedRectangleB => Intersects(rectangleA, orientedRectangleB), + RectangleF rectangleA when b is OrientedRectangle orientedRectangleB => Intersects(rectangleA, orientedRectangleB).Intersects, OrientedRectangle orientedRectangleA when b is CircleF circleB => Intersects(circleB, orientedRectangleA), - OrientedRectangle orientedRectangleA when b is RectangleF rectangleB => Intersects(rectangleB, orientedRectangleA), + OrientedRectangle orientedRectangleA when b is RectangleF rectangleB => Intersects(rectangleB, orientedRectangleA).Intersects, OrientedRectangle orientedRectangleA when b is OrientedRectangle orientedRectangleB - => OrientedRectangle.Intersects(orientedRectangleA, orientedRectangleB), + => OrientedRectangle.Intersects(orientedRectangleA, orientedRectangleB).Intersects, _ => throw new ArgumentOutOfRangeException(nameof(a)) }; @@ -86,7 +87,7 @@ namespace MonoGame.Extended /// /// /// True if objects are intersecting, otherwise false. - public static bool Intersects(RectangleF rectangleF, OrientedRectangle orientedRectangle) + public static (bool Intersects, Vector2 MinimumTranslationVector) Intersects(RectangleF rectangleF, OrientedRectangle orientedRectangle) { return OrientedRectangle.Intersects(orientedRectangle, (OrientedRectangle)rectangleF); }