/// <summary>Determines the contact location between a line and a triangle</summary> /// <param name="lineOffset"> /// Offset of the line from the coordinate system's center /// </param> /// <param name="lineDirection">Direction of the line</param> /// <param name="triangleA"> /// First corner point of triangle in counter-clockwise order /// </param> /// <param name="triangleB"> /// Second corner point of triangle in counter-clockwise order /// </param> /// <param name="triangleC"> /// Third corner point of triangle in counter-clockwise order /// </param> /// <returns>The point of intersection of the line with the triangle, if any</returns> /// <remarks> /// <para> /// I saw this algorithm in an article to line/triangle intersections tests /// by Christopher Bartlett. The material was stated to be free for learning /// purposes, so I felt free to apply what I've learned here =) /// </para> /// <para> /// There is no special case for when the line precisely touches one of /// the triangle's corners. It will either enter and exit the triangle or /// no contacts will be detected at all. /// </para> /// </remarks> internal static LineContacts FindContacts( Vector3 lineOffset, Vector3 lineDirection, Vector3 triangleA, Vector3 triangleB, Vector3 triangleC ) { // Calculate the normal vector of the triangle for the plane intersection check Vector3 ab = triangleB - triangleA; Vector3 bc = triangleC - triangleB; Vector3 normal = Vector3.Cross(ab, bc); // Find out when the line will touch the triangle's plane LineContacts contactPoints = Collisions.Line3Plane3Collider.FindContacts( lineOffset, lineDirection, triangleA, normal ); if (contactPoints.HasContact) { // Calculate the actual point of intersection on the plane Vector3 intersectionLocation = lineOffset + lineDirection * contactPoints.EntryTime; // Now all that's left to do is to find out whether this point is inside the triangle bool isInsideTriangle = isOnPositiveSide(triangleA, triangleB, intersectionLocation, normal) == isOnPositiveSide(triangleB, triangleC, intersectionLocation, normal) == isOnPositiveSide(triangleC, triangleA, intersectionLocation, normal); if (!isInsideTriangle) { contactPoints = LineContacts.None; } } return(contactPoints); }
/// <summary> /// Finds out whether a reported contact point lies within a line segment /// </summary> /// <param name="contacts">Reported contact point that will be checked</param> /// <param name="length"> /// Length of the line segment against which the test that created the contact /// point was made /// </param> /// <returns>True if the contact point is within the line segment</returns> private static bool isWithin(LineContacts contacts, float length) { return ((!float.IsNaN(contacts.EntryTime)) && (contacts.EntryTime >= 0.0f) && (contacts.EntryTime < length)); }
/// <summary>Determines where a ray will hit a triangle, if at all</summary> /// <param name="rayStart">Starting point of the ray</param> /// <param name="rayDirection">Direction into which the ray extends</param> /// <param name="triangleA"> /// First corner point of triangle in counter-clockwise order /// </param> /// <param name="triangleB"> /// Second corner point of triangle in counter-clockwise order /// </param> /// <param name="triangleC"> /// Third corner point of triangle in counter-clockwise order /// </param> /// <returns>The intersection points between the ray and the box, if any</returns> public static LineContacts FindContacts( Vector2 rayStart, Vector2 rayDirection, Vector2 triangleA, Vector2 triangleB, Vector2 triangleC ) { LineContacts contacts = Line2Triangle2Collider.FindContacts( rayStart, rayDirection, triangleA, triangleB, triangleC ); // If the line has entered the box before the reference point, this means // that the ray starts within the box, thus, its first contact occurs immediately if (!float.IsNaN(contacts.EntryTime)) { if (contacts.ExitTime < 0.0f) // Entry & exit before the ray's beginning? { return(LineContacts.None); } else if (contacts.EntryTime < 0.0f) // Only entry before ray's beginning? { contacts.EntryTime = 0.0f; } } return(contacts); }
public void TestParallelLineAbovePlane() { LineContacts contacts = Line3Plane3Collider.FindContacts( new Vector3(0.0f, 10.0f, 0.0f), Vector3.Right, new Vector3(0.0f, 0.0f, 0.0f), Vector3.Up ); Assert.IsFalse(contacts.HasContact); }
public void TestParallelLineBelowPlane() { LineContacts contacts = Line3Plane3Collider.FindContacts( new Vector3(-10.0f, 0.0f, 0.0f), Vector3.Up, new Vector3(0.0f, 0.0f, 0.0f), Vector3.Left ); Assert.IsFalse(contacts.HasContact); }
public void TestCloseMiss() { Vector3 boxExtents = new Vector3(10.0f, 10.0f, 10.0f); LineContacts contacts = Line3Aabb3Collider.FindContacts( new Vector3(0.0f, 0.0f, 10.1f), Vector3.Right, boxExtents ); Assert.IsNaN(contacts.EntryTime); Assert.IsNaN(contacts.ExitTime); }
public void TestCloseMiss() { Vector2 boxExtents = new Vector2(10.0f, 10.0f); LineContacts contacts = Line2Aabb2Collider.FindContacts( new Vector2(0.0f, 10.1f), Vector2.UnitX, boxExtents ); Assert.IsNaN(contacts.EntryTime); Assert.IsNaN(contacts.ExitTime); }
public void TestPointingTowards() { LineContacts contacts = Ray3Plane3Collider.FindContacts( new Vector3(0.0f, 100.0f, 0.0f), Vector3.Down, Vector3.Zero, Vector3.Up ); Assert.That( contacts.EntryTime, Is.EqualTo(100.0f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestRayCrossingLine() { LineContacts contacts = Ray2Line2Collider.FindContacts( new Vector2(-1.0f, 0.0f), Vector2.UnitX, Vector2.Zero, Vector2.UnitY ); Assert.That( contacts.EntryTime, Is.EqualTo(1.0f).Within(Specifications.MaximumDeviation).Ulps ); }
public Vector3 Pick(Ray ray, bool water) { Vector3 currentPoint = ray.Position; LineContacts con = TerrainPicker.Ray3HeightFieldCollider.FindContacts(ray.Position, ray.Direction, this); if (con.HasContact) { currentPoint += ray.Direction * con.EntryTime; return(currentPoint); } return(new Vector3(0, -99999, 0)); }
public void TestLineThroughCenter() { LineContacts contacts = Line2Disc2Collider.FindContacts( new Vector2(-3.0f, 0.0f), Vector2.UnitX, 2.0f ); Assert.That( contacts.EntryTime, Is.EqualTo(1.0f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(5.0f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestRayStartingInside() { LineContacts contacts = Ray2Aabb2Collider.FindContacts( Vector2.Zero, Vector2.UnitX, new Vector2(2.0f, 2.0f) ); Assert.That( contacts.EntryTime, Is.EqualTo(0.0f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(2.0f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestRayStartingInsideWithAbsoluteDiscPosition() { LineContacts contacts = Ray3Sphere3Collider.FindContacts( Vector3.UnitX * 3.0f, Vector3.UnitX, Vector3.UnitX * 3.0f, 2.0f ); Assert.That( contacts.EntryTime, Is.EqualTo(0.0f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(2.0f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestCircleWithAbsolutePosition() { Vector2 unitDiagonal = Vector2.Normalize(Vector2.One); LineContacts contacts = Line2Disc2Collider.FindContacts( Vector2.Zero, unitDiagonal, unitDiagonal * 5.0f, 2.0f ); Assert.That( contacts.EntryTime, Is.EqualTo(3.0f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(7.0f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestRayStartingInside() { Vector3 boxExtents = new Vector3(10.0f, 10.0f, 10.0f); LineContacts contacts = Ray3Aabb3Collider.FindContacts( new Vector3(-2.5f, -2.5f, -2.5f), Vector3.Up, boxExtents ); Assert.That( contacts.EntryTime, Is.EqualTo(0.0f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(12.5f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestContact() { Vector2 boxExtents = new Vector2(10.0f, 10.0f); LineContacts contacts = Line2Aabb2Collider.FindContacts( new Vector2(-2.5f, -2.5f), Vector2.UnitY, boxExtents ); Assert.That( contacts.EntryTime, Is.EqualTo(-7.5f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(12.5f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestContactOnCornerDefinedBox() { LineContacts contacts = Line2Aabb2Collider.FindContacts( new Vector2(97.5f, -102.5f), Vector2.UnitY, new Vector2(90.0f, -110.0f), new Vector2(110.0f, -90.0f) ); Assert.That( contacts.EntryTime, Is.EqualTo(-7.5f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(12.5f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestRayStartingInside() { LineContacts contacts = Ray2Triangle2Collider.FindContacts( new Vector2(0.75f, 0.5f), Vector2.UnitX, Vector2.UnitX, Vector2.One, Vector2.Zero ); Assert.That( contacts.EntryTime, Is.EqualTo(0.0f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(0.25f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestHitTopToBottom() { LineContacts contacts = Line2Triangle2Collider.FindContacts( new Vector2(0.5f, 1.0f), -Vector2.UnitY, Vector2.UnitX, Vector2.One, Vector2.Zero ); Assert.That( contacts.EntryTime, Is.EqualTo(0.5f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(1.0f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestRayStartingInsideWithAbsoluteDiscPosition() { Vector2 unitDiagonal = Vector2.Normalize(Vector2.One); LineContacts contacts = Ray2Disc2Collider.FindContacts( unitDiagonal * 4.0f, unitDiagonal, unitDiagonal * 5.0f, 2.0f ); Assert.That( contacts.EntryTime, Is.EqualTo(0.0f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(3.0f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestOrthogonallyCrossingLines() { LineContacts contacts = Line2Line2Collider.FindContacts( new Vector2(-1.0f, 0.0f), Vector2.UnitX, Vector2.Zero, Vector2.UnitY ); float touchTime = 1.0f; Assert.That( contacts.EntryTime, Is.EqualTo(touchTime).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(touchTime).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestHitThroughCenter() { LineContacts contacts = Line3Triangle3Collider.FindContacts( new Vector3(-0.25f, -0.5f, -1.0f), Vector3.Normalize(Vector3.One), Vector3.Zero, Vector3.UnitX, Vector3.UnitX + Vector3.UnitY ); float contactTime = 1.0f / Vector3.Normalize(Vector3.One).Z; Assert.That( contacts.EntryTime, Is.EqualTo(contactTime).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(contactTime).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestDiagonallyCrossingLines() { LineContacts contacts = Line2Line2Collider.FindContacts( new Vector2(1.0f, 0.0f), Vector2.Normalize(Vector2.One), Vector2.Zero, Vector2.UnitY ); float touchTime = -1.4142135623730950488016887242097f; Assert.That( contacts.EntryTime, Is.EqualTo(touchTime).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(touchTime).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestCenterCrossing() { Vector3 diagonalUnit = Vector3.Normalize(Vector3.One); LineContacts contacts = Line3Sphere3Collider.FindContacts( diagonalUnit * -15.0f, diagonalUnit, Vector3.Zero, 10.0f ); Assert.That( contacts.EntryTime, Is.EqualTo(5.0f).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(25.0f).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestLineCrossingPlaneDiagonally() { LineContacts contacts = Line3Plane3Collider.FindContacts( new Vector3(-10.0f, 0.0f, 0.0f), Vector3.Normalize(Vector3.One), new Vector3(0.0f, 0.0f, 0.0f), Vector3.Left ); float touchTime = 10.0f / Vector3.Normalize(Vector3.One).X; Assert.That( contacts.EntryTime, Is.EqualTo(touchTime).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(touchTime).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestHitBottomToRight() { LineContacts contacts = Line2Triangle2Collider.FindContacts( new Vector2(0.0f, -0.5f), Vector2.Normalize(Vector2.One), Vector2.UnitX, Vector2.One, Vector2.Zero ); float entryTime = 0.5f / Vector2.Normalize(Vector2.One).X; float exitTime = 1.0f / Vector2.Normalize(Vector2.One).Y; Assert.That( contacts.EntryTime, Is.EqualTo(entryTime).Within(Specifications.MaximumDeviation).Ulps ); Assert.That( contacts.ExitTime, Is.EqualTo(exitTime).Within(Specifications.MaximumDeviation).Ulps ); }
public void TestDiagonalCloseMiss() { Vector2 boxExtents = new Vector2(5.0f, 5.0f); Vector2 diagonalVector = Vector2.Normalize(Vector2.One); LineContacts contacts = Line2Aabb2Collider.FindContacts( new Vector2(-10.1f, 0.0f), diagonalVector, boxExtents ); Assert.IsNaN(contacts.EntryTime); Assert.IsNaN(contacts.ExitTime); diagonalVector.Y = -diagonalVector.Y; contacts = Line2Aabb2Collider.FindContacts( new Vector2(0.0f, 10.1f), diagonalVector, boxExtents ); Assert.IsNaN(contacts.EntryTime); Assert.IsNaN(contacts.ExitTime); }
/// <summary>Determines the contact location between a ray and a triangle</summary> /// <param name="rayStart"> /// Offset of the ray from the coordinate system's center /// </param> /// <param name="rayDirection">Direction of the line</param> /// <param name="triangleA"> /// First corner point of triangle in counter-clockwise order /// </param> /// <param name="triangleB"> /// Second corner point of triangle in counter-clockwise order /// </param> /// <param name="triangleC"> /// Third corner point of triangle in counter-clockwise order /// </param> /// <returns>The point of intersection of the line with the triangle, if any</returns> /// <remarks> /// <para> /// I saw this algorithm in an article to line/triangle intersections tests /// by Christopher Bartlett. The material was stated to be free for learning /// purposes, so I felt free to apply what I've learned here =) /// </para> /// <para> /// There is no special case for when the line precisely touches one of /// the triangle's corners. It will either enter and exit the triangle or /// no contacts will be detected at all. /// </para> /// </remarks> internal static LineContacts FindContacts( Vector3 rayStart, Vector3 rayDirection, Vector3 triangleA, Vector3 triangleB, Vector3 triangleC ) { LineContacts contacts = Line3Triangle3Collider.FindContacts( rayStart, rayDirection, triangleA, triangleB, triangleC ); // If the line has entered the triangle before the reference point, this means // that the ray starts within the triangle and its first contact occurs immediately if (!float.IsNaN(contacts.EntryTime)) { if (contacts.EntryTime < 0.0f) { return(LineContacts.None); } } return(contacts); }
/// <summary>Determines where a ray will hit a line, if at all</summary> /// <param name="rayStart">Starting point of the ray</param> /// <param name="rayDirection">Direction into which the ray extends</param> /// <param name="lineOffset">Offset of the line</param> /// <param name="lineDirection">Direction along which the line extends</param> /// <returns>The intersection points between the ray and the line, if any</returns> public static LineContacts FindContacts( Vector2 rayStart, Vector2 rayDirection, Vector2 lineOffset, Vector2 lineDirection ) { LineContacts contacts = Line2Line2Collider.FindContacts( rayStart, rayDirection, lineOffset, lineDirection ); // If the contact occured before the starting offset of the ray, // no collision took place since we're a ray if (!float.IsNaN(contacts.EntryTime)) { if (contacts.EntryTime < 0.0f) { return(LineContacts.None); } } return(contacts); }
/// <summary>Determines the contact location between a line and a triangle</summary> /// <param name="lineOffset"> /// Offset of the line from the coordinate system's center /// </param> /// <param name="lineDirection">Direction and length of the line</param> /// <param name="triangleA"> /// First corner point of triangle in counter-clockwise order /// </param> /// <param name="triangleB"> /// Second corner point of triangle in counter-clockwise order /// </param> /// <param name="triangleC"> /// Third corner point of triangle in counter-clockwise order /// </param> /// <returns>The point of intersection of the line with the triangle, if any</returns> /// <remarks> /// Everyone seems to know how to do 3D line / triangle intersections, but there /// are no resources whatsoever on 2D line / triangle intersections. The code in here /// is hand-written by myself. Instead of fancy math tricks, it simply tries to be /// efficient using the existing code. It requires 4 line checks to find the accurate /// intersection point with the triangle. /// </remarks> internal static LineContacts FindContacts( Vector2 lineOffset, Vector2 lineDirection, Vector2 triangleA, Vector2 triangleB, Vector2 triangleC ) { Vector2 ab = triangleB - triangleA; Vector2 bc = triangleC - triangleB; Vector2 ca = triangleA - triangleC; float abLength = ab.Length(); float bcLength = bc.Length(); float caLength = ca.Length(); Vector2 abNormalized = ab / abLength; Vector2 bcNormalized = bc / bcLength; Vector2 caNormalized = ca / caLength; LineContacts abContacts = Line2Line2Collider.FindContacts( triangleA, abNormalized, lineOffset, lineDirection ); LineContacts bcContacts = Line2Line2Collider.FindContacts( triangleB, bcNormalized, lineOffset, lineDirection ); // Does the line cross the A-B line of the triangle? if (isWithin(abContacts, abLength)) { abContacts = Line2Line2Collider.FindContacts( lineOffset, lineDirection, triangleA, ab / abLength ); // Find out which other line it crosses: B-C or C-A? if (isWithin(bcContacts, bcLength)) { bcContacts = Line2Line2Collider.FindContacts( lineOffset, lineDirection, triangleB, bc / bcLength ); } else { bcContacts = Line2Line2Collider.FindContacts( lineOffset, lineDirection, triangleC, ca / caLength ); } // Report the contacts in the right order if (abContacts.EntryTime < bcContacts.EntryTime) { return(new LineContacts(abContacts.EntryTime, bcContacts.EntryTime)); } else { return(new LineContacts(bcContacts.EntryTime, abContacts.EntryTime)); } } else if (isWithin(bcContacts, bcLength)) // Does is cross the B-C line? { bcContacts = Line2Line2Collider.FindContacts( lineOffset, lineDirection, triangleB, bc / abLength ); // We already checked A-B, so the other line it crosses must be C-A! abContacts = Line2Line2Collider.FindContacts( lineOffset, lineDirection, triangleC, ca / caLength ); // Report the contacts in the right order if (bcContacts.EntryTime < abContacts.EntryTime) { return(new LineContacts(bcContacts.EntryTime, abContacts.EntryTime)); } else { return(new LineContacts(abContacts.EntryTime, bcContacts.EntryTime)); } } else // No contact on A-B or B-C, contact is impossible { return(LineContacts.None); } }
/// <summary> /// Finds out whether a reported contact point lies within a line segment /// </summary> /// <param name="contacts">Reported contact point that will be checked</param> /// <param name="length"> /// Length of the line segment against which the test that created the contact /// point was made /// </param> /// <returns>True if the contact point is within the line segment</returns> private static bool isWithin(LineContacts contacts, float length) { return (!float.IsNaN(contacts.EntryTime)) && (contacts.EntryTime >= 0.0f) && (contacts.EntryTime < length); }