/// <summary> /// Checks if the specified ray intersects the specified sphere. /// </summary> /// <param name="ray"> /// The ray involved in the intersection test. /// </param> /// <param name="sphereCenter"> /// The center of the sphere. /// </param> /// <param name="sphereRadius"> /// The sphere's radius. /// </param> /// <param name="t"> /// The offset along the ray at which the intersection happens. Whenever the function returns /// false, this will be set to 0.0f. When checking the intersection between a ray and a sphere, /// we can obtain 2 intersection points and thus 2 t values. The function will always return /// the smallest positive t value. If both t values are negative, it means the ray only intersects /// the sphere from behind and in that case the method will return false. /// </param> /// <returns> /// True if the ray intersects the sphere and false otherwise. /// </returns> public static bool IntersectsSphere(this Ray ray, Vector3 sphereCenter, float sphereRadius, out float t) { t = 0.0f; // Calculate the coefficients of the quadratic equation 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 we have a solution to the equation, the ray most likely intersects the sphere. float t1, t2; if (Equation.SolveQuadratic(a, b, c, out t1, out t2)) { // Make sure the ray doesn't intersect the sphere 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; return(true); } // If we reach this point it means the ray does not intersect the sphere in any way return(false); }
/// <summary> /// Checks if the specified ray intersects the specified cone. /// </summary> /// <param name="ray"> /// The ray involved in the intersection test. /// </param> /// <param name="coneBaseRadius"> /// The radius of the cone's base. /// </param> /// <param name="coneHeight"> /// The cone's height. /// </param> /// <param name="coneTransformMatrix"> /// This is a 4x4 matrix which describes the rotation, scale and position of the cone in 3D space. /// </param> /// <param name="t"> /// The offset along the ray at which the intersection happens. Whenever the function returns /// false, this will be set to 0.0f. When checking the intersection between a ray and a cone, /// we can obtain 2 intersection points and thus 2 t values. The function will always return /// the smallest positive t value. If both t values are negative, it means the ray only intersects /// the cone from behind and in that case the method will return false. /// </param> /// <returns> /// True if the ray intersects the cone and false otherwise. /// </returns> public static bool IntersectsCone(this Ray ray, float coneBaseRadius, float coneHeight, Matrix4x4 coneTransformMatrix, out float t) { t = 0.0f; // Because of the way in which the cone equation works, we will need to work with a ray which exists // in the cone's local space. That will make sure that we are dealing with a cone which sits at the // origin of the coordinate system with its height axis extending along the global up vector. Ray coneSpaceRay = ray.InverseTransform(coneTransformMatrix); // 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 and we // would also get false positives when the ray intersects the 'imaginary' part of the cone. // Note: In order to calculate the cone's bottom cap plane, we need to know the plane normal and a // point known to be on the plane. Remembering that we are currently working in cone local space, // the bottom cap plane normal is pointing downwards along the Y axis and the point on the plane // is the center of the bottom cap, which is the zero vector. This is the standard default pose // for a cone object. float rayOffset; Plane bottomCapPlane = new Plane(-Vector3.up, Vector3.zero); if (bottomCapPlane.Raycast(coneSpaceRay, out rayOffset)) { // 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 * rayOffset; if (intersectionPoint.magnitude <= coneBaseRadius) { t = rayOffset; 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 (Equation.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 < 0.0f || intersectionPoint.y > coneHeight) { 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); }
/// <summary> /// Checks if the specified ray intersects the specified cylinder. /// </summary> /// <param name="ray"> /// The ray involved in the intersection test. /// </param> /// <param name="cylinderAxisFirstPoint"> /// The first point which makes up the cylinder axis. /// </param> /// <param name="cylinderAxisSecondPoint"> /// The second point which makes up the cylinder axis. /// </param> /// <param name="cylinderRadius"> /// The radius of the cylinder. /// </param> /// <param name="t"> /// The offset along the ray at which the intersection happens. Whenever the function returns /// false, this will be set to 0.0f. When checking the intersection between a ray and a cylinder, /// we can obtain 2 intersection points and thus 2 t values. The function will always return /// the smallest positive t value. If both t values are negative, it means the ray only intersects /// the cylinder from behind and in that case the method will return false. /// </param> /// <returns> /// True if the ray intersects the cylinder and false otherwise. /// </returns> public static bool IntersectsCylinder(this Ray ray, Vector3 cylinderAxisFirstPoint, Vector3 cylinderAxisSecondPoint, float cylinderRadius, out float t) { t = 0; // We will need the length of the cylinder axis later. We also need to normalize the // cylinder axis in order to use it for the quadratic coefficients calculation. Vector3 cylinderAxis = cylinderAxisSecondPoint - cylinderAxisFirstPoint; float cylinderAxisLength = cylinderAxis.magnitude; cylinderAxis.Normalize(); // We need these for the quadratic coefficients calculation Vector3 crossRayDirectionCylinderAxis = Vector3.Cross(ray.direction, cylinderAxis); Vector3 crossToOriginCylinderAxis = Vector3.Cross((ray.origin - cylinderAxisFirstPoint), 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 (Equation.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. We will // do this by constructing a vector which goes from the cylinder axis first point to the intersection // point. We then project the resulting vector on the cylinder axis and analyze the result. If the result // is less than 0, it means the intersection point exists below the first cylinder axis point. If the // result is greater than the cylinder axis length, it means the intersection point exists above the // second cylinder axis point. In both of these cases, we will return false because it means the intersection // point is outside the cylinder axis range. Vector3 intersectionPoint = ray.origin + ray.direction * t; float projection = Vector3.Dot(cylinderAxis, (intersectionPoint - cylinderAxisFirstPoint)); // Below the first cylinder axis point? if (projection < 0.0f) { return(false); } // Above the second cylinder axis point? if (projection > cylinderAxisLength) { 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); }