/// <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));
 }
Пример #3
0
        /// <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);
        }
Пример #4
0
        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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        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);
        }
Пример #7
0
        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
                );
        }
Пример #9
0
        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
                );
        }
Пример #10
0
        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
                );
        }
Пример #12
0
        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
                );
        }
Пример #15
0
        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
                );
        }
Пример #16
0
        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
                );
        }
Пример #17
0
        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
                );
        }
Пример #18
0
        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
                );
        }
Пример #21
0
        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
                );
        }
Пример #22
0
        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
                );
        }
Пример #23
0
        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
                );
        }
Пример #24
0
        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
                );
        }
Пример #25
0
        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
                );
        }
Пример #27
0
        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);
        }
Пример #29
0
        /// <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);
 }