// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #if USE_LARGE_WORLDS using Real = System.Double; #else using Real = System.Single; #endif // ----------------------------------------------------------------------------- // Original code from SharpDX project. https://github.com/sharpdx/SharpDX/ // Greetings to Alexandre Mutel. Original code published with the following license: // ----------------------------------------------------------------------------- // Copyright (c) 2010-2014 SharpDX - Alexandre Mutel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // ----------------------------------------------------------------------------- // Original code from SlimMath project. http://code.google.com/p/slimmath/ // Greetings to SlimDX Group. Original code published with the following license: // ----------------------------------------------------------------------------- /* * Copyright (c) 2007-2011 SlimDX Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ using System; namespace FlaxEngine { /// /// Describes how one bounding volume contains another. /// public enum ContainmentType { /// /// The two bounding volumes don't intersect at all. /// Disjoint, /// /// One bounding volume completely contains another. /// Contains, /// /// The two bounding volumes overlap. /// Intersects } /// /// Describes the result of an intersection with a plane in three dimensions. /// public enum PlaneIntersectionType { /// /// The object is behind the plane. /// Back, /// /// The object is in front of the plane. /// Front, /// /// The object is intersecting the plane. /// Intersecting } /* * This class is organized so that the least complex objects come first so that the least * complex objects will have the most methods in most cases. Note that not all shapes exist * at this time and not all shapes have a corresponding struct. Only the objects that have * a corresponding struct should come first in naming and in parameter order. The order of * complexity is as follows: * * 1. Point * 2. Ray * 3. Segment * 4. Plane * 5. Triangle * 6. Polygon * 7. Box * 8. Sphere * 9. Ellipsoid * 10. Cylinder * 11. Cone * 12. Capsule * 13. Torus * 14. Polyhedron * 15. Frustum */ /// /// Contains static methods to help in determining intersections, containment, etc. /// public static class CollisionsHelper { /// /// Determines the closest point between a point and a line segment. /// /// The point to test. /// The line first point. /// The line second point. /// When the method completes, contains the closest point between the two objects. public static void ClosestPointPointLine(ref Float2 point, ref Float2 p0, ref Float2 p1, out Float2 result) { var p = point - p0; var n = p1 - p0; float length = n.Length; if (length < 1e-10) { // Both points are the same, just give any result = p0; return; } n /= length; float dot = Float2.Dot(n, p); if (dot <= 0.0) { // Before first point result = p0; } else if (dot >= length) { // After first point result = p1; } else { // Inside result = p0 + n * dot; } } /// /// Determines the closest point between a point and a line. /// /// The point to test. /// The line first point. /// The line second point. /// When the method completes, contains the closest point between the two objects. public static void ClosestPointPointLine(ref Vector3 point, ref Vector3 p0, ref Vector3 p1, out Vector3 result) { Vector3 p = point - p0; Vector3 n = p1 - p0; Real length = n.Length; if (length < 1e-10f) { result = p0; return; } n /= length; Real dot = Vector3.Dot(ref n, ref p); if (dot <= 0.0f) { result = p0; return; } if (dot >= length) { result = p1; return; } result = p0 + n * dot; } /// /// Determines the closest point between a point and a triangle. /// /// The point to test. /// The first vertex to test. /// The second vertex to test. /// The third vertex to test. /// When the method completes, contains the closest point between the two objects. public static void ClosestPointPointTriangle(ref Vector3 point, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3, out Vector3 result) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 136 //Check if P in vertex region outside A Vector3 ab = vertex2 - vertex1; Vector3 ac = vertex3 - vertex1; Vector3 ap = point - vertex1; Real d1 = Vector3.Dot(ab, ap); Real d2 = Vector3.Dot(ac, ap); if ((d1 <= 0.0f) && (d2 <= 0.0f)) { result = vertex1; //Barycentric coordinates (1,0,0) return; } //Check if P in vertex region outside B Vector3 bp = point - vertex2; Real d3 = Vector3.Dot(ab, bp); Real d4 = Vector3.Dot(ac, bp); if ((d3 >= 0.0f) && (d4 <= d3)) { result = vertex2; // Barycentric coordinates (0,1,0) return; } //Check if P in edge region of AB, if so return projection of P onto AB Real vc = d1 * d4 - d3 * d2; if ((vc <= 0.0f) && (d1 >= 0.0f) && (d3 <= 0.0f)) { Real v = d1 / (d1 - d3); result = vertex1 + v * ab; //Barycentric coordinates (1-v,v,0) return; } //Check if P in vertex region outside C Vector3 cp = point - vertex3; Real d5 = Vector3.Dot(ab, cp); Real d6 = Vector3.Dot(ac, cp); if ((d6 >= 0.0f) && (d5 <= d6)) { result = vertex3; //Barycentric coordinates (0,0,1) return; } //Check if P in edge region of AC, if so return projection of P onto AC Real vb = d5 * d2 - d1 * d6; if ((vb <= 0.0f) && (d2 >= 0.0f) && (d6 <= 0.0f)) { Real w = d2 / (d2 - d6); result = vertex1 + w * ac; //Barycentric coordinates (1-w,0,w) return; } //Check if P in edge region of BC, if so return projection of P onto BC Real va = d3 * d6 - d5 * d4; if ((va <= 0.0f) && (d4 - d3 >= 0.0f) && (d5 - d6 >= 0.0f)) { Real w = (d4 - d3) / (d4 - d3 + (d5 - d6)); result = vertex2 + w * (vertex3 - vertex2); //Barycentric coordinates (0,1-w,w) return; } //P inside face region. Compute Q through its Barycentric coordinates (u,v,w) Real denom = 1.0f / (va + vb + vc); Real v2 = vb * denom; Real w2 = vc * denom; result = vertex1 + ab * v2 + ac * w2; //= u*vertex1 + v*vertex2 + w*vertex3, u = va * denom = 1.0f - v - w } /// /// Determines the closest point between a and a point. /// /// The plane to test. /// The point to test. /// When the method completes, contains the closest point between the two objects. public static void ClosestPointPlanePoint(ref Plane plane, ref Vector3 point, out Vector3 result) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 126 Vector3.Dot(ref plane.Normal, ref point, out Real dot); Real t = dot - plane.D; result = point - t * plane.Normal; } /// /// Determines the closest point between a and a point. /// /// The box to test. /// The point to test. /// When the method completes, contains the closest point between the two objects. public static void ClosestPointBoxPoint(ref BoundingBox box, ref Vector3 point, out Vector3 result) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 130 Vector3.Max(ref point, ref box.Minimum, out Vector3 temp); Vector3.Min(ref temp, ref box.Maximum, out result); } /// /// Determines the closest point between a and a point. /// /// The rectangle to test. /// The point to test. /// When the method completes, contains the closest point between the two objects. public static void ClosestPointRectanglePoint(ref Rectangle rect, ref Float2 point, out Float2 result) { Float2 end = rect.Location + rect.Size; Float2.Max(ref point, ref rect.Location, out var temp); Float2.Min(ref temp, ref end, out result); } /// /// Determines the closest point between a and a point. /// /// /// The point to test. /// When the method completes, contains the closest point between the two objects; or, if the point is directly in the center of the sphere, contains . /// public static void ClosestPointSpherePoint(ref BoundingSphere sphere, ref Vector3 point, out Vector3 result) { //Source: Jorgy343 //Reference: None //Get the unit direction from the sphere's center to the point. Vector3.Subtract(ref point, ref sphere.Center, out result); result.Normalize(); //Multiply the unit direction by the sphere's radius to get a vector //the length of the sphere. result *= sphere.Radius; //Add the sphere's center to the direction to get a point on the sphere. result += sphere.Center; } /// /// Determines the closest point between a and a . /// /// The first sphere to test. /// The second sphere to test. /// When the method completes, contains the closest point between the two objects; or, if the point is directly in the center of the sphere, contains . /// /// If the two spheres are overlapping, but not directly on top of each other, the closest point /// is the 'closest' point of intersection. This can also be considered is the deepest point of /// intersection. /// public static void ClosestPointSphereSphere(ref BoundingSphere sphere1, ref BoundingSphere sphere2, out Vector3 result) { //Source: Jorgy343 //Reference: None //Get the unit direction from the first sphere's center to the second sphere's center. Vector3.Subtract(ref sphere2.Center, ref sphere1.Center, out result); result.Normalize(); //Multiply the unit direction by the first sphere's radius to get a vector //the length of the first sphere. result *= sphere1.Radius; //Add the first sphere's center to the direction to get a point on the first sphere. result += sphere1.Center; } /// /// Determines the distance between a and a point. /// /// The plane to test. /// The point to test. /// The distance between the two objects. public static Real DistancePlanePoint(ref Plane plane, ref Vector3 point) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 127 Vector3.Dot(ref plane.Normal, ref point, out Real dot); return dot - plane.D; } /// /// Determines the distance between a and a point. /// /// The box to test. /// The point to test. /// The distance between the two objects. public static Real DistanceBoxPoint(ref BoundingBox box, ref Vector3 point) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 131 Real distance = 0f; if (point.X < box.Minimum.X) distance += (box.Minimum.X - point.X) * (box.Minimum.X - point.X); if (point.X > box.Maximum.X) distance += (point.X - box.Maximum.X) * (point.X - box.Maximum.X); if (point.Y < box.Minimum.Y) distance += (box.Minimum.Y - point.Y) * (box.Minimum.Y - point.Y); if (point.Y > box.Maximum.Y) distance += (point.Y - box.Maximum.Y) * (point.Y - box.Maximum.Y); if (point.Z < box.Minimum.Z) distance += (box.Minimum.Z - point.Z) * (box.Minimum.Z - point.Z); if (point.Z > box.Maximum.Z) distance += (point.Z - box.Maximum.Z) * (point.Z - box.Maximum.Z); return (Real)Math.Sqrt(distance); } /// /// Determines the distance between a and a . /// /// The first box to test. /// The second box to test. /// The distance between the two objects. public static Real DistanceBoxBox(ref BoundingBox box1, ref BoundingBox box2) { //Source: //Reference: Real distance = 0f; //Distance for X. if (box1.Minimum.X > box2.Maximum.X) { Real delta = box2.Maximum.X - box1.Minimum.X; distance += delta * delta; } else if (box2.Minimum.X > box1.Maximum.X) { Real delta = box1.Maximum.X - box2.Minimum.X; distance += delta * delta; } //Distance for Y. if (box1.Minimum.Y > box2.Maximum.Y) { Real delta = box2.Maximum.Y - box1.Minimum.Y; distance += delta * delta; } else if (box2.Minimum.Y > box1.Maximum.Y) { Real delta = box1.Maximum.Y - box2.Minimum.Y; distance += delta * delta; } //Distance for Z. if (box1.Minimum.Z > box2.Maximum.Z) { Real delta = box2.Maximum.Z - box1.Minimum.Z; distance += delta * delta; } else if (box2.Minimum.Z > box1.Maximum.Z) { Real delta = box1.Maximum.Z - box2.Minimum.Z; distance += delta * delta; } return (Real)Math.Sqrt(distance); } /// /// Determines the distance between a and a point. /// /// The sphere to test. /// The point to test. /// The distance between the two objects. public static Real DistanceSpherePoint(ref BoundingSphere sphere, ref Vector3 point) { //Source: Jorgy343 //Reference: None Vector3.Distance(ref sphere.Center, ref point, out Real distance); distance -= sphere.Radius; return Math.Max(distance, 0f); } /// /// Determines the distance between a and a . /// /// The first sphere to test. /// The second sphere to test. /// The distance between the two objects. public static Real DistanceSphereSphere(ref BoundingSphere sphere1, ref BoundingSphere sphere2) { //Source: Jorgy343 //Reference: None Vector3.Distance(ref sphere1.Center, ref sphere2.Center, out Real distance); distance -= sphere1.Radius + sphere2.Radius; return Math.Max(distance, 0f); } /// /// Determines whether there is an intersection between a and a point. /// /// The ray to test. /// The point to test. /// Whether the two objects intersect. public static bool RayIntersectsPoint(ref Ray ray, ref Vector3 point) { //Source: RayIntersectsSphere //Reference: None Vector3.Subtract(ref ray.Position, ref point, out Vector3 m); //Same thing as RayIntersectsSphere except that the radius of the sphere (point) is the epsilon for zero. Real b = Vector3.Dot(m, ray.Direction); Real c = Vector3.Dot(m, m) - Mathf.Epsilon; if ((c > 0f) && (b > 0f)) return false; Real discriminant = b * b - c; if (discriminant < 0f) return false; return true; } /// /// Determines whether there is an intersection between a and a . /// /// The first ray to test. /// The second ray to test. /// When the method completes, contains the point of intersection, or if there was no intersection. /// Whether the two objects intersect. /// /// This method performs a ray vs ray intersection test based on the following formula /// from Goldman. /// s = det([o_2 - o_1, d_2, d_1 x d_2]) / ||d_1 x d_2||^2 /// t = det([o_2 - o_1, d_1, d_1 x d_2]) / ||d_1 x d_2||^2 /// Where o_1 is the position of the first ray, o_2 is the position of the second ray, /// d_1 is the normalized direction of the first ray, d_2 is the normalized direction /// of the second ray, det denotes the determinant of a matrix, x denotes the cross /// product, [ ] denotes a matrix, and || || denotes the length or magnitude of a vector. /// public static bool RayIntersectsRay(ref Ray ray1, ref Ray ray2, out Vector3 point) { //Source: Real-Time Rendering, Third Edition //Reference: Page 780 Vector3.Cross(ref ray1.Direction, ref ray2.Direction, out Vector3 cross); Real denominator = cross.Length; //Lines are parallel. if (Mathf.IsZero(denominator)) if (Mathf.NearEqual(ray2.Position.X, ray1.Position.X) && Mathf.NearEqual(ray2.Position.Y, ray1.Position.Y) && Mathf.NearEqual(ray2.Position.Z, ray1.Position.Z)) { point = Vector3.Zero; return true; } denominator *= denominator; //3x3 matrix for the first ray. Real m11 = ray2.Position.X - ray1.Position.X; Real m12 = ray2.Position.Y - ray1.Position.Y; Real m13 = ray2.Position.Z - ray1.Position.Z; Real m21 = ray2.Direction.X; Real m22 = ray2.Direction.Y; Real m23 = ray2.Direction.Z; Real m31 = cross.X; Real m32 = cross.Y; Real m33 = cross.Z; //Determinant of first matrix. Real dets = m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32 - m11 * m23 * m32 - m12 * m21 * m33 - m13 * m22 * m31; //3x3 matrix for the second ray. m21 = ray1.Direction.X; m22 = ray1.Direction.Y; m23 = ray1.Direction.Z; //Determinant of the second matrix. Real dett = m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32 - m11 * m23 * m32 - m12 * m21 * m33 - m13 * m22 * m31; //t values of the point of intersection. Real s = dets / denominator; Real t = dett / denominator; //The points of intersection. Vector3 point1 = ray1.Position + s * ray1.Direction; Vector3 point2 = ray2.Position + t * ray2.Direction; //If the points are not equal, no intersection has occurred. if (!Mathf.NearEqual(point2.X, point1.X) || !Mathf.NearEqual(point2.Y, point1.Y) || !Mathf.NearEqual(point2.Z, point1.Z)) { point = Vector3.Zero; return false; } point = point1; return true; } /// /// Determines whether there is an intersection between a and a . /// /// The ray to test. /// The plane to test. /// When the method completes, contains the distance of the intersection, or 0 if there was no intersection. /// Whether the two objects intersect. public static bool RayIntersectsPlane(ref Ray ray, ref Plane plane, out Real distance) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 175 Vector3.Dot(ref plane.Normal, ref ray.Direction, out Real direction); if (Mathf.IsZero(direction)) { distance = 0f; return false; } Vector3.Dot(ref plane.Normal, ref ray.Position, out Real position); distance = (-plane.D - position) / direction; if (distance < 0f) { distance = 0f; return false; } return true; } #if USE_LARGE_WORLDS /// /// Determines whether there is an intersection between a and a . /// [Deprecated on 26.05.2022, expires on 26.05.2024] /// /// The ray to test. /// The plane to test. /// When the method completes, contains the distance of the intersection, or 0 if there was no intersection. /// Whether the two objects intersect. [Obsolete("Use RayIntersectsPlane with 'out Real distance' parameter instead")] public static bool RayIntersectsPlane(ref Ray ray, ref Plane plane, out float distance) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 175 Vector3.Dot(ref plane.Normal, ref ray.Direction, out Real direction); if (Mathf.IsZero(direction)) { distance = 0f; return false; } Vector3.Dot(ref plane.Normal, ref ray.Position, out Real position); distance = (float)((-plane.D - position) / direction); if (distance < 0f) { distance = 0f; return false; } return true; } #endif /// /// Determines whether there is an intersection between a and a . /// /// The ray to test. /// The plane to test /// When the method completes, contains the point of intersection, or if there was no intersection. /// Whether the two objects intersected. public static bool RayIntersectsPlane(ref Ray ray, ref Plane plane, out Vector3 point) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 175 if (!RayIntersectsPlane(ref ray, ref plane, out Real distance)) { point = Vector3.Zero; return false; } point = ray.Position + ray.Direction * distance; return true; } /// /// Determines whether there is an intersection between a and a triangle. /// /// The ray to test. /// The first vertex of the triangle to test. /// The second vertex of the triangle to test. /// The third vertex of the triangle to test. /// When the method completes, contains the distance of the intersection, or 0 if there was no intersection. /// Whether the two objects intersected. /// /// This method tests if the ray intersects either the front or back of the triangle. /// If the ray is parallel to the triangle's plane, no intersection is assumed to have /// happened. If the intersection of the ray and the triangle is behind the origin of /// the ray, no intersection is assumed to have happened. In both cases of assumptions, /// this method returns false. /// public static bool RayIntersectsTriangle(ref Ray ray, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3, out Real distance) { //Source: Fast Minimum Storage Ray / Triangle Intersection //Reference: http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf //Compute vectors along two edges of the triangle. Vector3 edge1, edge2; //Edge 1 edge1.X = vertex2.X - vertex1.X; edge1.Y = vertex2.Y - vertex1.Y; edge1.Z = vertex2.Z - vertex1.Z; //Edge2 edge2.X = vertex3.X - vertex1.X; edge2.Y = vertex3.Y - vertex1.Y; edge2.Z = vertex3.Z - vertex1.Z; //Cross product of ray direction and edge2 - first part of determinant. Vector3 directioncrossedge2; directioncrossedge2.X = ray.Direction.Y * edge2.Z - ray.Direction.Z * edge2.Y; directioncrossedge2.Y = ray.Direction.Z * edge2.X - ray.Direction.X * edge2.Z; directioncrossedge2.Z = ray.Direction.X * edge2.Y - ray.Direction.Y * edge2.X; //Compute the determinant. Real determinant; //Dot product of edge1 and the first part of determinant. determinant = edge1.X * directioncrossedge2.X + edge1.Y * directioncrossedge2.Y + edge1.Z * directioncrossedge2.Z; //If the ray is parallel to the triangle plane, there is no collision. //This also means that we are not culling, the ray may hit both the //back and the front of the triangle. if (Mathf.IsZero(determinant)) { distance = 0f; return false; } Real inversedeterminant = 1.0f / determinant; //Calculate the U parameter of the intersection point. Vector3 distanceVector; distanceVector.X = ray.Position.X - vertex1.X; distanceVector.Y = ray.Position.Y - vertex1.Y; distanceVector.Z = ray.Position.Z - vertex1.Z; Real triangleU; triangleU = distanceVector.X * directioncrossedge2.X + distanceVector.Y * directioncrossedge2.Y + distanceVector.Z * directioncrossedge2.Z; triangleU *= inversedeterminant; //Make sure it is inside the triangle. if ((triangleU < 0f) || (triangleU > 1f)) { distance = 0f; return false; } //Calculate the V parameter of the intersection point. Vector3 distancecrossedge1; distancecrossedge1.X = distanceVector.Y * edge1.Z - distanceVector.Z * edge1.Y; distancecrossedge1.Y = distanceVector.Z * edge1.X - distanceVector.X * edge1.Z; distancecrossedge1.Z = distanceVector.X * edge1.Y - distanceVector.Y * edge1.X; Real triangleV; triangleV = ray.Direction.X * distancecrossedge1.X + ray.Direction.Y * distancecrossedge1.Y + ray.Direction.Z * distancecrossedge1.Z; triangleV *= inversedeterminant; //Make sure it is inside the triangle. if ((triangleV < 0f) || (triangleU + triangleV > 1f)) { distance = 0f; return false; } //Compute the distance along the ray to the triangle. Real raydistance; raydistance = edge2.X * distancecrossedge1.X + edge2.Y * distancecrossedge1.Y + edge2.Z * distancecrossedge1.Z; raydistance *= inversedeterminant; //Is the triangle behind the ray origin? if (raydistance < 0f) { distance = 0f; return false; } distance = raydistance; return true; } /// /// Determines whether there is an intersection between a and a triangle. /// /// The ray to test. /// The first vertex of the triangle to test. /// The second vertex of the triangle to test. /// The third vertex of the triangle to test. /// When the method completes, contains the point of intersection, /// or if there was no intersection. /// Whether the two objects intersected. public static bool RayIntersectsTriangle(ref Ray ray, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3, out Vector3 point) { if (!RayIntersectsTriangle(ref ray, ref vertex1, ref vertex2, ref vertex3, out Real distance)) { point = Vector3.Zero; return false; } point = ray.Position + ray.Direction * distance; return true; } /// /// Determines whether there is an intersection between a and a . /// /// The ray to test. /// The box to test. /// When the method completes, contains the distance of the intersection, or 0 if there was no intersection. /// /// Whether the two objects intersected. public static bool RayIntersectsBox(ref Ray ray, ref BoundingBox box, out Real distance) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 179 distance = 0f; Real tmax = Real.MaxValue; if (Mathf.IsZero(ray.Direction.X)) { if ((ray.Position.X < box.Minimum.X) || (ray.Position.X > box.Maximum.X)) { distance = 0f; return false; } } else { Real inverse = 1.0f / ray.Direction.X; Real t1 = (box.Minimum.X - ray.Position.X) * inverse; Real t2 = (box.Maximum.X - ray.Position.X) * inverse; if (t1 > t2) { Real temp = t1; t1 = t2; t2 = temp; } distance = Math.Max(t1, distance); tmax = Math.Min(t2, tmax); if (distance > tmax) { distance = 0f; return false; } } if (Mathf.IsZero(ray.Direction.Y)) { if ((ray.Position.Y < box.Minimum.Y) || (ray.Position.Y > box.Maximum.Y)) { distance = 0f; return false; } } else { Real inverse = 1.0f / ray.Direction.Y; Real t1 = (box.Minimum.Y - ray.Position.Y) * inverse; Real t2 = (box.Maximum.Y - ray.Position.Y) * inverse; if (t1 > t2) { Real temp = t1; t1 = t2; t2 = temp; } distance = Math.Max(t1, distance); tmax = Math.Min(t2, tmax); if (distance > tmax) { distance = 0f; return false; } } if (Mathf.IsZero(ray.Direction.Z)) { if ((ray.Position.Z < box.Minimum.Z) || (ray.Position.Z > box.Maximum.Z)) { distance = 0f; return false; } } else { Real inverse = 1.0f / ray.Direction.Z; Real t1 = (box.Minimum.Z - ray.Position.Z) * inverse; Real t2 = (box.Maximum.Z - ray.Position.Z) * inverse; if (t1 > t2) { Real temp = t1; t1 = t2; t2 = temp; } distance = Math.Max(t1, distance); tmax = Math.Min(t2, tmax); if (distance > tmax) { distance = 0f; return false; } } return true; } /// /// Determines whether there is an intersection between a and a . /// /// The ray to test. /// The box to test. /// When the method completes, contains the point of intersection, or if there was no intersection. /// Whether the two objects intersected. public static bool RayIntersectsBox(ref Ray ray, ref BoundingBox box, out Vector3 point) { if (!RayIntersectsBox(ref ray, ref box, out Real distance)) { point = Vector3.Zero; return false; } point = ray.Position + ray.Direction * distance; return true; } /// /// Determines whether there is an intersection between a and a . /// /// The ray to test. /// The sphere to test. /// When the method completes, contains the distance of the intersection, or 0 if there was no intersection. /// Whether the two objects intersected. public static bool RayIntersectsSphere(ref Ray ray, ref BoundingSphere sphere, out Real distance) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 177 Vector3.Subtract(ref ray.Position, ref sphere.Center, out Vector3 m); Real b = Vector3.Dot(m, ray.Direction); Real c = Vector3.Dot(m, m) - sphere.Radius * sphere.Radius; if ((c > 0f) && (b > 0f)) { distance = 0f; return false; } Real discriminant = b * b - c; if (discriminant < 0f) { distance = 0f; return false; } distance = -b - (Real)Math.Sqrt(discriminant); if (distance < 0f) distance = 0f; return true; } /// /// Determines whether there is an intersection between a and a . /// /// The ray to test. /// The sphere to test. /// When the method completes, contains the point of intersection, or if there was no intersection. /// Whether the two objects intersected. public static bool RayIntersectsSphere(ref Ray ray, ref BoundingSphere sphere, out Vector3 point) { if (!RayIntersectsSphere(ref ray, ref sphere, out Real distance)) { point = Vector3.Zero; return false; } point = ray.Position + ray.Direction * distance; return true; } /// /// Determines whether there is an intersection between a and a point. /// /// The plane to test. /// The point to test. /// Whether the two objects intersected. public static PlaneIntersectionType PlaneIntersectsPoint(ref Plane plane, ref Vector3 point) { Vector3.Dot(ref plane.Normal, ref point, out Real distance); distance += plane.D; if (distance > 0f) return PlaneIntersectionType.Front; if (distance < 0f) return PlaneIntersectionType.Back; return PlaneIntersectionType.Intersecting; } /// /// Determines whether there is an intersection between a and a . /// /// The first plane to test. /// The second plane to test. /// Whether the two objects intersected. public static bool PlaneIntersectsPlane(ref Plane plane1, ref Plane plane2) { Vector3.Cross(ref plane1.Normal, ref plane2.Normal, out Vector3 direction); //If direction is the zero vector, the planes are parallel and possibly //coincident. It is not an intersection. The dot product will tell us. Vector3.Dot(ref direction, ref direction, out Real denominator); if (Mathf.IsZero(denominator)) return false; return true; } /// /// Determines whether there is an intersection between a and a . /// /// The first plane to test. /// The second plane to test. /// When the method completes, contains the line of intersection as a , or a zero ray if there was no intersection. /// Whether the two objects intersected. /// /// Although a ray is set to have an origin, the ray returned by this method is really /// a line in three dimensions which has no real origin. The ray is considered valid when /// both the positive direction is used and when the negative direction is used. /// public static bool PlaneIntersectsPlane(ref Plane plane1, ref Plane plane2, out Ray line) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 207 Vector3.Cross(ref plane1.Normal, ref plane2.Normal, out Vector3 direction); //If direction is the zero vector, the planes are parallel and possibly //coincident. It is not an intersection. The dot product will tell us. Vector3.Dot(ref direction, ref direction, out Real denominator); //We assume the planes are normalized, therefore the denominator //only serves as a parallel and coincident check. Otherwise we need //to divide the point by the denominator. if (Mathf.IsZero(denominator)) { line = new Ray(); return false; } Vector3 temp = plane1.D * plane2.Normal - plane2.D * plane1.Normal; Vector3.Cross(ref temp, ref direction, out Vector3 point); line.Position = point; line.Direction = direction; line.Direction.Normalize(); return true; } /// /// Determines whether there is an intersection between a and a triangle. /// /// The plane to test. /// The first vertex of the triangle to test. /// The second vertex of the triangle to test. /// The third vertex of the triangle to test. /// Whether the two objects intersected. public static PlaneIntersectionType PlaneIntersectsTriangle(ref Plane plane, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 207 PlaneIntersectionType test1 = PlaneIntersectsPoint(ref plane, ref vertex1); PlaneIntersectionType test2 = PlaneIntersectsPoint(ref plane, ref vertex2); PlaneIntersectionType test3 = PlaneIntersectsPoint(ref plane, ref vertex3); if ((test1 == PlaneIntersectionType.Front) && (test2 == PlaneIntersectionType.Front) && (test3 == PlaneIntersectionType.Front)) return PlaneIntersectionType.Front; if ((test1 == PlaneIntersectionType.Back) && (test2 == PlaneIntersectionType.Back) && (test3 == PlaneIntersectionType.Back)) return PlaneIntersectionType.Back; return PlaneIntersectionType.Intersecting; } /// /// Determines whether there is an intersection between a and a . /// /// The plane to test. /// The box to test. /// Whether the two objects intersected. public static PlaneIntersectionType PlaneIntersectsBox(ref Plane plane, ref BoundingBox box) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 161 Vector3 min; Vector3 max; max.X = plane.Normal.X >= 0.0f ? box.Minimum.X : box.Maximum.X; max.Y = plane.Normal.Y >= 0.0f ? box.Minimum.Y : box.Maximum.Y; max.Z = plane.Normal.Z >= 0.0f ? box.Minimum.Z : box.Maximum.Z; min.X = plane.Normal.X >= 0.0f ? box.Maximum.X : box.Minimum.X; min.Y = plane.Normal.Y >= 0.0f ? box.Maximum.Y : box.Minimum.Y; min.Z = plane.Normal.Z >= 0.0f ? box.Maximum.Z : box.Minimum.Z; Vector3.Dot(ref plane.Normal, ref max, out Real distance); if (distance + plane.D > 0.0f) return PlaneIntersectionType.Front; distance = Vector3.Dot(plane.Normal, min); if (distance + plane.D < 0.0f) return PlaneIntersectionType.Back; return PlaneIntersectionType.Intersecting; } /// /// Determines whether there is an intersection between a and a . /// /// The plane to test. /// The sphere to test. /// Whether the two objects intersected. public static PlaneIntersectionType PlaneIntersectsSphere(ref Plane plane, ref BoundingSphere sphere) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 160 Vector3.Dot(ref plane.Normal, ref sphere.Center, out Real distance); distance += plane.D; if (distance > sphere.Radius) return PlaneIntersectionType.Front; if (distance < -sphere.Radius) return PlaneIntersectionType.Back; return PlaneIntersectionType.Intersecting; } /* This implementation is wrong /// /// Determines whether there is an intersection between a and a triangle. /// /// The box to test. /// The first vertex of the triangle to test. /// The second vertex of the triangle to test. /// The third vertex of the triangle to test. /// Whether the two objects intersected. public static bool BoxIntersectsTriangle(ref BoundingBox box, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) { if (BoxContainsPoint(ref box, ref vertex1) == ContainmentType.Contains) return true; if (BoxContainsPoint(ref box, ref vertex2) == ContainmentType.Contains) return true; if (BoxContainsPoint(ref box, ref vertex3) == ContainmentType.Contains) return true; return false; } */ /// /// Determines whether there is an intersection between a and a . /// /// The first box to test. /// The second box to test. /// Whether the two objects intersected. public static bool BoxIntersectsBox(ref BoundingBox box1, ref BoundingBox box2) { if ((box1.Minimum.X > box2.Maximum.X) || (box2.Minimum.X > box1.Maximum.X)) return false; if ((box1.Minimum.Y > box2.Maximum.Y) || (box2.Minimum.Y > box1.Maximum.Y)) return false; if ((box1.Minimum.Z > box2.Maximum.Z) || (box2.Minimum.Z > box1.Maximum.Z)) return false; return true; } /// /// Determines whether there is an intersection between a and a . /// /// The box to test. /// The sphere to test. /// Whether the two objects intersected. public static bool BoxIntersectsSphere(ref BoundingBox box, ref BoundingSphere sphere) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 166 Vector3.Clamp(ref sphere.Center, ref box.Minimum, ref box.Maximum, out Vector3 vector); Real distance = Vector3.DistanceSquared(ref sphere.Center, ref vector); return distance <= sphere.Radius * sphere.Radius; } /// /// Determines whether there is an intersection between a and a triangle. /// /// The sphere to test. /// The first vertex of the triangle to test. /// The second vertex of the triangle to test. /// The third vertex of the triangle to test. /// Whether the two objects intersected. public static bool SphereIntersectsTriangle(ref BoundingSphere sphere, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) { //Source: Real-Time Collision Detection by Christer Ericson //Reference: Page 167 ClosestPointPointTriangle(ref sphere.Center, ref vertex1, ref vertex2, ref vertex3, out Vector3 point); Vector3 v = point - sphere.Center; Vector3.Dot(ref v, ref v, out Real dot); return dot <= sphere.Radius * sphere.Radius; } /// /// Determines whether there is an intersection between a and a /// . /// /// First sphere to test. /// Second sphere to test. /// Whether the two objects intersected. public static bool SphereIntersectsSphere(ref BoundingSphere sphere1, ref BoundingSphere sphere2) { Real radiisum = sphere1.Radius + sphere2.Radius; return Vector3.DistanceSquared(ref sphere1.Center, ref sphere2.Center) <= radiisum * radiisum; } /// /// Determines whether a contains a point. /// /// The box to test. /// The point to test. /// The type of containment the two objects have. public static ContainmentType BoxContainsPoint(ref BoundingBox box, ref Vector3 point) { if ((box.Minimum.X <= point.X) && (box.Maximum.X >= point.X) && (box.Minimum.Y <= point.Y) && (box.Maximum.Y >= point.Y) && (box.Minimum.Z <= point.Z) && (box.Maximum.Z >= point.Z)) return ContainmentType.Contains; return ContainmentType.Disjoint; } /* This implementation is wrong /// /// Determines whether a contains a triangle. /// /// The box to test. /// The first vertex of the triangle to test. /// The second vertex of the triangle to test. /// The third vertex of the triangle to test. /// The type of containment the two objects have. public static ContainmentType BoxContainsTriangle(ref BoundingBox box, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) { ContainmentType test1 = BoxContainsPoint(ref box, ref vertex1); ContainmentType test2 = BoxContainsPoint(ref box, ref vertex2); ContainmentType test3 = BoxContainsPoint(ref box, ref vertex3); if (test1 == ContainmentType.Contains && test2 == ContainmentType.Contains && test3 == ContainmentType.Contains) return ContainmentType.Contains; if (test1 == ContainmentType.Contains || test2 == ContainmentType.Contains || test3 == ContainmentType.Contains) return ContainmentType.Intersects; return ContainmentType.Disjoint; } */ /// /// Determines whether a contains a . /// /// The first box to test. /// The second box to test. /// The type of containment the two objects have. public static ContainmentType BoxContainsBox(ref BoundingBox box1, ref BoundingBox box2) { if ((box1.Maximum.X < box2.Minimum.X) || (box1.Minimum.X > box2.Maximum.X)) return ContainmentType.Disjoint; if ((box1.Maximum.Y < box2.Minimum.Y) || (box1.Minimum.Y > box2.Maximum.Y)) return ContainmentType.Disjoint; if ((box1.Maximum.Z < box2.Minimum.Z) || (box1.Minimum.Z > box2.Maximum.Z)) return ContainmentType.Disjoint; if ((box1.Minimum.X <= box2.Minimum.X) && (box2.Maximum.X <= box1.Maximum.X) && (box1.Minimum.Y <= box2.Minimum.Y) && (box2.Maximum.Y <= box1.Maximum.Y) && (box1.Minimum.Z <= box2.Minimum.Z) && (box2.Maximum.Z <= box1.Maximum.Z)) return ContainmentType.Contains; return ContainmentType.Intersects; } /// /// Determines whether a contains a . /// /// The box to test. /// The sphere to test. /// The type of containment the two objects have. public static ContainmentType BoxContainsSphere(ref BoundingBox box, ref BoundingSphere sphere) { Vector3.Clamp(ref sphere.Center, ref box.Minimum, ref box.Maximum, out Vector3 vector); Real distance = Vector3.DistanceSquared(ref sphere.Center, ref vector); if (distance > sphere.Radius * sphere.Radius) return ContainmentType.Disjoint; if ((box.Minimum.X + sphere.Radius <= sphere.Center.X) && (sphere.Center.X <= box.Maximum.X - sphere.Radius) && (box.Maximum.X - box.Minimum.X > sphere.Radius) && (box.Minimum.Y + sphere.Radius <= sphere.Center.Y) && (sphere.Center.Y <= box.Maximum.Y - sphere.Radius) && (box.Maximum.Y - box.Minimum.Y > sphere.Radius) && (box.Minimum.Z + sphere.Radius <= sphere.Center.Z) && (sphere.Center.Z <= box.Maximum.Z - sphere.Radius) && (box.Maximum.Z - box.Minimum.Z > sphere.Radius)) return ContainmentType.Contains; return ContainmentType.Intersects; } /// /// Determines whether a contains a point. /// /// The sphere to test. /// The point to test. /// The type of containment the two objects have. public static ContainmentType SphereContainsPoint(ref BoundingSphere sphere, ref Vector3 point) { if (Vector3.DistanceSquared(ref point, ref sphere.Center) <= sphere.Radius * sphere.Radius) return ContainmentType.Contains; return ContainmentType.Disjoint; } /// /// Determines whether a contains a triangle. /// /// The sphere to test. /// The first vertex of the triangle to test. /// The second vertex of the triangle to test. /// The third vertex of the triangle to test. /// The type of containment the two objects have. public static ContainmentType SphereContainsTriangle(ref BoundingSphere sphere, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) { //Source: Jorgy343 //Reference: None ContainmentType test1 = SphereContainsPoint(ref sphere, ref vertex1); ContainmentType test2 = SphereContainsPoint(ref sphere, ref vertex2); ContainmentType test3 = SphereContainsPoint(ref sphere, ref vertex3); if ((test1 == ContainmentType.Contains) && (test2 == ContainmentType.Contains) && (test3 == ContainmentType.Contains)) return ContainmentType.Contains; if (SphereIntersectsTriangle(ref sphere, ref vertex1, ref vertex2, ref vertex3)) return ContainmentType.Intersects; return ContainmentType.Disjoint; } /// /// Determines whether a contains a . /// /// The sphere to test. /// The box to test. /// The type of containment the two objects have. public static ContainmentType SphereContainsBox(ref BoundingSphere sphere, ref BoundingBox box) { Vector3 vector; if (!BoxIntersectsSphere(ref box, ref sphere)) return ContainmentType.Disjoint; Real radiusSquared = sphere.Radius * sphere.Radius; vector.X = sphere.Center.X - box.Minimum.X; vector.Y = sphere.Center.Y - box.Maximum.Y; vector.Z = sphere.Center.Z - box.Maximum.Z; if (vector.LengthSquared > radiusSquared) return ContainmentType.Intersects; vector.X = sphere.Center.X - box.Maximum.X; vector.Y = sphere.Center.Y - box.Maximum.Y; vector.Z = sphere.Center.Z - box.Maximum.Z; if (vector.LengthSquared > radiusSquared) return ContainmentType.Intersects; vector.X = sphere.Center.X - box.Maximum.X; vector.Y = sphere.Center.Y - box.Minimum.Y; vector.Z = sphere.Center.Z - box.Maximum.Z; if (vector.LengthSquared > radiusSquared) return ContainmentType.Intersects; vector.X = sphere.Center.X - box.Minimum.X; vector.Y = sphere.Center.Y - box.Minimum.Y; vector.Z = sphere.Center.Z - box.Maximum.Z; if (vector.LengthSquared > radiusSquared) return ContainmentType.Intersects; vector.X = sphere.Center.X - box.Minimum.X; vector.Y = sphere.Center.Y - box.Maximum.Y; vector.Z = sphere.Center.Z - box.Minimum.Z; if (vector.LengthSquared > radiusSquared) return ContainmentType.Intersects; vector.X = sphere.Center.X - box.Maximum.X; vector.Y = sphere.Center.Y - box.Maximum.Y; vector.Z = sphere.Center.Z - box.Minimum.Z; if (vector.LengthSquared > radiusSquared) return ContainmentType.Intersects; vector.X = sphere.Center.X - box.Maximum.X; vector.Y = sphere.Center.Y - box.Minimum.Y; vector.Z = sphere.Center.Z - box.Minimum.Z; if (vector.LengthSquared > radiusSquared) return ContainmentType.Intersects; vector.X = sphere.Center.X - box.Minimum.X; vector.Y = sphere.Center.Y - box.Minimum.Y; vector.Z = sphere.Center.Z - box.Minimum.Z; if (vector.LengthSquared > radiusSquared) return ContainmentType.Intersects; return ContainmentType.Contains; } /// /// Determines whether a contains a . /// /// The first sphere to test. /// The second sphere to test. /// The type of containment the two objects have. public static ContainmentType SphereContainsSphere(ref BoundingSphere sphere1, ref BoundingSphere sphere2) { Real distance = Vector3.Distance(ref sphere1.Center, ref sphere2.Center); if (sphere1.Radius + sphere2.Radius < distance) return ContainmentType.Disjoint; if (sphere1.Radius - sphere2.Radius < distance) return ContainmentType.Intersects; return ContainmentType.Contains; } /// /// Determines whether a line intersects with the other line. /// /// The first line point 0. /// The first line point 1. /// The second line point 0. /// The second line point 1. /// True if line intersects with the other line public static bool LineIntersectsLine(ref Float2 l1p1, ref Float2 l1p2, ref Float2 l2p1, ref Float2 l2p2) { float q = (l1p1.Y - l2p1.Y) * (l2p2.X - l2p1.X) - (l1p1.X - l2p1.X) * (l2p2.Y - l2p1.Y); float d = (l1p2.X - l1p1.X) * (l2p2.Y - l2p1.Y) - (l1p2.Y - l1p1.Y) * (l2p2.X - l2p1.X); if (Mathf.IsZero(d)) return false; float r = q / d; q = (l1p1.Y - l2p1.Y) * (l1p2.X - l1p1.X) - (l1p1.X - l2p1.X) * (l1p2.Y - l1p1.Y); float s = q / d; return !(r < 0 || r > 1 || s < 0 || s > 1); } /// /// Determines whether a line intersects with the rectangle. /// /// The line point 0. /// The line point 1. /// The rectangle. /// True if line intersects with the rectangle public static bool LineIntersectsRect(ref Float2 p1, ref Float2 p2, ref Rectangle rect) { // TODO: optimize it var pA = new Float2(rect.Right, rect.Y); var pB = new Float2(rect.Right, rect.Bottom); var pC = new Float2(rect.X, rect.Bottom); return LineIntersectsLine(ref p1, ref p2, ref rect.Location, ref pA) || LineIntersectsLine(ref p1, ref p2, ref pA, ref pB) || LineIntersectsLine(ref p1, ref p2, ref pB, ref pC) || LineIntersectsLine(ref p1, ref p2, ref pC, ref rect.Location) || (rect.Contains(ref p1) && rect.Contains(ref p2)); } /// /// Determines whether the given 2D point is inside the specified triangle. /// /// The point to check. /// The first vertex of the triangle. /// The second vertex of the triangle. /// The third vertex of the triangle. /// true if point is inside the triangle; otherwise, false. public static bool IsPointInTriangle(ref Float2 point, ref Float2 a, ref Float2 b, ref Float2 c) { var an = a - point; var bn = b - point; var cn = c - point; bool orientation = Float2.Cross(an, bn) > 0; if (Float2.Cross(bn, cn) > 0 != orientation) return false; return Float2.Cross(cn, an) > 0 == orientation; } } }