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); }