int TestSphereTriangle(Sphere& s, glm::vec3& a, glm::vec3& b, glm::vec3& c, glm::vec3 &p) { // Find point P on triangle ABC closest to sphere center p = ClosestPointTriangle(s.c, a, b, c); // Sphere and triangle intersect if the (squared) distance from sphere // center to point p is less than the (squared) sphere radius glm::vec3 v = p - s.c; return glm::dot(v, v) <= s.r * s.r; }
/// //Performs a dynamic collision check between a moving sphere and a triangle // //Overview: // Uses a plethora of different algorithms to detect collision between a sphere and a triangle, including: // Line segment - Sphere // Line segment - Cylinder // Point - Triangle // and Sphere - Point // //Parameters: // s: The moving sphere // tri: The static triangle // mvmt: The relative movement vector of sphere from an observer on triangle // tStart: The start of the interval, 0.0f <= tStart < 1.0f // tEnd: The end of the interval, 0.0f < tEnd <= 1.0f // //Returns: // a t value between 0 and 1 indicating the "relative time" since the start of this frame that the collision occurred. // a t value of 0.0f would indicate the very start of this frame, a t value of 1.0f would indicate the very end of this frame. // This function will return a negative number if no collision was registered. float CheckDynamicCollision(const struct Sphere& s, const Triangle &tri, const glm::vec3 &mvmt, float tStart, float tEnd) { //Get the three edge vectors of the triangle glm::vec3 AB = tri.b - tri.a; glm::vec3 BC = tri.c - tri.b; glm::vec3 CA = tri.a - tri.c; //Get normal of plane which triangle lies in glm::vec3 triangleNormal = glm::normalize(glm::cross(AB, -CA)); //Determine if sphere is travelling parallel to triangle plane if (fabs(glm::dot(mvmt, triangleNormal)) > FLT_EPSILON) { //Not travelling parallel to plane //Find the point on the sphere which will hit the triangle first glm::vec3 pointOnSphere = s.center; //Determine which side of plane triangle is on glm::vec3 triToSphere = s.center - tri.center; if (glm::dot(triToSphere, triangleNormal) < 0.0f) { pointOnSphere += s.radius * triangleNormal; } else { pointOnSphere -= s.radius * triangleNormal; } //Consider the line segment this point creates over the movement vector struct Line circleMvmt; circleMvmt.start = pointOnSphere; circleMvmt.direction = mvmt; //Find the time which this line segment collides with the triangle's plane float dist = glm::dot(triangleNormal, tri.a); float t = (dist - glm::dot(triangleNormal, circleMvmt.start)) / glm::dot(triangleNormal, circleMvmt.direction); //If t is not within this timestep, there cannot be a collision if (t < 0.0f || t > 1.0f) return -1.0f; //Else, determine the point along the line which collides with the plane glm::vec3 pointOfPossibleCollision = circleMvmt.start + t * circleMvmt.direction; //If this point is contained within the triangle there is a collision if (checkPointTriangleCollision(tri, pointOfPossibleCollision)) return t; //Else, we can still have a collision. We must raycast from the closest point on the triangle to the sphere along -mvmt and see if it hits. //Determine the closest point on triangle t to sphere glm::vec3 closestPoint = ClosestPointTriangle(s.center, tri.a, tri.b, tri.c); struct Line ray; ray.start = closestPoint; ray.direction = -circleMvmt.direction; return checkSphereLineSegmentCollision(s, ray.start, ray.direction); } else { //Travelling parallel to plane //Create a line segment which represents the path travelled by the center of the circle struct Line circleMvmt; circleMvmt.start = s.center; circleMvmt.direction = mvmt; //Create A cylinder at each triangle edge with a radius of the sphere struct Cylinder cyl; cyl.start = tri.a + tri.center; cyl.direction = AB; cyl.radius = s.radius; //Determine if the sphere's movement line segment intersects the cylinder float minTimeOfIntersection = CheckCylinderSegmentCollision(cyl, circleMvmt); //Cylinder along BC cyl.start = tri.b + tri.center; cyl.direction = BC; //Determine if the sphere's movement line segment intersects the cylinder float timeOfIntersection = CheckCylinderSegmentCollision(cyl, circleMvmt); if (timeOfIntersection != -1.0f && (timeOfIntersection < minTimeOfIntersection || minTimeOfIntersection == -1.0f)) minTimeOfIntersection = timeOfIntersection; //Cylinder along CA cyl.start = tri.b + tri.center; cyl.direction = CA; //Determine if the sphere's movement line segment intersects the cylinder timeOfIntersection = CheckCylinderSegmentCollision(cyl, circleMvmt); if (timeOfIntersection != -1.0f && (timeOfIntersection < minTimeOfIntersection || minTimeOfIntersection == -1.0f)) minTimeOfIntersection = timeOfIntersection; //Create spheres at each one of the triangle vertices matching the circle Sphere vertexSphere; vertexSphere.radius = s.radius; vertexSphere.center = tri.center + tri.a; timeOfIntersection = checkSphereLineSegmentCollision(vertexSphere, circleMvmt.start, circleMvmt.direction); if (timeOfIntersection != -1.0f && (timeOfIntersection < minTimeOfIntersection || minTimeOfIntersection == -1.0f)) minTimeOfIntersection = timeOfIntersection; vertexSphere.center = tri.center + tri.b; timeOfIntersection = checkSphereLineSegmentCollision(vertexSphere, circleMvmt.start, circleMvmt.direction); if (timeOfIntersection != -1.0f && (timeOfIntersection < minTimeOfIntersection || minTimeOfIntersection == -1.0f)) minTimeOfIntersection = timeOfIntersection; vertexSphere.center = tri.center + tri.c; timeOfIntersection = checkSphereLineSegmentCollision(vertexSphere, circleMvmt.start, circleMvmt.direction); if (timeOfIntersection != -1.0f && (timeOfIntersection < minTimeOfIntersection || minTimeOfIntersection == -1.0f)) minTimeOfIntersection = timeOfIntersection; return minTimeOfIntersection; } }