//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Common collision utility methods // // $Header: $ // $NoKeywords: $ //=============================================================================// #ifndef COLLISIONUTILS_H #define COLLISIONUTILS_H #include "tier0/platform.h" #ifdef _WIN32 #pragma once #endif #include "mathlib/ssemath.h" //----------------------------------------------------------------------------- // forward declarations //----------------------------------------------------------------------------- struct Ray_t; class Vector; class Vector2D; class Vector4D; struct cplane_t; class QAngle; class CBaseTrace; struct matrix3x4_t; //----------------------------------------------------------------------------- // // IntersectRayWithTriangle // // Intersects a ray with a triangle, returns distance t along ray. // t will be less than zero if no intersection occurred // oneSided will cull collisions which approach the triangle from the back // side, assuming the vertices are specified in counter-clockwise order // The vertices need not be specified in that order if oneSided is not used // //----------------------------------------------------------------------------- float IntersectRayWithTriangle(const Ray_t &ray, const Vector &v1, const Vector &v2, const Vector &v3, bool oneSided); //----------------------------------------------------------------------------- // // ComputeIntersectionBarycentricCoordinates // // Figures out the barycentric coordinates (u,v) where a ray hits a // triangle. Note that this will ignore the ray extents, and it also ignores // the ray length. Note that the edge from v1->v2 represents u (v2: u = 1), // and the edge from v1->v3 represents v (v3: v = 1). It returns false // if the ray is parallel to the triangle (or when t is specified if t is less // than zero). // //----------------------------------------------------------------------------- bool ComputeIntersectionBarycentricCoordinates(const Ray_t &ray, const Vector &v1, const Vector &v2, const Vector &v3, float &u, float &v, float *t = 0); //----------------------------------------------------------------------------- // // IntersectRayWithRay // // Returns whether or not there was an intersection. The "t" paramter is the // distance along ray0 and the "s" parameter is the distance along ray1. If // the two lines to not intersect the "t" and "s" represent the closest // approach. "t" and "s" will not change if the rays are parallel. // //----------------------------------------------------------------------------- bool IntersectRayWithRay(const Ray_t &ray0, const Ray_t &ray1, float &t, float &s); //----------------------------------------------------------------------------- // // IntersectRayWithSphere // // Returns whether or not there was an intersection. Returns the two // intersection points. NOTE: The point of closest approach can be found at the // average t value. // //----------------------------------------------------------------------------- bool IntersectRayWithSphere(const Vector &vecRayOrigin, const Vector &vecRayDelta, const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2); //----------------------------------------------------------------------------- // // IntersectInfiniteRayWithSphere // // Returns whether or not there was an intersection of a sphere against an // infinitely extending ray. Returns the two intersection points // //----------------------------------------------------------------------------- bool IntersectInfiniteRayWithSphere(const Vector &vecRayOrigin, const Vector &vecRayDelta, const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2); // returns true if the sphere and cone intersect // NOTE: cone sine/cosine are the half angle of the cone bool IsSphereIntersectingCone(const Vector &sphereCenter, float sphereRadius, const Vector &coneOrigin, const Vector &coneNormal, float coneSine, float coneCosine); //----------------------------------------------------------------------------- // // IntersectRayWithPlane // // Intersects a ray with a plane, returns distance t along ray. // t will be less than zero the intersection occurs in the opposite direction of // the ray. // //----------------------------------------------------------------------------- float IntersectRayWithPlane(const Ray_t &ray, const cplane_t &plane); float IntersectRayWithPlane(const Vector &org, const Vector &dir, const cplane_t &plane); float IntersectRayWithPlane(const Vector &org, const Vector &dir, const Vector &normal, float dist); // This version intersects a ray with an axis-aligned plane float IntersectRayWithAAPlane(const Vector &vecStart, const Vector &vecEnd, int nAxis, float flSign, float flDist); //----------------------------------------------------------------------------- // IntersectRayWithBox // // Purpose: Computes the intersection of a ray with a box (AABB) // Output : Returns true if there is an intersection + trace information //----------------------------------------------------------------------------- bool IntersectRayWithBox(const Vector &rayStart, const Vector &rayDelta, const Vector &boxMins, const Vector &boxMaxs, float epsilon, CBaseTrace *pTrace, float *pFractionLeftSolid = NULL); bool IntersectRayWithBox(const Ray_t &ray, const Vector &boxMins, const Vector &boxMaxs, float epsilon, CBaseTrace *pTrace, float *pFractionLeftSolid = NULL); //----------------------------------------------------------------------------- // Intersects a ray against a box //----------------------------------------------------------------------------- struct BoxTraceInfo_t { float t1; float t2; int hitside; bool startsolid; }; bool IntersectRayWithBox(const Vector &vecRayStart, const Vector &vecRayDelta, const Vector &boxMins, const Vector &boxMaxs, float flTolerance, BoxTraceInfo_t *pTrace); //----------------------------------------------------------------------------- // IntersectRayWithOBB // // Purpose: Computes the intersection of a ray with a oriented box (OBB) // Output : Returns true if there is an intersection + trace information //----------------------------------------------------------------------------- bool IntersectRayWithOBB(const Vector &vecRayStart, const Vector &vecRayDelta, const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace); bool IntersectRayWithOBB(const Vector &vecRayOrigin, const Vector &vecRayDelta, const Vector &vecBoxOrigin, const QAngle &angBoxRotation, const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace); bool IntersectRayWithOBB(const Ray_t &ray, const Vector &vecBoxOrigin, const QAngle &angBoxRotation, const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace); bool IntersectRayWithOBB(const Ray_t &ray, const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace); bool IntersectRayWithOBB(const Vector &vecRayStart, const Vector &vecRayDelta, const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, BoxTraceInfo_t *pTrace); //----------------------------------------------------------------------------- // // IsSphereIntersectingSphere // // returns true if there's an intersection between sphere and sphere // //----------------------------------------------------------------------------- bool IsSphereIntersectingSphere(const Vector ¢er1, float radius1, const Vector ¢er2, float radius2); //----------------------------------------------------------------------------- // // IsBoxIntersectingSphere // // returns true if there's an intersection between box and sphere // //----------------------------------------------------------------------------- bool IsBoxIntersectingSphere(const Vector &boxMin, const Vector &boxMax, const Vector ¢er, float radius); bool IsBoxIntersectingSphereExtents(const Vector &boxCenter, const Vector &boxHalfDiag, const Vector ¢er, float radius); //----------------------------------------------------------------------------- // returns true if there's an intersection between ray and sphere //----------------------------------------------------------------------------- bool IsRayIntersectingSphere(const Vector &vecRayOrigin, const Vector &vecRayDelta, const Vector &vecSphereCenter, float flRadius, float flTolerance = 0.0f); //----------------------------------------------------------------------------- // // IsCircleIntersectingRectangle // // returns true if there's an intersection between rectangle and circle // //----------------------------------------------------------------------------- bool IsCircleIntersectingRectangle(const Vector2D &boxMin, const Vector2D &boxMax, const Vector2D ¢er, float radius); //----------------------------------------------------------------------------- // // IsBoxIntersectingBox // // returns true if there's an intersection between two boxes // //----------------------------------------------------------------------------- bool IsBoxIntersectingBox(const Vector &boxMin1, const Vector &boxMax1, const Vector &boxMin2, const Vector &boxMax2); bool IsBoxIntersectingBoxExtents(const Vector &boxCenter1, const Vector &boxHalfDiagonal1, const Vector &boxCenter2, const Vector &boxHalfDiagonal2); #ifdef _X360 // inline version: #include "mathlib/ssemath.h" inline bool IsBoxIntersectingBoxExtents(const fltx4 boxCenter1, const fltx4 boxHalfDiagonal1, const fltx4 boxCenter2, const fltx4 boxHalfDiagonal2); #endif //----------------------------------------------------------------------------- // // IsOBBIntersectingOBB // // returns true if there's an intersection between two OBBs // //----------------------------------------------------------------------------- bool IsOBBIntersectingOBB(const Vector &vecOrigin1, const QAngle &vecAngles1, const Vector &boxMin1, const Vector &boxMax1, const Vector &vecOrigin2, const QAngle &vecAngles2, const Vector &boxMin2, const Vector &boxMax2, float flTolerance = 0.0f); //----------------------------------------------------------------------------- // // IsBoxIntersectingRay // // returns true if there's an intersection between box and ray // //----------------------------------------------------------------------------- bool FASTCALL IsBoxIntersectingRay(const Vector &boxMin, const Vector &boxMax, const Vector &origin, const Vector &delta, float flTolerance = 0.0f); bool FASTCALL IsBoxIntersectingRay(const Vector &boxMin, const Vector &boxMax, const Ray_t &ray, float flTolerance = 0.0f); bool FASTCALL IsBoxIntersectingRay(const Vector &boxMin, const Vector &boxMax, const Vector &origin, const Vector &delta, const Vector &invDelta, float flTolerance = 0.0f); // On the PC, we can't pass fltx4's in registers like this. On the x360, it is // much better if we do. #ifdef _X360 bool FASTCALL IsBoxIntersectingRay( fltx4 boxMin, fltx4 boxMax, fltx4 origin, fltx4 delta, fltx4 invDelta, // ray parameters fltx4 vTolerance = LoadZeroSIMD() ///< eg from ReplicateX4(flTolerance) ); #else bool FASTCALL IsBoxIntersectingRay( const fltx4 &boxMin, const fltx4 &boxMax, const fltx4 &origin, const fltx4 &delta, const fltx4 &invDelta, // ray parameters const fltx4 &vTolerance = Four_Zeros ///< eg from ReplicateX4(flTolerance) ); #endif bool inline FASTCALL IsBoxIntersectingRay(const fltx4 &boxMin, const fltx4 &boxMax, const fltx4 &origin, const fltx4 &delta, float flTolerance = 0.0f) { return IsBoxIntersectingRay(boxMin, boxMax, origin, delta, ReciprocalSIMD(delta), ReplicateX4(flTolerance)); } bool FASTCALL IsBoxIntersectingRay(const fltx4 &boxMin, const fltx4 &boxMax, const Ray_t &ray, float flTolerance = 0.0f); //----------------------------------------------------------------------------- // // IsPointInBox // // returns true if the point is in the box // //----------------------------------------------------------------------------- bool IsPointInBox(const Vector &pt, const Vector &boxMin, const Vector &boxMax); // SIMD version FORCEINLINE bool IsPointInBox(const fltx4 &pt, const fltx4 &boxMin, const fltx4 &boxMax) { fltx4 greater = CmpGtSIMD(pt, boxMax); fltx4 less = CmpLtSIMD(pt, boxMin); return (IsAllZeros(SetWToZeroSIMD(OrSIMD(greater, less)))); } //----------------------------------------------------------------------------- // Purpose: returns true if pt intersects the truncated cone // origin - cone tip, axis unit cone axis, cosAngle - cosine of cone axis to // surface angle //----------------------------------------------------------------------------- bool IsPointInCone(const Vector &pt, const Vector &origin, const Vector &axis, float cosAngle, float length); //----------------------------------------------------------------------------- // Intersects a plane with a triangle (using barycentric definition) // The return value, in pIntersection, is an array of barycentric coordinates // describing at most 2 intersection points. // The return value is the number of intersection points //----------------------------------------------------------------------------- int IntersectTriangleWithPlaneBarycentric(const Vector &org, const Vector &edgeU, const Vector &edgeV, const Vector4D &plane, Vector2D *pIntersection); //----------------------------------------------------------------------------- // // PointInQuadBarycentric // // Given a point and a quad in a plane return the u and v (barycentric) //positions // of the point relative to the quad. The points (v1,v2,v3,v4) should be given // in a counter-clockwise order with v1 acting as the primary corner (u=0, // v=0). Thus, u0 = v2 - v1, and v0 = v4 - v1. // //----------------------------------------------------------------------------- enum QuadBarycentricRetval_t { BARY_QUADRATIC_FALSE = 0, BARY_QUADRATIC_TRUE = 1, BARY_QUADRATIC_NEGATIVE_DISCRIMINANT = 2 }; QuadBarycentricRetval_t PointInQuadToBarycentric( const Vector &v1, const Vector &v2, const Vector &v3, const Vector &v4, const Vector &point, Vector2D &uv); void PointInQuadFromBarycentric(const Vector &v1, const Vector &v2, const Vector &v3, const Vector &v4, const Vector2D &uv, Vector &point); void TexCoordInQuadFromBarycentric(const Vector2D &v1, const Vector2D &v2, const Vector2D &v3, const Vector2D &v4, const Vector2D &uv, Vector2D &texCoord); //----------------------------------------------------------------------------- // Compute point from barycentric specification // Edge u goes from v0 to v1, edge v goes from v0 to v2 //----------------------------------------------------------------------------- void ComputePointFromBarycentric(const Vector &v0, const Vector &v1, const Vector &v2, float u, float v, Vector &pt); void ComputePointFromBarycentric(const Vector2D &v0, const Vector2D &v1, const Vector2D &v2, float u, float v, Vector2D &pt); //----------------------------------------------------------------------------- // Swept OBB test //----------------------------------------------------------------------------- bool IsRayIntersectingOBB(const Ray_t &ray, const Vector &org, const QAngle &angles, const Vector &mins, const Vector &maxs); //----------------------------------------------------------------------------- // Compute a separating plane between two boxes (expensive!) // Returns false if no separating plane exists //----------------------------------------------------------------------------- bool ComputeSeparatingPlane(const Vector &org1, const QAngle &angles1, const Vector &min1, const Vector &max1, const Vector &org2, const QAngle &angles2, const Vector &min2, const Vector &max2, float tolerance, cplane_t *pPlane); //----------------------------------------------------------------------------- // IsBoxIntersectingTriangle // // Test for an intersection (overlap) between an axial-aligned bounding // box (AABB) and a triangle. // // Triangle points are in counter-clockwise order with the normal facing "out." // // Using the "Separating-Axis Theorem" to test for intersections between // a triangle and an axial-aligned bounding box (AABB). // 1. 3 Axis Plane Tests - x, y, z // 2. 9 Edge Planes Tests - the 3 edges of the triangle crossed with all 3 axial // planes (x, y, z) // 3. 1 Face Plane Test - the plane the triangle resides in (cplane_t plane) //----------------------------------------------------------------------------- bool IsBoxIntersectingTriangle(const Vector &vecBoxCenter, const Vector &vecBoxExtents, const Vector &v1, const Vector &v2, const Vector &v3, const cplane_t &plane, float flTolerance); Vector CalcClosestPointOnTriangle(const Vector &P, const Vector &v0, const Vector &v1, const Vector &v2); //----------------------------------------------------------------------------- // Compute if the OBB intersects the quad plane, and whether the entire // OBB/Quad intersection is contained within the quad itself // // False if no intersection exists, or if part of the intersection is // outside the quad's extents //----------------------------------------------------------------------------- bool OBBHasFullyContainedIntersectionWithQuad( const Vector &vOBBExtent1_Scaled, const Vector &vOBBExtent2_Scaled, const Vector &vOBBExtent3_Scaled, const Vector &ptOBBCenter, const Vector &vQuadNormal, float fQuadPlaneDist, const Vector &ptQuadCenter, const Vector &vQuadExtent1_Normalized, float fQuadExtent1Length, const Vector &vQuadExtent2_Normalized, float fQuadExtent2Length); //----------------------------------------------------------------------------- // Compute if the Ray intersects the quad plane, and whether the entire // Ray/Quad intersection is contained within the quad itself // // False if no intersection exists, or if part of the intersection is // outside the quad's extents //----------------------------------------------------------------------------- bool RayHasFullyContainedIntersectionWithQuad( const Ray_t &ray, const Vector &vQuadNormal, float fQuadPlaneDist, const Vector &ptQuadCenter, const Vector &vQuadExtent1_Normalized, float fQuadExtent1Length, const Vector &vQuadExtent2_Normalized, float fQuadExtent2Length); //----------------------------------------------------------------------------- // INLINES //----------------------------------------------------------------------------- #ifdef _X360 inline bool IsBoxIntersectingBoxExtents(const fltx4 boxCenter1, const fltx4 boxHalfDiagonal1, const fltx4 boxCenter2, const fltx4 boxHalfDiagonal2) { fltx4 vecDelta, vecSize; vecDelta = SubSIMD(boxCenter1, boxCenter2); vecSize = AddSIMD(boxHalfDiagonal1, boxHalfDiagonal2); uint condition; XMVectorInBoundsR(&condition, vecDelta, vecSize); // we want the top three words to be all 1's ; that means in bounds return XMComparisonAllInBounds(condition); } #endif #endif // COLLISIONUTILS_H