Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3262 lines
110 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Common collision utility methods
  4. //
  5. // $Header: $
  6. // $NoKeywords: $
  7. //=============================================================================//
  8. #if !defined(_STATIC_LINKED) || defined(_SHARED_LIB)
  9. #include "collisionutils.h"
  10. #include "cmodel.h"
  11. #include "mathlib/mathlib.h"
  12. #include "mathlib/vector.h"
  13. #include "tier0/dbg.h"
  14. #include <float.h>
  15. #include "mathlib/vector4d.h"
  16. #include "trace.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. #define UNINIT -99999.0
  20. //-----------------------------------------------------------------------------
  21. // Clears the trace
  22. //-----------------------------------------------------------------------------
  23. static void Collision_ClearTrace( const Vector &vecRayStart, const Vector &vecRayDelta, CBaseTrace *pTrace )
  24. {
  25. pTrace->startpos = vecRayStart;
  26. pTrace->endpos = vecRayStart;
  27. pTrace->endpos += vecRayDelta;
  28. pTrace->startsolid = false;
  29. pTrace->allsolid = false;
  30. pTrace->fraction = 1.0f;
  31. pTrace->contents = 0;
  32. }
  33. //-----------------------------------------------------------------------------
  34. // Compute the offset in t along the ray that we'll use for the collision
  35. //-----------------------------------------------------------------------------
  36. static float ComputeBoxOffset( const Ray_t& ray )
  37. {
  38. if (ray.m_IsRay)
  39. return 1e-3f;
  40. // Find the projection of the box diagonal along the ray...
  41. float offset = FloatMakePositive(ray.m_Extents[0] * ray.m_Delta[0]) +
  42. FloatMakePositive(ray.m_Extents[1] * ray.m_Delta[1]) +
  43. FloatMakePositive(ray.m_Extents[2] * ray.m_Delta[2]);
  44. // We need to divide twice: Once to normalize the computation above
  45. // so we get something in units of extents, and the second to normalize
  46. // that with respect to the entire raycast.
  47. offset *= InvRSquared( ray.m_Delta );
  48. // 1e-3 is an epsilon
  49. return offset + 1e-3;
  50. }
  51. //-----------------------------------------------------------------------------
  52. // Intersects a swept box against a triangle
  53. //-----------------------------------------------------------------------------
  54. float IntersectRayWithTriangle( const Ray_t& ray,
  55. const Vector& v1, const Vector& v2, const Vector& v3, bool oneSided )
  56. {
  57. // This is cute: Use barycentric coordinates to represent the triangle
  58. // Vo(1-u-v) + V1u + V2v and intersect that with a line Po + Dt
  59. // This gives us 3 equations + 3 unknowns, which we can solve with
  60. // Cramer's rule...
  61. // E1x u + E2x v - Dx t = Pox - Vox
  62. // There's a couple of other optimizations, Cramer's rule involves
  63. // computing the determinant of a matrix which has been constructed
  64. // by three vectors. It turns out that
  65. // det | A B C | = -( A x C ) dot B or -(C x B) dot A
  66. // which we'll use below..
  67. Vector edge1, edge2, org;
  68. VectorSubtract( v2, v1, edge1 );
  69. VectorSubtract( v3, v1, edge2 );
  70. // Cull out one-sided stuff
  71. if (oneSided)
  72. {
  73. Vector normal;
  74. CrossProduct( edge1, edge2, normal );
  75. if (DotProduct( normal, ray.m_Delta ) >= 0.0f)
  76. return -1.0f;
  77. }
  78. // FIXME: This is inaccurate, but fast for boxes
  79. // We want to do a fast separating axis implementation here
  80. // with a swept triangle along the reverse direction of the ray.
  81. // Compute some intermediary terms
  82. Vector dirCrossEdge2, orgCrossEdge1;
  83. CrossProduct( ray.m_Delta, edge2, dirCrossEdge2 );
  84. // Compute the denominator of Cramer's rule:
  85. // | -Dx E1x E2x |
  86. // det | -Dy E1y E2y | = (D x E2) dot E1
  87. // | -Dz E1z E2z |
  88. float denom = DotProduct( dirCrossEdge2, edge1 );
  89. if( FloatMakePositive( denom ) < 1e-6 )
  90. return -1.0f;
  91. denom = 1.0f / denom;
  92. // Compute u. It's gotta lie in the range of 0 to 1.
  93. // | -Dx orgx E2x |
  94. // u = denom * det | -Dy orgy E2y | = (D x E2) dot org
  95. // | -Dz orgz E2z |
  96. VectorSubtract( ray.m_Start, v1, org );
  97. float u = DotProduct( dirCrossEdge2, org ) * denom;
  98. if ((u < 0.0f) || (u > 1.0f))
  99. return -1.0f;
  100. // Compute t and v the same way...
  101. // In barycentric coords, u + v < 1
  102. CrossProduct( org, edge1, orgCrossEdge1 );
  103. float v = DotProduct( orgCrossEdge1, ray.m_Delta ) * denom;
  104. if ((v < 0.0f) || (v + u > 1.0f))
  105. return -1.0f;
  106. // Compute the distance along the ray direction that we need to fudge
  107. // when using swept boxes
  108. float boxt = ComputeBoxOffset( ray );
  109. float t = DotProduct( orgCrossEdge1, edge2 ) * denom;
  110. if ((t < -boxt) || (t > 1.0f + boxt))
  111. return -1.0f;
  112. return clamp( t, 0.f, 1.f );
  113. }
  114. //-----------------------------------------------------------------------------
  115. // computes the barycentric coordinates of an intersection
  116. //-----------------------------------------------------------------------------
  117. bool ComputeIntersectionBarycentricCoordinates( const Ray_t& ray,
  118. const Vector& v1, const Vector& v2, const Vector& v3, float& u, float& v,
  119. float *t )
  120. {
  121. Vector edge1, edge2, org;
  122. VectorSubtract( v2, v1, edge1 );
  123. VectorSubtract( v3, v1, edge2 );
  124. // Compute some intermediary terms
  125. Vector dirCrossEdge2, orgCrossEdge1;
  126. CrossProduct( ray.m_Delta, edge2, dirCrossEdge2 );
  127. // Compute the denominator of Cramer's rule:
  128. // | -Dx E1x E2x |
  129. // det | -Dy E1y E2y | = (D x E2) dot E1
  130. // | -Dz E1z E2z |
  131. float denom = DotProduct( dirCrossEdge2, edge1 );
  132. if( FloatMakePositive( denom ) < 1e-6 )
  133. return false;
  134. denom = 1.0f / denom;
  135. // Compute u. It's gotta lie in the range of 0 to 1.
  136. // | -Dx orgx E2x |
  137. // u = denom * det | -Dy orgy E2y | = (D x E2) dot org
  138. // | -Dz orgz E2z |
  139. VectorSubtract( ray.m_Start, v1, org );
  140. u = DotProduct( dirCrossEdge2, org ) * denom;
  141. // Compute t and v the same way...
  142. // In barycentric coords, u + v < 1
  143. CrossProduct( org, edge1, orgCrossEdge1 );
  144. v = DotProduct( orgCrossEdge1, ray.m_Delta ) * denom;
  145. // Compute the distance along the ray direction that we need to fudge
  146. // when using swept boxes
  147. if( t )
  148. {
  149. float boxt = ComputeBoxOffset( ray );
  150. *t = DotProduct( orgCrossEdge1, edge2 ) * denom;
  151. if( ( *t < -boxt ) || ( *t > 1.0f + boxt ) )
  152. return false;
  153. }
  154. return true;
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Intersects a plane with a triangle (requires barycentric definition)
  158. //-----------------------------------------------------------------------------
  159. int IntersectTriangleWithPlaneBarycentric( const Vector& org, const Vector& edgeU,
  160. const Vector& edgeV, const Vector4D& plane, Vector2D* pIntersection )
  161. {
  162. // This uses a barycentric method, since we need that to determine
  163. // interpolated points, alphas, and normals
  164. // Given the plane equation P dot N + d = 0
  165. // and the barycentric coodinate equation P = Org + EdgeU * u + EdgeV * v
  166. // Plug em in. Intersection occurs at u = 0 or v = 0 or u + v = 1
  167. float orgDotNormal = DotProduct( org, plane.AsVector3D() );
  168. float edgeUDotNormal = DotProduct( edgeU, plane.AsVector3D() );
  169. float edgeVDotNormal = DotProduct( edgeV, plane.AsVector3D() );
  170. int ptIdx = 0;
  171. // u = 0
  172. if ( edgeVDotNormal != 0.0f )
  173. {
  174. pIntersection[ptIdx].x = 0.0f;
  175. pIntersection[ptIdx].y = - ( orgDotNormal - plane.w ) / edgeVDotNormal;
  176. if ((pIntersection[ptIdx].y >= 0.0f) && (pIntersection[ptIdx].y <= 1.0f))
  177. ++ptIdx;
  178. }
  179. // v = 0
  180. if ( edgeUDotNormal != 0.0f )
  181. {
  182. pIntersection[ptIdx].x = - ( orgDotNormal - plane.w ) / edgeUDotNormal;
  183. pIntersection[ptIdx].y = 0.0f;
  184. if ((pIntersection[ptIdx].x >= 0.0f) && (pIntersection[ptIdx].x <= 1.0f))
  185. ++ptIdx;
  186. }
  187. // u + v = 1
  188. if (ptIdx == 2)
  189. return ptIdx;
  190. if ( edgeVDotNormal != edgeUDotNormal )
  191. {
  192. pIntersection[ptIdx].x = - ( orgDotNormal - plane.w + edgeVDotNormal) /
  193. ( edgeUDotNormal - edgeVDotNormal);
  194. pIntersection[ptIdx].y = 1.0f - pIntersection[ptIdx].x;
  195. if ((pIntersection[ptIdx].x >= 0.0f) && (pIntersection[ptIdx].x <= 1.0f) &&
  196. (pIntersection[ptIdx].y >= 0.0f) && (pIntersection[ptIdx].y <= 1.0f))
  197. ++ptIdx;
  198. }
  199. Assert( ptIdx < 3 );
  200. return ptIdx;
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Returns true if a box intersects with a sphere
  204. //-----------------------------------------------------------------------------
  205. bool IsSphereIntersectingSphere( const Vector& center1, float radius1,
  206. const Vector& center2, float radius2 )
  207. {
  208. Vector delta;
  209. VectorSubtract( center2, center1, delta );
  210. float distSq = delta.LengthSqr();
  211. float radiusSum = radius1 + radius2;
  212. return (distSq <= (radiusSum * radiusSum));
  213. }
  214. //-----------------------------------------------------------------------------
  215. // Returns true if a box intersects with a sphere
  216. //-----------------------------------------------------------------------------
  217. bool IsBoxIntersectingSphere( const Vector& boxMin, const Vector& boxMax,
  218. const Vector& center, float radius )
  219. {
  220. // See Graphics Gems, box-sphere intersection
  221. float dmin = 0.0f;
  222. float flDelta;
  223. // Unrolled the loop.. this is a big cycle stealer...
  224. if (center[0] < boxMin[0])
  225. {
  226. flDelta = center[0] - boxMin[0];
  227. dmin += flDelta * flDelta;
  228. }
  229. else if (center[0] > boxMax[0])
  230. {
  231. flDelta = boxMax[0] - center[0];
  232. dmin += flDelta * flDelta;
  233. }
  234. if (center[1] < boxMin[1])
  235. {
  236. flDelta = center[1] - boxMin[1];
  237. dmin += flDelta * flDelta;
  238. }
  239. else if (center[1] > boxMax[1])
  240. {
  241. flDelta = boxMax[1] - center[1];
  242. dmin += flDelta * flDelta;
  243. }
  244. if (center[2] < boxMin[2])
  245. {
  246. flDelta = center[2] - boxMin[2];
  247. dmin += flDelta * flDelta;
  248. }
  249. else if (center[2] > boxMax[2])
  250. {
  251. flDelta = boxMax[2] - center[2];
  252. dmin += flDelta * flDelta;
  253. }
  254. return dmin < radius * radius;
  255. }
  256. bool IsBoxIntersectingSphereExtents( const Vector& boxCenter, const Vector& boxHalfDiag,
  257. const Vector& center, float radius )
  258. {
  259. // See Graphics Gems, box-sphere intersection
  260. float dmin = 0.0f;
  261. float flDelta, flDiff;
  262. // Unrolled the loop.. this is a big cycle stealer...
  263. flDiff = FloatMakePositive( center.x - boxCenter.x );
  264. if (flDiff > boxHalfDiag.x)
  265. {
  266. flDelta = flDiff - boxHalfDiag.x;
  267. dmin += flDelta * flDelta;
  268. }
  269. flDiff = FloatMakePositive( center.y - boxCenter.y );
  270. if (flDiff > boxHalfDiag.y)
  271. {
  272. flDelta = flDiff - boxHalfDiag.y;
  273. dmin += flDelta * flDelta;
  274. }
  275. flDiff = FloatMakePositive( center.z - boxCenter.z );
  276. if (flDiff > boxHalfDiag.z)
  277. {
  278. flDelta = flDiff - boxHalfDiag.z;
  279. dmin += flDelta * flDelta;
  280. }
  281. return dmin < radius * radius;
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Returns true if a rectangle intersects with a circle
  285. //-----------------------------------------------------------------------------
  286. bool IsCircleIntersectingRectangle( const Vector2D& boxMin, const Vector2D& boxMax,
  287. const Vector2D& center, float radius )
  288. {
  289. // See Graphics Gems, box-sphere intersection
  290. float dmin = 0.0f;
  291. float flDelta;
  292. if (center[0] < boxMin[0])
  293. {
  294. flDelta = center[0] - boxMin[0];
  295. dmin += flDelta * flDelta;
  296. }
  297. else if (center[0] > boxMax[0])
  298. {
  299. flDelta = boxMax[0] - center[0];
  300. dmin += flDelta * flDelta;
  301. }
  302. if (center[1] < boxMin[1])
  303. {
  304. flDelta = center[1] - boxMin[1];
  305. dmin += flDelta * flDelta;
  306. }
  307. else if (center[1] > boxMax[1])
  308. {
  309. flDelta = boxMax[1] - center[1];
  310. dmin += flDelta * flDelta;
  311. }
  312. return dmin < radius * radius;
  313. }
  314. //-----------------------------------------------------------------------------
  315. // returns true if there's an intersection between ray and sphere
  316. //-----------------------------------------------------------------------------
  317. bool IsRayIntersectingSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta,
  318. const Vector& vecCenter, float flRadius, float flTolerance )
  319. {
  320. // For this algorithm, find a point on the ray which is closest to the sphere origin
  321. // Do this by making a plane passing through the sphere origin
  322. // whose normal is parallel to the ray. Intersect that plane with the ray.
  323. // Plane: N dot P = I, N = D (ray direction), I = C dot N = C dot D
  324. // Ray: P = O + D * t
  325. // D dot ( O + D * t ) = C dot D
  326. // D dot O + D dot D * t = C dot D
  327. // t = (C - O) dot D / D dot D
  328. // Clamp t to (0,1)
  329. // Find distance of the point on the ray to the sphere center.
  330. Assert( flTolerance >= 0.0f );
  331. flRadius += flTolerance;
  332. Vector vecRayToSphere;
  333. VectorSubtract( vecCenter, vecRayOrigin, vecRayToSphere );
  334. float flNumerator = DotProduct( vecRayToSphere, vecRayDelta );
  335. float t;
  336. if (flNumerator <= 0.0f)
  337. {
  338. t = 0.0f;
  339. }
  340. else
  341. {
  342. float flDenominator = DotProduct( vecRayDelta, vecRayDelta );
  343. if ( flNumerator > flDenominator )
  344. t = 1.0f;
  345. else
  346. t = flNumerator / flDenominator;
  347. }
  348. Vector vecClosestPoint;
  349. VectorMA( vecRayOrigin, t, vecRayDelta, vecClosestPoint );
  350. return ( vecClosestPoint.DistToSqr( vecCenter ) <= flRadius * flRadius );
  351. // NOTE: This in an alternate algorithm which I didn't use because I'd have to use a sqrt
  352. // So it's probably faster to do this other algorithm. I'll leave the comments here
  353. // for how to go back if we want to
  354. // Solve using the ray equation + the sphere equation
  355. // P = o + dt
  356. // (x - xc)^2 + (y - yc)^2 + (z - zc)^2 = r^2
  357. // (ox + dx * t - xc)^2 + (oy + dy * t - yc)^2 + (oz + dz * t - zc)^2 = r^2
  358. // (ox - xc)^2 + 2 * (ox-xc) * dx * t + dx^2 * t^2 +
  359. // (oy - yc)^2 + 2 * (oy-yc) * dy * t + dy^2 * t^2 +
  360. // (oz - zc)^2 + 2 * (oz-zc) * dz * t + dz^2 * t^2 = r^2
  361. // (dx^2 + dy^2 + dz^2) * t^2 + 2 * ((ox-xc)dx + (oy-yc)dy + (oz-zc)dz) t +
  362. // (ox-xc)^2 + (oy-yc)^2 + (oz-zc)^2 - r^2 = 0
  363. // or, t = (-b +/- sqrt( b^2 - 4ac)) / 2a
  364. // a = DotProduct( vecRayDelta, vecRayDelta );
  365. // b = 2 * DotProduct( vecRayOrigin - vecCenter, vecRayDelta )
  366. // c = DotProduct(vecRayOrigin - vecCenter, vecRayOrigin - vecCenter) - flRadius * flRadius;
  367. // Valid solutions are possible only if b^2 - 4ac >= 0
  368. // Therefore, compute that value + see if we got it
  369. }
  370. //-----------------------------------------------------------------------------
  371. //
  372. // IntersectInfiniteRayWithSphere
  373. //
  374. // Returns whether or not there was an intersection.
  375. // Returns the two intersection points
  376. //
  377. //-----------------------------------------------------------------------------
  378. bool IntersectInfiniteRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta,
  379. const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 )
  380. {
  381. // Solve using the ray equation + the sphere equation
  382. // P = o + dt
  383. // (x - xc)^2 + (y - yc)^2 + (z - zc)^2 = r^2
  384. // (ox + dx * t - xc)^2 + (oy + dy * t - yc)^2 + (oz + dz * t - zc)^2 = r^2
  385. // (ox - xc)^2 + 2 * (ox-xc) * dx * t + dx^2 * t^2 +
  386. // (oy - yc)^2 + 2 * (oy-yc) * dy * t + dy^2 * t^2 +
  387. // (oz - zc)^2 + 2 * (oz-zc) * dz * t + dz^2 * t^2 = r^2
  388. // (dx^2 + dy^2 + dz^2) * t^2 + 2 * ((ox-xc)dx + (oy-yc)dy + (oz-zc)dz) t +
  389. // (ox-xc)^2 + (oy-yc)^2 + (oz-zc)^2 - r^2 = 0
  390. // or, t = (-b +/- sqrt( b^2 - 4ac)) / 2a
  391. // a = DotProduct( vecRayDelta, vecRayDelta );
  392. // b = 2 * DotProduct( vecRayOrigin - vecCenter, vecRayDelta )
  393. // c = DotProduct(vecRayOrigin - vecCenter, vecRayOrigin - vecCenter) - flRadius * flRadius;
  394. Vector vecSphereToRay;
  395. VectorSubtract( vecRayOrigin, vecSphereCenter, vecSphereToRay );
  396. float a = DotProduct( vecRayDelta, vecRayDelta );
  397. // This would occur in the case of a zero-length ray
  398. if ( a == 0.0f )
  399. {
  400. *pT1 = *pT2 = 0.0f;
  401. return vecSphereToRay.LengthSqr() <= flRadius * flRadius;
  402. }
  403. float b = 2 * DotProduct( vecSphereToRay, vecRayDelta );
  404. float c = DotProduct( vecSphereToRay, vecSphereToRay ) - flRadius * flRadius;
  405. float flDiscrim = b * b - 4 * a * c;
  406. if ( flDiscrim < 0.0f )
  407. return false;
  408. flDiscrim = sqrt( flDiscrim );
  409. float oo2a = 0.5f / a;
  410. *pT1 = ( - b - flDiscrim ) * oo2a;
  411. *pT2 = ( - b + flDiscrim ) * oo2a;
  412. return true;
  413. }
  414. //-----------------------------------------------------------------------------
  415. //
  416. // IntersectRayWithSphere
  417. //
  418. // Returns whether or not there was an intersection.
  419. // Returns the two intersection points, clamped to (0,1)
  420. //
  421. //-----------------------------------------------------------------------------
  422. bool IntersectRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta,
  423. const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 )
  424. {
  425. if ( !IntersectInfiniteRayWithSphere( vecRayOrigin, vecRayDelta, vecSphereCenter, flRadius, pT1, pT2 ) )
  426. return false;
  427. if (( *pT1 > 1.0f ) || ( *pT2 < 0.0f ))
  428. return false;
  429. // Clamp it!
  430. if ( *pT1 < 0.0f )
  431. *pT1 = 0.0f;
  432. if ( *pT2 > 1.0f )
  433. *pT2 = 1.0f;
  434. return true;
  435. }
  436. // returns true if the sphere and cone intersect
  437. // NOTE: cone sine/cosine are the half angle of the cone
  438. bool IsSphereIntersectingCone( const Vector &sphereCenter, float sphereRadius, const Vector &coneOrigin, const Vector &coneNormal, float coneSine, float coneCosine )
  439. {
  440. Vector backCenter = coneOrigin - (sphereRadius / coneSine) * coneNormal;
  441. Vector delta = sphereCenter - backCenter;
  442. float deltaLen = delta.Length();
  443. if ( DotProduct(coneNormal, delta) >= deltaLen*coneCosine )
  444. {
  445. delta = sphereCenter - coneOrigin;
  446. deltaLen = delta.Length();
  447. if ( -DotProduct(coneNormal, delta) >= deltaLen * coneSine )
  448. {
  449. return ( deltaLen <= sphereRadius ) ? true : false;
  450. }
  451. return true;
  452. }
  453. return false;
  454. }
  455. //-----------------------------------------------------------------------------
  456. // returns true if the point is in the box
  457. //-----------------------------------------------------------------------------
  458. bool IsPointInBox( const Vector& pt, const Vector& boxMin, const Vector& boxMax )
  459. {
  460. Assert( boxMin[0] <= boxMax[0] );
  461. Assert( boxMin[1] <= boxMax[1] );
  462. Assert( boxMin[2] <= boxMax[2] );
  463. // on x360, force use of SIMD version.
  464. if (IsX360())
  465. {
  466. return IsPointInBox( LoadUnaligned3SIMD(pt.Base()), LoadUnaligned3SIMD(boxMin.Base()), LoadUnaligned3SIMD(boxMax.Base()) ) ;
  467. }
  468. if ( (pt[0] > boxMax[0]) || (pt[0] < boxMin[0]) )
  469. return false;
  470. if ( (pt[1] > boxMax[1]) || (pt[1] < boxMin[1]) )
  471. return false;
  472. if ( (pt[2] > boxMax[2]) || (pt[2] < boxMin[2]) )
  473. return false;
  474. return true;
  475. }
  476. bool IsPointInCone( const Vector &pt, const Vector &origin, const Vector &axis, float cosAngle, float length )
  477. {
  478. Vector delta = pt - origin;
  479. float dist = VectorNormalize( delta );
  480. float dot = DotProduct( delta, axis );
  481. if ( dot < cosAngle )
  482. return false;
  483. if ( dist * dot > length )
  484. return false;
  485. return true;
  486. }
  487. //-----------------------------------------------------------------------------
  488. // returns true if there's an intersection between two boxes
  489. //-----------------------------------------------------------------------------
  490. bool IsBoxIntersectingBox( const Vector& boxMin1, const Vector& boxMax1,
  491. const Vector& boxMin2, const Vector& boxMax2 )
  492. {
  493. Assert( boxMin1[0] <= boxMax1[0] );
  494. Assert( boxMin1[1] <= boxMax1[1] );
  495. Assert( boxMin1[2] <= boxMax1[2] );
  496. Assert( boxMin2[0] <= boxMax2[0] );
  497. Assert( boxMin2[1] <= boxMax2[1] );
  498. Assert( boxMin2[2] <= boxMax2[2] );
  499. if ( (boxMin1[0] > boxMax2[0]) || (boxMax1[0] < boxMin2[0]) )
  500. return false;
  501. if ( (boxMin1[1] > boxMax2[1]) || (boxMax1[1] < boxMin2[1]) )
  502. return false;
  503. if ( (boxMin1[2] > boxMax2[2]) || (boxMax1[2] < boxMin2[2]) )
  504. return false;
  505. return true;
  506. }
  507. bool IsBoxIntersectingBoxExtents( const Vector& boxCenter1, const Vector& boxHalfDiagonal1,
  508. const Vector& boxCenter2, const Vector& boxHalfDiagonal2 )
  509. {
  510. Vector vecDelta, vecSize;
  511. VectorSubtract( boxCenter1, boxCenter2, vecDelta );
  512. VectorAdd( boxHalfDiagonal1, boxHalfDiagonal2, vecSize );
  513. return ( FloatMakePositive( vecDelta.x ) <= vecSize.x ) &&
  514. ( FloatMakePositive( vecDelta.y ) <= vecSize.y ) &&
  515. ( FloatMakePositive( vecDelta.z ) <= vecSize.z );
  516. }
  517. //-----------------------------------------------------------------------------
  518. //
  519. // IsOBBIntersectingOBB
  520. //
  521. // returns true if there's an intersection between two OBBs
  522. //
  523. //-----------------------------------------------------------------------------
  524. bool IsOBBIntersectingOBB( const Vector &vecOrigin1, const QAngle &vecAngles1, const Vector& boxMin1, const Vector& boxMax1,
  525. const Vector &vecOrigin2, const QAngle &vecAngles2, const Vector& boxMin2, const Vector& boxMax2, float flTolerance )
  526. {
  527. // FIXME: Simple case AABB check doesn't work because the min and max extents are not oriented based on the angle
  528. // this fast check would only be good for cubes.
  529. /*if ( vecAngles1 == vecAngles2 )
  530. {
  531. const Vector &vecDelta = vecOrigin2 - vecOrigin1;
  532. Vector vecOtherMins, vecOtherMaxs;
  533. VectorAdd( boxMin2, vecDelta, vecOtherMins );
  534. VectorAdd( boxMax2, vecDelta, vecOtherMaxs );
  535. return IsBoxIntersectingBox( boxMin1, boxMax1, vecOtherMins, vecOtherMaxs );
  536. }*/
  537. // OBB test...
  538. cplane_t plane;
  539. bool bFoundPlane = ComputeSeparatingPlane( vecOrigin1, vecAngles1, boxMin1, boxMax1,
  540. vecOrigin2, vecAngles2, boxMin2, boxMax2, flTolerance, &plane );
  541. return (bFoundPlane == false);
  542. }
  543. // NOTE: This is only very slightly faster on high end PCs and x360
  544. #define USE_SIMD_RAY_CHECKS 1
  545. //-----------------------------------------------------------------------------
  546. // returns true if there's an intersection between box and ray
  547. //-----------------------------------------------------------------------------
  548. bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax,
  549. const Vector& origin, const Vector& vecDelta, float flTolerance )
  550. {
  551. #if USE_SIMD_RAY_CHECKS
  552. // Load the unaligned ray/box parameters into SIMD registers
  553. fltx4 start = LoadUnaligned3SIMD(origin.Base());
  554. fltx4 delta = LoadUnaligned3SIMD(vecDelta.Base());
  555. fltx4 boxMins = LoadUnaligned3SIMD( boxMin.Base() );
  556. fltx4 boxMaxs = LoadUnaligned3SIMD( boxMax.Base() );
  557. fltx4 epsilon = ReplicateX4(flTolerance);
  558. // compute the mins/maxs of the box expanded by the ray extents
  559. // relocate the problem so that the ray start is at the origin.
  560. fltx4 offsetMins = SubSIMD(boxMins, start);
  561. fltx4 offsetMaxs = SubSIMD(boxMaxs, start);
  562. fltx4 offsetMinsExpanded = SubSIMD(offsetMins, epsilon);
  563. fltx4 offsetMaxsExpanded = AddSIMD(offsetMaxs, epsilon);
  564. // Check to see if both the origin (start point) and the end point (delta) are on the front side
  565. // of any of the box sides - if so there can be no intersection
  566. fltx4 startOutMins = CmpLtSIMD(Four_Zeros, offsetMinsExpanded);
  567. fltx4 endOutMins = CmpLtSIMD(delta,offsetMinsExpanded);
  568. fltx4 minsMask = AndSIMD( startOutMins, endOutMins );
  569. fltx4 startOutMaxs = CmpGtSIMD(Four_Zeros, offsetMaxsExpanded);
  570. fltx4 endOutMaxs = CmpGtSIMD(delta,offsetMaxsExpanded);
  571. fltx4 maxsMask = AndSIMD( startOutMaxs, endOutMaxs );
  572. if ( IsAnyNegative(SetWToZeroSIMD(OrSIMD(minsMask,maxsMask))))
  573. return false;
  574. // now build the per-axis interval of t for intersections
  575. fltx4 invDelta = ReciprocalSaturateSIMD(delta);
  576. fltx4 tmins = MulSIMD( offsetMinsExpanded, invDelta );
  577. fltx4 tmaxs = MulSIMD( offsetMaxsExpanded, invDelta );
  578. fltx4 crossPlane = OrSIMD(XorSIMD(startOutMins,endOutMins), XorSIMD(startOutMaxs,endOutMaxs));
  579. // only consider axes where we crossed a plane
  580. tmins = MaskedAssign( crossPlane, tmins, Four_Negative_FLT_MAX );
  581. tmaxs = MaskedAssign( crossPlane, tmaxs, Four_FLT_MAX );
  582. // now sort the interval per axis
  583. fltx4 mint = MinSIMD( tmins, tmaxs );
  584. fltx4 maxt = MaxSIMD( tmins, tmaxs );
  585. // now find the intersection of the intervals on all axes
  586. fltx4 firstOut = FindLowestSIMD3(maxt);
  587. fltx4 lastIn = FindHighestSIMD3(mint);
  588. // NOTE: This is really a scalar quantity now [t0,t1] == [lastIn,firstOut]
  589. firstOut = MinSIMD(firstOut, Four_Ones);
  590. lastIn = MaxSIMD(lastIn, Four_Zeros);
  591. // If the final interval is valid lastIn<firstOut, check for separation
  592. fltx4 separation = CmpGtSIMD(lastIn, firstOut);
  593. return IsAllZeros(separation);
  594. #else
  595. // On the x360, we force use of the SIMD functions.
  596. #if defined(_X360)
  597. if (IsX360())
  598. {
  599. fltx4 delta = LoadUnaligned3SIMD(vecDelta.Base());
  600. return IsBoxIntersectingRay(
  601. LoadUnaligned3SIMD(boxMin.Base()), LoadUnaligned3SIMD(boxMax.Base()),
  602. LoadUnaligned3SIMD(origin.Base()), delta, ReciprocalSIMD(delta), // ray parameters
  603. ReplicateX4(flTolerance) ///< eg from ReplicateX4(flTolerance)
  604. );
  605. }
  606. #endif
  607. Assert( boxMin[0] <= boxMax[0] );
  608. Assert( boxMin[1] <= boxMax[1] );
  609. Assert( boxMin[2] <= boxMax[2] );
  610. // FIXME: Surely there's a faster way
  611. float tmin = -FLT_MAX;
  612. float tmax = FLT_MAX;
  613. for (int i = 0; i < 3; ++i)
  614. {
  615. // Parallel case...
  616. if (FloatMakePositive(vecDelta[i]) < 1e-8)
  617. {
  618. // Check that origin is in the box
  619. // if not, then it doesn't intersect..
  620. if ( (origin[i] < boxMin[i] - flTolerance) || (origin[i] > boxMax[i] + flTolerance) )
  621. return false;
  622. continue;
  623. }
  624. // non-parallel case
  625. // Find the t's corresponding to the entry and exit of
  626. // the ray along x, y, and z. The find the furthest entry
  627. // point, and the closest exit point. Once that is done,
  628. // we know we don't collide if the closest exit point
  629. // is behind the starting location. We also don't collide if
  630. // the closest exit point is in front of the furthest entry point
  631. float invDelta = 1.0f / vecDelta[i];
  632. float t1 = (boxMin[i] - flTolerance - origin[i]) * invDelta;
  633. float t2 = (boxMax[i] + flTolerance - origin[i]) * invDelta;
  634. if (t1 > t2)
  635. {
  636. float temp = t1;
  637. t1 = t2;
  638. t2 = temp;
  639. }
  640. if (t1 > tmin)
  641. tmin = t1;
  642. if (t2 < tmax)
  643. tmax = t2;
  644. if (tmin > tmax)
  645. return false;
  646. if (tmax < 0)
  647. return false;
  648. if (tmin > 1)
  649. return false;
  650. }
  651. return true;
  652. #endif
  653. }
  654. //-----------------------------------------------------------------------------
  655. // returns true if there's an intersection between box and ray
  656. //-----------------------------------------------------------------------------
  657. bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax,
  658. const Vector& origin, const Vector& vecDelta,
  659. const Vector& vecInvDelta, float flTolerance )
  660. {
  661. #if USE_SIMD_RAY_CHECKS
  662. // Load the unaligned ray/box parameters into SIMD registers
  663. fltx4 start = LoadUnaligned3SIMD(origin.Base());
  664. fltx4 delta = LoadUnaligned3SIMD(vecDelta.Base());
  665. fltx4 boxMins = LoadUnaligned3SIMD( boxMin.Base() );
  666. fltx4 boxMaxs = LoadUnaligned3SIMD( boxMax.Base() );
  667. // compute the mins/maxs of the box expanded by the ray extents
  668. // relocate the problem so that the ray start is at the origin.
  669. boxMins = SubSIMD(boxMins, start);
  670. boxMaxs = SubSIMD(boxMaxs, start);
  671. // Check to see if both the origin (start point) and the end point (delta) are on the front side
  672. // of any of the box sides - if so there can be no intersection
  673. fltx4 startOutMins = CmpLtSIMD(Four_Zeros, boxMins);
  674. fltx4 endOutMins = CmpLtSIMD(delta,boxMins);
  675. fltx4 minsMask = AndSIMD( startOutMins, endOutMins );
  676. fltx4 startOutMaxs = CmpGtSIMD(Four_Zeros, boxMaxs);
  677. fltx4 endOutMaxs = CmpGtSIMD(delta,boxMaxs);
  678. fltx4 maxsMask = AndSIMD( startOutMaxs, endOutMaxs );
  679. if ( IsAnyNegative(SetWToZeroSIMD(OrSIMD(minsMask,maxsMask))))
  680. return false;
  681. // now build the per-axis interval of t for intersections
  682. fltx4 epsilon = ReplicateX4(flTolerance);
  683. fltx4 invDelta = LoadUnaligned3SIMD(vecInvDelta.Base());
  684. boxMins = SubSIMD(boxMins, epsilon);
  685. boxMaxs = AddSIMD(boxMaxs, epsilon);
  686. boxMins = MulSIMD( boxMins, invDelta );
  687. boxMaxs = MulSIMD( boxMaxs, invDelta );
  688. fltx4 crossPlane = OrSIMD(XorSIMD(startOutMins,endOutMins), XorSIMD(startOutMaxs,endOutMaxs));
  689. // only consider axes where we crossed a plane
  690. boxMins = MaskedAssign( crossPlane, boxMins, Four_Negative_FLT_MAX );
  691. boxMaxs = MaskedAssign( crossPlane, boxMaxs, Four_FLT_MAX );
  692. // now sort the interval per axis
  693. fltx4 mint = MinSIMD( boxMins, boxMaxs );
  694. fltx4 maxt = MaxSIMD( boxMins, boxMaxs );
  695. // now find the intersection of the intervals on all axes
  696. fltx4 firstOut = FindLowestSIMD3(maxt);
  697. fltx4 lastIn = FindHighestSIMD3(mint);
  698. // NOTE: This is really a scalar quantity now [t0,t1] == [lastIn,firstOut]
  699. firstOut = MinSIMD(firstOut, Four_Ones);
  700. lastIn = MaxSIMD(lastIn, Four_Zeros);
  701. // If the final interval is valid lastIn<firstOut, check for separation
  702. fltx4 separation = CmpGtSIMD(lastIn, firstOut);
  703. return IsAllZeros(separation);
  704. #else
  705. // On the x360, we force use of the SIMD functions.
  706. #if defined(_X360) && !defined(PARANOID_SIMD_ASSERTING)
  707. if (IsX360())
  708. {
  709. return IsBoxIntersectingRay(
  710. LoadUnaligned3SIMD(boxMin.Base()), LoadUnaligned3SIMD(boxMax.Base()),
  711. LoadUnaligned3SIMD(origin.Base()), LoadUnaligned3SIMD(vecDelta.Base()), LoadUnaligned3SIMD(vecInvDelta.Base()), // ray parameters
  712. ReplicateX4(flTolerance) ///< eg from ReplicateX4(flTolerance)
  713. );
  714. }
  715. #endif
  716. Assert( boxMin[0] <= boxMax[0] );
  717. Assert( boxMin[1] <= boxMax[1] );
  718. Assert( boxMin[2] <= boxMax[2] );
  719. // FIXME: Surely there's a faster way
  720. float tmin = -FLT_MAX;
  721. float tmax = FLT_MAX;
  722. for ( int i = 0; i < 3; ++i )
  723. {
  724. // Parallel case...
  725. if ( FloatMakePositive( vecDelta[i] ) < 1e-8 )
  726. {
  727. // Check that origin is in the box, if not, then it doesn't intersect..
  728. if ( ( origin[i] < boxMin[i] - flTolerance ) || ( origin[i] > boxMax[i] + flTolerance ) )
  729. return false;
  730. continue;
  731. }
  732. // Non-parallel case
  733. // Find the t's corresponding to the entry and exit of
  734. // the ray along x, y, and z. The find the furthest entry
  735. // point, and the closest exit point. Once that is done,
  736. // we know we don't collide if the closest exit point
  737. // is behind the starting location. We also don't collide if
  738. // the closest exit point is in front of the furthest entry point
  739. float t1 = ( boxMin[i] - flTolerance - origin[i] ) * vecInvDelta[i];
  740. float t2 = ( boxMax[i] + flTolerance - origin[i] ) * vecInvDelta[i];
  741. if ( t1 > t2 )
  742. {
  743. float temp = t1;
  744. t1 = t2;
  745. t2 = temp;
  746. }
  747. if (t1 > tmin)
  748. tmin = t1;
  749. if (t2 < tmax)
  750. tmax = t2;
  751. if (tmin > tmax)
  752. return false;
  753. if (tmax < 0)
  754. return false;
  755. if (tmin > 1)
  756. return false;
  757. }
  758. return true;
  759. #endif
  760. }
  761. //-----------------------------------------------------------------------------
  762. // Intersects a ray with a aabb, return true if they intersect
  763. //-----------------------------------------------------------------------------
  764. bool FASTCALL IsBoxIntersectingRay( const Vector& vecBoxMin, const Vector& vecBoxMax, const Ray_t& ray, float flTolerance )
  765. {
  766. // On the x360, we force use of the SIMD functions.
  767. #if defined(_X360)
  768. if (IsX360())
  769. {
  770. return IsBoxIntersectingRay(
  771. LoadUnaligned3SIMD(vecBoxMin.Base()), LoadUnaligned3SIMD(vecBoxMax.Base()),
  772. ray, flTolerance);
  773. }
  774. #endif
  775. if ( !ray.m_IsSwept )
  776. {
  777. Vector rayMins, rayMaxs;
  778. VectorSubtract( ray.m_Start, ray.m_Extents, rayMins );
  779. VectorAdd( ray.m_Start, ray.m_Extents, rayMaxs );
  780. if ( flTolerance != 0.0f )
  781. {
  782. rayMins.x -= flTolerance; rayMins.y -= flTolerance; rayMins.z -= flTolerance;
  783. rayMaxs.x += flTolerance; rayMaxs.y += flTolerance; rayMaxs.z += flTolerance;
  784. }
  785. return IsBoxIntersectingBox( vecBoxMin, vecBoxMax, rayMins, rayMaxs );
  786. }
  787. Vector vecExpandedBoxMin, vecExpandedBoxMax;
  788. VectorSubtract( vecBoxMin, ray.m_Extents, vecExpandedBoxMin );
  789. VectorAdd( vecBoxMax, ray.m_Extents, vecExpandedBoxMax );
  790. return IsBoxIntersectingRay( vecExpandedBoxMin, vecExpandedBoxMax, ray.m_Start, ray.m_Delta, flTolerance );
  791. }
  792. //-----------------------------------------------------------------------------
  793. // returns true if there's an intersection between box and ray (SIMD version)
  794. //-----------------------------------------------------------------------------
  795. #ifdef _X360
  796. bool FASTCALL IsBoxIntersectingRay( fltx4 boxMin, fltx4 boxMax,
  797. fltx4 origin, fltx4 delta, fltx4 invDelta, // ray parameters
  798. fltx4 vTolerance ///< eg from ReplicateX4(flTolerance)
  799. )
  800. #else
  801. bool FASTCALL IsBoxIntersectingRay( const fltx4 &inBoxMin, const fltx4 & inBoxMax,
  802. const fltx4 & origin, const fltx4 & delta, const fltx4 & invDelta, // ray parameters
  803. const fltx4 & vTolerance ///< eg from ReplicateX4(flTolerance)
  804. )
  805. #endif
  806. {
  807. // Load the unaligned ray/box parameters into SIMD registers
  808. // compute the mins/maxs of the box expanded by the ray extents
  809. // relocate the problem so that the ray start is at the origin.
  810. #ifdef _X360
  811. boxMin = SubSIMD(boxMin, origin);
  812. boxMax = SubSIMD(boxMax, origin);
  813. #else
  814. fltx4 boxMin = SubSIMD(inBoxMin, origin);
  815. fltx4 boxMax = SubSIMD(inBoxMax, origin);
  816. #endif
  817. // Check to see if the origin (start point) and the end point (delta) are on the same side
  818. // of any of the box sides - if so there can be no intersection
  819. fltx4 startOutMins = AndSIMD( CmpLtSIMD(Four_Zeros, boxMin), CmpLtSIMD(delta,boxMin) );
  820. fltx4 startOutMaxs = AndSIMD( CmpGtSIMD(Four_Zeros, boxMax), CmpGtSIMD(delta,boxMax) );
  821. if ( IsAnyNegative(SetWToZeroSIMD(OrSIMD(startOutMaxs,startOutMins))))
  822. return false;
  823. // now build the per-axis interval of t for intersections
  824. boxMin = SubSIMD(boxMin, vTolerance);
  825. boxMax = AddSIMD(boxMax, vTolerance);
  826. boxMin = MulSIMD( boxMin, invDelta );
  827. boxMax = MulSIMD( boxMax, invDelta );
  828. // now sort the interval per axis
  829. fltx4 mint = MinSIMD( boxMin, boxMax );
  830. fltx4 maxt = MaxSIMD( boxMin, boxMax );
  831. // now find the intersection of the intervals on all axes
  832. fltx4 firstOut = FindLowestSIMD3(maxt);
  833. fltx4 lastIn = FindHighestSIMD3(mint);
  834. // NOTE: This is really a scalar quantity now [t0,t1] == [lastIn,firstOut]
  835. firstOut = MinSIMD(firstOut, Four_Ones);
  836. lastIn = MaxSIMD(lastIn, Four_Zeros);
  837. // If the final interval is valid lastIn<firstOut, check for separation
  838. fltx4 separation = CmpGtSIMD(lastIn, firstOut);
  839. return IsAllZeros(separation);
  840. }
  841. bool FASTCALL IsBoxIntersectingRay( const fltx4& boxMin, const fltx4& boxMax,
  842. const Ray_t& ray, float flTolerance )
  843. {
  844. fltx4 vTolerance = ReplicateX4(flTolerance);
  845. fltx4 rayStart = LoadAlignedSIMD(ray.m_Start);
  846. fltx4 rayExtents = LoadAlignedSIMD(ray.m_Extents);
  847. if ( !ray.m_IsSwept )
  848. {
  849. fltx4 rayMins, rayMaxs;
  850. rayMins = SubSIMD(rayStart, rayExtents);
  851. rayMaxs = AddSIMD(rayStart, rayExtents);
  852. rayMins = AddSIMD(rayMins, vTolerance);
  853. rayMaxs = AddSIMD(rayMaxs, vTolerance);
  854. VectorAligned vecBoxMin, vecBoxMax, vecRayMins, vecRayMaxs;
  855. StoreAlignedSIMD( vecBoxMin.Base(), boxMin );
  856. StoreAlignedSIMD( vecBoxMax.Base(), boxMax );
  857. StoreAlignedSIMD( vecRayMins.Base(), rayMins );
  858. StoreAlignedSIMD( vecRayMaxs.Base(), rayMaxs );
  859. return IsBoxIntersectingBox( vecBoxMin, vecBoxMax, vecRayMins, vecRayMaxs );
  860. }
  861. fltx4 rayDelta = LoadAlignedSIMD(ray.m_Delta);
  862. fltx4 vecExpandedBoxMin, vecExpandedBoxMax;
  863. vecExpandedBoxMin = SubSIMD( boxMin, rayExtents );
  864. vecExpandedBoxMax = AddSIMD( boxMax, rayExtents );
  865. return IsBoxIntersectingRay( vecExpandedBoxMin, vecExpandedBoxMax, rayStart, rayDelta, ReciprocalSIMD(rayDelta), ReplicateX4(flTolerance) );
  866. }
  867. //-----------------------------------------------------------------------------
  868. // Intersects a ray with a ray, return true if they intersect
  869. // t, s = parameters of closest approach (if not intersecting!)
  870. //-----------------------------------------------------------------------------
  871. bool IntersectRayWithRay( const Ray_t &ray0, const Ray_t &ray1, float &t, float &s )
  872. {
  873. Assert( ray0.m_IsRay && ray1.m_IsRay );
  874. //
  875. // r0 = p0 + v0t
  876. // r1 = p1 + v1s
  877. //
  878. // intersection : r0 = r1 :: p0 + v0t = p1 + v1s
  879. // NOTE: v(0,1) are unit direction vectors
  880. //
  881. // subtract p0 from both sides and cross with v1 (NOTE: v1 x v1 = 0)
  882. // (v0 x v1)t = ((p1 - p0 ) x v1)
  883. //
  884. // dotting with (v0 x v1) and dividing by |v0 x v1|^2
  885. // t = Det | (p1 - p0) , v1 , (v0 x v1) | / |v0 x v1|^2
  886. // s = Det | (p1 - p0) , v0 , (v0 x v1) | / |v0 x v1|^2
  887. //
  888. // Det | A B C | = -( A x C ) dot B or -( C x B ) dot A
  889. //
  890. // NOTE: if |v0 x v1|^2 = 0, then the lines are parallel
  891. //
  892. Vector v0( ray0.m_Delta );
  893. Vector v1( ray1.m_Delta );
  894. VectorNormalize( v0 );
  895. VectorNormalize( v1 );
  896. Vector v0xv1 = v0.Cross( v1 );
  897. float lengthSq = v0xv1.LengthSqr();
  898. if( lengthSq == 0.0f )
  899. {
  900. t = 0; s = 0;
  901. return false; // parallel
  902. }
  903. Vector p1p0 = ray1.m_Start - ray0.m_Start;
  904. Vector AxC = p1p0.Cross( v0xv1 );
  905. AxC.Negate();
  906. float detT = AxC.Dot( v1 );
  907. AxC = p1p0.Cross( v0xv1 );
  908. AxC.Negate();
  909. float detS = AxC.Dot( v0 );
  910. t = detT / lengthSq;
  911. s = detS / lengthSq;
  912. // intersection????
  913. Vector i0, i1;
  914. i0 = v0 * t;
  915. i1 = v1 * s;
  916. i0 += ray0.m_Start;
  917. i1 += ray1.m_Start;
  918. if( i0.x == i1.x && i0.y == i1.y && i0.z == i1.z )
  919. return true;
  920. return false;
  921. }
  922. //-----------------------------------------------------------------------------
  923. // Intersects a ray with a plane, returns distance t along ray.
  924. //-----------------------------------------------------------------------------
  925. float IntersectRayWithPlane( const Ray_t& ray, const cplane_t& plane )
  926. {
  927. float denom = DotProduct( ray.m_Delta, plane.normal );
  928. if (denom == 0.0f)
  929. return 0.0f;
  930. denom = 1.0f / denom;
  931. return (plane.dist - DotProduct( ray.m_Start, plane.normal )) * denom;
  932. }
  933. float IntersectRayWithPlane( const Vector& org, const Vector& dir, const cplane_t& plane )
  934. {
  935. float denom = DotProduct( dir, plane.normal );
  936. if (denom == 0.0f)
  937. return 0.0f;
  938. denom = 1.0f / denom;
  939. return (plane.dist - DotProduct( org, plane.normal )) * denom;
  940. }
  941. float IntersectRayWithPlane( const Vector& org, const Vector& dir, const Vector& normal, float dist )
  942. {
  943. float denom = DotProduct( dir, normal );
  944. if (denom == 0.0f)
  945. return 0.0f;
  946. denom = 1.0f / denom;
  947. return (dist - DotProduct( org, normal )) * denom;
  948. }
  949. float IntersectRayWithAAPlane( const Vector& vecStart, const Vector& vecEnd, int nAxis, float flSign, float flDist )
  950. {
  951. float denom = flSign * (vecEnd[nAxis] - vecStart[nAxis]);
  952. if (denom == 0.0f)
  953. return 0.0f;
  954. denom = 1.0f / denom;
  955. return (flDist - flSign * vecStart[nAxis]) * denom;
  956. }
  957. //-----------------------------------------------------------------------------
  958. // Intersects a ray against a box
  959. //-----------------------------------------------------------------------------
  960. bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta,
  961. const Vector &boxMins, const Vector &boxMaxs, float flTolerance, BoxTraceInfo_t *pTrace )
  962. {
  963. int i;
  964. float d1, d2;
  965. float f;
  966. pTrace->t1 = -1.0f;
  967. pTrace->t2 = 1.0f;
  968. pTrace->hitside = -1;
  969. // UNDONE: This makes this code a little messy
  970. pTrace->startsolid = true;
  971. for ( i = 0; i < 6; ++i )
  972. {
  973. if ( i >= 3 )
  974. {
  975. d1 = vecRayStart[i-3] - boxMaxs[i-3];
  976. d2 = d1 + vecRayDelta[i-3];
  977. }
  978. else
  979. {
  980. d1 = -vecRayStart[i] + boxMins[i];
  981. d2 = d1 - vecRayDelta[i];
  982. }
  983. // if completely in front of face, no intersection
  984. if (d1 > 0 && d2 > 0)
  985. {
  986. // UNDONE: Have to revert this in case it's still set
  987. // UNDONE: Refactor to have only 2 return points (true/false) from this function
  988. pTrace->startsolid = false;
  989. return false;
  990. }
  991. // completely inside, check next face
  992. if (d1 <= 0 && d2 <= 0)
  993. continue;
  994. if (d1 > 0)
  995. {
  996. pTrace->startsolid = false;
  997. }
  998. // crosses face
  999. if (d1 > d2)
  1000. {
  1001. f = d1 - flTolerance;
  1002. if ( f < 0 )
  1003. {
  1004. f = 0;
  1005. }
  1006. f = f / (d1-d2);
  1007. if (f > pTrace->t1)
  1008. {
  1009. pTrace->t1 = f;
  1010. pTrace->hitside = i;
  1011. }
  1012. }
  1013. else
  1014. {
  1015. // leave
  1016. f = (d1 + flTolerance) / (d1-d2);
  1017. if (f < pTrace->t2)
  1018. {
  1019. pTrace->t2 = f;
  1020. }
  1021. }
  1022. }
  1023. return pTrace->startsolid || (pTrace->t1 < pTrace->t2 && pTrace->t1 >= 0.0f);
  1024. }
  1025. //-----------------------------------------------------------------------------
  1026. // Intersects a ray against a box
  1027. //-----------------------------------------------------------------------------
  1028. bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta,
  1029. const Vector &boxMins, const Vector &boxMaxs, float flTolerance, CBaseTrace *pTrace, float *pFractionLeftSolid )
  1030. {
  1031. Collision_ClearTrace( vecRayStart, vecRayDelta, pTrace );
  1032. BoxTraceInfo_t trace;
  1033. if ( IntersectRayWithBox( vecRayStart, vecRayDelta, boxMins, boxMaxs, flTolerance, &trace ) )
  1034. {
  1035. pTrace->startsolid = trace.startsolid;
  1036. if (trace.t1 < trace.t2 && trace.t1 >= 0.0f)
  1037. {
  1038. pTrace->fraction = trace.t1;
  1039. VectorMA( pTrace->startpos, trace.t1, vecRayDelta, pTrace->endpos );
  1040. pTrace->contents = CONTENTS_SOLID;
  1041. pTrace->plane.normal = vec3_origin;
  1042. if ( trace.hitside >= 3 )
  1043. {
  1044. trace.hitside -= 3;
  1045. pTrace->plane.dist = boxMaxs[trace.hitside];
  1046. pTrace->plane.normal[trace.hitside] = 1.0f;
  1047. pTrace->plane.type = trace.hitside;
  1048. }
  1049. else
  1050. {
  1051. pTrace->plane.dist = -boxMins[trace.hitside];
  1052. pTrace->plane.normal[trace.hitside] = -1.0f;
  1053. pTrace->plane.type = trace.hitside;
  1054. }
  1055. return true;
  1056. }
  1057. if ( pTrace->startsolid )
  1058. {
  1059. pTrace->allsolid = (trace.t2 <= 0.0f) || (trace.t2 >= 1.0f);
  1060. pTrace->fraction = 0;
  1061. if ( pFractionLeftSolid )
  1062. {
  1063. *pFractionLeftSolid = trace.t2;
  1064. }
  1065. pTrace->endpos = pTrace->startpos;
  1066. pTrace->contents = CONTENTS_SOLID;
  1067. pTrace->plane.dist = pTrace->startpos[0];
  1068. pTrace->plane.normal.Init( 1.0f, 0.0f, 0.0f );
  1069. pTrace->plane.type = 0;
  1070. pTrace->startpos = vecRayStart + (trace.t2 * vecRayDelta);
  1071. return true;
  1072. }
  1073. }
  1074. return false;
  1075. }
  1076. //-----------------------------------------------------------------------------
  1077. // Intersects a ray against a box
  1078. //-----------------------------------------------------------------------------
  1079. bool IntersectRayWithBox( const Ray_t &ray, const Vector &boxMins, const Vector &boxMaxs,
  1080. float flTolerance, CBaseTrace *pTrace, float *pFractionLeftSolid )
  1081. {
  1082. if ( !ray.m_IsRay )
  1083. {
  1084. Vector vecExpandedMins = boxMins - ray.m_Extents;
  1085. Vector vecExpandedMaxs = boxMaxs + ray.m_Extents;
  1086. bool bIntersects = IntersectRayWithBox( ray.m_Start, ray.m_Delta, vecExpandedMins, vecExpandedMaxs, flTolerance, pTrace, pFractionLeftSolid );
  1087. pTrace->startpos += ray.m_StartOffset;
  1088. pTrace->endpos += ray.m_StartOffset;
  1089. return bIntersects;
  1090. }
  1091. return IntersectRayWithBox( ray.m_Start, ray.m_Delta, boxMins, boxMaxs, flTolerance, pTrace, pFractionLeftSolid );
  1092. }
  1093. //-----------------------------------------------------------------------------
  1094. // Intersects a ray against an OBB, returns t1 and t2
  1095. //-----------------------------------------------------------------------------
  1096. bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta,
  1097. const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs,
  1098. float flTolerance, BoxTraceInfo_t *pTrace )
  1099. {
  1100. // FIXME: Two transforms is pretty expensive. Should we optimize this?
  1101. Vector start, delta;
  1102. VectorITransform( vecRayStart, matOBBToWorld, start );
  1103. VectorIRotate( vecRayDelta, matOBBToWorld, delta );
  1104. return IntersectRayWithBox( start, delta, vecOBBMins, vecOBBMaxs, flTolerance, pTrace );
  1105. }
  1106. //-----------------------------------------------------------------------------
  1107. // Intersects a ray against an OBB
  1108. //-----------------------------------------------------------------------------
  1109. bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta,
  1110. const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs,
  1111. float flTolerance, CBaseTrace *pTrace )
  1112. {
  1113. Collision_ClearTrace( vecRayStart, vecRayDelta, pTrace );
  1114. // FIXME: Make it work with tolerance
  1115. Assert( flTolerance == 0.0f );
  1116. // OPTIMIZE: Store this in the box instead of computing it here
  1117. // compute center in local space
  1118. Vector vecBoxExtents = (vecOBBMins + vecOBBMaxs) * 0.5;
  1119. Vector vecBoxCenter;
  1120. // transform to world space
  1121. VectorTransform( vecBoxExtents, matOBBToWorld, vecBoxCenter );
  1122. // calc extents from local center
  1123. vecBoxExtents = vecOBBMaxs - vecBoxExtents;
  1124. // OPTIMIZE: This is optimized for world space. If the transform is fast enough, it may make more
  1125. // sense to just xform and call UTIL_ClipToBox() instead. MEASURE THIS.
  1126. // save the extents of the ray along
  1127. Vector extent, uextent;
  1128. Vector segmentCenter = vecRayStart + vecRayDelta - vecBoxCenter;
  1129. extent.Init();
  1130. // check box axes for separation
  1131. for ( int j = 0; j < 3; j++ )
  1132. {
  1133. extent[j] = vecRayDelta.x * matOBBToWorld[0][j] + vecRayDelta.y * matOBBToWorld[1][j] + vecRayDelta.z * matOBBToWorld[2][j];
  1134. uextent[j] = fabsf(extent[j]);
  1135. float coord = segmentCenter.x * matOBBToWorld[0][j] + segmentCenter.y * matOBBToWorld[1][j] + segmentCenter.z * matOBBToWorld[2][j];
  1136. coord = fabsf(coord);
  1137. if ( coord > (vecBoxExtents[j] + uextent[j]) )
  1138. return false;
  1139. }
  1140. // now check cross axes for separation
  1141. float tmp, cextent;
  1142. Vector cross = vecRayDelta.Cross( segmentCenter );
  1143. cextent = cross.x * matOBBToWorld[0][0] + cross.y * matOBBToWorld[1][0] + cross.z * matOBBToWorld[2][0];
  1144. cextent = fabsf(cextent);
  1145. tmp = vecBoxExtents[1]*uextent[2] + vecBoxExtents[2]*uextent[1];
  1146. if ( cextent > tmp )
  1147. return false;
  1148. cextent = cross.x * matOBBToWorld[0][1] + cross.y * matOBBToWorld[1][1] + cross.z * matOBBToWorld[2][1];
  1149. cextent = fabsf(cextent);
  1150. tmp = vecBoxExtents[0]*uextent[2] + vecBoxExtents[2]*uextent[0];
  1151. if ( cextent > tmp )
  1152. return false;
  1153. cextent = cross.x * matOBBToWorld[0][2] + cross.y * matOBBToWorld[1][2] + cross.z * matOBBToWorld[2][2];
  1154. cextent = fabsf(cextent);
  1155. tmp = vecBoxExtents[0]*uextent[1] + vecBoxExtents[1]*uextent[0];
  1156. if ( cextent > tmp )
  1157. return false;
  1158. // !!! We hit this box !!! compute intersection point and return
  1159. // Compute ray start in bone space
  1160. Vector start;
  1161. VectorITransform( vecRayStart, matOBBToWorld, start );
  1162. // extent is ray.m_Delta in bone space, recompute delta in bone space
  1163. extent *= 2.0f;
  1164. // delta was prescaled by the current t, so no need to see if this intersection
  1165. // is closer
  1166. trace_t boxTrace;
  1167. if ( !IntersectRayWithBox( start, extent, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ) )
  1168. return false;
  1169. // Fix up the start/end pos and fraction
  1170. Vector vecTemp;
  1171. VectorTransform( pTrace->endpos, matOBBToWorld, vecTemp );
  1172. pTrace->endpos = vecTemp;
  1173. pTrace->startpos = vecRayStart;
  1174. pTrace->fraction *= 2.0f;
  1175. // Fix up the plane information
  1176. float flSign = pTrace->plane.normal[ pTrace->plane.type ];
  1177. pTrace->plane.normal[0] = flSign * matOBBToWorld[0][pTrace->plane.type];
  1178. pTrace->plane.normal[1] = flSign * matOBBToWorld[1][pTrace->plane.type];
  1179. pTrace->plane.normal[2] = flSign * matOBBToWorld[2][pTrace->plane.type];
  1180. pTrace->plane.dist = DotProduct( pTrace->endpos, pTrace->plane.normal );
  1181. pTrace->plane.type = 3;
  1182. return true;
  1183. }
  1184. //-----------------------------------------------------------------------------
  1185. // Intersects a ray against an OBB
  1186. //-----------------------------------------------------------------------------
  1187. bool IntersectRayWithOBB( const Vector &vecRayOrigin, const Vector &vecRayDelta,
  1188. const Vector &vecBoxOrigin, const QAngle &angBoxRotation,
  1189. const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace )
  1190. {
  1191. if (angBoxRotation == vec3_angle)
  1192. {
  1193. Vector vecAbsMins, vecAbsMaxs;
  1194. VectorAdd( vecBoxOrigin, vecOBBMins, vecAbsMins );
  1195. VectorAdd( vecBoxOrigin, vecOBBMaxs, vecAbsMaxs );
  1196. return IntersectRayWithBox( vecRayOrigin, vecRayDelta, vecAbsMins, vecAbsMaxs, flTolerance, pTrace );
  1197. }
  1198. matrix3x4_t obbToWorld;
  1199. AngleMatrix( angBoxRotation, vecBoxOrigin, obbToWorld );
  1200. return IntersectRayWithOBB( vecRayOrigin, vecRayDelta, obbToWorld, vecOBBMins, vecOBBMaxs, flTolerance, pTrace );
  1201. }
  1202. //-----------------------------------------------------------------------------
  1203. // Box support map
  1204. //-----------------------------------------------------------------------------
  1205. inline void ComputeSupportMap( const Vector &vecDirection, const Vector &vecBoxMins,
  1206. const Vector &vecBoxMaxs, float pDist[2] )
  1207. {
  1208. int nIndex = (vecDirection.x > 0.0f);
  1209. pDist[nIndex] = vecBoxMaxs.x * vecDirection.x;
  1210. pDist[1 - nIndex] = vecBoxMins.x * vecDirection.x;
  1211. nIndex = (vecDirection.y > 0.0f);
  1212. pDist[nIndex] += vecBoxMaxs.y * vecDirection.y;
  1213. pDist[1 - nIndex] += vecBoxMins.y * vecDirection.y;
  1214. nIndex = (vecDirection.z > 0.0f);
  1215. pDist[nIndex] += vecBoxMaxs.z * vecDirection.z;
  1216. pDist[1 - nIndex] += vecBoxMins.z * vecDirection.z;
  1217. }
  1218. inline void ComputeSupportMap( const Vector &vecDirection, int i1, int i2,
  1219. const Vector &vecBoxMins, const Vector &vecBoxMaxs, float pDist[2] )
  1220. {
  1221. int nIndex = (vecDirection[i1] > 0.0f);
  1222. pDist[nIndex] = vecBoxMaxs[i1] * vecDirection[i1];
  1223. pDist[1 - nIndex] = vecBoxMins[i1] * vecDirection[i1];
  1224. nIndex = (vecDirection[i2] > 0.0f);
  1225. pDist[nIndex] += vecBoxMaxs[i2] * vecDirection[i2];
  1226. pDist[1 - nIndex] += vecBoxMins[i2] * vecDirection[i2];
  1227. }
  1228. //-----------------------------------------------------------------------------
  1229. // Intersects a ray against an OBB
  1230. //-----------------------------------------------------------------------------
  1231. static int s_ExtIndices[3][2] =
  1232. {
  1233. { 2, 1 },
  1234. { 0, 2 },
  1235. { 0, 1 },
  1236. };
  1237. static int s_MatIndices[3][2] =
  1238. {
  1239. { 1, 2 },
  1240. { 2, 0 },
  1241. { 1, 0 },
  1242. };
  1243. bool IntersectRayWithOBB( const Ray_t &ray, const matrix3x4_t &matOBBToWorld,
  1244. const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace )
  1245. {
  1246. if ( ray.m_IsRay )
  1247. {
  1248. return IntersectRayWithOBB( ray.m_Start, ray.m_Delta, matOBBToWorld,
  1249. vecOBBMins, vecOBBMaxs, flTolerance, pTrace );
  1250. }
  1251. Collision_ClearTrace( ray.m_Start + ray.m_StartOffset, ray.m_Delta, pTrace );
  1252. // Compute a bounding sphere around the bloated OBB
  1253. Vector vecOBBCenter;
  1254. VectorAdd( vecOBBMins, vecOBBMaxs, vecOBBCenter );
  1255. vecOBBCenter *= 0.5f;
  1256. vecOBBCenter.x += matOBBToWorld[0][3];
  1257. vecOBBCenter.y += matOBBToWorld[1][3];
  1258. vecOBBCenter.z += matOBBToWorld[2][3];
  1259. Vector vecOBBHalfDiagonal;
  1260. VectorSubtract( vecOBBMaxs, vecOBBMins, vecOBBHalfDiagonal );
  1261. vecOBBHalfDiagonal *= 0.5f;
  1262. float flRadius = vecOBBHalfDiagonal.Length() + ray.m_Extents.Length();
  1263. if ( !IsRayIntersectingSphere( ray.m_Start, ray.m_Delta, vecOBBCenter, flRadius, flTolerance ) )
  1264. return false;
  1265. // Ok, we passed the trivial reject, so lets do the dirty deed.
  1266. // Basically we're going to do the GJK thing explicitly. We'll shrink the ray down
  1267. // to a point, and bloat the OBB by the ray's extents. This will generate facet
  1268. // planes which are perpendicular to all of the separating axes typically seen in
  1269. // a standard seperating axis implementation.
  1270. // We're going to create a number of planes through various vertices in the OBB
  1271. // which represent all of the separating planes. Then we're going to bloat the planes
  1272. // by the ray extents.
  1273. // We're going to do all work in OBB-space because it's easier to do the
  1274. // support-map in this case
  1275. // First, transform the ray into the space of the OBB
  1276. Vector vecLocalRayOrigin, vecLocalRayDirection;
  1277. VectorITransform( ray.m_Start, matOBBToWorld, vecLocalRayOrigin );
  1278. VectorIRotate( ray.m_Delta, matOBBToWorld, vecLocalRayDirection );
  1279. // Next compute all separating planes
  1280. Vector pPlaneNormal[15];
  1281. float ppPlaneDist[15][2];
  1282. int i;
  1283. for ( i = 0; i < 3; ++i )
  1284. {
  1285. // Each plane needs to be bloated an amount = to the abs dot product of
  1286. // the ray extents with the plane normal
  1287. // For the OBB planes, do it in world space;
  1288. // and use the direction of the OBB (the ith column of matOBBToWorld) in world space vs extents
  1289. pPlaneNormal[i].Init( );
  1290. pPlaneNormal[i][i] = 1.0f;
  1291. float flExtentDotNormal =
  1292. FloatMakePositive( matOBBToWorld[0][i] * ray.m_Extents.x ) +
  1293. FloatMakePositive( matOBBToWorld[1][i] * ray.m_Extents.y ) +
  1294. FloatMakePositive( matOBBToWorld[2][i] * ray.m_Extents.z );
  1295. ppPlaneDist[i][0] = vecOBBMins[i] - flExtentDotNormal;
  1296. ppPlaneDist[i][1] = vecOBBMaxs[i] + flExtentDotNormal;
  1297. // For the ray-extents planes, they are bloated by the extents
  1298. // Use the support map to determine which
  1299. VectorCopy( matOBBToWorld[i], pPlaneNormal[i+3].Base() );
  1300. ComputeSupportMap( pPlaneNormal[i+3], vecOBBMins, vecOBBMaxs, ppPlaneDist[i+3] );
  1301. ppPlaneDist[i+3][0] -= ray.m_Extents[i];
  1302. ppPlaneDist[i+3][1] += ray.m_Extents[i];
  1303. // Now the edge cases... (take the cross product of x,y,z axis w/ ray extent axes
  1304. // given by the rows of the obb to world matrix.
  1305. // Compute the ray extent bloat in world space because it's easier...
  1306. // These are necessary to compute the world-space versions of
  1307. // the edges so we can compute the extent dot products
  1308. float flRayExtent0 = ray.m_Extents[s_ExtIndices[i][0]];
  1309. float flRayExtent1 = ray.m_Extents[s_ExtIndices[i][1]];
  1310. const float *pMatRow0 = matOBBToWorld[s_MatIndices[i][0]];
  1311. const float *pMatRow1 = matOBBToWorld[s_MatIndices[i][1]];
  1312. // x axis of the OBB + world ith axis
  1313. pPlaneNormal[i+6].Init( 0.0f, -matOBBToWorld[i][2], matOBBToWorld[i][1] );
  1314. ComputeSupportMap( pPlaneNormal[i+6], 1, 2, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+6] );
  1315. flExtentDotNormal =
  1316. FloatMakePositive( pMatRow0[0] ) * flRayExtent0 +
  1317. FloatMakePositive( pMatRow1[0] ) * flRayExtent1;
  1318. ppPlaneDist[i+6][0] -= flExtentDotNormal;
  1319. ppPlaneDist[i+6][1] += flExtentDotNormal;
  1320. // y axis of the OBB + world ith axis
  1321. pPlaneNormal[i+9].Init( matOBBToWorld[i][2], 0.0f, -matOBBToWorld[i][0] );
  1322. ComputeSupportMap( pPlaneNormal[i+9], 0, 2, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+9] );
  1323. flExtentDotNormal =
  1324. FloatMakePositive( pMatRow0[1] ) * flRayExtent0 +
  1325. FloatMakePositive( pMatRow1[1] ) * flRayExtent1;
  1326. ppPlaneDist[i+9][0] -= flExtentDotNormal;
  1327. ppPlaneDist[i+9][1] += flExtentDotNormal;
  1328. // z axis of the OBB + world ith axis
  1329. pPlaneNormal[i+12].Init( -matOBBToWorld[i][1], matOBBToWorld[i][0], 0.0f );
  1330. ComputeSupportMap( pPlaneNormal[i+12], 0, 1, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+12] );
  1331. flExtentDotNormal =
  1332. FloatMakePositive( pMatRow0[2] ) * flRayExtent0 +
  1333. FloatMakePositive( pMatRow1[2] ) * flRayExtent1;
  1334. ppPlaneDist[i+12][0] -= flExtentDotNormal;
  1335. ppPlaneDist[i+12][1] += flExtentDotNormal;
  1336. }
  1337. float enterfrac, leavefrac;
  1338. float d1[2], d2[2];
  1339. float f;
  1340. int hitplane = -1;
  1341. int hitside = -1;
  1342. enterfrac = -1.0f;
  1343. leavefrac = 1.0f;
  1344. pTrace->startsolid = true;
  1345. Vector vecLocalRayEnd;
  1346. VectorAdd( vecLocalRayOrigin, vecLocalRayDirection, vecLocalRayEnd );
  1347. for ( i = 0; i < 15; ++i )
  1348. {
  1349. // FIXME: Not particularly optimal since there's a lot of 0's in the plane normals
  1350. float flStartDot = DotProduct( pPlaneNormal[i], vecLocalRayOrigin );
  1351. float flEndDot = DotProduct( pPlaneNormal[i], vecLocalRayEnd );
  1352. // NOTE: Negative here is because the plane normal + dist
  1353. // are defined in negative terms for the far plane (plane dist index 0)
  1354. d1[0] = -(flStartDot - ppPlaneDist[i][0]);
  1355. d2[0] = -(flEndDot - ppPlaneDist[i][0]);
  1356. d1[1] = flStartDot - ppPlaneDist[i][1];
  1357. d2[1] = flEndDot - ppPlaneDist[i][1];
  1358. int j;
  1359. for ( j = 0; j < 2; ++j )
  1360. {
  1361. // if completely in front near plane or behind far plane no intersection
  1362. if (d1[j] > 0 && d2[j] > 0)
  1363. return false;
  1364. // completely inside, check next plane set
  1365. if (d1[j] <= 0 && d2[j] <= 0)
  1366. continue;
  1367. if (d1[j] > 0)
  1368. {
  1369. pTrace->startsolid = false;
  1370. }
  1371. // crosses face
  1372. float flDenom = 1.0f / (d1[j] - d2[j]);
  1373. if (d1[j] > d2[j])
  1374. {
  1375. f = d1[j] - flTolerance;
  1376. if ( f < 0 )
  1377. {
  1378. f = 0;
  1379. }
  1380. f *= flDenom;
  1381. if (f > enterfrac)
  1382. {
  1383. enterfrac = f;
  1384. hitplane = i;
  1385. hitside = j;
  1386. }
  1387. }
  1388. else
  1389. {
  1390. // leave
  1391. f = (d1[j] + flTolerance) * flDenom;
  1392. if (f < leavefrac)
  1393. {
  1394. leavefrac = f;
  1395. }
  1396. }
  1397. }
  1398. }
  1399. if (enterfrac < leavefrac && enterfrac >= 0.0f)
  1400. {
  1401. pTrace->fraction = enterfrac;
  1402. VectorMA( pTrace->startpos, enterfrac, ray.m_Delta, pTrace->endpos );
  1403. pTrace->contents = CONTENTS_SOLID;
  1404. // Need to transform the plane into world space...
  1405. cplane_t temp;
  1406. temp.normal = pPlaneNormal[hitplane];
  1407. temp.dist = ppPlaneDist[hitplane][hitside];
  1408. if (hitside == 0)
  1409. {
  1410. temp.normal *= -1.0f;
  1411. temp.dist *= -1.0f;
  1412. }
  1413. temp.type = 3;
  1414. MatrixITransformPlane( matOBBToWorld, temp, pTrace->plane );
  1415. return true;
  1416. }
  1417. if ( pTrace->startsolid )
  1418. {
  1419. pTrace->allsolid = (leavefrac <= 0.0f) || (leavefrac >= 1.0f);
  1420. pTrace->fraction = 0;
  1421. pTrace->endpos = pTrace->startpos;
  1422. pTrace->contents = CONTENTS_SOLID;
  1423. pTrace->plane.dist = pTrace->startpos[0];
  1424. pTrace->plane.normal.Init( 1.0f, 0.0f, 0.0f );
  1425. pTrace->plane.type = 0;
  1426. return true;
  1427. }
  1428. return false;
  1429. }
  1430. //-----------------------------------------------------------------------------
  1431. // Intersects a ray against an OBB
  1432. //-----------------------------------------------------------------------------
  1433. bool IntersectRayWithOBB( const Ray_t &ray, const Vector &vecBoxOrigin, const QAngle &angBoxRotation,
  1434. const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace )
  1435. {
  1436. if ( angBoxRotation == vec3_angle )
  1437. {
  1438. Vector vecWorldMins, vecWorldMaxs;
  1439. VectorAdd( vecBoxOrigin, vecOBBMins, vecWorldMins );
  1440. VectorAdd( vecBoxOrigin, vecOBBMaxs, vecWorldMaxs );
  1441. return IntersectRayWithBox( ray, vecWorldMins, vecWorldMaxs, flTolerance, pTrace );
  1442. }
  1443. if ( ray.m_IsRay )
  1444. {
  1445. return IntersectRayWithOBB( ray.m_Start, ray.m_Delta, vecBoxOrigin, angBoxRotation, vecOBBMins, vecOBBMaxs, flTolerance, pTrace );
  1446. }
  1447. matrix3x4_t matOBBToWorld;
  1448. AngleMatrix( angBoxRotation, vecBoxOrigin, matOBBToWorld );
  1449. return IntersectRayWithOBB( ray, matOBBToWorld, vecOBBMins, vecOBBMaxs, flTolerance, pTrace );
  1450. }
  1451. //-----------------------------------------------------------------------------
  1452. //
  1453. //-----------------------------------------------------------------------------
  1454. void GetNonMajorAxes( const Vector &vNormal, Vector2D &axes )
  1455. {
  1456. axes[0] = 0;
  1457. axes[1] = 1;
  1458. if( FloatMakePositive( vNormal.x ) > FloatMakePositive( vNormal.y ) )
  1459. {
  1460. if( FloatMakePositive( vNormal.x ) > FloatMakePositive( vNormal.z ) )
  1461. {
  1462. axes[0] = 1;
  1463. axes[1] = 2;
  1464. }
  1465. }
  1466. else
  1467. {
  1468. if( FloatMakePositive( vNormal.y ) > FloatMakePositive( vNormal.z ) )
  1469. {
  1470. axes[0] = 0;
  1471. axes[1] = 2;
  1472. }
  1473. }
  1474. }
  1475. //-----------------------------------------------------------------------------
  1476. //-----------------------------------------------------------------------------
  1477. QuadBarycentricRetval_t QuadWithParallelEdges( const Vector &vecOrigin,
  1478. const Vector &vecU, float lengthU, const Vector &vecV, float lengthV,
  1479. const Vector &pt, Vector2D &vecUV )
  1480. {
  1481. Ray_t rayAxis;
  1482. Ray_t rayPt;
  1483. //
  1484. // handle the u axis
  1485. //
  1486. rayAxis.m_Start = vecOrigin;
  1487. rayAxis.m_Delta = vecU;
  1488. rayAxis.m_IsRay = true;
  1489. rayPt.m_Start = pt;
  1490. rayPt.m_Delta = vecV * -( lengthV * 10.0f );
  1491. rayPt.m_IsRay = true;
  1492. float s, t;
  1493. IntersectRayWithRay( rayAxis, rayPt, t, s );
  1494. vecUV[0] = t / lengthU;
  1495. //
  1496. // handle the v axis
  1497. //
  1498. rayAxis.m_Delta = vecV;
  1499. rayPt.m_Delta = vecU * -( lengthU * 10.0f );
  1500. IntersectRayWithRay( rayAxis, rayPt, t, s );
  1501. vecUV[1] = t / lengthV;
  1502. // inside of the quad??
  1503. if( ( vecUV[0] < 0.0f ) || ( vecUV[0] > 1.0f ) ||
  1504. ( vecUV[1] < 0.0f ) || ( vecUV[1] > 1.0f ) )
  1505. return BARY_QUADRATIC_FALSE;
  1506. return BARY_QUADRATIC_TRUE;
  1507. }
  1508. //-----------------------------------------------------------------------------
  1509. //-----------------------------------------------------------------------------
  1510. void ResolveQuadratic( double tPlus, double tMinus,
  1511. const Vector axisU0, const Vector axisU1,
  1512. const Vector axisV0, const Vector axisV1,
  1513. const Vector axisOrigin, const Vector pt,
  1514. int projU, double &s, double &t )
  1515. {
  1516. // calculate the sPlus, sMinus pair(s)
  1517. double sDenomPlus = ( axisU0[projU] * ( 1 - tPlus ) ) + ( axisU1[projU] * tPlus );
  1518. double sDenomMinus = ( axisU0[projU] * ( 1 - tMinus ) ) + ( axisU1[projU] * tMinus );
  1519. double sPlus = UNINIT, sMinus = UNINIT;
  1520. if( FloatMakePositive( sDenomPlus ) >= 1e-5 )
  1521. {
  1522. sPlus = ( pt[projU] - axisOrigin[projU] - ( axisV0[projU] * tPlus ) ) / sDenomPlus;
  1523. }
  1524. if( FloatMakePositive( sDenomMinus ) >= 1e-5 )
  1525. {
  1526. sMinus = ( pt[projU] - axisOrigin[projU] - ( axisV0[projU] * tMinus ) ) / sDenomMinus;
  1527. }
  1528. if( ( tPlus >= 0.0 ) && ( tPlus <= 1.0 ) && ( sPlus >= 0.0 ) && ( sPlus <= 1.0 ) )
  1529. {
  1530. s = sPlus;
  1531. t = tPlus;
  1532. return;
  1533. }
  1534. if( ( tMinus >= 0.0 ) && ( tMinus <= 1.0 ) && ( sMinus >= 0.0 ) && ( sMinus <= 1.0 ) )
  1535. {
  1536. s = sMinus;
  1537. t = tMinus;
  1538. return;
  1539. }
  1540. double s0, t0, s1, t1;
  1541. s0 = sPlus;
  1542. t0 = tPlus;
  1543. if( s0 >= 1.0 ) { s0 -= 1.0; }
  1544. if( t0 >= 1.0 ) { t0 -= 1.0; }
  1545. s1 = sMinus;
  1546. t1 = tMinus;
  1547. if( s1 >= 1.0 ) { s1 -= 1.0; }
  1548. if( t1 >= 1.0 ) { t1 -= 1.0; }
  1549. s0 = FloatMakePositive( s0 );
  1550. t0 = FloatMakePositive( t0 );
  1551. s1 = FloatMakePositive( s1 );
  1552. t1 = FloatMakePositive( t1 );
  1553. double max0, max1;
  1554. max0 = s0;
  1555. if( t0 > max0 ) { max0 = t0; }
  1556. max1 = s1;
  1557. if( t1 > max1 ) { max1 = t1; }
  1558. if( max0 > max1 )
  1559. {
  1560. s = sMinus;
  1561. t = tMinus;
  1562. }
  1563. else
  1564. {
  1565. s = sPlus;
  1566. t = tPlus;
  1567. }
  1568. }
  1569. //-----------------------------------------------------------------------------
  1570. //
  1571. //-----------------------------------------------------------------------------
  1572. QuadBarycentricRetval_t PointInQuadToBarycentric( const Vector &v1, const Vector &v2,
  1573. const Vector &v3, const Vector &v4, const Vector &point, Vector2D &uv )
  1574. {
  1575. #define PIQ_TEXTURE_EPSILON 0.001
  1576. #define PIQ_PLANE_EPSILON 0.1
  1577. #define PIQ_DOT_EPSILON 0.99f
  1578. //
  1579. // Think of a quad with points v1, v2, v3, v4 and u, v line segments
  1580. // u0 = v2 - v1
  1581. // u1 = v3 - v4
  1582. // v0 = v4 - v1
  1583. // v1 = v3 - v2
  1584. //
  1585. Vector axisU[2], axisV[2];
  1586. Vector axisUNorm[2], axisVNorm[2];
  1587. axisU[0] = axisUNorm[0] = v2 - v1;
  1588. axisU[1] = axisUNorm[1] = v3 - v4;
  1589. axisV[0] = axisVNorm[0] = v4 - v1;
  1590. axisV[1] = axisVNorm[1] = v3 - v2;
  1591. float lengthU[2], lengthV[2];
  1592. lengthU[0] = VectorNormalize( axisUNorm[0] );
  1593. lengthU[1] = VectorNormalize( axisUNorm[1] );
  1594. lengthV[0] = VectorNormalize( axisVNorm[0] );
  1595. lengthV[1] = VectorNormalize( axisVNorm[1] );
  1596. //
  1597. // check for an early out - parallel opposite edges!
  1598. // NOTE: quad property if 1 set of opposite edges is parallel and equal
  1599. // in length, then the other set of edges is as well
  1600. //
  1601. if( axisUNorm[0].Dot( axisUNorm[1] ) > PIQ_DOT_EPSILON )
  1602. {
  1603. if( FloatMakePositive( lengthU[0] - lengthU[1] ) < PIQ_PLANE_EPSILON )
  1604. {
  1605. return QuadWithParallelEdges( v1, axisUNorm[0], lengthU[0], axisVNorm[0], lengthV[0], point, uv );
  1606. }
  1607. }
  1608. //
  1609. // since we are solving for s in our equations below we need to ensure that
  1610. // the v axes are non-parallel
  1611. //
  1612. bool bFlipped = false;
  1613. if( axisVNorm[0].Dot( axisVNorm[1] ) > PIQ_DOT_EPSILON )
  1614. {
  1615. Vector tmp[2];
  1616. tmp[0] = axisV[0];
  1617. tmp[1] = axisV[1];
  1618. axisV[0] = axisU[0];
  1619. axisV[1] = axisU[1];
  1620. axisU[0] = tmp[0];
  1621. axisU[1] = tmp[1];
  1622. bFlipped = true;
  1623. }
  1624. //
  1625. // get the "projection" axes
  1626. //
  1627. Vector2D projAxes;
  1628. Vector vNormal = axisU[0].Cross( axisV[0] );
  1629. GetNonMajorAxes( vNormal, projAxes );
  1630. //
  1631. // NOTE: axisU[0][projAxes[0]] < axisU[0][projAxes[1]],
  1632. // this is done to decrease error when dividing later
  1633. //
  1634. if( FloatMakePositive( axisU[0][projAxes[0]] ) < FloatMakePositive( axisU[0][projAxes[1]] ) )
  1635. {
  1636. int tmp = projAxes[0];
  1637. projAxes[0] = projAxes[1];
  1638. projAxes[1] = tmp;
  1639. }
  1640. // Here's how we got these equations:
  1641. //
  1642. // Given the points and u,v line segments above...
  1643. //
  1644. // Then:
  1645. //
  1646. // (1.0) PT = P0 + U0 * s + V * t
  1647. //
  1648. // where
  1649. //
  1650. // (1.1) V = V0 + s * (V1 - V0)
  1651. // (1.2) U = U0 + t * (U1 - U0)
  1652. //
  1653. // Therefore (from 1.1 + 1.0):
  1654. // PT - P0 = U0 * s + (V0 + s * (V1-V0)) * t
  1655. // Group s's:
  1656. // PT - P0 - t * V0 = s * (U0 + t * (V1-V0))
  1657. // Two equations and two unknowns in x and y get you the following quadratic:
  1658. //
  1659. // solve the quadratic
  1660. //
  1661. double s = 0.0, t = 0.0;
  1662. double A, negB, C;
  1663. A = ( axisU[0][projAxes[1]] * axisV[0][projAxes[0]] ) -
  1664. ( axisU[0][projAxes[0]] * axisV[0][projAxes[1]] ) -
  1665. ( axisU[1][projAxes[1]] * axisV[0][projAxes[0]] ) +
  1666. ( axisU[1][projAxes[0]] * axisV[0][projAxes[1]] );
  1667. C = ( v1[projAxes[1]] * axisU[0][projAxes[0]] ) -
  1668. ( point[projAxes[1]] * axisU[0][projAxes[0]] ) -
  1669. ( v1[projAxes[0]] * axisU[0][projAxes[1]] ) +
  1670. ( point[projAxes[0]] * axisU[0][projAxes[1]] );
  1671. negB = C -
  1672. ( v1[projAxes[1]] * axisU[1][projAxes[0]] ) +
  1673. ( point[projAxes[1]] * axisU[1][projAxes[0]] ) +
  1674. ( v1[projAxes[0]] * axisU[1][projAxes[1]] ) -
  1675. ( point[projAxes[0]] * axisU[1][projAxes[1]] ) +
  1676. ( axisU[0][projAxes[1]] * axisV[0][projAxes[0]] ) -
  1677. ( axisU[0][projAxes[0]] * axisV[0][projAxes[1]] );
  1678. if( ( A > -PIQ_PLANE_EPSILON ) && ( A < PIQ_PLANE_EPSILON ) )
  1679. {
  1680. // shouldn't be here -- this should have been take care of in the "early out"
  1681. // Assert( 0 );
  1682. Vector vecUAvg, vecVAvg;
  1683. vecUAvg = ( axisUNorm[0] + axisUNorm[1] ) * 0.5f;
  1684. vecVAvg = ( axisVNorm[0] + axisVNorm[1] ) * 0.5f;
  1685. float fLengthUAvg = ( lengthU[0] + lengthU[1] ) * 0.5f;
  1686. float fLengthVAvg = ( lengthV[0] + lengthV[1] ) * 0.5f;
  1687. return QuadWithParallelEdges( v1, vecUAvg, fLengthUAvg, vecVAvg, fLengthVAvg, point, uv );
  1688. #if 0
  1689. // legacy code -- kept here for completeness!
  1690. // not a quadratic -- solve linearly
  1691. t = C / negB;
  1692. // See (1.2) above
  1693. float ui = axisU[0][projAxes[0]] + t * ( axisU[1][projAxes[0]] - axisU[0][projAxes[0]] );
  1694. if( FloatMakePositive( ui ) >= 1e-5 )
  1695. {
  1696. // See (1.0) above
  1697. s = ( point[projAxes[0]] - v1[projAxes[0]] - axisV[0][projAxes[0]] * t ) / ui;
  1698. }
  1699. #endif
  1700. }
  1701. else
  1702. {
  1703. // (-b +/- sqrt( b^2 - 4ac )) / 2a
  1704. double discriminant = (negB*negB) - (4.0f * A * C);
  1705. if( discriminant < 0.0f )
  1706. {
  1707. uv[0] = -99999.0f;
  1708. uv[1] = -99999.0f;
  1709. return BARY_QUADRATIC_NEGATIVE_DISCRIMINANT;
  1710. }
  1711. double quad = sqrt( discriminant );
  1712. double QPlus = ( negB + quad ) / ( 2.0f * A );
  1713. double QMinus = ( negB - quad ) / ( 2.0f * A );
  1714. ResolveQuadratic( QPlus, QMinus, axisU[0], axisU[1], axisV[0], axisV[1], v1, point, projAxes[0], s, t );
  1715. }
  1716. if( !bFlipped )
  1717. {
  1718. uv[0] = ( float )s;
  1719. uv[1] = ( float )t;
  1720. }
  1721. else
  1722. {
  1723. uv[0] = ( float )t;
  1724. uv[1] = ( float )s;
  1725. }
  1726. // inside of the quad??
  1727. if( ( uv[0] < 0.0f ) || ( uv[0] > 1.0f ) || ( uv[1] < 0.0f ) || ( uv[1] > 1.0f ) )
  1728. return BARY_QUADRATIC_FALSE;
  1729. return BARY_QUADRATIC_TRUE;
  1730. #undef PIQ_TEXTURE_EPSILON
  1731. #undef PIQ_PLANE_EPSILON
  1732. }
  1733. //-----------------------------------------------------------------------------
  1734. //-----------------------------------------------------------------------------
  1735. void PointInQuadFromBarycentric( const Vector &v1, const Vector &v2, const Vector &v3, const Vector &v4,
  1736. const Vector2D &uv, Vector &point )
  1737. {
  1738. //
  1739. // Think of a quad with points v1, v2, v3, v4 and u, v line segments
  1740. // find the ray from v0 edge to v1 edge at v
  1741. //
  1742. Vector vPts[2];
  1743. VectorLerp( v1, v4, uv[1], vPts[0] );
  1744. VectorLerp( v2, v3, uv[1], vPts[1] );
  1745. VectorLerp( vPts[0], vPts[1], uv[0], point );
  1746. }
  1747. //-----------------------------------------------------------------------------
  1748. //-----------------------------------------------------------------------------
  1749. void TexCoordInQuadFromBarycentric( const Vector2D &v1, const Vector2D &v2, const Vector2D &v3, const Vector2D &v4,
  1750. const Vector2D &uv, Vector2D &texCoord )
  1751. {
  1752. //
  1753. // Think of a quad with points v1, v2, v3, v4 and u, v line segments
  1754. // find the ray from v0 edge to v1 edge at v
  1755. //
  1756. Vector2D vCoords[2];
  1757. Vector2DLerp( v1, v4, uv[1], vCoords[0] );
  1758. Vector2DLerp( v2, v3, uv[1], vCoords[1] );
  1759. Vector2DLerp( vCoords[0], vCoords[1], uv[0], texCoord );
  1760. }
  1761. //-----------------------------------------------------------------------------
  1762. // Compute point from barycentric specification
  1763. // Edge u goes from v0 to v1, edge v goes from v0 to v2
  1764. //-----------------------------------------------------------------------------
  1765. void ComputePointFromBarycentric( const Vector& v0, const Vector& v1, const Vector& v2,
  1766. float u, float v, Vector& pt )
  1767. {
  1768. Vector edgeU, edgeV;
  1769. VectorSubtract( v1, v0, edgeU );
  1770. VectorSubtract( v2, v0, edgeV );
  1771. VectorMA( v0, u, edgeU, pt );
  1772. VectorMA( pt, v, edgeV, pt );
  1773. }
  1774. void ComputePointFromBarycentric( const Vector2D& v0, const Vector2D& v1, const Vector2D& v2,
  1775. float u, float v, Vector2D& pt )
  1776. {
  1777. Vector2D edgeU, edgeV;
  1778. Vector2DSubtract( v1, v0, edgeU );
  1779. Vector2DSubtract( v2, v0, edgeV );
  1780. Vector2DMA( v0, u, edgeU, pt );
  1781. Vector2DMA( pt, v, edgeV, pt );
  1782. }
  1783. //-----------------------------------------------------------------------------
  1784. // Compute a matrix that has the correct orientation but which has an origin at
  1785. // the center of the bounds
  1786. //-----------------------------------------------------------------------------
  1787. static void ComputeCenterMatrix( const Vector& origin, const QAngle& angles,
  1788. const Vector& mins, const Vector& maxs, matrix3x4_t& matrix )
  1789. {
  1790. Vector centroid;
  1791. VectorAdd( mins, maxs, centroid );
  1792. centroid *= 0.5f;
  1793. AngleMatrix( angles, matrix );
  1794. Vector worldCentroid;
  1795. VectorRotate( centroid, matrix, worldCentroid );
  1796. worldCentroid += origin;
  1797. MatrixSetColumn( worldCentroid, 3, matrix );
  1798. }
  1799. static void ComputeCenterIMatrix( const Vector& origin, const QAngle& angles,
  1800. const Vector& mins, const Vector& maxs, matrix3x4_t& matrix )
  1801. {
  1802. Vector centroid;
  1803. VectorAdd( mins, maxs, centroid );
  1804. centroid *= -0.5f;
  1805. AngleIMatrix( angles, matrix );
  1806. // For the translational component here, note that the origin in world space
  1807. // is T = R * C + O, (R = rotation matrix, C = centroid in local space, O = origin in world space)
  1808. // The IMatrix translation = - transpose(R) * T = -C - transpose(R) * 0
  1809. Vector localOrigin;
  1810. VectorRotate( origin, matrix, localOrigin );
  1811. centroid -= localOrigin;
  1812. MatrixSetColumn( centroid, 3, matrix );
  1813. }
  1814. //-----------------------------------------------------------------------------
  1815. // Compute a matrix which is the absolute value of another
  1816. //-----------------------------------------------------------------------------
  1817. static inline void ComputeAbsMatrix( const matrix3x4_t& in, matrix3x4_t& out )
  1818. {
  1819. FloatBits(out[0][0]) = FloatAbsBits(in[0][0]);
  1820. FloatBits(out[0][1]) = FloatAbsBits(in[0][1]);
  1821. FloatBits(out[0][2]) = FloatAbsBits(in[0][2]);
  1822. FloatBits(out[1][0]) = FloatAbsBits(in[1][0]);
  1823. FloatBits(out[1][1]) = FloatAbsBits(in[1][1]);
  1824. FloatBits(out[1][2]) = FloatAbsBits(in[1][2]);
  1825. FloatBits(out[2][0]) = FloatAbsBits(in[2][0]);
  1826. FloatBits(out[2][1]) = FloatAbsBits(in[2][1]);
  1827. FloatBits(out[2][2]) = FloatAbsBits(in[2][2]);
  1828. }
  1829. //-----------------------------------------------------------------------------
  1830. // Compute a separating plane between two boxes (expensive!)
  1831. // Returns false if no separating plane exists
  1832. //-----------------------------------------------------------------------------
  1833. static bool ComputeSeparatingPlane( const matrix3x4_t &worldToBox1, const matrix3x4_t &box2ToWorld,
  1834. const Vector& box1Size, const Vector& box2Size, float tolerance, cplane_t* pPlane )
  1835. {
  1836. // The various separating planes can be either
  1837. // 1) A plane parallel to one of the box face planes
  1838. // 2) A plane parallel to the cross-product of an edge from each box
  1839. // First, compute the basis of second box in the space of the first box
  1840. // NOTE: These basis place the origin at the centroid of each box!
  1841. matrix3x4_t box2ToBox1;
  1842. ConcatTransforms( worldToBox1, box2ToWorld, box2ToBox1 );
  1843. // We're going to be using the origin of box2 in the space of box1 alot,
  1844. // lets extract it from the matrix....
  1845. Vector box2Origin;
  1846. MatrixGetColumn( box2ToBox1, 3, box2Origin );
  1847. // Next get the absolute values of these entries and store in absbox2ToBox1.
  1848. matrix3x4_t absBox2ToBox1;
  1849. ComputeAbsMatrix( box2ToBox1, absBox2ToBox1 );
  1850. // There are 15 tests to make. The first 3 involve trying planes parallel
  1851. // to the faces of the first box.
  1852. // NOTE: The algorithm here involves finding the projections of the two boxes
  1853. // onto a particular line. If the projections on the line do not overlap,
  1854. // that means that there's a plane perpendicular to the line which separates
  1855. // the two boxes; and we've therefore found a separating plane.
  1856. // The way we check for overlay is we find the projections of the two boxes
  1857. // onto the line, and add them up. We compare the sum with the projection
  1858. // of the relative center of box2 onto the same line.
  1859. Vector tmp;
  1860. float boxProjectionSum;
  1861. float originProjection;
  1862. // NOTE: For these guys, we're taking advantage of the fact that the ith
  1863. // row of the box2ToBox1 is the direction of the box1 (x,y,z)-axis
  1864. // transformed into the space of box2.
  1865. // First side of box 1
  1866. boxProjectionSum = box1Size.x + MatrixRowDotProduct( absBox2ToBox1, 0, box2Size );
  1867. originProjection = FloatMakePositive( box2Origin.x ) + tolerance;
  1868. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1869. {
  1870. VectorCopy( worldToBox1[0], pPlane->normal.Base() );
  1871. return true;
  1872. }
  1873. // Second side of box 1
  1874. boxProjectionSum = box1Size.y + MatrixRowDotProduct( absBox2ToBox1, 1, box2Size );
  1875. originProjection = FloatMakePositive( box2Origin.y ) + tolerance;
  1876. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1877. {
  1878. VectorCopy( worldToBox1[1], pPlane->normal.Base() );
  1879. return true;
  1880. }
  1881. // Third side of box 1
  1882. boxProjectionSum = box1Size.z + MatrixRowDotProduct( absBox2ToBox1, 2, box2Size );
  1883. originProjection = FloatMakePositive( box2Origin.z ) + tolerance;
  1884. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1885. {
  1886. VectorCopy( worldToBox1[2], pPlane->normal.Base() );
  1887. return true;
  1888. }
  1889. // The next three involve checking splitting planes parallel to the
  1890. // faces of the second box.
  1891. // NOTE: For these guys, we're taking advantage of the fact that the 0th
  1892. // column of the box2ToBox1 is the direction of the box2 x-axis
  1893. // transformed into the space of box1.
  1894. // Here, we're determining the distance of box2's center from box1's center
  1895. // by projecting it onto a line parallel to box2's axis
  1896. // First side of box 2
  1897. boxProjectionSum = box2Size.x + MatrixColumnDotProduct( absBox2ToBox1, 0, box1Size );
  1898. originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 0, box2Origin ) ) + tolerance;
  1899. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1900. {
  1901. MatrixGetColumn( box2ToWorld, 0, pPlane->normal );
  1902. return true;
  1903. }
  1904. // Second side of box 2
  1905. boxProjectionSum = box2Size.y + MatrixColumnDotProduct( absBox2ToBox1, 1, box1Size );
  1906. originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 1, box2Origin ) ) + tolerance;
  1907. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1908. {
  1909. MatrixGetColumn( box2ToWorld, 1, pPlane->normal );
  1910. return true;
  1911. }
  1912. // Third side of box 2
  1913. boxProjectionSum = box2Size.z + MatrixColumnDotProduct( absBox2ToBox1, 2, box1Size );
  1914. originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 2, box2Origin ) ) + tolerance;
  1915. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1916. {
  1917. MatrixGetColumn( box2ToWorld, 2, pPlane->normal );
  1918. return true;
  1919. }
  1920. // Next check the splitting planes which are orthogonal to the pairs
  1921. // of edges, one from box1 and one from box2. As only direction matters,
  1922. // there are 9 pairs since each box has 3 distinct edge directions.
  1923. // Here, we take advantage of the fact that the edges from box 1 are all
  1924. // axis aligned; therefore the crossproducts are simplified. Let's walk through
  1925. // the example of b1e1 x b2e1:
  1926. // In this example, the line to check is perpendicular to b1e1 + b2e2
  1927. // we can compute this line by taking the cross-product:
  1928. //
  1929. // [ i j k ]
  1930. // [ 1 0 0 ] = - ez j + ey k = l1
  1931. // [ ex ey ez ]
  1932. // Where ex, ey, ez is the components of box2's x axis in the space of box 1,
  1933. // which is == to the 0th column of of box2toBox1
  1934. // The projection of box1 onto this line = the absolute dot product of the box size
  1935. // against the line, which =
  1936. // AbsDot( box1Size, l1 ) = abs( -ez * box1.y ) + abs( ey * box1.z )
  1937. // To compute the projection of box2 onto this line, we'll do it in the space of box 2
  1938. //
  1939. // [ i j k ]
  1940. // [ fx fy fz ] = fz j - fy k = l2
  1941. // [ 1 0 0 ]
  1942. // Where fx, fy, fz is the components of box1's x axis in the space of box 2,
  1943. // which is == to the 0th row of of box2toBox1
  1944. // The projection of box2 onto this line = the absolute dot product of the box size
  1945. // against the line, which =
  1946. // AbsDot( box2Size, l2 ) = abs( fz * box2.y ) + abs ( fy * box2.z )
  1947. // The projection of the relative origin position on this line is done in the
  1948. // space of box 1:
  1949. //
  1950. // originProjection = DotProduct( <-ez j + ey k>, box2Origin ) =
  1951. // -ez * box2Origin.y + ey * box2Origin.z
  1952. // NOTE: These checks can be bogus if both edges are parallel. The if
  1953. // checks at the beginning of each block are designed to catch that case
  1954. // b1e1 x b2e1
  1955. if ( absBox2ToBox1[0][0] < 1.0f - 1e-3f )
  1956. {
  1957. boxProjectionSum =
  1958. box1Size.y * absBox2ToBox1[2][0] + box1Size.z * absBox2ToBox1[1][0] +
  1959. box2Size.y * absBox2ToBox1[0][2] + box2Size.z * absBox2ToBox1[0][1];
  1960. originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][0] + box2Origin.z * box2ToBox1[1][0] ) + tolerance;
  1961. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1962. {
  1963. MatrixGetColumn( box2ToWorld, 0, tmp );
  1964. CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() );
  1965. return true;
  1966. }
  1967. }
  1968. // b1e1 x b2e2
  1969. if ( absBox2ToBox1[0][1] < 1.0f - 1e-3f )
  1970. {
  1971. boxProjectionSum =
  1972. box1Size.y * absBox2ToBox1[2][1] + box1Size.z * absBox2ToBox1[1][1] +
  1973. box2Size.x * absBox2ToBox1[0][2] + box2Size.z * absBox2ToBox1[0][0];
  1974. originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][1] + box2Origin.z * box2ToBox1[1][1] ) + tolerance;
  1975. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1976. {
  1977. MatrixGetColumn( box2ToWorld, 1, tmp );
  1978. CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() );
  1979. return true;
  1980. }
  1981. }
  1982. // b1e1 x b2e3
  1983. if ( absBox2ToBox1[0][2] < 1.0f - 1e-3f )
  1984. {
  1985. boxProjectionSum =
  1986. box1Size.y * absBox2ToBox1[2][2] + box1Size.z * absBox2ToBox1[1][2] +
  1987. box2Size.x * absBox2ToBox1[0][1] + box2Size.y * absBox2ToBox1[0][0];
  1988. originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][2] + box2Origin.z * box2ToBox1[1][2] ) + tolerance;
  1989. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1990. {
  1991. MatrixGetColumn( box2ToWorld, 2, tmp );
  1992. CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() );
  1993. return true;
  1994. }
  1995. }
  1996. // b1e2 x b2e1
  1997. if ( absBox2ToBox1[1][0] < 1.0f - 1e-3f )
  1998. {
  1999. boxProjectionSum =
  2000. box1Size.x * absBox2ToBox1[2][0] + box1Size.z * absBox2ToBox1[0][0] +
  2001. box2Size.y * absBox2ToBox1[1][2] + box2Size.z * absBox2ToBox1[1][1];
  2002. originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][0] - box2Origin.z * box2ToBox1[0][0] ) + tolerance;
  2003. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2004. {
  2005. MatrixGetColumn( box2ToWorld, 0, tmp );
  2006. CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() );
  2007. return true;
  2008. }
  2009. }
  2010. // b1e2 x b2e2
  2011. if ( absBox2ToBox1[1][1] < 1.0f - 1e-3f )
  2012. {
  2013. boxProjectionSum =
  2014. box1Size.x * absBox2ToBox1[2][1] + box1Size.z * absBox2ToBox1[0][1] +
  2015. box2Size.x * absBox2ToBox1[1][2] + box2Size.z * absBox2ToBox1[1][0];
  2016. originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][1] - box2Origin.z * box2ToBox1[0][1] ) + tolerance;
  2017. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2018. {
  2019. MatrixGetColumn( box2ToWorld, 1, tmp );
  2020. CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() );
  2021. return true;
  2022. }
  2023. }
  2024. // b1e2 x b2e3
  2025. if ( absBox2ToBox1[1][2] < 1.0f - 1e-3f )
  2026. {
  2027. boxProjectionSum =
  2028. box1Size.x * absBox2ToBox1[2][2] + box1Size.z * absBox2ToBox1[0][2] +
  2029. box2Size.x * absBox2ToBox1[1][1] + box2Size.y * absBox2ToBox1[1][0];
  2030. originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][2] - box2Origin.z * box2ToBox1[0][2] ) + tolerance;
  2031. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2032. {
  2033. MatrixGetColumn( box2ToWorld, 2, tmp );
  2034. CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() );
  2035. return true;
  2036. }
  2037. }
  2038. // b1e3 x b2e1
  2039. if ( absBox2ToBox1[2][0] < 1.0f - 1e-3f )
  2040. {
  2041. boxProjectionSum =
  2042. box1Size.x * absBox2ToBox1[1][0] + box1Size.y * absBox2ToBox1[0][0] +
  2043. box2Size.y * absBox2ToBox1[2][2] + box2Size.z * absBox2ToBox1[2][1];
  2044. originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][0] + box2Origin.y * box2ToBox1[0][0] ) + tolerance;
  2045. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2046. {
  2047. MatrixGetColumn( box2ToWorld, 0, tmp );
  2048. CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() );
  2049. return true;
  2050. }
  2051. }
  2052. // b1e3 x b2e2
  2053. if ( absBox2ToBox1[2][1] < 1.0f - 1e-3f )
  2054. {
  2055. boxProjectionSum =
  2056. box1Size.x * absBox2ToBox1[1][1] + box1Size.y * absBox2ToBox1[0][1] +
  2057. box2Size.x * absBox2ToBox1[2][2] + box2Size.z * absBox2ToBox1[2][0];
  2058. originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][1] + box2Origin.y * box2ToBox1[0][1] ) + tolerance;
  2059. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2060. {
  2061. MatrixGetColumn( box2ToWorld, 1, tmp );
  2062. CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() );
  2063. return true;
  2064. }
  2065. }
  2066. // b1e3 x b2e3
  2067. if ( absBox2ToBox1[2][2] < 1.0f - 1e-3f )
  2068. {
  2069. boxProjectionSum =
  2070. box1Size.x * absBox2ToBox1[1][2] + box1Size.y * absBox2ToBox1[0][2] +
  2071. box2Size.x * absBox2ToBox1[2][1] + box2Size.y * absBox2ToBox1[2][0];
  2072. originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][2] + box2Origin.y * box2ToBox1[0][2] ) + tolerance;
  2073. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2074. {
  2075. MatrixGetColumn( box2ToWorld, 2, tmp );
  2076. CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() );
  2077. return true;
  2078. }
  2079. }
  2080. return false;
  2081. }
  2082. //-----------------------------------------------------------------------------
  2083. // Compute a separating plane between two boxes (expensive!)
  2084. // Returns false if no separating plane exists
  2085. //-----------------------------------------------------------------------------
  2086. bool ComputeSeparatingPlane( const Vector& org1, const QAngle& angles1, const Vector& min1, const Vector& max1,
  2087. const Vector& org2, const QAngle& angles2, const Vector& min2, const Vector& max2,
  2088. float tolerance, cplane_t* pPlane )
  2089. {
  2090. matrix3x4_t worldToBox1, box2ToWorld;
  2091. ComputeCenterIMatrix( org1, angles1, min1, max1, worldToBox1 );
  2092. ComputeCenterMatrix( org2, angles2, min2, max2, box2ToWorld );
  2093. // Then compute the size of the two boxes
  2094. Vector box1Size, box2Size;
  2095. VectorSubtract( max1, min1, box1Size );
  2096. VectorSubtract( max2, min2, box2Size );
  2097. box1Size *= 0.5f;
  2098. box2Size *= 0.5f;
  2099. return ComputeSeparatingPlane( worldToBox1, box2ToWorld, box1Size, box2Size, tolerance, pPlane );
  2100. }
  2101. //-----------------------------------------------------------------------------
  2102. // Swept OBB test
  2103. //-----------------------------------------------------------------------------
  2104. bool IsRayIntersectingOBB( const Ray_t &ray, const Vector& org, const QAngle& angles,
  2105. const Vector& mins, const Vector& maxs )
  2106. {
  2107. if ( angles == vec3_angle )
  2108. {
  2109. Vector vecWorldMins, vecWorldMaxs;
  2110. VectorAdd( org, mins, vecWorldMins );
  2111. VectorAdd( org, maxs, vecWorldMaxs );
  2112. return IsBoxIntersectingRay( vecWorldMins, vecWorldMaxs, ray );
  2113. }
  2114. if ( ray.m_IsRay )
  2115. {
  2116. matrix3x4_t worldToBox;
  2117. AngleIMatrix( angles, org, worldToBox );
  2118. Ray_t rotatedRay;
  2119. VectorTransform( ray.m_Start, worldToBox, rotatedRay.m_Start );
  2120. VectorRotate( ray.m_Delta, worldToBox, rotatedRay.m_Delta );
  2121. rotatedRay.m_StartOffset = vec3_origin;
  2122. rotatedRay.m_Extents = vec3_origin;
  2123. rotatedRay.m_IsRay = ray.m_IsRay;
  2124. rotatedRay.m_IsSwept = ray.m_IsSwept;
  2125. return IsBoxIntersectingRay( mins, maxs, rotatedRay );
  2126. }
  2127. if ( !ray.m_IsSwept )
  2128. {
  2129. cplane_t plane;
  2130. return ComputeSeparatingPlane( ray.m_Start, vec3_angle, -ray.m_Extents, ray.m_Extents,
  2131. org, angles, mins, maxs, 0.0f, &plane ) == false;
  2132. }
  2133. // NOTE: See the comments in ComputeSeparatingPlane to understand this math
  2134. // First, compute the basis of box in the space of the ray
  2135. // NOTE: These basis place the origin at the centroid of each box!
  2136. matrix3x4_t worldToBox1, box2ToWorld;
  2137. ComputeCenterMatrix( org, angles, mins, maxs, box2ToWorld );
  2138. // Find the center + extents of an AABB surrounding the ray
  2139. Vector vecRayCenter;
  2140. VectorMA( ray.m_Start, 0.5, ray.m_Delta, vecRayCenter );
  2141. vecRayCenter *= -1.0f;
  2142. SetIdentityMatrix( worldToBox1 );
  2143. MatrixSetColumn( vecRayCenter, 3, worldToBox1 );
  2144. Vector box1Size;
  2145. box1Size.x = ray.m_Extents.x + FloatMakePositive( ray.m_Delta.x ) * 0.5f;
  2146. box1Size.y = ray.m_Extents.y + FloatMakePositive( ray.m_Delta.y ) * 0.5f;
  2147. box1Size.z = ray.m_Extents.z + FloatMakePositive( ray.m_Delta.z ) * 0.5f;
  2148. // Then compute the size of the box
  2149. Vector box2Size;
  2150. VectorSubtract( maxs, mins, box2Size );
  2151. box2Size *= 0.5f;
  2152. // Do an OBB test of the box with the AABB surrounding the ray
  2153. cplane_t plane;
  2154. if ( ComputeSeparatingPlane( worldToBox1, box2ToWorld, box1Size, box2Size, 0.0f, &plane ) )
  2155. return false;
  2156. // Now deal with the planes which are the cross products of the ray sweep direction vs box edges
  2157. Vector vecRayDirection = ray.m_Delta;
  2158. VectorNormalize( vecRayDirection );
  2159. // Need a vector between ray center vs box center measured in the space of the ray (world)
  2160. Vector vecCenterDelta;
  2161. vecCenterDelta.x = box2ToWorld[0][3] - ray.m_Start.x;
  2162. vecCenterDelta.y = box2ToWorld[1][3] - ray.m_Start.y;
  2163. vecCenterDelta.z = box2ToWorld[2][3] - ray.m_Start.z;
  2164. // Rotate the ray direction into the space of the OBB
  2165. Vector vecAbsRayDirBox2;
  2166. VectorIRotate( vecRayDirection, box2ToWorld, vecAbsRayDirBox2 );
  2167. // Make abs versions of the ray in world space + ray in box2 space
  2168. VectorAbs( vecAbsRayDirBox2, vecAbsRayDirBox2 );
  2169. // Now do the work for the planes which are perpendicular to the edges of the AABB
  2170. // and the sweep direction edges...
  2171. // In this example, the line to check is perpendicular to box edge x + ray delta
  2172. // we can compute this line by taking the cross-product:
  2173. //
  2174. // [ i j k ]
  2175. // [ 1 0 0 ] = - dz j + dy k = l1
  2176. // [ dx dy dz ]
  2177. // Where dx, dy, dz is the ray delta (normalized)
  2178. // The projection of the box onto this line = the absolute dot product of the box size
  2179. // against the line, which =
  2180. // AbsDot( vecBoxHalfDiagonal, l1 ) = abs( -dz * vecBoxHalfDiagonal.y ) + abs( dy * vecBoxHalfDiagonal.z )
  2181. // Because the plane contains the sweep direction, the sweep will produce
  2182. // no extra projection onto the line normal to the plane.
  2183. // Therefore all we need to do is project the ray extents onto this line also:
  2184. // AbsDot( ray.m_Extents, l1 ) = abs( -dz * ray.m_Extents.y ) + abs( dy * ray.m_Extents.z )
  2185. Vector vecPlaneNormal;
  2186. // box x x ray delta
  2187. CrossProduct( vecRayDirection, Vector( box2ToWorld[0][0], box2ToWorld[1][0], box2ToWorld[2][0] ), vecPlaneNormal );
  2188. float flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) );
  2189. float flBoxProjectionSum =
  2190. vecAbsRayDirBox2.z * box2Size.y + vecAbsRayDirBox2.y * box2Size.z +
  2191. DotProductAbs( vecPlaneNormal, ray.m_Extents );
  2192. if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) )
  2193. return false;
  2194. // box y x ray delta
  2195. CrossProduct( vecRayDirection, Vector( box2ToWorld[0][1], box2ToWorld[1][1], box2ToWorld[2][1] ), vecPlaneNormal );
  2196. flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) );
  2197. flBoxProjectionSum =
  2198. vecAbsRayDirBox2.z * box2Size.x + vecAbsRayDirBox2.x * box2Size.z +
  2199. DotProductAbs( vecPlaneNormal, ray.m_Extents );
  2200. if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) )
  2201. return false;
  2202. // box z x ray delta
  2203. CrossProduct( vecRayDirection, Vector( box2ToWorld[0][2], box2ToWorld[1][2], box2ToWorld[2][2] ), vecPlaneNormal );
  2204. flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) );
  2205. flBoxProjectionSum =
  2206. vecAbsRayDirBox2.y * box2Size.x + vecAbsRayDirBox2.x * box2Size.y +
  2207. DotProductAbs( vecPlaneNormal, ray.m_Extents );
  2208. if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) )
  2209. return false;
  2210. return true;
  2211. }
  2212. //--------------------------------------------------------------------------
  2213. // Purpose:
  2214. //
  2215. // NOTE:
  2216. // triangle points are given in clockwise order (aabb-triangle test)
  2217. //
  2218. // 1 edge0 = 1 - 0
  2219. // | \ edge1 = 2 - 1
  2220. // | \ edge2 = 0 - 2
  2221. // | \ .
  2222. // | \ .
  2223. // 0-----2 .
  2224. //
  2225. //--------------------------------------------------------------------------
  2226. //-----------------------------------------------------------------------------
  2227. // Purpose: find the minima and maxima of the 3 given values
  2228. //-----------------------------------------------------------------------------
  2229. inline void FindMinMax( float v1, float v2, float v3, float &min, float &max )
  2230. {
  2231. min = max = v1;
  2232. if ( v2 < min ) { min = v2; }
  2233. if ( v2 > max ) { max = v2; }
  2234. if ( v3 < min ) { min = v3; }
  2235. if ( v3 > max ) { max = v3; }
  2236. }
  2237. //-----------------------------------------------------------------------------
  2238. // Purpose:
  2239. //-----------------------------------------------------------------------------
  2240. inline bool AxisTestEdgeCrossX2( float flEdgeZ, float flEdgeY, float flAbsEdgeZ, float flAbsEdgeY,
  2241. const Vector &p1, const Vector &p3, const Vector &vecExtents,
  2242. float flTolerance )
  2243. {
  2244. // Cross Product( axialX(1,0,0) x edge ): x = 0.0f, y = edge.z, z = -edge.y
  2245. // Triangle Point Distances: dist(x) = normal.y * pt(x).y + normal.z * pt(x).z
  2246. float flDist1 = flEdgeZ * p1.y - flEdgeY * p1.z;
  2247. float flDist3 = flEdgeZ * p3.y - flEdgeY * p3.z;
  2248. // Extents are symmetric: dist = abs( normal.y ) * extents.y + abs( normal.z ) * extents.z
  2249. float flDistBox = flAbsEdgeZ * vecExtents.y + flAbsEdgeY * vecExtents.z;
  2250. // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB).
  2251. if ( flDist1 < flDist3 )
  2252. {
  2253. if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) )
  2254. return false;
  2255. }
  2256. else
  2257. {
  2258. if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) )
  2259. return false;
  2260. }
  2261. return true;
  2262. }
  2263. //--------------------------------------------------------------------------
  2264. // Purpose:
  2265. //--------------------------------------------------------------------------
  2266. inline bool AxisTestEdgeCrossX3( float flEdgeZ, float flEdgeY, float flAbsEdgeZ, float flAbsEdgeY,
  2267. const Vector &p1, const Vector &p2, const Vector &vecExtents,
  2268. float flTolerance )
  2269. {
  2270. // Cross Product( axialX(1,0,0) x edge ): x = 0.0f, y = edge.z, z = -edge.y
  2271. // Triangle Point Distances: dist(x) = normal.y * pt(x).y + normal.z * pt(x).z
  2272. float flDist1 = flEdgeZ * p1.y - flEdgeY * p1.z;
  2273. float flDist2 = flEdgeZ * p2.y - flEdgeY * p2.z;
  2274. // Extents are symmetric: dist = abs( normal.y ) * extents.y + abs( normal.z ) * extents.z
  2275. float flDistBox = flAbsEdgeZ * vecExtents.y + flAbsEdgeY * vecExtents.z;
  2276. // Either dist1, dist2 is the closest point to the box, determine which and test of overlap with box(AABB).
  2277. if ( flDist1 < flDist2 )
  2278. {
  2279. if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) )
  2280. return false;
  2281. }
  2282. else
  2283. {
  2284. if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) )
  2285. return false;
  2286. }
  2287. return true;
  2288. }
  2289. //--------------------------------------------------------------------------
  2290. //--------------------------------------------------------------------------
  2291. inline bool AxisTestEdgeCrossY2( float flEdgeZ, float flEdgeX, float flAbsEdgeZ, float flAbsEdgeX,
  2292. const Vector &p1, const Vector &p3, const Vector &vecExtents,
  2293. float flTolerance )
  2294. {
  2295. // Cross Product( axialY(0,1,0) x edge ): x = -edge.z, y = 0.0f, z = edge.x
  2296. // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.z * pt(x).z
  2297. float flDist1 = -flEdgeZ * p1.x + flEdgeX * p1.z;
  2298. float flDist3 = -flEdgeZ * p3.x + flEdgeX * p3.z;
  2299. // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.z ) * extents.z
  2300. float flDistBox = flAbsEdgeZ * vecExtents.x + flAbsEdgeX * vecExtents.z;
  2301. // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB).
  2302. if ( flDist1 < flDist3 )
  2303. {
  2304. if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) )
  2305. return false;
  2306. }
  2307. else
  2308. {
  2309. if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) )
  2310. return false;
  2311. }
  2312. return true;
  2313. }
  2314. //--------------------------------------------------------------------------
  2315. //--------------------------------------------------------------------------
  2316. inline bool AxisTestEdgeCrossY3( float flEdgeZ, float flEdgeX, float flAbsEdgeZ, float flAbsEdgeX,
  2317. const Vector &p1, const Vector &p2, const Vector &vecExtents,
  2318. float flTolerance )
  2319. {
  2320. // Cross Product( axialY(0,1,0) x edge ): x = -edge.z, y = 0.0f, z = edge.x
  2321. // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.z * pt(x).z
  2322. float flDist1 = -flEdgeZ * p1.x + flEdgeX * p1.z;
  2323. float flDist2 = -flEdgeZ * p2.x + flEdgeX * p2.z;
  2324. // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.z ) * extents.z
  2325. float flDistBox = flAbsEdgeZ * vecExtents.x + flAbsEdgeX * vecExtents.z;
  2326. // Either dist1, dist2 is the closest point to the box, determine which and test of overlap with box(AABB).
  2327. if ( flDist1 < flDist2 )
  2328. {
  2329. if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) )
  2330. return false;
  2331. }
  2332. else
  2333. {
  2334. if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) )
  2335. return false;
  2336. }
  2337. return true;
  2338. }
  2339. //--------------------------------------------------------------------------
  2340. //--------------------------------------------------------------------------
  2341. inline bool AxisTestEdgeCrossZ1( float flEdgeY, float flEdgeX, float flAbsEdgeY, float flAbsEdgeX,
  2342. const Vector &p2, const Vector &p3, const Vector &vecExtents,
  2343. float flTolerance )
  2344. {
  2345. // Cross Product( axialZ(0,0,1) x edge ): x = edge.y, y = -edge.x, z = 0.0f
  2346. // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.y * pt(x).y
  2347. float flDist2 = flEdgeY * p2.x - flEdgeX * p2.y;
  2348. float flDist3 = flEdgeY * p3.x - flEdgeX * p3.y;
  2349. // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.y ) * extents.y
  2350. float flDistBox = flAbsEdgeY * vecExtents.x + flAbsEdgeX * vecExtents.y;
  2351. // Either dist2, dist3 is the closest point to the box, determine which and test of overlap with box(AABB).
  2352. if ( flDist3 < flDist2 )
  2353. {
  2354. if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) )
  2355. return false;
  2356. }
  2357. else
  2358. {
  2359. if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) )
  2360. return false;
  2361. }
  2362. return true;
  2363. }
  2364. //--------------------------------------------------------------------------
  2365. //--------------------------------------------------------------------------
  2366. inline bool AxisTestEdgeCrossZ2( float flEdgeY, float flEdgeX, float flAbsEdgeY, float flAbsEdgeX,
  2367. const Vector &p1, const Vector &p3, const Vector &vecExtents,
  2368. float flTolerance )
  2369. {
  2370. // Cross Product( axialZ(0,0,1) x edge ): x = edge.y, y = -edge.x, z = 0.0f
  2371. // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.y * pt(x).y
  2372. float flDist1 = flEdgeY * p1.x - flEdgeX * p1.y;
  2373. float flDist3 = flEdgeY * p3.x - flEdgeX * p3.y;
  2374. // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.y ) * extents.y
  2375. float flDistBox = flAbsEdgeY * vecExtents.x + flAbsEdgeX * vecExtents.y;
  2376. // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB).
  2377. if ( flDist1 < flDist3 )
  2378. {
  2379. if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) )
  2380. return false;
  2381. }
  2382. else
  2383. {
  2384. if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) )
  2385. return false;
  2386. }
  2387. return true;
  2388. }
  2389. //-----------------------------------------------------------------------------
  2390. // Purpose: Test for an intersection (overlap) between an axial-aligned bounding
  2391. // box (AABB) and a triangle.
  2392. //
  2393. // Using the "Separating-Axis Theorem" to test for intersections between
  2394. // a triangle and an axial-aligned bounding box (AABB).
  2395. // 1. 3 Axis Planes - x, y, z
  2396. // 2. 9 Edge Planes Tests - the 3 edges of the triangle crossed with all 3 axial
  2397. // planes (x, y, z)
  2398. // 3. 1 Face Plane - the triangle plane (cplane_t plane below)
  2399. // Output: false = separating axis (no intersection)
  2400. // true = intersection
  2401. //-----------------------------------------------------------------------------
  2402. bool IsBoxIntersectingTriangle( const Vector &vecBoxCenter, const Vector &vecBoxExtents,
  2403. const Vector &v1, const Vector &v2, const Vector &v3,
  2404. const cplane_t &plane, float flTolerance )
  2405. {
  2406. // Test the axial planes (x,y,z) against the min, max of the triangle.
  2407. float flMin, flMax;
  2408. Vector p1, p2, p3;
  2409. // x plane
  2410. p1.x = v1.x - vecBoxCenter.x;
  2411. p2.x = v2.x - vecBoxCenter.x;
  2412. p3.x = v3.x - vecBoxCenter.x;
  2413. FindMinMax( p1.x, p2.x, p3.x, flMin, flMax );
  2414. if ( ( flMin > ( vecBoxExtents.x + flTolerance ) ) || ( flMax < -( vecBoxExtents.x + flTolerance ) ) )
  2415. return false;
  2416. // y plane
  2417. p1.y = v1.y - vecBoxCenter.y;
  2418. p2.y = v2.y - vecBoxCenter.y;
  2419. p3.y = v3.y - vecBoxCenter.y;
  2420. FindMinMax( p1.y, p2.y, p3.y, flMin, flMax );
  2421. if ( ( flMin > ( vecBoxExtents.y + flTolerance ) ) || ( flMax < -( vecBoxExtents.y + flTolerance ) ) )
  2422. return false;
  2423. // z plane
  2424. p1.z = v1.z - vecBoxCenter.z;
  2425. p2.z = v2.z - vecBoxCenter.z;
  2426. p3.z = v3.z - vecBoxCenter.z;
  2427. FindMinMax( p1.z, p2.z, p3.z, flMin, flMax );
  2428. if ( ( flMin > ( vecBoxExtents.z + flTolerance ) ) || ( flMax < -( vecBoxExtents.z + flTolerance ) ) )
  2429. return false;
  2430. // Test the 9 edge cases.
  2431. Vector vecEdge, vecAbsEdge;
  2432. // edge 0 (cross x,y,z)
  2433. vecEdge = p2 - p1;
  2434. vecAbsEdge.y = FloatMakePositive( vecEdge.y );
  2435. vecAbsEdge.z = FloatMakePositive( vecEdge.z );
  2436. if ( !AxisTestEdgeCrossX2( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p3, vecBoxExtents, flTolerance ) )
  2437. return false;
  2438. vecAbsEdge.x = FloatMakePositive( vecEdge.x );
  2439. if ( !AxisTestEdgeCrossY2( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p3, vecBoxExtents, flTolerance ) )
  2440. return false;
  2441. if ( !AxisTestEdgeCrossZ1( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p2, p3, vecBoxExtents, flTolerance ) )
  2442. return false;
  2443. // edge 1 (cross x,y,z)
  2444. vecEdge = p3 - p2;
  2445. vecAbsEdge.y = FloatMakePositive( vecEdge.y );
  2446. vecAbsEdge.z = FloatMakePositive( vecEdge.z );
  2447. if ( !AxisTestEdgeCrossX2( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p2, vecBoxExtents, flTolerance ) )
  2448. return false;
  2449. vecAbsEdge.x = FloatMakePositive( vecEdge.x );
  2450. if ( !AxisTestEdgeCrossY2( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p2, vecBoxExtents, flTolerance ) )
  2451. return false;
  2452. if ( !AxisTestEdgeCrossZ2( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p1, p3, vecBoxExtents, flTolerance ) )
  2453. return false;
  2454. // edge 2 (cross x,y,z)
  2455. vecEdge = p1 - p3;
  2456. vecAbsEdge.y = FloatMakePositive( vecEdge.y );
  2457. vecAbsEdge.z = FloatMakePositive( vecEdge.z );
  2458. if ( !AxisTestEdgeCrossX3( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p2, vecBoxExtents, flTolerance ) )
  2459. return false;
  2460. vecAbsEdge.x = FloatMakePositive( vecEdge.x );
  2461. if ( !AxisTestEdgeCrossY3( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p2, vecBoxExtents, flTolerance ) )
  2462. return false;
  2463. if ( !AxisTestEdgeCrossZ1( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p2, p3, vecBoxExtents, flTolerance ) )
  2464. return false;
  2465. // Test against the triangle face plane.
  2466. Vector vecMin, vecMax;
  2467. VectorSubtract( vecBoxCenter, vecBoxExtents, vecMin );
  2468. VectorAdd( vecBoxCenter, vecBoxExtents, vecMax );
  2469. if ( BoxOnPlaneSide( vecMin, vecMax, &plane ) != 3 )
  2470. return false;
  2471. return true;
  2472. }
  2473. // NOTE: JAY: This is untested code based on Real-time Collision Detection by Ericson
  2474. #if 0
  2475. Vector CalcClosestPointOnTriangle( const Vector &P, const Vector &v0, const Vector &v1, const Vector &v2 )
  2476. {
  2477. Vector e0 = v1 - v0;
  2478. Vector e1 = v2 - v0;
  2479. Vector p0 = P - v0;
  2480. // voronoi region of v0
  2481. float d1 = DotProduct( e0, p0 );
  2482. float d2 = DotProduct( e1, p0 );
  2483. if (d1 <= 0.0f && d2 <= 0.0f)
  2484. return v0;
  2485. // voronoi region of v1
  2486. Vector p1 = P - v1;
  2487. float d3 = DotProduct( e0, p1 );
  2488. float d4 = DotProduct( e1, p1 );
  2489. if (d3 >=0.0f && d4 <= d3)
  2490. return v1;
  2491. // voronoi region of e0 (v0-v1)
  2492. float ve2 = d1*d4 - d3*d2;
  2493. if ( ve2 <= 0.0f && d1 >= 0.0f && d3 <= 0.0f )
  2494. {
  2495. float v = d1 / (d1-d3);
  2496. return v0 + v * e0;
  2497. }
  2498. // voronoi region of v2
  2499. Vector p2 = P - v2;
  2500. float d5 = DotProduct( e0, p2 );
  2501. float d6 = DotProduct( e1, p2 );
  2502. if (d6 >= 0.0f && d5 <= d6)
  2503. return v2;
  2504. // voronoi region of e1
  2505. float ve1 = d5*d2 - d1*d6;
  2506. if (ve1 <= 0.0f && d2 >= 0.0f && d6 >= 0.0f)
  2507. {
  2508. float w = d2 / (d2-d6);
  2509. return v0 + w * e1;
  2510. }
  2511. // voronoi region on e2
  2512. float ve0 = d3*d6 - d5*d4;
  2513. if ( ve0 <= 0.0f && (d4-d3) >= 0.0f && (d5-d6) >= 0.0f )
  2514. {
  2515. float w = (d4-d3)/((d4-d3) + (d5-d6));
  2516. return v1 + w * (v2-v1);
  2517. }
  2518. // voronoi region of v0v1v2 triangle
  2519. float denom = 1.0f / (ve0+ve1+ve2);
  2520. float v = ve1*denom;
  2521. float w = ve2 * denom;
  2522. return v0 + e0 * v + e1 * w;
  2523. }
  2524. #endif
  2525. bool OBBHasFullyContainedIntersectionWithQuad( const Vector &vOBBExtent1_Scaled, const Vector &vOBBExtent2_Scaled, const Vector &vOBBExtent3_Scaled, const Vector &ptOBBCenter,
  2526. const Vector &vQuadNormal, float fQuadPlaneDist, const Vector &ptQuadCenter,
  2527. const Vector &vQuadExtent1_Normalized, float fQuadExtent1Length,
  2528. const Vector &vQuadExtent2_Normalized, float fQuadExtent2Length )
  2529. {
  2530. Vector ptOBB[8]; //this specific ordering helps us web out from a point to its 3 connecting points with some bit math (most importantly, no if's)
  2531. ptOBB[0] = ptOBBCenter - vOBBExtent1_Scaled - vOBBExtent2_Scaled - vOBBExtent3_Scaled;
  2532. ptOBB[1] = ptOBBCenter - vOBBExtent1_Scaled - vOBBExtent2_Scaled + vOBBExtent3_Scaled;
  2533. ptOBB[2] = ptOBBCenter - vOBBExtent1_Scaled + vOBBExtent2_Scaled + vOBBExtent3_Scaled;
  2534. ptOBB[3] = ptOBBCenter - vOBBExtent1_Scaled + vOBBExtent2_Scaled - vOBBExtent3_Scaled;
  2535. ptOBB[4] = ptOBBCenter + vOBBExtent1_Scaled - vOBBExtent2_Scaled - vOBBExtent3_Scaled;
  2536. ptOBB[5] = ptOBBCenter + vOBBExtent1_Scaled - vOBBExtent2_Scaled + vOBBExtent3_Scaled;
  2537. ptOBB[6] = ptOBBCenter + vOBBExtent1_Scaled + vOBBExtent2_Scaled + vOBBExtent3_Scaled;
  2538. ptOBB[7] = ptOBBCenter + vOBBExtent1_Scaled + vOBBExtent2_Scaled - vOBBExtent3_Scaled;
  2539. float fDists[8];
  2540. for( int i = 0; i != 8; ++i )
  2541. fDists[i] = vQuadNormal.Dot( ptOBB[i] ) - fQuadPlaneDist;
  2542. int iSides[8];
  2543. int iSideMask = 0;
  2544. for( int i = 0; i != 8; ++i )
  2545. {
  2546. if( fDists[i] > 0.0f )
  2547. {
  2548. iSides[i] = 1;
  2549. iSideMask |= 1;
  2550. }
  2551. else
  2552. {
  2553. iSides[i] = 2;
  2554. iSideMask |= 2;
  2555. }
  2556. }
  2557. if( iSideMask != 3 ) //points reside entirely on one side of the quad's plane
  2558. return false;
  2559. Vector ptPlaneIntersections[12]; //only have 12 lines, can only possibly generate 12 split points
  2560. int iPlaneIntersectionsCount = 0;
  2561. for( int i = 0; i != 8; ++i )
  2562. {
  2563. if( iSides[i] == 2 ) //point behind the plane
  2564. {
  2565. int iAxisCrossings[3];
  2566. iAxisCrossings[0] = i ^ 4; //upper 4 vs lower 4 crosses vOBBExtent1 axis
  2567. iAxisCrossings[1] = ((i + 1) & 3) + (i & 4); //cycle to the next element while staying within the upper 4 or lower 4, this will cross either vOBBExtent2 or vOBBExtent3 axis, we don't care which
  2568. iAxisCrossings[2] = ((i - 1) & 3) + (i & 4); //cylce to the previous element while staying within the upper 4 or lower 4, this will cross the axis iAxisCrossings[1] didn't cross
  2569. for( int j = 0; j != 3; ++j )
  2570. {
  2571. if( iSides[iAxisCrossings[j]] == 1 ) //point in front of the plane
  2572. {
  2573. //line between ptOBB[i] and ptOBB[iAxisCrossings[j]] intersects the plane, generate a point at the intersection for further testing
  2574. float fTotalDist = fDists[iAxisCrossings[j]] - fDists[i]; //remember that fDists[i] is a negative value
  2575. ptPlaneIntersections[iPlaneIntersectionsCount] = (ptOBB[iAxisCrossings[j]] * (-fDists[i]/fTotalDist)) + (ptOBB[i] * (fDists[iAxisCrossings[j]]/fTotalDist));
  2576. Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane
  2577. ++iPlaneIntersectionsCount;
  2578. }
  2579. }
  2580. }
  2581. }
  2582. Assert( iPlaneIntersectionsCount != 0 );
  2583. for( int i = 0; i != iPlaneIntersectionsCount; ++i )
  2584. {
  2585. //these points are guaranteed to be on the plane, now just check to see if they're within the quad's extents
  2586. Vector vToPointFromQuadCenter = ptPlaneIntersections[i] - ptQuadCenter;
  2587. float fExt1Dist = vQuadExtent1_Normalized.Dot( vToPointFromQuadCenter );
  2588. if( fabs( fExt1Dist ) > fQuadExtent1Length )
  2589. return false; //point is outside boundaries
  2590. //vToPointFromQuadCenter -= vQuadExtent1_Normalized * fExt1Dist; //to handle diamond shaped quads
  2591. float fExt2Dist = vQuadExtent2_Normalized.Dot( vToPointFromQuadCenter );
  2592. if( fabs( fExt2Dist ) > fQuadExtent2Length )
  2593. return false; //point is outside boundaries
  2594. }
  2595. return true; //there were lines crossing the quad plane, and every line crossing that plane had its intersection with the plane within the quad's boundaries
  2596. }
  2597. //-----------------------------------------------------------------------------
  2598. // Compute if the Ray intersects the quad plane, and whether the entire
  2599. // Ray/Quad intersection is contained within the quad itself
  2600. //
  2601. // False if no intersection exists, or if part of the intersection is
  2602. // outside the quad's extents
  2603. //-----------------------------------------------------------------------------
  2604. bool RayHasFullyContainedIntersectionWithQuad( const Ray_t &ray,
  2605. const Vector &vQuadNormal, float fQuadPlaneDist, const Vector &ptQuadCenter,
  2606. const Vector &vQuadExtent1_Normalized, float fQuadExtent1Length,
  2607. const Vector &vQuadExtent2_Normalized, float fQuadExtent2Length )
  2608. {
  2609. Vector ptPlaneIntersections[(12 + 12 + 8)]; //absolute max possible: 12 lines to connect the start box, 12 more to connect the end box, 8 to connect the boxes to eachother
  2610. //8 points to make an AABB, 8 lines to connect each point from it's start to end point along the ray, 8 possible intersections
  2611. int iPlaneIntersectionsCount = 0;
  2612. if( ray.m_IsRay )
  2613. {
  2614. //just 1 line
  2615. if( ray.m_IsSwept )
  2616. {
  2617. Vector ptEndPoints[2];
  2618. ptEndPoints[0] = ray.m_Start;
  2619. ptEndPoints[1] = ptEndPoints[0] + ray.m_Delta;
  2620. int i;
  2621. float fDists[2];
  2622. for( i = 0; i != 2; ++i )
  2623. fDists[i] = vQuadNormal.Dot( ptEndPoints[i] ) - fQuadPlaneDist;
  2624. for( i = 0; i != 2; ++i )
  2625. {
  2626. if( fDists[i] <= 0.0f )
  2627. {
  2628. int j = 1-i;
  2629. if( fDists[j] >= 0.0f )
  2630. {
  2631. float fInvTotalDist = 1.0f / (fDists[j] - fDists[i]); //fDists[i] <= 0, ray is swept so no chance that the denom was 0
  2632. ptPlaneIntersections[0] = (ptEndPoints[i] * (fDists[j] * fInvTotalDist)) - (ptEndPoints[j] * (fDists[i] * fInvTotalDist)); //fDists[i] <= 0
  2633. Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane
  2634. iPlaneIntersectionsCount = 1;
  2635. }
  2636. else
  2637. {
  2638. return false;
  2639. }
  2640. break;
  2641. }
  2642. }
  2643. if( i == 2 )
  2644. return false;
  2645. }
  2646. else //not swept, so this is actually a point on quad question
  2647. {
  2648. if( fabs( vQuadNormal.Dot( ray.m_Start ) - fQuadPlaneDist ) < 1e-6 )
  2649. {
  2650. ptPlaneIntersections[0] = ray.m_Start;
  2651. iPlaneIntersectionsCount = 1;
  2652. }
  2653. else
  2654. {
  2655. return false;
  2656. }
  2657. }
  2658. }
  2659. else
  2660. {
  2661. Vector ptEndPoints[2][8];
  2662. //this specific ordering helps us web out from a point to its 3 connecting points with some bit math (most importantly, no if's)
  2663. ptEndPoints[0][0] = ray.m_Start; ptEndPoints[0][0].x -= ray.m_Extents.x; ptEndPoints[0][0].y -= ray.m_Extents.y; ptEndPoints[0][0].z -= ray.m_Extents.z;
  2664. ptEndPoints[0][1] = ray.m_Start; ptEndPoints[0][1].x -= ray.m_Extents.x; ptEndPoints[0][1].y -= ray.m_Extents.y; ptEndPoints[0][1].z += ray.m_Extents.z;
  2665. ptEndPoints[0][2] = ray.m_Start; ptEndPoints[0][2].x -= ray.m_Extents.x; ptEndPoints[0][2].y += ray.m_Extents.y; ptEndPoints[0][2].z += ray.m_Extents.z;
  2666. ptEndPoints[0][3] = ray.m_Start; ptEndPoints[0][3].x -= ray.m_Extents.x; ptEndPoints[0][3].y += ray.m_Extents.y; ptEndPoints[0][3].z -= ray.m_Extents.z;
  2667. ptEndPoints[0][4] = ray.m_Start; ptEndPoints[0][4].x += ray.m_Extents.x; ptEndPoints[0][4].y -= ray.m_Extents.y; ptEndPoints[0][4].z -= ray.m_Extents.z;
  2668. ptEndPoints[0][5] = ray.m_Start; ptEndPoints[0][5].x += ray.m_Extents.x; ptEndPoints[0][5].y -= ray.m_Extents.y; ptEndPoints[0][5].z += ray.m_Extents.z;
  2669. ptEndPoints[0][6] = ray.m_Start; ptEndPoints[0][6].x += ray.m_Extents.x; ptEndPoints[0][6].y += ray.m_Extents.y; ptEndPoints[0][6].z += ray.m_Extents.z;
  2670. ptEndPoints[0][7] = ray.m_Start; ptEndPoints[0][7].x += ray.m_Extents.x; ptEndPoints[0][7].y += ray.m_Extents.y; ptEndPoints[0][7].z -= ray.m_Extents.z;
  2671. float fDists[2][8];
  2672. int iSides[2][8];
  2673. int iSideMask[2] = { 0, 0 };
  2674. for( int i = 0; i != 8; ++i )
  2675. {
  2676. fDists[0][i] = vQuadNormal.Dot( ptEndPoints[0][i] ) - fQuadPlaneDist;
  2677. if( fDists[0][i] > 0.0f )
  2678. {
  2679. iSides[0][i] = 1;
  2680. iSideMask[0] |= 1;
  2681. }
  2682. else
  2683. {
  2684. iSides[0][i] = 2;
  2685. iSideMask[0] |= 2;
  2686. }
  2687. }
  2688. if( ray.m_IsSwept )
  2689. {
  2690. for( int i = 0; i != 8; ++i )
  2691. ptEndPoints[1][i] = ptEndPoints[0][i] + ray.m_Delta;
  2692. for( int i = 0; i != 8; ++i )
  2693. {
  2694. fDists[1][i] = vQuadNormal.Dot( ptEndPoints[1][i] ) - fQuadPlaneDist;
  2695. if( fDists[1][i] > 0.0f )
  2696. {
  2697. iSides[1][i] = 1;
  2698. iSideMask[1] |= 1;
  2699. }
  2700. else
  2701. {
  2702. iSides[1][i] = 2;
  2703. iSideMask[1] |= 2;
  2704. }
  2705. }
  2706. }
  2707. if( (iSideMask[0] | iSideMask[1]) != 3 )
  2708. {
  2709. //Assert( (iSideMask[0] | iSideMask[1]) != 2 );
  2710. return false; //all points resides entirely on one side of the quad
  2711. }
  2712. //generate intersections for boxes split by the plane at either end of the ray
  2713. for( int k = 0; k != 2; ++k )
  2714. {
  2715. if( iSideMask[k] == 3 ) //box is split by the plane
  2716. {
  2717. for( int i = 0; i != 8; ++i )
  2718. {
  2719. if( iSides[k][i] == 2 ) //point behind the plane
  2720. {
  2721. int iAxisCrossings[3];
  2722. iAxisCrossings[0] = i ^ 4; //upper 4 vs lower 4 crosses X axis
  2723. iAxisCrossings[1] = ((i + 1) & 3) + (i & 4); //cycle to the next element while staying within the upper 4 or lower 4, this will cross either Y or Z axis, we don't care which
  2724. iAxisCrossings[2] = ((i - 1) & 3) + (i & 4); //cylce to the previous element while staying within the upper 4 or lower 4, this will cross the axis iAxisCrossings[1] didn't cross
  2725. for( int j = 0; j != 3; ++j )
  2726. {
  2727. if( iSides[k][iAxisCrossings[j]] == 1 ) //point in front of the plane
  2728. {
  2729. //line between ptEndPoints[i] and ptEndPoints[iAxisCrossings[j]] intersects the plane, generate a point at the intersection for further testing
  2730. float fInvTotalDist = 1.0f / (fDists[k][iAxisCrossings[j]] - fDists[k][i]); //remember that fDists[k][i] is a negative value
  2731. ptPlaneIntersections[iPlaneIntersectionsCount] = (ptEndPoints[k][iAxisCrossings[j]] * (-fDists[k][i] * fInvTotalDist)) + (ptEndPoints[k][i] * (fDists[k][iAxisCrossings[j]] * fInvTotalDist));
  2732. Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane
  2733. ++iPlaneIntersectionsCount;
  2734. }
  2735. }
  2736. }
  2737. }
  2738. }
  2739. }
  2740. if( ray.m_IsSwept )
  2741. {
  2742. for( int i = 0; i != 8; ++i )
  2743. {
  2744. if( iSides[0][i] != iSides[1][i] )
  2745. {
  2746. int iPosSide, iNegSide;
  2747. if( iSides[0][i] == 1 )
  2748. {
  2749. iPosSide = 0;
  2750. iNegSide = 1;
  2751. }
  2752. else
  2753. {
  2754. iPosSide = 1;
  2755. iNegSide = 0;
  2756. }
  2757. Assert( (fDists[iPosSide][i] >= 0.0f) && (fDists[iNegSide][i] <= 0.0f) );
  2758. float fInvTotalDist = 1.0f / (fDists[iPosSide][i] - fDists[iNegSide][i]); //remember that fDists[iNegSide][i] is a negative value
  2759. ptPlaneIntersections[iPlaneIntersectionsCount] = (ptEndPoints[iPosSide][i] * (-fDists[iNegSide][i] * fInvTotalDist)) + (ptEndPoints[iNegSide][i] * (fDists[iPosSide][i] * fInvTotalDist));
  2760. Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane
  2761. ++iPlaneIntersectionsCount;
  2762. }
  2763. }
  2764. }
  2765. }
  2766. //down here, we should simply have a collection of plane intersections, now we see if they reside within the quad
  2767. Assert( iPlaneIntersectionsCount != 0 );
  2768. for( int i = 0; i != iPlaneIntersectionsCount; ++i )
  2769. {
  2770. //these points are guaranteed to be on the plane, now just check to see if they're within the quad's extents
  2771. Vector vToPointFromQuadCenter = ptPlaneIntersections[i] - ptQuadCenter;
  2772. float fExt1Dist = vQuadExtent1_Normalized.Dot( vToPointFromQuadCenter );
  2773. if( fabs( fExt1Dist ) > fQuadExtent1Length )
  2774. return false; //point is outside boundaries
  2775. //vToPointFromQuadCenter -= vQuadExtent1_Normalized * fExt1Dist; //to handle diamond shaped quads
  2776. float fExt2Dist = vQuadExtent2_Normalized.Dot( vToPointFromQuadCenter );
  2777. if( fabs( fExt2Dist ) > fQuadExtent2Length )
  2778. return false; //point is outside boundaries
  2779. }
  2780. return true; //there were lines crossing the quad plane, and every line crossing that plane had its intersection with the plane within the quad's boundaries
  2781. }
  2782. #endif // !_STATIC_LINKED || _SHARED_LIB