Beispiel #1
0
        public static bool Raycast(Ray ray, out float t0, out float t1, Vector3 sphereCenter, float sphereRadius, SphereEpsilon epsilon = new SphereEpsilon())
        {
            t0            = t1 = 0.0f;
            sphereRadius += epsilon.RadiusEps;

            Vector3 sphereCenterToRayOrigin = ray.origin - sphereCenter;
            float   a = Vector3.SqrMagnitude(ray.direction);
            float   b = 2.0f * Vector3.Dot(ray.direction, sphereCenterToRayOrigin);
            float   c = Vector3.SqrMagnitude(sphereCenterToRayOrigin) - sphereRadius * sphereRadius;

            if (MathEx.SolveQuadratic(a, b, c, out t0, out t1))
            {
                if (t0 < 0.0f && t1 < 0.0f)
                {
                    return(false);
                }

                if (t0 > t1)
                {
                    float temp = t0;
                    t0 = t1;
                    t1 = temp;
                }

                if (t0 < 0.0f)
                {
                    t0 = t1;
                }
                return(true);
            }

            return(false);
        }
        public static bool Raycast(Ray ray, out float t, Vector3 coneBaseCenter, float coneBaseRadius, float coneHeight, Quaternion coneRotation, ConeEpsilon epsilon = new ConeEpsilon())
        {
            t = 0.0f;
            Ray coneSpaceRay = ray.InverseTransform(Matrix4x4.TRS(coneBaseCenter, coneRotation, Vector3.one));

            float   xzAABBSize = coneBaseRadius * 2.0f;
            Vector3 aabbSize   = new Vector3(xzAABBSize, coneHeight + epsilon.VertEps * 2.0f, xzAABBSize);

            if (!BoxMath.Raycast(coneSpaceRay, Vector3.up * coneHeight * 0.5f, aabbSize, Quaternion.identity))
            {
                return(false);
            }

            // We will first perform a preliminary check to see if the ray intersects the bottom cap of the cone.
            // This is necessary because the cone equation views the cone as infinite (i.e. no bottom cap), and
            // if we didn't perform this check, we would never be able to tell when the bottom cap was hit.
            float rayEnter;
            Plane bottomCapPlane = new Plane(-Vector3.up, Vector3.zero);

            if (bottomCapPlane.Raycast(coneSpaceRay, out rayEnter))
            {
                // If the ray intersects the plane of the bottom cap, we will calculate the intersection point
                // and if it lies inside the cone's bottom cap area, it means we have a valid intersection. We
                // store the t value and then return true.
                Vector3 intersectionPoint = coneSpaceRay.origin + coneSpaceRay.direction * rayEnter;
                if (intersectionPoint.magnitude <= coneBaseRadius)
                {
                    t = rayEnter;
                    return(true);
                }
            }

            // We need this for the calculation of the quadratic coefficients
            float ratioSquared = coneBaseRadius / coneHeight;

            ratioSquared *= ratioSquared;

            // Calculate the coefficients.
            // Note: The cone equation which was used is: (X^2 + Z^2) / ratioSquared = (Y - coneHeight)^2.
            //       Where X, Y and Z are the coordinates of the point along the ray: (Origin + Direction * t).xyz
            float a = coneSpaceRay.direction.x * coneSpaceRay.direction.x + coneSpaceRay.direction.z * coneSpaceRay.direction.z - ratioSquared * coneSpaceRay.direction.y * coneSpaceRay.direction.y;
            float b = 2.0f * (coneSpaceRay.origin.x * coneSpaceRay.direction.x + coneSpaceRay.origin.z * coneSpaceRay.direction.z - ratioSquared * coneSpaceRay.direction.y * (coneSpaceRay.origin.y - coneHeight));
            float c = coneSpaceRay.origin.x * coneSpaceRay.origin.x + coneSpaceRay.origin.z * coneSpaceRay.origin.z - ratioSquared * (coneSpaceRay.origin.y - coneHeight) * (coneSpaceRay.origin.y - coneHeight);

            // The intersection happnes only if the quadratic equation has solutions
            float t1, t2;

            if (MathEx.SolveQuadratic(a, b, c, out t1, out t2))
            {
                // Make sure the ray does not intersect the cone only from behind
                if (t1 < 0.0f && t2 < 0.0f)
                {
                    return(false);
                }

                // Make sure we are using the smallest positive t value
                if (t1 < 0.0f)
                {
                    float temp = t1;
                    t1 = t2;
                    t2 = temp;
                }
                t = t1;

                // Make sure the intersection point does not sit below the cone's bottom cap or above the cone's cap
                Vector3 intersectionPoint = coneSpaceRay.origin + coneSpaceRay.direction * t;
                if (intersectionPoint.y < -epsilon.VertEps || intersectionPoint.y > coneHeight + epsilon.VertEps)
                {
                    t = 0.0f;
                    return(false);
                }

                // The intersection point is valid
                return(true);
            }

            // If we reached this point, it means the ray does not intersect the cone in any way
            return(false);
        }
        public static bool Raycast(Ray ray, out float t, Vector3 cylinderAxisPt0,
                                   Vector3 cylinderAxisPt1, float cylinderRadius, float cylinderHeight, CylinderEpsilon epsilon = new CylinderEpsilon())
        {
            t = 0.0f;
            cylinderRadius += epsilon.RadiusEps;

            Vector3 cylinderAxis = (cylinderAxisPt1 - cylinderAxisPt0).normalized;

            cylinderHeight += epsilon.VertEps * 2.0f;
            const float minCylinderHeight = 1e-6f;

            if (cylinderHeight < minCylinderHeight)
            {
                return(false);
            }

            cylinderAxisPt0 -= cylinderAxis * epsilon.VertEps;
            cylinderAxisPt1 += cylinderAxis * epsilon.VertEps;

            // Check intersection with cylinder plane caps
            float capEnterBottom = 0.0f, capEnterTop = 0.0f;
            bool  hitBottomCap = false, hitTopCap = false;
            Plane bottomCapPlane = new Plane(cylinderAxis, cylinderAxisPt0);

            if (bottomCapPlane.Raycast(ray, out capEnterBottom))
            {
                Vector3 ptOnCap = ray.GetPoint(capEnterBottom);
                if ((ptOnCap - cylinderAxisPt0).sqrMagnitude <= cylinderRadius * cylinderRadius)
                {
                    hitBottomCap = true;
                }
            }

            Plane topCapPlane = new Plane(cylinderAxis, cylinderAxisPt1);

            if (topCapPlane.Raycast(ray, out capEnterTop))
            {
                Vector3 ptOnCap = ray.GetPoint(capEnterTop);
                if ((ptOnCap - cylinderAxisPt1).sqrMagnitude <= cylinderRadius * cylinderRadius)
                {
                    hitTopCap = true;
                }
            }

            // We need these for the quadratic coefficients calculation
            Vector3 crossRayDirectionCylinderAxis = Vector3.Cross(ray.direction, cylinderAxis);
            Vector3 crossToOriginCylinderAxis     = Vector3.Cross((ray.origin - cylinderAxisPt0), cylinderAxis);

            // Calculate the quadratic coefficients
            float a = crossRayDirectionCylinderAxis.sqrMagnitude;
            float b = 2.0f * Vector3.Dot(crossRayDirectionCylinderAxis, crossToOriginCylinderAxis);
            float c = crossToOriginCylinderAxis.sqrMagnitude - cylinderRadius * cylinderRadius;

            // If we have a solution to the equation, the ray most likely intersects the cylinder.
            float t1, t2;

            if (MathEx.SolveQuadratic(a, b, c, out t1, out t2))
            {
                // Make sure the ray doesn't intersect the cylinder only from behind
                if (t1 < 0.0f && t2 < 0.0f)
                {
                    return(false);
                }

                // Make sure we are using the smallest positive t value
                if (t1 < 0.0f)
                {
                    float temp = t1;
                    t1 = t2;
                    t2 = temp;
                }
                t = t1;

                // Now make sure that the intersection point lies within the boundaries of the cylinder axis. That is,
                // make sure its projection on the cylinder axis lies between the first and second axis points.
                Vector3 intersectionPoint = ray.origin + ray.direction * t;
                float   projection        = Vector3.Dot(cylinderAxis, (intersectionPoint - cylinderAxisPt0));

                // Below the first cylinder axis point?
                if (projection < 0.0f)
                {
                    // Check for intersection with caps
                    if (hitTopCap || hitBottomCap)
                    {
                        t = float.MaxValue;
                        if (hitTopCap)
                        {
                            t = capEnterTop;
                        }
                        if (hitBottomCap && t > capEnterBottom)
                        {
                            t = capEnterBottom;
                        }
                    }
                    else
                    {
                        t = 0.0f;
                        return(false);
                    }
                }

                // Above the second cylinder axis point?
                if (projection > cylinderHeight)
                {
                    // Check for intersection with caps
                    if (hitTopCap || hitBottomCap)
                    {
                        t = float.MaxValue;
                        if (hitTopCap)
                        {
                            t = capEnterTop;
                        }
                        if (hitBottomCap && t > capEnterBottom)
                        {
                            t = capEnterBottom;
                        }
                    }
                    else
                    {
                        t = 0.0f;
                        return(false);
                    }
                }

                // The intersection point is valid, so we can return true
                return(true);
            }

            // If we reach this point, it means the ray does not intersect the cylinder in any way
            return(false);
        }