BOOL Plane::GetIntersection(const Vect &p1, const Vect &p2, float &fT) const { float P1Dist = p1.DistFromPlane(*this); float P2Dist = p2.DistFromPlane(*this); if(CloseFloat(P1Dist, 0.0f, EPSILON)) { if(P2Dist == 0.0f) return 0; fT = 0.0f; return 1; } else if(CloseFloat(P2Dist, 0.0f, EPSILON)) { fT = 1.0f; return 1; } BOOL bP1Over = (P1Dist > 0.0f); BOOL bP2Over = (P2Dist > 0.0f); if(bP1Over == bP2Over) return FALSE; float P1AbsDist = fabs(P1Dist); float dist2 = (P1AbsDist+fabs(P2Dist)); if(dist2 < EPSILON) return FALSE; fT = P1AbsDist/dist2; return TRUE; }
BOOL PointOnFiniteLine(const Vect &lineV1, const Vect &lineV2, const Vect &p) { Vect line = lineV2-lineV1; float lineDist = line.Len(); Plane plane; plane.Dir = line*(1.0f/lineDist); plane.Dist = plane.Dir.Dot(lineV1); float dist = p.DistFromPlane(plane); if((dist < 0.0f) || (dist > lineDist)) return FALSE; float fT = (dist/lineDist); return p.CloseTo(Lerp(lineV1, lineV2, fT)); }
// yes, I realize this breaks on caps when the ray origin is inside the cylinder. // it's not like it's actually going to be in there anyway. BOOL CylinderRayCollision(const Vect ¢er, float radius, float height, const Vect &rayOrig, const Vect &rayDir, Vect *collision, Plane *collisionPlane) { Vect collisionValue; BOOL bHit = FALSE; float fT; Plane axisPlane(0.0f, 1.0f, 0.0f, center.y); //--------------------------------------- // test the cap if(fabs(rayDir.y) > EPSILON) { BOOL bUnder = (rayDir.y<0.0f); Plane planeCap; planeCap.Dir.Set(0.0f, bUnder ? 1.0f : -1.0f, 0.0f); planeCap.Dist = (bUnder ? center.y : -center.y)+height; if(rayOrig.DistFromPlane(planeCap) > 0.0f) { if(planeCap.GetRayIntersection(rayOrig, rayDir, fT)) { collisionValue = rayOrig+(rayDir*fT); Vect CapCenter = center+(planeCap.Dir*height); if(collisionValue.Dist(CapCenter) <= radius) { if(collisionPlane) *collisionPlane = planeCap; bHit = TRUE; } } } } if(!bHit && ((1.0f-fabs(rayDir.y)) > EPSILON)) { //--------------------------------------- // test the body Vect adjDir, adjCenter; adjDir.Set(rayDir.x, 0.0f, rayDir.z).Norm(); adjCenter.Set(center.x, rayOrig.y, center.z); Vect l = (adjCenter-rayOrig); float d = l | adjDir; //distance from adjDir Plane float l2 = l | l; //c-o distance squared float r2 = radius*radius; if(l2 < r2) //if inside the cylinder, fail return FALSE; if((d < 0.0f) && (l2 > r2)) //if the plane distance is negative, and return FALSE; //the distance is over the radius, fail float m2 = l2 - (d*d); //distance from the cylinder center to //the closest ray point if(m2 > r2) //if m2 is larger than the radius, fail return FALSE; float q = sqrt(r2-m2); //real distance from the edge of the //cylinder to the cloest ray point //(forms a sort of triangle) fT = (l2 > r2) ? (d-q) : (d+q); //if the distance is over the radius, //d-q = T, else d+q=T //distance fT /= (adjDir|rayDir); //divide by angle to get the proper //value collisionValue = rayOrig+(rayDir*fT); if(fabs(collisionValue.DistFromPlane(axisPlane)) >= height) return FALSE; if(collisionPlane) { Vect temp = collisionValue; temp.y = center.y; collisionPlane->Dir = (temp-center).Norm(); collisionPlane->Dist = collisionPlane->Dir|temp; } bHit = TRUE; } if(!bHit) return FALSE; if(collision) *collision = collisionValue; return TRUE; }