Counter Strike : Global Offensive Source Code
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.

3409 lines
114 KiB

  1. //========= Copyright � 1996-2005, 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, 1 );
  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/PS3, force use of SIMD version.
  464. if (IsX360() || IsPS3())
  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. bi32x4 startOutMins = CmpLtSIMD(Four_Zeros, offsetMinsExpanded);
  567. bi32x4 endOutMins = CmpLtSIMD(delta,offsetMinsExpanded);
  568. bi32x4 minsMask = AndSIMD( startOutMins, endOutMins );
  569. bi32x4 startOutMaxs = CmpGtSIMD(Four_Zeros, offsetMaxsExpanded);
  570. bi32x4 endOutMaxs = CmpGtSIMD(delta,offsetMaxsExpanded);
  571. bi32x4 maxsMask = AndSIMD( startOutMaxs, endOutMaxs );
  572. if ( IsAnyTrue(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. bi32x4 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. bi32x4 separation = CmpGtSIMD(lastIn, firstOut);
  593. return IsAllZeros(separation);
  594. #else
  595. // On the x360/ps3, we force use of the SIMD functions.
  596. #if defined( _X360 ) || defined( _PS3 )
  597. if ( IsX360() || IsPS3() )
  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. bi32x4 startOutMins = CmpLtSIMD(Four_Zeros, boxMins);
  674. bi32x4 endOutMins = CmpLtSIMD(delta,boxMins);
  675. bi32x4 minsMask = AndSIMD( startOutMins, endOutMins );
  676. bi32x4 startOutMaxs = CmpGtSIMD(Four_Zeros, boxMaxs);
  677. bi32x4 endOutMaxs = CmpGtSIMD(delta,boxMaxs);
  678. bi32x4 maxsMask = AndSIMD( startOutMaxs, endOutMaxs );
  679. if ( IsAnyTrue(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. bi32x4 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. bi32x4 separation = CmpGtSIMD(lastIn, firstOut);
  703. return IsAllZeros(separation);
  704. #else
  705. // On the x360/ps3, we force use of the SIMD functions.
  706. #if (defined(_X360) || defined(_PS3)) && !defined(PARANOID_SIMD_ASSERTING)
  707. if (IsX360() || IsPS3())
  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/PS3, we force use of the SIMD functions.
  767. #if defined( _X360 ) || defined( _PS3 )
  768. if ( IsX360() || IsPS3() )
  769. {
  770. return IsBoxIntersectingRay(
  771. LoadUnaligned3SIMD(vecBoxMin.Base()), LoadUnaligned3SIMD(vecBoxMax.Base()),
  772. ray, ReplicateX4(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. #if defined( _X360 ) || defined( _PS3 )
  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. #if defined( _X360 ) || defined( _PS3 )
  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. bi32x4 startOutMins = AndSIMD( CmpLtSIMD(Four_Zeros, boxMin), CmpLtSIMD(delta,boxMin) );
  820. bi32x4 startOutMaxs = AndSIMD( CmpGtSIMD(Four_Zeros, boxMax), CmpGtSIMD(delta,boxMax) );
  821. if ( IsAnyTrue(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. bi32x4 separation = CmpGtSIMD(lastIn, firstOut);
  839. return IsAllZeros(separation);
  840. }
  841. #if defined( _X360 ) || defined( _PS3 )
  842. bool FASTCALL IsBoxIntersectingRay( fltx4 boxMin, fltx4 boxMax,
  843. const Ray_t& ray, fltx4 fl4Tolerance )
  844. #else
  845. bool FASTCALL IsBoxIntersectingRay( const fltx4& boxMin, const fltx4& boxMax,
  846. const Ray_t& ray, const fltx4 &fl4Tolerance )
  847. #endif
  848. {
  849. fltx4 rayStart = LoadAlignedSIMD(ray.m_Start);
  850. fltx4 rayExtents = LoadAlignedSIMD(ray.m_Extents);
  851. if ( !ray.m_IsSwept )
  852. {
  853. fltx4 rayMins, rayMaxs;
  854. rayMins = SubSIMD(rayStart, rayExtents);
  855. rayMaxs = AddSIMD(rayStart, rayExtents);
  856. rayMins = AddSIMD(rayMins, fl4Tolerance);
  857. rayMaxs = AddSIMD(rayMaxs, fl4Tolerance);
  858. VectorAligned vecBoxMin, vecBoxMax, vecRayMins, vecRayMaxs;
  859. StoreAlignedSIMD( vecBoxMin.Base(), boxMin );
  860. StoreAlignedSIMD( vecBoxMax.Base(), boxMax );
  861. StoreAlignedSIMD( vecRayMins.Base(), rayMins );
  862. StoreAlignedSIMD( vecRayMaxs.Base(), rayMaxs );
  863. return IsBoxIntersectingBox( vecBoxMin, vecBoxMax, vecRayMins, vecRayMaxs );
  864. }
  865. fltx4 rayDelta = LoadAlignedSIMD(ray.m_Delta);
  866. fltx4 vecExpandedBoxMin, vecExpandedBoxMax;
  867. vecExpandedBoxMin = SubSIMD( boxMin, rayExtents );
  868. vecExpandedBoxMax = AddSIMD( boxMax, rayExtents );
  869. return IsBoxIntersectingRay( vecExpandedBoxMin, vecExpandedBoxMax, rayStart, rayDelta, ReciprocalSIMD(rayDelta), fl4Tolerance );
  870. }
  871. //-----------------------------------------------------------------------------
  872. // Intersects a ray with a ray, return true if they intersect
  873. // t, s = parameters of closest approach (if not intersecting!)
  874. //-----------------------------------------------------------------------------
  875. bool IntersectRayWithRay( const Ray_t &ray0, const Ray_t &ray1, float &t, float &s )
  876. {
  877. Assert( ray0.m_IsRay && ray1.m_IsRay );
  878. //
  879. // r0 = p0 + v0t
  880. // r1 = p1 + v1s
  881. //
  882. // intersection : r0 = r1 :: p0 + v0t = p1 + v1s
  883. // NOTE: v(0,1) are unit direction vectors
  884. //
  885. // subtract p0 from both sides and cross with v1 (NOTE: v1 x v1 = 0)
  886. // (v0 x v1)t = ((p1 - p0 ) x v1)
  887. //
  888. // dotting with (v0 x v1) and dividing by |v0 x v1|^2
  889. // t = Det | (p1 - p0) , v1 , (v0 x v1) | / |v0 x v1|^2
  890. // s = Det | (p1 - p0) , v0 , (v0 x v1) | / |v0 x v1|^2
  891. //
  892. // Det | A B C | = -( A x C ) dot B or -( C x B ) dot A
  893. //
  894. // NOTE: if |v0 x v1|^2 = 0, then the lines are parallel
  895. //
  896. Vector v0( ray0.m_Delta );
  897. Vector v1( ray1.m_Delta );
  898. VectorNormalize( v0 );
  899. VectorNormalize( v1 );
  900. Vector v0xv1 = v0.Cross( v1 );
  901. float lengthSq = v0xv1.LengthSqr();
  902. if( lengthSq == 0.0f )
  903. {
  904. t = 0; s = 0;
  905. return false; // parallel
  906. }
  907. Vector p1p0 = ray1.m_Start - ray0.m_Start;
  908. Vector AxC = p1p0.Cross( v0xv1 );
  909. AxC.Negate();
  910. float detT = AxC.Dot( v1 );
  911. AxC = p1p0.Cross( v0xv1 );
  912. AxC.Negate();
  913. float detS = AxC.Dot( v0 );
  914. t = detT / lengthSq;
  915. s = detS / lengthSq;
  916. // intersection????
  917. Vector i0, i1;
  918. i0 = v0 * t;
  919. i1 = v1 * s;
  920. i0 += ray0.m_Start;
  921. i1 += ray1.m_Start;
  922. if( i0.x == i1.x && i0.y == i1.y && i0.z == i1.z )
  923. return true;
  924. return false;
  925. }
  926. //-----------------------------------------------------------------------------
  927. // Intersects a ray with a plane, returns distance t along ray.
  928. //-----------------------------------------------------------------------------
  929. float IntersectRayWithPlane( const Ray_t& ray, const cplane_t& plane )
  930. {
  931. float denom = DotProduct( ray.m_Delta, plane.normal );
  932. if (denom == 0.0f)
  933. return 0.0f;
  934. denom = 1.0f / denom;
  935. return (plane.dist - DotProduct( ray.m_Start, plane.normal )) * denom;
  936. }
  937. float IntersectRayWithPlane( const Vector& org, const Vector& dir, const cplane_t& plane )
  938. {
  939. float denom = DotProduct( dir, plane.normal );
  940. if (denom == 0.0f)
  941. return 0.0f;
  942. denom = 1.0f / denom;
  943. return (plane.dist - DotProduct( org, plane.normal )) * denom;
  944. }
  945. float IntersectRayWithPlane( const Vector& org, const Vector& dir, const Vector& normal, float dist )
  946. {
  947. float denom = DotProduct( dir, normal );
  948. if (denom == 0.0f)
  949. return 0.0f;
  950. denom = 1.0f / denom;
  951. return (dist - DotProduct( org, normal )) * denom;
  952. }
  953. float IntersectRayWithAAPlane( const Vector& vecStart, const Vector& vecEnd, int nAxis, float flSign, float flDist )
  954. {
  955. float denom = flSign * (vecEnd[nAxis] - vecStart[nAxis]);
  956. if (denom == 0.0f)
  957. return 0.0f;
  958. denom = 1.0f / denom;
  959. return (flDist - flSign * vecStart[nAxis]) * denom;
  960. }
  961. //-----------------------------------------------------------------------------
  962. // Intersects a ray against a box
  963. //-----------------------------------------------------------------------------
  964. bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta,
  965. const Vector &boxMins, const Vector &boxMaxs, float flTolerance, BoxTraceInfo_t *pTrace )
  966. {
  967. int i;
  968. float d1, d2;
  969. float f;
  970. pTrace->t1 = -1.0f;
  971. pTrace->t2 = 1.0f;
  972. pTrace->hitside = -1;
  973. // UNDONE: This makes this code a little messy
  974. pTrace->startsolid = true;
  975. for ( i = 0; i < 6; ++i )
  976. {
  977. if ( i >= 3 )
  978. {
  979. d1 = vecRayStart[i-3] - boxMaxs[i-3];
  980. d2 = d1 + vecRayDelta[i-3];
  981. }
  982. else
  983. {
  984. d1 = -vecRayStart[i] + boxMins[i];
  985. d2 = d1 - vecRayDelta[i];
  986. }
  987. // if completely in front of face, no intersection
  988. if (d1 > 0 && d2 > 0)
  989. {
  990. // UNDONE: Have to revert this in case it's still set
  991. // UNDONE: Refactor to have only 2 return points (true/false) from this function
  992. pTrace->startsolid = false;
  993. return false;
  994. }
  995. // completely inside, check next face
  996. if (d1 <= 0 && d2 <= 0)
  997. continue;
  998. if (d1 > 0)
  999. {
  1000. pTrace->startsolid = false;
  1001. }
  1002. // crosses face
  1003. if (d1 > d2)
  1004. {
  1005. f = d1 - flTolerance;
  1006. if ( f < 0 )
  1007. {
  1008. f = 0;
  1009. }
  1010. f = f / (d1-d2);
  1011. if (f > pTrace->t1)
  1012. {
  1013. pTrace->t1 = f;
  1014. pTrace->hitside = i;
  1015. }
  1016. }
  1017. else
  1018. {
  1019. // leave
  1020. f = (d1 + flTolerance) / (d1-d2);
  1021. if (f < pTrace->t2)
  1022. {
  1023. pTrace->t2 = f;
  1024. }
  1025. }
  1026. }
  1027. return pTrace->startsolid || (pTrace->t1 < pTrace->t2 && pTrace->t1 >= 0.0f);
  1028. }
  1029. //-----------------------------------------------------------------------------
  1030. // Intersects a ray against a box
  1031. //-----------------------------------------------------------------------------
  1032. bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta,
  1033. const Vector &boxMins, const Vector &boxMaxs, float flTolerance, CBaseTrace *pTrace, float *pFractionLeftSolid )
  1034. {
  1035. Collision_ClearTrace( vecRayStart, vecRayDelta, pTrace );
  1036. BoxTraceInfo_t trace;
  1037. if ( IntersectRayWithBox( vecRayStart, vecRayDelta, boxMins, boxMaxs, flTolerance, &trace ) )
  1038. {
  1039. pTrace->startsolid = trace.startsolid;
  1040. if (trace.t1 < trace.t2 && trace.t1 >= 0.0f)
  1041. {
  1042. pTrace->fraction = trace.t1;
  1043. VectorMA( pTrace->startpos, trace.t1, vecRayDelta, pTrace->endpos );
  1044. pTrace->contents = CONTENTS_SOLID;
  1045. pTrace->plane.normal = vec3_origin;
  1046. if ( trace.hitside >= 3 )
  1047. {
  1048. trace.hitside -= 3;
  1049. pTrace->plane.dist = boxMaxs[trace.hitside];
  1050. pTrace->plane.normal[trace.hitside] = 1.0f;
  1051. pTrace->plane.type = trace.hitside;
  1052. }
  1053. else
  1054. {
  1055. pTrace->plane.dist = -boxMins[trace.hitside];
  1056. pTrace->plane.normal[trace.hitside] = -1.0f;
  1057. pTrace->plane.type = trace.hitside;
  1058. }
  1059. return true;
  1060. }
  1061. if ( pTrace->startsolid )
  1062. {
  1063. pTrace->allsolid = (trace.t2 <= 0.0f) || (trace.t2 >= 1.0f);
  1064. pTrace->fraction = 0;
  1065. if ( pFractionLeftSolid )
  1066. {
  1067. *pFractionLeftSolid = trace.t2;
  1068. }
  1069. pTrace->endpos = pTrace->startpos;
  1070. pTrace->contents = CONTENTS_SOLID;
  1071. pTrace->plane.dist = pTrace->startpos[0];
  1072. pTrace->plane.normal.Init( 1.0f, 0.0f, 0.0f );
  1073. pTrace->plane.type = 0;
  1074. pTrace->startpos = vecRayStart + (trace.t2 * vecRayDelta);
  1075. return true;
  1076. }
  1077. }
  1078. return false;
  1079. }
  1080. //-----------------------------------------------------------------------------
  1081. // Intersects a ray against a box
  1082. //-----------------------------------------------------------------------------
  1083. bool IntersectRayWithBox( const Ray_t &ray, const Vector &boxMins, const Vector &boxMaxs,
  1084. float flTolerance, CBaseTrace *pTrace, float *pFractionLeftSolid )
  1085. {
  1086. if ( !ray.m_IsRay )
  1087. {
  1088. Vector vecExpandedMins = boxMins - ray.m_Extents;
  1089. Vector vecExpandedMaxs = boxMaxs + ray.m_Extents;
  1090. bool bIntersects = IntersectRayWithBox( ray.m_Start, ray.m_Delta, vecExpandedMins, vecExpandedMaxs, flTolerance, pTrace, pFractionLeftSolid );
  1091. pTrace->startpos += ray.m_StartOffset;
  1092. pTrace->endpos += ray.m_StartOffset;
  1093. return bIntersects;
  1094. }
  1095. return IntersectRayWithBox( ray.m_Start, ray.m_Delta, boxMins, boxMaxs, flTolerance, pTrace, pFractionLeftSolid );
  1096. }
  1097. //-----------------------------------------------------------------------------
  1098. // Intersects a ray against an OBB, returns t1 and t2
  1099. //-----------------------------------------------------------------------------
  1100. bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta,
  1101. const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs,
  1102. float flTolerance, BoxTraceInfo_t *pTrace )
  1103. {
  1104. // FIXME: Two transforms is pretty expensive. Should we optimize this?
  1105. Vector start, delta;
  1106. VectorITransform( vecRayStart, matOBBToWorld, start );
  1107. VectorIRotate( vecRayDelta, matOBBToWorld, delta );
  1108. return IntersectRayWithBox( start, delta, vecOBBMins, vecOBBMaxs, flTolerance, pTrace );
  1109. }
  1110. //-----------------------------------------------------------------------------
  1111. // Intersects a ray against an OBB
  1112. //-----------------------------------------------------------------------------
  1113. bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta,
  1114. const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs,
  1115. float flTolerance, CBaseTrace *pTrace )
  1116. {
  1117. Collision_ClearTrace( vecRayStart, vecRayDelta, pTrace );
  1118. // FIXME: Make it work with tolerance
  1119. Assert( flTolerance == 0.0f );
  1120. // OPTIMIZE: Store this in the box instead of computing it here
  1121. // compute center in local space
  1122. Vector vecBoxExtents = (vecOBBMins + vecOBBMaxs) * 0.5;
  1123. Vector vecBoxCenter;
  1124. // transform to world space
  1125. VectorTransform( vecBoxExtents, matOBBToWorld, vecBoxCenter );
  1126. // calc extents from local center
  1127. vecBoxExtents = vecOBBMaxs - vecBoxExtents;
  1128. // OPTIMIZE: This is optimized for world space. If the transform is fast enough, it may make more
  1129. // sense to just xform and call UTIL_ClipToBox() instead. MEASURE THIS.
  1130. // save the extents of the ray along
  1131. Vector extent, uextent;
  1132. Vector segmentCenter = vecRayStart + vecRayDelta - vecBoxCenter;
  1133. extent.Init();
  1134. // check box axes for separation
  1135. for ( int j = 0; j < 3; j++ )
  1136. {
  1137. extent[j] = vecRayDelta.x * matOBBToWorld[0][j] + vecRayDelta.y * matOBBToWorld[1][j] + vecRayDelta.z * matOBBToWorld[2][j];
  1138. uextent[j] = fabsf(extent[j]);
  1139. float coord = segmentCenter.x * matOBBToWorld[0][j] + segmentCenter.y * matOBBToWorld[1][j] + segmentCenter.z * matOBBToWorld[2][j];
  1140. coord = fabsf(coord);
  1141. if ( coord > (vecBoxExtents[j] + uextent[j]) )
  1142. return false;
  1143. }
  1144. // now check cross axes for separation
  1145. float tmp, cextent;
  1146. Vector cross = vecRayDelta.Cross( segmentCenter );
  1147. cextent = cross.x * matOBBToWorld[0][0] + cross.y * matOBBToWorld[1][0] + cross.z * matOBBToWorld[2][0];
  1148. cextent = fabsf(cextent);
  1149. tmp = vecBoxExtents[1]*uextent[2] + vecBoxExtents[2]*uextent[1];
  1150. if ( cextent > tmp )
  1151. return false;
  1152. cextent = cross.x * matOBBToWorld[0][1] + cross.y * matOBBToWorld[1][1] + cross.z * matOBBToWorld[2][1];
  1153. cextent = fabsf(cextent);
  1154. tmp = vecBoxExtents[0]*uextent[2] + vecBoxExtents[2]*uextent[0];
  1155. if ( cextent > tmp )
  1156. return false;
  1157. cextent = cross.x * matOBBToWorld[0][2] + cross.y * matOBBToWorld[1][2] + cross.z * matOBBToWorld[2][2];
  1158. cextent = fabsf(cextent);
  1159. tmp = vecBoxExtents[0]*uextent[1] + vecBoxExtents[1]*uextent[0];
  1160. if ( cextent > tmp )
  1161. return false;
  1162. // !!! We hit this box !!! compute intersection point and return
  1163. // Compute ray start in bone space
  1164. Vector start;
  1165. VectorITransform( vecRayStart, matOBBToWorld, start );
  1166. // extent is ray.m_Delta in bone space, recompute delta in bone space
  1167. extent *= 2.0f;
  1168. // delta was prescaled by the current t, so no need to see if this intersection
  1169. // is closer
  1170. trace_t boxTrace;
  1171. if ( !IntersectRayWithBox( start, extent, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ) )
  1172. return false;
  1173. // Fix up the start/end pos and fraction
  1174. Vector vecTemp;
  1175. VectorTransform( pTrace->endpos, matOBBToWorld, vecTemp );
  1176. pTrace->endpos = vecTemp;
  1177. pTrace->startpos = vecRayStart;
  1178. pTrace->fraction *= 2.0f;
  1179. // Fix up the plane information
  1180. float flSign = pTrace->plane.normal[ pTrace->plane.type ];
  1181. pTrace->plane.normal[0] = flSign * matOBBToWorld[0][pTrace->plane.type];
  1182. pTrace->plane.normal[1] = flSign * matOBBToWorld[1][pTrace->plane.type];
  1183. pTrace->plane.normal[2] = flSign * matOBBToWorld[2][pTrace->plane.type];
  1184. pTrace->plane.dist = DotProduct( pTrace->endpos, pTrace->plane.normal );
  1185. pTrace->plane.type = 3;
  1186. return true;
  1187. }
  1188. //-----------------------------------------------------------------------------
  1189. // Intersects a ray against an OBB
  1190. //-----------------------------------------------------------------------------
  1191. bool IntersectRayWithOBB( const Vector &vecRayOrigin, const Vector &vecRayDelta,
  1192. const Vector &vecBoxOrigin, const QAngle &angBoxRotation,
  1193. const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace )
  1194. {
  1195. if (angBoxRotation == vec3_angle)
  1196. {
  1197. Vector vecAbsMins, vecAbsMaxs;
  1198. VectorAdd( vecBoxOrigin, vecOBBMins, vecAbsMins );
  1199. VectorAdd( vecBoxOrigin, vecOBBMaxs, vecAbsMaxs );
  1200. return IntersectRayWithBox( vecRayOrigin, vecRayDelta, vecAbsMins, vecAbsMaxs, flTolerance, pTrace );
  1201. }
  1202. matrix3x4_t obbToWorld;
  1203. AngleMatrix( angBoxRotation, vecBoxOrigin, obbToWorld );
  1204. return IntersectRayWithOBB( vecRayOrigin, vecRayDelta, obbToWorld, vecOBBMins, vecOBBMaxs, flTolerance, pTrace );
  1205. }
  1206. //-----------------------------------------------------------------------------
  1207. // Box support map
  1208. //-----------------------------------------------------------------------------
  1209. inline void ComputeSupportMap( const Vector &vecDirection, const Vector &vecBoxMins,
  1210. const Vector &vecBoxMaxs, float pDist[2] )
  1211. {
  1212. int nIndex = (vecDirection.x > 0.0f);
  1213. pDist[nIndex] = vecBoxMaxs.x * vecDirection.x;
  1214. pDist[1 - nIndex] = vecBoxMins.x * vecDirection.x;
  1215. nIndex = (vecDirection.y > 0.0f);
  1216. pDist[nIndex] += vecBoxMaxs.y * vecDirection.y;
  1217. pDist[1 - nIndex] += vecBoxMins.y * vecDirection.y;
  1218. nIndex = (vecDirection.z > 0.0f);
  1219. pDist[nIndex] += vecBoxMaxs.z * vecDirection.z;
  1220. pDist[1 - nIndex] += vecBoxMins.z * vecDirection.z;
  1221. }
  1222. inline void ComputeSupportMap( const Vector &vecDirection, int i1, int i2,
  1223. const Vector &vecBoxMins, const Vector &vecBoxMaxs, float pDist[2] )
  1224. {
  1225. int nIndex = (vecDirection[i1] > 0.0f);
  1226. pDist[nIndex] = vecBoxMaxs[i1] * vecDirection[i1];
  1227. pDist[1 - nIndex] = vecBoxMins[i1] * vecDirection[i1];
  1228. nIndex = (vecDirection[i2] > 0.0f);
  1229. pDist[nIndex] += vecBoxMaxs[i2] * vecDirection[i2];
  1230. pDist[1 - nIndex] += vecBoxMins[i2] * vecDirection[i2];
  1231. }
  1232. //-----------------------------------------------------------------------------
  1233. // Intersects a ray against an OBB
  1234. //-----------------------------------------------------------------------------
  1235. static int s_ExtIndices[3][2] =
  1236. {
  1237. { 2, 1 },
  1238. { 0, 2 },
  1239. { 0, 1 },
  1240. };
  1241. static int s_MatIndices[3][2] =
  1242. {
  1243. { 1, 2 },
  1244. { 2, 0 },
  1245. { 1, 0 },
  1246. };
  1247. bool IntersectRayWithOBB( const Ray_t &ray, const matrix3x4_t &matOBBToWorld,
  1248. const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace )
  1249. {
  1250. if ( ray.m_IsRay )
  1251. {
  1252. return IntersectRayWithOBB( ray.m_Start, ray.m_Delta, matOBBToWorld,
  1253. vecOBBMins, vecOBBMaxs, flTolerance, pTrace );
  1254. }
  1255. Collision_ClearTrace( ray.m_Start + ray.m_StartOffset, ray.m_Delta, pTrace );
  1256. // Compute a bounding sphere around the bloated OBB
  1257. Vector vecOBBCenter;
  1258. VectorAdd( vecOBBMins, vecOBBMaxs, vecOBBCenter );
  1259. vecOBBCenter *= 0.5f;
  1260. vecOBBCenter.x += matOBBToWorld[0][3];
  1261. vecOBBCenter.y += matOBBToWorld[1][3];
  1262. vecOBBCenter.z += matOBBToWorld[2][3];
  1263. Vector vecOBBHalfDiagonal;
  1264. VectorSubtract( vecOBBMaxs, vecOBBMins, vecOBBHalfDiagonal );
  1265. vecOBBHalfDiagonal *= 0.5f;
  1266. float flRadius = vecOBBHalfDiagonal.Length() + ray.m_Extents.Length();
  1267. if ( !IsRayIntersectingSphere( ray.m_Start, ray.m_Delta, vecOBBCenter, flRadius, flTolerance ) )
  1268. return false;
  1269. // Ok, we passed the trivial reject, so lets do the dirty deed.
  1270. // Basically we're going to do the GJK thing explicitly. We'll shrink the ray down
  1271. // to a point, and bloat the OBB by the ray's extents. This will generate facet
  1272. // planes which are perpendicular to all of the separating axes typically seen in
  1273. // a standard seperating axis implementation.
  1274. // We're going to create a number of planes through various vertices in the OBB
  1275. // which represent all of the separating planes. Then we're going to bloat the planes
  1276. // by the ray extents.
  1277. // We're going to do all work in OBB-space because it's easier to do the
  1278. // support-map in this case
  1279. // First, transform the ray into the space of the OBB
  1280. Vector vecLocalRayOrigin, vecLocalRayDirection;
  1281. VectorITransform( ray.m_Start, matOBBToWorld, vecLocalRayOrigin );
  1282. VectorIRotate( ray.m_Delta, matOBBToWorld, vecLocalRayDirection );
  1283. // Next compute all separating planes
  1284. Vector pPlaneNormal[15];
  1285. float ppPlaneDist[15][2];
  1286. int i;
  1287. for ( i = 0; i < 3; ++i )
  1288. {
  1289. // Each plane needs to be bloated an amount = to the abs dot product of
  1290. // the ray extents with the plane normal
  1291. // For the OBB planes, do it in world space;
  1292. // and use the direction of the OBB (the ith column of matOBBToWorld) in world space vs extents
  1293. pPlaneNormal[i].Init( );
  1294. pPlaneNormal[i][i] = 1.0f;
  1295. float flExtentDotNormal =
  1296. FloatMakePositive( matOBBToWorld[0][i] * ray.m_Extents.x ) +
  1297. FloatMakePositive( matOBBToWorld[1][i] * ray.m_Extents.y ) +
  1298. FloatMakePositive( matOBBToWorld[2][i] * ray.m_Extents.z );
  1299. ppPlaneDist[i][0] = vecOBBMins[i] - flExtentDotNormal;
  1300. ppPlaneDist[i][1] = vecOBBMaxs[i] + flExtentDotNormal;
  1301. // For the ray-extents planes, they are bloated by the extents
  1302. // Use the support map to determine which
  1303. VectorCopy( matOBBToWorld[i], pPlaneNormal[i+3].Base() );
  1304. ComputeSupportMap( pPlaneNormal[i+3], vecOBBMins, vecOBBMaxs, ppPlaneDist[i+3] );
  1305. ppPlaneDist[i+3][0] -= ray.m_Extents[i];
  1306. ppPlaneDist[i+3][1] += ray.m_Extents[i];
  1307. // Now the edge cases... (take the cross product of x,y,z axis w/ ray extent axes
  1308. // given by the rows of the obb to world matrix.
  1309. // Compute the ray extent bloat in world space because it's easier...
  1310. // These are necessary to compute the world-space versions of
  1311. // the edges so we can compute the extent dot products
  1312. float flRayExtent0 = ray.m_Extents[s_ExtIndices[i][0]];
  1313. float flRayExtent1 = ray.m_Extents[s_ExtIndices[i][1]];
  1314. const float *pMatRow0 = matOBBToWorld[s_MatIndices[i][0]];
  1315. const float *pMatRow1 = matOBBToWorld[s_MatIndices[i][1]];
  1316. // x axis of the OBB + world ith axis
  1317. pPlaneNormal[i+6].Init( 0.0f, -matOBBToWorld[i][2], matOBBToWorld[i][1] );
  1318. ComputeSupportMap( pPlaneNormal[i+6], 1, 2, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+6] );
  1319. flExtentDotNormal =
  1320. FloatMakePositive( pMatRow0[0] ) * flRayExtent0 +
  1321. FloatMakePositive( pMatRow1[0] ) * flRayExtent1;
  1322. ppPlaneDist[i+6][0] -= flExtentDotNormal;
  1323. ppPlaneDist[i+6][1] += flExtentDotNormal;
  1324. // y axis of the OBB + world ith axis
  1325. pPlaneNormal[i+9].Init( matOBBToWorld[i][2], 0.0f, -matOBBToWorld[i][0] );
  1326. ComputeSupportMap( pPlaneNormal[i+9], 0, 2, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+9] );
  1327. flExtentDotNormal =
  1328. FloatMakePositive( pMatRow0[1] ) * flRayExtent0 +
  1329. FloatMakePositive( pMatRow1[1] ) * flRayExtent1;
  1330. ppPlaneDist[i+9][0] -= flExtentDotNormal;
  1331. ppPlaneDist[i+9][1] += flExtentDotNormal;
  1332. // z axis of the OBB + world ith axis
  1333. pPlaneNormal[i+12].Init( -matOBBToWorld[i][1], matOBBToWorld[i][0], 0.0f );
  1334. ComputeSupportMap( pPlaneNormal[i+12], 0, 1, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+12] );
  1335. flExtentDotNormal =
  1336. FloatMakePositive( pMatRow0[2] ) * flRayExtent0 +
  1337. FloatMakePositive( pMatRow1[2] ) * flRayExtent1;
  1338. ppPlaneDist[i+12][0] -= flExtentDotNormal;
  1339. ppPlaneDist[i+12][1] += flExtentDotNormal;
  1340. }
  1341. float enterfrac, leavefrac;
  1342. float d1[2], d2[2];
  1343. float f;
  1344. int hitplane = -1;
  1345. int hitside = -1;
  1346. enterfrac = -1.0f;
  1347. leavefrac = 1.0f;
  1348. pTrace->startsolid = true;
  1349. Vector vecLocalRayEnd;
  1350. VectorAdd( vecLocalRayOrigin, vecLocalRayDirection, vecLocalRayEnd );
  1351. for ( i = 0; i < 15; ++i )
  1352. {
  1353. // FIXME: Not particularly optimal since there's a lot of 0's in the plane normals
  1354. float flStartDot = DotProduct( pPlaneNormal[i], vecLocalRayOrigin );
  1355. float flEndDot = DotProduct( pPlaneNormal[i], vecLocalRayEnd );
  1356. // NOTE: Negative here is because the plane normal + dist
  1357. // are defined in negative terms for the far plane (plane dist index 0)
  1358. d1[0] = -(flStartDot - ppPlaneDist[i][0]);
  1359. d2[0] = -(flEndDot - ppPlaneDist[i][0]);
  1360. d1[1] = flStartDot - ppPlaneDist[i][1];
  1361. d2[1] = flEndDot - ppPlaneDist[i][1];
  1362. int j;
  1363. for ( j = 0; j < 2; ++j )
  1364. {
  1365. // if completely in front near plane or behind far plane no intersection
  1366. if (d1[j] > 0 && d2[j] > 0)
  1367. return false;
  1368. // completely inside, check next plane set
  1369. if (d1[j] <= 0 && d2[j] <= 0)
  1370. continue;
  1371. if (d1[j] > 0)
  1372. {
  1373. pTrace->startsolid = false;
  1374. }
  1375. // crosses face
  1376. float flDenom = 1.0f / (d1[j] - d2[j]);
  1377. if (d1[j] > d2[j])
  1378. {
  1379. f = d1[j] - flTolerance;
  1380. if ( f < 0 )
  1381. {
  1382. f = 0;
  1383. }
  1384. f *= flDenom;
  1385. if (f > enterfrac)
  1386. {
  1387. enterfrac = f;
  1388. hitplane = i;
  1389. hitside = j;
  1390. }
  1391. }
  1392. else
  1393. {
  1394. // leave
  1395. f = (d1[j] + flTolerance) * flDenom;
  1396. if (f < leavefrac)
  1397. {
  1398. leavefrac = f;
  1399. }
  1400. }
  1401. }
  1402. }
  1403. if (enterfrac < leavefrac && enterfrac >= 0.0f)
  1404. {
  1405. pTrace->fraction = enterfrac;
  1406. VectorMA( pTrace->startpos, enterfrac, ray.m_Delta, pTrace->endpos );
  1407. pTrace->contents = CONTENTS_SOLID;
  1408. // Need to transform the plane into world space...
  1409. cplane_t temp;
  1410. temp.normal = pPlaneNormal[hitplane];
  1411. temp.dist = ppPlaneDist[hitplane][hitside];
  1412. if (hitside == 0)
  1413. {
  1414. temp.normal *= -1.0f;
  1415. temp.dist *= -1.0f;
  1416. }
  1417. temp.type = 3;
  1418. MatrixITransformPlane( matOBBToWorld, temp, pTrace->plane );
  1419. return true;
  1420. }
  1421. if ( pTrace->startsolid )
  1422. {
  1423. pTrace->allsolid = (leavefrac <= 0.0f) || (leavefrac >= 1.0f);
  1424. pTrace->fraction = 0;
  1425. pTrace->endpos = pTrace->startpos;
  1426. pTrace->contents = CONTENTS_SOLID;
  1427. pTrace->plane.dist = pTrace->startpos[0];
  1428. pTrace->plane.normal.Init( 1.0f, 0.0f, 0.0f );
  1429. pTrace->plane.type = 0;
  1430. return true;
  1431. }
  1432. return false;
  1433. }
  1434. //-----------------------------------------------------------------------------
  1435. // Intersects a ray against an OBB
  1436. //-----------------------------------------------------------------------------
  1437. bool IntersectRayWithOBB( const Ray_t &ray, const Vector &vecBoxOrigin, const QAngle &angBoxRotation,
  1438. const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace )
  1439. {
  1440. if ( angBoxRotation == vec3_angle )
  1441. {
  1442. Vector vecWorldMins, vecWorldMaxs;
  1443. VectorAdd( vecBoxOrigin, vecOBBMins, vecWorldMins );
  1444. VectorAdd( vecBoxOrigin, vecOBBMaxs, vecWorldMaxs );
  1445. return IntersectRayWithBox( ray, vecWorldMins, vecWorldMaxs, flTolerance, pTrace );
  1446. }
  1447. if ( ray.m_IsRay )
  1448. {
  1449. return IntersectRayWithOBB( ray.m_Start, ray.m_Delta, vecBoxOrigin, angBoxRotation, vecOBBMins, vecOBBMaxs, flTolerance, pTrace );
  1450. }
  1451. matrix3x4_t matOBBToWorld;
  1452. AngleMatrix( angBoxRotation, vecBoxOrigin, matOBBToWorld );
  1453. return IntersectRayWithOBB( ray, matOBBToWorld, vecOBBMins, vecOBBMaxs, flTolerance, pTrace );
  1454. }
  1455. //-----------------------------------------------------------------------------
  1456. //
  1457. //-----------------------------------------------------------------------------
  1458. void GetNonMajorAxes( const Vector &vNormal, Vector2D &axes )
  1459. {
  1460. axes[0] = 0;
  1461. axes[1] = 1;
  1462. if( FloatMakePositive( vNormal.x ) > FloatMakePositive( vNormal.y ) )
  1463. {
  1464. if( FloatMakePositive( vNormal.x ) > FloatMakePositive( vNormal.z ) )
  1465. {
  1466. axes[0] = 1;
  1467. axes[1] = 2;
  1468. }
  1469. }
  1470. else
  1471. {
  1472. if( FloatMakePositive( vNormal.y ) > FloatMakePositive( vNormal.z ) )
  1473. {
  1474. axes[0] = 0;
  1475. axes[1] = 2;
  1476. }
  1477. }
  1478. }
  1479. //-----------------------------------------------------------------------------
  1480. //-----------------------------------------------------------------------------
  1481. QuadBarycentricRetval_t QuadWithParallelEdges( const Vector &vecOrigin,
  1482. const Vector &vecU, float lengthU, const Vector &vecV, float lengthV,
  1483. const Vector &pt, Vector2D &vecUV )
  1484. {
  1485. Ray_t rayAxis;
  1486. Ray_t rayPt;
  1487. //
  1488. // handle the u axis
  1489. //
  1490. rayAxis.m_Start = vecOrigin;
  1491. rayAxis.m_Delta = vecU;
  1492. rayAxis.m_IsRay = true;
  1493. rayPt.m_Start = pt;
  1494. rayPt.m_Delta = vecV * -( lengthV * 10.0f );
  1495. rayPt.m_IsRay = true;
  1496. float s, t;
  1497. IntersectRayWithRay( rayAxis, rayPt, t, s );
  1498. vecUV[0] = t / lengthU;
  1499. //
  1500. // handle the v axis
  1501. //
  1502. rayAxis.m_Delta = vecV;
  1503. rayPt.m_Delta = vecU * -( lengthU * 10.0f );
  1504. IntersectRayWithRay( rayAxis, rayPt, t, s );
  1505. vecUV[1] = t / lengthV;
  1506. // inside of the quad??
  1507. if( ( vecUV[0] < 0.0f ) || ( vecUV[0] > 1.0f ) ||
  1508. ( vecUV[1] < 0.0f ) || ( vecUV[1] > 1.0f ) )
  1509. return BARY_QUADRATIC_FALSE;
  1510. return BARY_QUADRATIC_TRUE;
  1511. }
  1512. //-----------------------------------------------------------------------------
  1513. //-----------------------------------------------------------------------------
  1514. void ResolveQuadratic( double tPlus, double tMinus,
  1515. const Vector axisU0, const Vector axisU1,
  1516. const Vector axisV0, const Vector axisV1,
  1517. const Vector axisOrigin, const Vector pt,
  1518. int projU, double &s, double &t )
  1519. {
  1520. // calculate the sPlus, sMinus pair(s)
  1521. double sDenomPlus = ( axisU0[projU] * ( 1 - tPlus ) ) + ( axisU1[projU] * tPlus );
  1522. double sDenomMinus = ( axisU0[projU] * ( 1 - tMinus ) ) + ( axisU1[projU] * tMinus );
  1523. double sPlus = UNINIT, sMinus = UNINIT;
  1524. if( FloatMakePositive( sDenomPlus ) >= 1e-5 )
  1525. {
  1526. sPlus = ( pt[projU] - axisOrigin[projU] - ( axisV0[projU] * tPlus ) ) / sDenomPlus;
  1527. }
  1528. if( FloatMakePositive( sDenomMinus ) >= 1e-5 )
  1529. {
  1530. sMinus = ( pt[projU] - axisOrigin[projU] - ( axisV0[projU] * tMinus ) ) / sDenomMinus;
  1531. }
  1532. if( ( tPlus >= 0.0 ) && ( tPlus <= 1.0 ) && ( sPlus >= 0.0 ) && ( sPlus <= 1.0 ) )
  1533. {
  1534. s = sPlus;
  1535. t = tPlus;
  1536. return;
  1537. }
  1538. if( ( tMinus >= 0.0 ) && ( tMinus <= 1.0 ) && ( sMinus >= 0.0 ) && ( sMinus <= 1.0 ) )
  1539. {
  1540. s = sMinus;
  1541. t = tMinus;
  1542. return;
  1543. }
  1544. double s0, t0, s1, t1;
  1545. s0 = sPlus;
  1546. t0 = tPlus;
  1547. if( s0 >= 1.0 ) { s0 -= 1.0; }
  1548. if( t0 >= 1.0 ) { t0 -= 1.0; }
  1549. s1 = sMinus;
  1550. t1 = tMinus;
  1551. if( s1 >= 1.0 ) { s1 -= 1.0; }
  1552. if( t1 >= 1.0 ) { t1 -= 1.0; }
  1553. s0 = FloatMakePositive( s0 );
  1554. t0 = FloatMakePositive( t0 );
  1555. s1 = FloatMakePositive( s1 );
  1556. t1 = FloatMakePositive( t1 );
  1557. double max0, max1;
  1558. max0 = s0;
  1559. if( t0 > max0 ) { max0 = t0; }
  1560. max1 = s1;
  1561. if( t1 > max1 ) { max1 = t1; }
  1562. if( max0 > max1 )
  1563. {
  1564. s = sMinus;
  1565. t = tMinus;
  1566. }
  1567. else
  1568. {
  1569. s = sPlus;
  1570. t = tPlus;
  1571. }
  1572. }
  1573. //-----------------------------------------------------------------------------
  1574. //
  1575. //-----------------------------------------------------------------------------
  1576. QuadBarycentricRetval_t PointInQuadToBarycentric( const Vector &v1, const Vector &v2,
  1577. const Vector &v3, const Vector &v4, const Vector &point, Vector2D &uv )
  1578. {
  1579. #define PIQ_TEXTURE_EPSILON 0.001
  1580. #define PIQ_PLANE_EPSILON 0.1
  1581. #define PIQ_DOT_EPSILON 0.99f
  1582. //
  1583. // Think of a quad with points v1, v2, v3, v4 and u, v line segments
  1584. // u0 = v2 - v1
  1585. // u1 = v3 - v4
  1586. // v0 = v4 - v1
  1587. // v1 = v3 - v2
  1588. //
  1589. Vector axisU[2], axisV[2];
  1590. Vector axisUNorm[2], axisVNorm[2];
  1591. axisU[0] = axisUNorm[0] = v2 - v1;
  1592. axisU[1] = axisUNorm[1] = v3 - v4;
  1593. axisV[0] = axisVNorm[0] = v4 - v1;
  1594. axisV[1] = axisVNorm[1] = v3 - v2;
  1595. float lengthU[2], lengthV[2];
  1596. lengthU[0] = VectorNormalize( axisUNorm[0] );
  1597. lengthU[1] = VectorNormalize( axisUNorm[1] );
  1598. lengthV[0] = VectorNormalize( axisVNorm[0] );
  1599. lengthV[1] = VectorNormalize( axisVNorm[1] );
  1600. //
  1601. // check for an early out - parallel opposite edges!
  1602. // NOTE: quad property if 1 set of opposite edges is parallel and equal
  1603. // in length, then the other set of edges is as well
  1604. //
  1605. if( axisUNorm[0].Dot( axisUNorm[1] ) > PIQ_DOT_EPSILON )
  1606. {
  1607. if( FloatMakePositive( lengthU[0] - lengthU[1] ) < PIQ_PLANE_EPSILON )
  1608. {
  1609. return QuadWithParallelEdges( v1, axisUNorm[0], lengthU[0], axisVNorm[0], lengthV[0], point, uv );
  1610. }
  1611. }
  1612. //
  1613. // since we are solving for s in our equations below we need to ensure that
  1614. // the v axes are non-parallel
  1615. //
  1616. bool bFlipped = false;
  1617. if( axisVNorm[0].Dot( axisVNorm[1] ) > PIQ_DOT_EPSILON )
  1618. {
  1619. Vector tmp[2];
  1620. tmp[0] = axisV[0];
  1621. tmp[1] = axisV[1];
  1622. axisV[0] = axisU[0];
  1623. axisV[1] = axisU[1];
  1624. axisU[0] = tmp[0];
  1625. axisU[1] = tmp[1];
  1626. bFlipped = true;
  1627. }
  1628. //
  1629. // get the "projection" axes
  1630. //
  1631. Vector2D projAxes;
  1632. Vector vNormal = axisU[0].Cross( axisV[0] );
  1633. GetNonMajorAxes( vNormal, projAxes );
  1634. //
  1635. // NOTE: axisU[0][projAxes[0]] < axisU[0][projAxes[1]],
  1636. // this is done to decrease error when dividing later
  1637. //
  1638. if( FloatMakePositive( axisU[0][projAxes[0]] ) < FloatMakePositive( axisU[0][projAxes[1]] ) )
  1639. {
  1640. int tmp = projAxes[0];
  1641. projAxes[0] = projAxes[1];
  1642. projAxes[1] = tmp;
  1643. }
  1644. // Here's how we got these equations:
  1645. //
  1646. // Given the points and u,v line segments above...
  1647. //
  1648. // Then:
  1649. //
  1650. // (1.0) PT = P0 + U0 * s + V * t
  1651. //
  1652. // where
  1653. //
  1654. // (1.1) V = V0 + s * (V1 - V0)
  1655. // (1.2) U = U0 + t * (U1 - U0)
  1656. //
  1657. // Therefore (from 1.1 + 1.0):
  1658. // PT - P0 = U0 * s + (V0 + s * (V1-V0)) * t
  1659. // Group s's:
  1660. // PT - P0 - t * V0 = s * (U0 + t * (V1-V0))
  1661. // Two equations and two unknowns in x and y get you the following quadratic:
  1662. //
  1663. // solve the quadratic
  1664. //
  1665. double s = 0.0, t = 0.0;
  1666. double A, negB, C;
  1667. A = ( axisU[0][projAxes[1]] * axisV[0][projAxes[0]] ) -
  1668. ( axisU[0][projAxes[0]] * axisV[0][projAxes[1]] ) -
  1669. ( axisU[1][projAxes[1]] * axisV[0][projAxes[0]] ) +
  1670. ( axisU[1][projAxes[0]] * axisV[0][projAxes[1]] );
  1671. C = ( v1[projAxes[1]] * axisU[0][projAxes[0]] ) -
  1672. ( point[projAxes[1]] * axisU[0][projAxes[0]] ) -
  1673. ( v1[projAxes[0]] * axisU[0][projAxes[1]] ) +
  1674. ( point[projAxes[0]] * axisU[0][projAxes[1]] );
  1675. negB = C -
  1676. ( v1[projAxes[1]] * axisU[1][projAxes[0]] ) +
  1677. ( point[projAxes[1]] * axisU[1][projAxes[0]] ) +
  1678. ( v1[projAxes[0]] * axisU[1][projAxes[1]] ) -
  1679. ( point[projAxes[0]] * axisU[1][projAxes[1]] ) +
  1680. ( axisU[0][projAxes[1]] * axisV[0][projAxes[0]] ) -
  1681. ( axisU[0][projAxes[0]] * axisV[0][projAxes[1]] );
  1682. if( ( A > -PIQ_PLANE_EPSILON ) && ( A < PIQ_PLANE_EPSILON ) )
  1683. {
  1684. // shouldn't be here -- this should have been take care of in the "early out"
  1685. // Assert( 0 );
  1686. Vector vecUAvg, vecVAvg;
  1687. vecUAvg = ( axisUNorm[0] + axisUNorm[1] ) * 0.5f;
  1688. vecVAvg = ( axisVNorm[0] + axisVNorm[1] ) * 0.5f;
  1689. float fLengthUAvg = ( lengthU[0] + lengthU[1] ) * 0.5f;
  1690. float fLengthVAvg = ( lengthV[0] + lengthV[1] ) * 0.5f;
  1691. return QuadWithParallelEdges( v1, vecUAvg, fLengthUAvg, vecVAvg, fLengthVAvg, point, uv );
  1692. #if 0
  1693. // legacy code -- kept here for completeness!
  1694. // not a quadratic -- solve linearly
  1695. t = C / negB;
  1696. // See (1.2) above
  1697. float ui = axisU[0][projAxes[0]] + t * ( axisU[1][projAxes[0]] - axisU[0][projAxes[0]] );
  1698. if( FloatMakePositive( ui ) >= 1e-5 )
  1699. {
  1700. // See (1.0) above
  1701. s = ( point[projAxes[0]] - v1[projAxes[0]] - axisV[0][projAxes[0]] * t ) / ui;
  1702. }
  1703. #endif
  1704. }
  1705. else
  1706. {
  1707. // (-b +/- sqrt( b^2 - 4ac )) / 2a
  1708. double discriminant = (negB*negB) - (4.0f * A * C);
  1709. if( discriminant < 0.0f )
  1710. {
  1711. uv[0] = -99999.0f;
  1712. uv[1] = -99999.0f;
  1713. return BARY_QUADRATIC_NEGATIVE_DISCRIMINANT;
  1714. }
  1715. double quad = sqrt( discriminant );
  1716. double QPlus = ( negB + quad ) / ( 2.0f * A );
  1717. double QMinus = ( negB - quad ) / ( 2.0f * A );
  1718. ResolveQuadratic( QPlus, QMinus, axisU[0], axisU[1], axisV[0], axisV[1], v1, point, projAxes[0], s, t );
  1719. }
  1720. if( !bFlipped )
  1721. {
  1722. uv[0] = ( float )s;
  1723. uv[1] = ( float )t;
  1724. }
  1725. else
  1726. {
  1727. uv[0] = ( float )t;
  1728. uv[1] = ( float )s;
  1729. }
  1730. // inside of the quad??
  1731. if( ( uv[0] < 0.0f ) || ( uv[0] > 1.0f ) || ( uv[1] < 0.0f ) || ( uv[1] > 1.0f ) )
  1732. return BARY_QUADRATIC_FALSE;
  1733. return BARY_QUADRATIC_TRUE;
  1734. #undef PIQ_TEXTURE_EPSILON
  1735. #undef PIQ_PLANE_EPSILON
  1736. }
  1737. //-----------------------------------------------------------------------------
  1738. //-----------------------------------------------------------------------------
  1739. void PointInQuadFromBarycentric( const Vector &v1, const Vector &v2, const Vector &v3, const Vector &v4,
  1740. const Vector2D &uv, Vector &point )
  1741. {
  1742. //
  1743. // Think of a quad with points v1, v2, v3, v4 and u, v line segments
  1744. // find the ray from v0 edge to v1 edge at v
  1745. //
  1746. Vector vPts[2];
  1747. VectorLerp( v1, v4, uv[1], vPts[0] );
  1748. VectorLerp( v2, v3, uv[1], vPts[1] );
  1749. VectorLerp( vPts[0], vPts[1], uv[0], point );
  1750. }
  1751. //-----------------------------------------------------------------------------
  1752. //-----------------------------------------------------------------------------
  1753. void TexCoordInQuadFromBarycentric( const Vector2D &v1, const Vector2D &v2, const Vector2D &v3, const Vector2D &v4,
  1754. const Vector2D &uv, Vector2D &texCoord )
  1755. {
  1756. //
  1757. // Think of a quad with points v1, v2, v3, v4 and u, v line segments
  1758. // find the ray from v0 edge to v1 edge at v
  1759. //
  1760. Vector2D vCoords[2];
  1761. Vector2DLerp( v1, v4, uv[1], vCoords[0] );
  1762. Vector2DLerp( v2, v3, uv[1], vCoords[1] );
  1763. Vector2DLerp( vCoords[0], vCoords[1], uv[0], texCoord );
  1764. }
  1765. //-----------------------------------------------------------------------------
  1766. // Compute point from barycentric specification
  1767. // Edge u goes from v0 to v1, edge v goes from v0 to v2
  1768. //-----------------------------------------------------------------------------
  1769. void ComputePointFromBarycentric( const Vector& v0, const Vector& v1, const Vector& v2,
  1770. float u, float v, Vector& pt )
  1771. {
  1772. Vector edgeU, edgeV;
  1773. VectorSubtract( v1, v0, edgeU );
  1774. VectorSubtract( v2, v0, edgeV );
  1775. VectorMA( v0, u, edgeU, pt );
  1776. VectorMA( pt, v, edgeV, pt );
  1777. }
  1778. void ComputePointFromBarycentric( const Vector2D& v0, const Vector2D& v1, const Vector2D& v2,
  1779. float u, float v, Vector2D& pt )
  1780. {
  1781. Vector2D edgeU, edgeV;
  1782. Vector2DSubtract( v1, v0, edgeU );
  1783. Vector2DSubtract( v2, v0, edgeV );
  1784. Vector2DMA( v0, u, edgeU, pt );
  1785. Vector2DMA( pt, v, edgeV, pt );
  1786. }
  1787. //-----------------------------------------------------------------------------
  1788. // Compute a matrix that has the correct orientation but which has an origin at
  1789. // the center of the bounds
  1790. //-----------------------------------------------------------------------------
  1791. static void ComputeCenterMatrix( const Vector& origin, const QAngle& angles,
  1792. const Vector& mins, const Vector& maxs, matrix3x4_t& matrix )
  1793. {
  1794. Vector centroid;
  1795. VectorAdd( mins, maxs, centroid );
  1796. centroid *= 0.5f;
  1797. AngleMatrix( angles, matrix );
  1798. Vector worldCentroid;
  1799. VectorRotate( centroid, matrix, worldCentroid );
  1800. worldCentroid += origin;
  1801. MatrixSetColumn( worldCentroid, 3, matrix );
  1802. }
  1803. static void ComputeCenterIMatrix( const Vector& origin, const QAngle& angles,
  1804. const Vector& mins, const Vector& maxs, matrix3x4_t& matrix )
  1805. {
  1806. Vector centroid;
  1807. VectorAdd( mins, maxs, centroid );
  1808. centroid *= -0.5f;
  1809. AngleIMatrix( angles, matrix );
  1810. // For the translational component here, note that the origin in world space
  1811. // is T = R * C + O, (R = rotation matrix, C = centroid in local space, O = origin in world space)
  1812. // The IMatrix translation = - transpose(R) * T = -C - transpose(R) * 0
  1813. Vector localOrigin;
  1814. VectorRotate( origin, matrix, localOrigin );
  1815. centroid -= localOrigin;
  1816. MatrixSetColumn( centroid, 3, matrix );
  1817. }
  1818. //-----------------------------------------------------------------------------
  1819. // Compute a matrix which is the absolute value of another
  1820. //-----------------------------------------------------------------------------
  1821. static inline void ComputeAbsMatrix( const matrix3x4_t& in, matrix3x4_t& out )
  1822. {
  1823. (out[0][0]) = fabsf(in[0][0]);
  1824. (out[0][1]) = fabsf(in[0][1]);
  1825. (out[0][2]) = fabsf(in[0][2]);
  1826. (out[1][0]) = fabsf(in[1][0]);
  1827. (out[1][1]) = fabsf(in[1][1]);
  1828. (out[1][2]) = fabsf(in[1][2]);
  1829. (out[2][0]) = fabsf(in[2][0]);
  1830. (out[2][1]) = fabsf(in[2][1]);
  1831. (out[2][2]) = fabsf(in[2][2]);
  1832. }
  1833. //-----------------------------------------------------------------------------
  1834. // Compute a separating plane between two boxes (expensive!)
  1835. // Returns false if no separating plane exists
  1836. //-----------------------------------------------------------------------------
  1837. static bool ComputeSeparatingPlane( const matrix3x4_t &worldToBox1, const matrix3x4_t &box2ToWorld,
  1838. const Vector& box1Size, const Vector& box2Size, float tolerance, cplane_t* pPlane )
  1839. {
  1840. // The various separating planes can be either
  1841. // 1) A plane parallel to one of the box face planes
  1842. // 2) A plane parallel to the cross-product of an edge from each box
  1843. // First, compute the basis of second box in the space of the first box
  1844. // NOTE: These basis place the origin at the centroid of each box!
  1845. matrix3x4_t box2ToBox1;
  1846. ConcatTransforms( worldToBox1, box2ToWorld, box2ToBox1 );
  1847. // We're going to be using the origin of box2 in the space of box1 alot,
  1848. // lets extract it from the matrix....
  1849. Vector box2Origin;
  1850. MatrixGetColumn( box2ToBox1, 3, box2Origin );
  1851. // Next get the absolute values of these entries and store in absbox2ToBox1.
  1852. matrix3x4_t absBox2ToBox1;
  1853. ComputeAbsMatrix( box2ToBox1, absBox2ToBox1 );
  1854. // There are 15 tests to make. The first 3 involve trying planes parallel
  1855. // to the faces of the first box.
  1856. // NOTE: The algorithm here involves finding the projections of the two boxes
  1857. // onto a particular line. If the projections on the line do not overlap,
  1858. // that means that there's a plane perpendicular to the line which separates
  1859. // the two boxes; and we've therefore found a separating plane.
  1860. // The way we check for overlay is we find the projections of the two boxes
  1861. // onto the line, and add them up. We compare the sum with the projection
  1862. // of the relative center of box2 onto the same line.
  1863. Vector tmp;
  1864. float boxProjectionSum;
  1865. float originProjection;
  1866. // NOTE: For these guys, we're taking advantage of the fact that the ith
  1867. // row of the box2ToBox1 is the direction of the box1 (x,y,z)-axis
  1868. // transformed into the space of box2.
  1869. // First side of box 1
  1870. boxProjectionSum = box1Size.x + MatrixRowDotProduct( absBox2ToBox1, 0, box2Size );
  1871. originProjection = FloatMakePositive( box2Origin.x ) + tolerance;
  1872. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1873. {
  1874. VectorCopy( worldToBox1[0], pPlane->normal.Base() );
  1875. return true;
  1876. }
  1877. // Second side of box 1
  1878. boxProjectionSum = box1Size.y + MatrixRowDotProduct( absBox2ToBox1, 1, box2Size );
  1879. originProjection = FloatMakePositive( box2Origin.y ) + tolerance;
  1880. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1881. {
  1882. VectorCopy( worldToBox1[1], pPlane->normal.Base() );
  1883. return true;
  1884. }
  1885. // Third side of box 1
  1886. boxProjectionSum = box1Size.z + MatrixRowDotProduct( absBox2ToBox1, 2, box2Size );
  1887. originProjection = FloatMakePositive( box2Origin.z ) + tolerance;
  1888. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1889. {
  1890. VectorCopy( worldToBox1[2], pPlane->normal.Base() );
  1891. return true;
  1892. }
  1893. // The next three involve checking splitting planes parallel to the
  1894. // faces of the second box.
  1895. // NOTE: For these guys, we're taking advantage of the fact that the 0th
  1896. // column of the box2ToBox1 is the direction of the box2 x-axis
  1897. // transformed into the space of box1.
  1898. // Here, we're determining the distance of box2's center from box1's center
  1899. // by projecting it onto a line parallel to box2's axis
  1900. // First side of box 2
  1901. boxProjectionSum = box2Size.x + MatrixColumnDotProduct( absBox2ToBox1, 0, box1Size );
  1902. originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 0, box2Origin ) ) + tolerance;
  1903. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1904. {
  1905. MatrixGetColumn( box2ToWorld, 0, pPlane->normal );
  1906. return true;
  1907. }
  1908. // Second side of box 2
  1909. boxProjectionSum = box2Size.y + MatrixColumnDotProduct( absBox2ToBox1, 1, box1Size );
  1910. originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 1, box2Origin ) ) + tolerance;
  1911. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1912. {
  1913. MatrixGetColumn( box2ToWorld, 1, pPlane->normal );
  1914. return true;
  1915. }
  1916. // Third side of box 2
  1917. boxProjectionSum = box2Size.z + MatrixColumnDotProduct( absBox2ToBox1, 2, box1Size );
  1918. originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 2, box2Origin ) ) + tolerance;
  1919. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1920. {
  1921. MatrixGetColumn( box2ToWorld, 2, pPlane->normal );
  1922. return true;
  1923. }
  1924. // Next check the splitting planes which are orthogonal to the pairs
  1925. // of edges, one from box1 and one from box2. As only direction matters,
  1926. // there are 9 pairs since each box has 3 distinct edge directions.
  1927. // Here, we take advantage of the fact that the edges from box 1 are all
  1928. // axis aligned; therefore the crossproducts are simplified. Let's walk through
  1929. // the example of b1e1 x b2e1:
  1930. // In this example, the line to check is perpendicular to b1e1 + b2e2
  1931. // we can compute this line by taking the cross-product:
  1932. //
  1933. // [ i j k ]
  1934. // [ 1 0 0 ] = - ez j + ey k = l1
  1935. // [ ex ey ez ]
  1936. // Where ex, ey, ez is the components of box2's x axis in the space of box 1,
  1937. // which is == to the 0th column of of box2toBox1
  1938. // The projection of box1 onto this line = the absolute dot product of the box size
  1939. // against the line, which =
  1940. // AbsDot( box1Size, l1 ) = abs( -ez * box1.y ) + abs( ey * box1.z )
  1941. // To compute the projection of box2 onto this line, we'll do it in the space of box 2
  1942. //
  1943. // [ i j k ]
  1944. // [ fx fy fz ] = fz j - fy k = l2
  1945. // [ 1 0 0 ]
  1946. // Where fx, fy, fz is the components of box1's x axis in the space of box 2,
  1947. // which is == to the 0th row of of box2toBox1
  1948. // The projection of box2 onto this line = the absolute dot product of the box size
  1949. // against the line, which =
  1950. // AbsDot( box2Size, l2 ) = abs( fz * box2.y ) + abs ( fy * box2.z )
  1951. // The projection of the relative origin position on this line is done in the
  1952. // space of box 1:
  1953. //
  1954. // originProjection = DotProduct( <-ez j + ey k>, box2Origin ) =
  1955. // -ez * box2Origin.y + ey * box2Origin.z
  1956. // NOTE: These checks can be bogus if both edges are parallel. The if
  1957. // checks at the beginning of each block are designed to catch that case
  1958. // b1e1 x b2e1
  1959. if ( absBox2ToBox1[0][0] < 1.0f - 1e-3f )
  1960. {
  1961. boxProjectionSum =
  1962. box1Size.y * absBox2ToBox1[2][0] + box1Size.z * absBox2ToBox1[1][0] +
  1963. box2Size.y * absBox2ToBox1[0][2] + box2Size.z * absBox2ToBox1[0][1];
  1964. originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][0] + box2Origin.z * box2ToBox1[1][0] ) + tolerance;
  1965. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1966. {
  1967. MatrixGetColumn( box2ToWorld, 0, tmp );
  1968. CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() );
  1969. return true;
  1970. }
  1971. }
  1972. // b1e1 x b2e2
  1973. if ( absBox2ToBox1[0][1] < 1.0f - 1e-3f )
  1974. {
  1975. boxProjectionSum =
  1976. box1Size.y * absBox2ToBox1[2][1] + box1Size.z * absBox2ToBox1[1][1] +
  1977. box2Size.x * absBox2ToBox1[0][2] + box2Size.z * absBox2ToBox1[0][0];
  1978. originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][1] + box2Origin.z * box2ToBox1[1][1] ) + tolerance;
  1979. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1980. {
  1981. MatrixGetColumn( box2ToWorld, 1, tmp );
  1982. CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() );
  1983. return true;
  1984. }
  1985. }
  1986. // b1e1 x b2e3
  1987. if ( absBox2ToBox1[0][2] < 1.0f - 1e-3f )
  1988. {
  1989. boxProjectionSum =
  1990. box1Size.y * absBox2ToBox1[2][2] + box1Size.z * absBox2ToBox1[1][2] +
  1991. box2Size.x * absBox2ToBox1[0][1] + box2Size.y * absBox2ToBox1[0][0];
  1992. originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][2] + box2Origin.z * box2ToBox1[1][2] ) + tolerance;
  1993. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  1994. {
  1995. MatrixGetColumn( box2ToWorld, 2, tmp );
  1996. CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() );
  1997. return true;
  1998. }
  1999. }
  2000. // b1e2 x b2e1
  2001. if ( absBox2ToBox1[1][0] < 1.0f - 1e-3f )
  2002. {
  2003. boxProjectionSum =
  2004. box1Size.x * absBox2ToBox1[2][0] + box1Size.z * absBox2ToBox1[0][0] +
  2005. box2Size.y * absBox2ToBox1[1][2] + box2Size.z * absBox2ToBox1[1][1];
  2006. originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][0] - box2Origin.z * box2ToBox1[0][0] ) + tolerance;
  2007. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2008. {
  2009. MatrixGetColumn( box2ToWorld, 0, tmp );
  2010. CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() );
  2011. return true;
  2012. }
  2013. }
  2014. // b1e2 x b2e2
  2015. if ( absBox2ToBox1[1][1] < 1.0f - 1e-3f )
  2016. {
  2017. boxProjectionSum =
  2018. box1Size.x * absBox2ToBox1[2][1] + box1Size.z * absBox2ToBox1[0][1] +
  2019. box2Size.x * absBox2ToBox1[1][2] + box2Size.z * absBox2ToBox1[1][0];
  2020. originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][1] - box2Origin.z * box2ToBox1[0][1] ) + tolerance;
  2021. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2022. {
  2023. MatrixGetColumn( box2ToWorld, 1, tmp );
  2024. CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() );
  2025. return true;
  2026. }
  2027. }
  2028. // b1e2 x b2e3
  2029. if ( absBox2ToBox1[1][2] < 1.0f - 1e-3f )
  2030. {
  2031. boxProjectionSum =
  2032. box1Size.x * absBox2ToBox1[2][2] + box1Size.z * absBox2ToBox1[0][2] +
  2033. box2Size.x * absBox2ToBox1[1][1] + box2Size.y * absBox2ToBox1[1][0];
  2034. originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][2] - box2Origin.z * box2ToBox1[0][2] ) + tolerance;
  2035. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2036. {
  2037. MatrixGetColumn( box2ToWorld, 2, tmp );
  2038. CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() );
  2039. return true;
  2040. }
  2041. }
  2042. // b1e3 x b2e1
  2043. if ( absBox2ToBox1[2][0] < 1.0f - 1e-3f )
  2044. {
  2045. boxProjectionSum =
  2046. box1Size.x * absBox2ToBox1[1][0] + box1Size.y * absBox2ToBox1[0][0] +
  2047. box2Size.y * absBox2ToBox1[2][2] + box2Size.z * absBox2ToBox1[2][1];
  2048. originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][0] + box2Origin.y * box2ToBox1[0][0] ) + tolerance;
  2049. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2050. {
  2051. MatrixGetColumn( box2ToWorld, 0, tmp );
  2052. CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() );
  2053. return true;
  2054. }
  2055. }
  2056. // b1e3 x b2e2
  2057. if ( absBox2ToBox1[2][1] < 1.0f - 1e-3f )
  2058. {
  2059. boxProjectionSum =
  2060. box1Size.x * absBox2ToBox1[1][1] + box1Size.y * absBox2ToBox1[0][1] +
  2061. box2Size.x * absBox2ToBox1[2][2] + box2Size.z * absBox2ToBox1[2][0];
  2062. originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][1] + box2Origin.y * box2ToBox1[0][1] ) + tolerance;
  2063. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2064. {
  2065. MatrixGetColumn( box2ToWorld, 1, tmp );
  2066. CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() );
  2067. return true;
  2068. }
  2069. }
  2070. // b1e3 x b2e3
  2071. if ( absBox2ToBox1[2][2] < 1.0f - 1e-3f )
  2072. {
  2073. boxProjectionSum =
  2074. box1Size.x * absBox2ToBox1[1][2] + box1Size.y * absBox2ToBox1[0][2] +
  2075. box2Size.x * absBox2ToBox1[2][1] + box2Size.y * absBox2ToBox1[2][0];
  2076. originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][2] + box2Origin.y * box2ToBox1[0][2] ) + tolerance;
  2077. if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) )
  2078. {
  2079. MatrixGetColumn( box2ToWorld, 2, tmp );
  2080. CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() );
  2081. return true;
  2082. }
  2083. }
  2084. return false;
  2085. }
  2086. //-----------------------------------------------------------------------------
  2087. // Compute a separating plane between two boxes (expensive!)
  2088. // Returns false if no separating plane exists
  2089. //-----------------------------------------------------------------------------
  2090. bool ComputeSeparatingPlane( const Vector& org1, const QAngle& angles1, const Vector& min1, const Vector& max1,
  2091. const Vector& org2, const QAngle& angles2, const Vector& min2, const Vector& max2,
  2092. float tolerance, cplane_t* pPlane )
  2093. {
  2094. matrix3x4_t worldToBox1, box2ToWorld;
  2095. ComputeCenterIMatrix( org1, angles1, min1, max1, worldToBox1 );
  2096. ComputeCenterMatrix( org2, angles2, min2, max2, box2ToWorld );
  2097. // Then compute the size of the two boxes
  2098. Vector box1Size, box2Size;
  2099. VectorSubtract( max1, min1, box1Size );
  2100. VectorSubtract( max2, min2, box2Size );
  2101. box1Size *= 0.5f;
  2102. box2Size *= 0.5f;
  2103. return ComputeSeparatingPlane( worldToBox1, box2ToWorld, box1Size, box2Size, tolerance, pPlane );
  2104. }
  2105. //-----------------------------------------------------------------------------
  2106. // Swept OBB test
  2107. //-----------------------------------------------------------------------------
  2108. bool IsRayIntersectingOBB( const Ray_t &ray, const Vector& org, const QAngle& angles,
  2109. const Vector& mins, const Vector& maxs )
  2110. {
  2111. if ( angles == vec3_angle )
  2112. {
  2113. Vector vecWorldMins, vecWorldMaxs;
  2114. VectorAdd( org, mins, vecWorldMins );
  2115. VectorAdd( org, maxs, vecWorldMaxs );
  2116. return IsBoxIntersectingRay( vecWorldMins, vecWorldMaxs, ray );
  2117. }
  2118. if ( ray.m_IsRay )
  2119. {
  2120. matrix3x4_t worldToBox;
  2121. AngleIMatrix( angles, org, worldToBox );
  2122. Ray_t rotatedRay;
  2123. VectorTransform( ray.m_Start, worldToBox, rotatedRay.m_Start );
  2124. VectorRotate( ray.m_Delta, worldToBox, rotatedRay.m_Delta );
  2125. rotatedRay.m_StartOffset = vec3_origin;
  2126. rotatedRay.m_Extents = vec3_origin;
  2127. rotatedRay.m_IsRay = ray.m_IsRay;
  2128. rotatedRay.m_IsSwept = ray.m_IsSwept;
  2129. return IsBoxIntersectingRay( mins, maxs, rotatedRay );
  2130. }
  2131. if ( !ray.m_IsSwept )
  2132. {
  2133. cplane_t plane;
  2134. return ComputeSeparatingPlane( ray.m_Start, vec3_angle, -ray.m_Extents, ray.m_Extents,
  2135. org, angles, mins, maxs, 0.0f, &plane ) == false;
  2136. }
  2137. // NOTE: See the comments in ComputeSeparatingPlane to understand this math
  2138. // First, compute the basis of box in the space of the ray
  2139. // NOTE: These basis place the origin at the centroid of each box!
  2140. matrix3x4_t worldToBox1, box2ToWorld;
  2141. ComputeCenterMatrix( org, angles, mins, maxs, box2ToWorld );
  2142. // Find the center + extents of an AABB surrounding the ray
  2143. Vector vecRayCenter;
  2144. VectorMA( ray.m_Start, 0.5, ray.m_Delta, vecRayCenter );
  2145. vecRayCenter *= -1.0f;
  2146. SetIdentityMatrix( worldToBox1 );
  2147. MatrixSetColumn( vecRayCenter, 3, worldToBox1 );
  2148. Vector box1Size;
  2149. box1Size.x = ray.m_Extents.x + FloatMakePositive( ray.m_Delta.x ) * 0.5f;
  2150. box1Size.y = ray.m_Extents.y + FloatMakePositive( ray.m_Delta.y ) * 0.5f;
  2151. box1Size.z = ray.m_Extents.z + FloatMakePositive( ray.m_Delta.z ) * 0.5f;
  2152. // Then compute the size of the box
  2153. Vector box2Size;
  2154. VectorSubtract( maxs, mins, box2Size );
  2155. box2Size *= 0.5f;
  2156. // Do an OBB test of the box with the AABB surrounding the ray
  2157. cplane_t plane;
  2158. if ( ComputeSeparatingPlane( worldToBox1, box2ToWorld, box1Size, box2Size, 0.0f, &plane ) )
  2159. return false;
  2160. // Now deal with the planes which are the cross products of the ray sweep direction vs box edges
  2161. Vector vecRayDirection = ray.m_Delta;
  2162. VectorNormalize( vecRayDirection );
  2163. // Need a vector between ray center vs box center measured in the space of the ray (world)
  2164. Vector vecCenterDelta;
  2165. vecCenterDelta.x = box2ToWorld[0][3] - ray.m_Start.x;
  2166. vecCenterDelta.y = box2ToWorld[1][3] - ray.m_Start.y;
  2167. vecCenterDelta.z = box2ToWorld[2][3] - ray.m_Start.z;
  2168. // Rotate the ray direction into the space of the OBB
  2169. Vector vecAbsRayDirBox2;
  2170. VectorIRotate( vecRayDirection, box2ToWorld, vecAbsRayDirBox2 );
  2171. // Make abs versions of the ray in world space + ray in box2 space
  2172. VectorAbs( vecAbsRayDirBox2, vecAbsRayDirBox2 );
  2173. // Now do the work for the planes which are perpendicular to the edges of the AABB
  2174. // and the sweep direction edges...
  2175. // In this example, the line to check is perpendicular to box edge x + ray delta
  2176. // we can compute this line by taking the cross-product:
  2177. //
  2178. // [ i j k ]
  2179. // [ 1 0 0 ] = - dz j + dy k = l1
  2180. // [ dx dy dz ]
  2181. // Where dx, dy, dz is the ray delta (normalized)
  2182. // The projection of the box onto this line = the absolute dot product of the box size
  2183. // against the line, which =
  2184. // AbsDot( vecBoxHalfDiagonal, l1 ) = abs( -dz * vecBoxHalfDiagonal.y ) + abs( dy * vecBoxHalfDiagonal.z )
  2185. // Because the plane contains the sweep direction, the sweep will produce
  2186. // no extra projection onto the line normal to the plane.
  2187. // Therefore all we need to do is project the ray extents onto this line also:
  2188. // AbsDot( ray.m_Extents, l1 ) = abs( -dz * ray.m_Extents.y ) + abs( dy * ray.m_Extents.z )
  2189. Vector vecPlaneNormal;
  2190. // box x x ray delta
  2191. CrossProduct( vecRayDirection, Vector( box2ToWorld[0][0], box2ToWorld[1][0], box2ToWorld[2][0] ), vecPlaneNormal );
  2192. float flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) );
  2193. float flBoxProjectionSum =
  2194. vecAbsRayDirBox2.z * box2Size.y + vecAbsRayDirBox2.y * box2Size.z +
  2195. DotProductAbs( vecPlaneNormal, ray.m_Extents );
  2196. if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) )
  2197. return false;
  2198. // box y x ray delta
  2199. CrossProduct( vecRayDirection, Vector( box2ToWorld[0][1], box2ToWorld[1][1], box2ToWorld[2][1] ), vecPlaneNormal );
  2200. flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) );
  2201. flBoxProjectionSum =
  2202. vecAbsRayDirBox2.z * box2Size.x + vecAbsRayDirBox2.x * box2Size.z +
  2203. DotProductAbs( vecPlaneNormal, ray.m_Extents );
  2204. if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) )
  2205. return false;
  2206. // box z x ray delta
  2207. CrossProduct( vecRayDirection, Vector( box2ToWorld[0][2], box2ToWorld[1][2], box2ToWorld[2][2] ), vecPlaneNormal );
  2208. flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) );
  2209. flBoxProjectionSum =
  2210. vecAbsRayDirBox2.y * box2Size.x + vecAbsRayDirBox2.x * box2Size.y +
  2211. DotProductAbs( vecPlaneNormal, ray.m_Extents );
  2212. if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) )
  2213. return false;
  2214. return true;
  2215. }
  2216. //--------------------------------------------------------------------------
  2217. // Purpose:
  2218. //
  2219. // NOTE:
  2220. // triangle points are given in clockwise order (aabb-triangle test)
  2221. //
  2222. // 1 edge0 = 1 - 0
  2223. // | \ edge1 = 2 - 1
  2224. // | \ edge2 = 0 - 2
  2225. // | \ .
  2226. // | \ .
  2227. // 0-----2 .
  2228. //
  2229. //--------------------------------------------------------------------------
  2230. //-----------------------------------------------------------------------------
  2231. // Purpose: find the minima and maxima of the 3 given values
  2232. //-----------------------------------------------------------------------------
  2233. inline void FindMinMax( float v1, float v2, float v3, float &min, float &max )
  2234. {
  2235. min = max = v1;
  2236. if ( v2 < min ) { min = v2; }
  2237. if ( v2 > max ) { max = v2; }
  2238. if ( v3 < min ) { min = v3; }
  2239. if ( v3 > max ) { max = v3; }
  2240. }
  2241. //-----------------------------------------------------------------------------
  2242. // Purpose:
  2243. //-----------------------------------------------------------------------------
  2244. inline bool AxisTestEdgeCrossX2( float flEdgeZ, float flEdgeY, float flAbsEdgeZ, float flAbsEdgeY,
  2245. const Vector &p1, const Vector &p3, const Vector &vecExtents,
  2246. float flTolerance )
  2247. {
  2248. // Cross Product( axialX(1,0,0) x edge ): x = 0.0f, y = edge.z, z = -edge.y
  2249. // Triangle Point Distances: dist(x) = normal.y * pt(x).y + normal.z * pt(x).z
  2250. float flDist1 = flEdgeZ * p1.y - flEdgeY * p1.z;
  2251. float flDist3 = flEdgeZ * p3.y - flEdgeY * p3.z;
  2252. // Extents are symmetric: dist = abs( normal.y ) * extents.y + abs( normal.z ) * extents.z
  2253. float flDistBox = flAbsEdgeZ * vecExtents.y + flAbsEdgeY * vecExtents.z;
  2254. // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB).
  2255. if ( flDist1 < flDist3 )
  2256. {
  2257. if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) )
  2258. return false;
  2259. }
  2260. else
  2261. {
  2262. if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) )
  2263. return false;
  2264. }
  2265. return true;
  2266. }
  2267. //--------------------------------------------------------------------------
  2268. // Purpose:
  2269. //--------------------------------------------------------------------------
  2270. inline bool AxisTestEdgeCrossX3( float flEdgeZ, float flEdgeY, float flAbsEdgeZ, float flAbsEdgeY,
  2271. const Vector &p1, const Vector &p2, const Vector &vecExtents,
  2272. float flTolerance )
  2273. {
  2274. // Cross Product( axialX(1,0,0) x edge ): x = 0.0f, y = edge.z, z = -edge.y
  2275. // Triangle Point Distances: dist(x) = normal.y * pt(x).y + normal.z * pt(x).z
  2276. float flDist1 = flEdgeZ * p1.y - flEdgeY * p1.z;
  2277. float flDist2 = flEdgeZ * p2.y - flEdgeY * p2.z;
  2278. // Extents are symmetric: dist = abs( normal.y ) * extents.y + abs( normal.z ) * extents.z
  2279. float flDistBox = flAbsEdgeZ * vecExtents.y + flAbsEdgeY * vecExtents.z;
  2280. // Either dist1, dist2 is the closest point to the box, determine which and test of overlap with box(AABB).
  2281. if ( flDist1 < flDist2 )
  2282. {
  2283. if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) )
  2284. return false;
  2285. }
  2286. else
  2287. {
  2288. if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) )
  2289. return false;
  2290. }
  2291. return true;
  2292. }
  2293. //--------------------------------------------------------------------------
  2294. //--------------------------------------------------------------------------
  2295. inline bool AxisTestEdgeCrossY2( float flEdgeZ, float flEdgeX, float flAbsEdgeZ, float flAbsEdgeX,
  2296. const Vector &p1, const Vector &p3, const Vector &vecExtents,
  2297. float flTolerance )
  2298. {
  2299. // Cross Product( axialY(0,1,0) x edge ): x = -edge.z, y = 0.0f, z = edge.x
  2300. // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.z * pt(x).z
  2301. float flDist1 = -flEdgeZ * p1.x + flEdgeX * p1.z;
  2302. float flDist3 = -flEdgeZ * p3.x + flEdgeX * p3.z;
  2303. // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.z ) * extents.z
  2304. float flDistBox = flAbsEdgeZ * vecExtents.x + flAbsEdgeX * vecExtents.z;
  2305. // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB).
  2306. if ( flDist1 < flDist3 )
  2307. {
  2308. if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) )
  2309. return false;
  2310. }
  2311. else
  2312. {
  2313. if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) )
  2314. return false;
  2315. }
  2316. return true;
  2317. }
  2318. //--------------------------------------------------------------------------
  2319. //--------------------------------------------------------------------------
  2320. inline bool AxisTestEdgeCrossY3( float flEdgeZ, float flEdgeX, float flAbsEdgeZ, float flAbsEdgeX,
  2321. const Vector &p1, const Vector &p2, const Vector &vecExtents,
  2322. float flTolerance )
  2323. {
  2324. // Cross Product( axialY(0,1,0) x edge ): x = -edge.z, y = 0.0f, z = edge.x
  2325. // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.z * pt(x).z
  2326. float flDist1 = -flEdgeZ * p1.x + flEdgeX * p1.z;
  2327. float flDist2 = -flEdgeZ * p2.x + flEdgeX * p2.z;
  2328. // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.z ) * extents.z
  2329. float flDistBox = flAbsEdgeZ * vecExtents.x + flAbsEdgeX * vecExtents.z;
  2330. // Either dist1, dist2 is the closest point to the box, determine which and test of overlap with box(AABB).
  2331. if ( flDist1 < flDist2 )
  2332. {
  2333. if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) )
  2334. return false;
  2335. }
  2336. else
  2337. {
  2338. if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) )
  2339. return false;
  2340. }
  2341. return true;
  2342. }
  2343. //--------------------------------------------------------------------------
  2344. //--------------------------------------------------------------------------
  2345. inline bool AxisTestEdgeCrossZ1( float flEdgeY, float flEdgeX, float flAbsEdgeY, float flAbsEdgeX,
  2346. const Vector &p2, const Vector &p3, const Vector &vecExtents,
  2347. float flTolerance )
  2348. {
  2349. // Cross Product( axialZ(0,0,1) x edge ): x = edge.y, y = -edge.x, z = 0.0f
  2350. // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.y * pt(x).y
  2351. float flDist2 = flEdgeY * p2.x - flEdgeX * p2.y;
  2352. float flDist3 = flEdgeY * p3.x - flEdgeX * p3.y;
  2353. // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.y ) * extents.y
  2354. float flDistBox = flAbsEdgeY * vecExtents.x + flAbsEdgeX * vecExtents.y;
  2355. // Either dist2, dist3 is the closest point to the box, determine which and test of overlap with box(AABB).
  2356. if ( flDist3 < flDist2 )
  2357. {
  2358. if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) )
  2359. return false;
  2360. }
  2361. else
  2362. {
  2363. if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) )
  2364. return false;
  2365. }
  2366. return true;
  2367. }
  2368. //--------------------------------------------------------------------------
  2369. //--------------------------------------------------------------------------
  2370. inline bool AxisTestEdgeCrossZ2( float flEdgeY, float flEdgeX, float flAbsEdgeY, float flAbsEdgeX,
  2371. const Vector &p1, const Vector &p3, const Vector &vecExtents,
  2372. float flTolerance )
  2373. {
  2374. // Cross Product( axialZ(0,0,1) x edge ): x = edge.y, y = -edge.x, z = 0.0f
  2375. // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.y * pt(x).y
  2376. float flDist1 = flEdgeY * p1.x - flEdgeX * p1.y;
  2377. float flDist3 = flEdgeY * p3.x - flEdgeX * p3.y;
  2378. // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.y ) * extents.y
  2379. float flDistBox = flAbsEdgeY * vecExtents.x + flAbsEdgeX * vecExtents.y;
  2380. // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB).
  2381. if ( flDist1 < flDist3 )
  2382. {
  2383. if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) )
  2384. return false;
  2385. }
  2386. else
  2387. {
  2388. if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) )
  2389. return false;
  2390. }
  2391. return true;
  2392. }
  2393. //-----------------------------------------------------------------------------
  2394. // Purpose: Test for an intersection (overlap) between an axial-aligned bounding
  2395. // box (AABB) and a triangle.
  2396. //
  2397. // Using the "Separating-Axis Theorem" to test for intersections between
  2398. // a triangle and an axial-aligned bounding box (AABB).
  2399. // 1. 3 Axis Planes - x, y, z
  2400. // 2. 9 Edge Planes Tests - the 3 edges of the triangle crossed with all 3 axial
  2401. // planes (x, y, z)
  2402. // 3. 1 Face Plane - the triangle plane (cplane_t plane below)
  2403. // Output: false = separating axis (no intersection)
  2404. // true = intersection
  2405. //-----------------------------------------------------------------------------
  2406. bool IsBoxIntersectingTriangle( const Vector &vecBoxCenter, const Vector &vecBoxExtents,
  2407. const Vector &v1, const Vector &v2, const Vector &v3,
  2408. const cplane_t &plane, float flTolerance )
  2409. {
  2410. // Test the axial planes (x,y,z) against the min, max of the triangle.
  2411. float flMin, flMax;
  2412. Vector p1, p2, p3;
  2413. // x plane
  2414. p1.x = v1.x - vecBoxCenter.x;
  2415. p2.x = v2.x - vecBoxCenter.x;
  2416. p3.x = v3.x - vecBoxCenter.x;
  2417. FindMinMax( p1.x, p2.x, p3.x, flMin, flMax );
  2418. if ( ( flMin > ( vecBoxExtents.x + flTolerance ) ) || ( flMax < -( vecBoxExtents.x + flTolerance ) ) )
  2419. return false;
  2420. // y plane
  2421. p1.y = v1.y - vecBoxCenter.y;
  2422. p2.y = v2.y - vecBoxCenter.y;
  2423. p3.y = v3.y - vecBoxCenter.y;
  2424. FindMinMax( p1.y, p2.y, p3.y, flMin, flMax );
  2425. if ( ( flMin > ( vecBoxExtents.y + flTolerance ) ) || ( flMax < -( vecBoxExtents.y + flTolerance ) ) )
  2426. return false;
  2427. // z plane
  2428. p1.z = v1.z - vecBoxCenter.z;
  2429. p2.z = v2.z - vecBoxCenter.z;
  2430. p3.z = v3.z - vecBoxCenter.z;
  2431. FindMinMax( p1.z, p2.z, p3.z, flMin, flMax );
  2432. if ( ( flMin > ( vecBoxExtents.z + flTolerance ) ) || ( flMax < -( vecBoxExtents.z + flTolerance ) ) )
  2433. return false;
  2434. // Test the 9 edge cases.
  2435. Vector vecEdge, vecAbsEdge;
  2436. // edge 0 (cross x,y,z)
  2437. vecEdge = p2 - p1;
  2438. vecAbsEdge.y = FloatMakePositive( vecEdge.y );
  2439. vecAbsEdge.z = FloatMakePositive( vecEdge.z );
  2440. if ( !AxisTestEdgeCrossX2( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p3, vecBoxExtents, flTolerance ) )
  2441. return false;
  2442. vecAbsEdge.x = FloatMakePositive( vecEdge.x );
  2443. if ( !AxisTestEdgeCrossY2( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p3, vecBoxExtents, flTolerance ) )
  2444. return false;
  2445. if ( !AxisTestEdgeCrossZ1( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p2, p3, vecBoxExtents, flTolerance ) )
  2446. return false;
  2447. // edge 1 (cross x,y,z)
  2448. vecEdge = p3 - p2;
  2449. vecAbsEdge.y = FloatMakePositive( vecEdge.y );
  2450. vecAbsEdge.z = FloatMakePositive( vecEdge.z );
  2451. if ( !AxisTestEdgeCrossX2( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p2, vecBoxExtents, flTolerance ) )
  2452. return false;
  2453. vecAbsEdge.x = FloatMakePositive( vecEdge.x );
  2454. if ( !AxisTestEdgeCrossY2( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p2, vecBoxExtents, flTolerance ) )
  2455. return false;
  2456. if ( !AxisTestEdgeCrossZ2( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p1, p3, vecBoxExtents, flTolerance ) )
  2457. return false;
  2458. // edge 2 (cross x,y,z)
  2459. vecEdge = p1 - p3;
  2460. vecAbsEdge.y = FloatMakePositive( vecEdge.y );
  2461. vecAbsEdge.z = FloatMakePositive( vecEdge.z );
  2462. if ( !AxisTestEdgeCrossX3( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p2, vecBoxExtents, flTolerance ) )
  2463. return false;
  2464. vecAbsEdge.x = FloatMakePositive( vecEdge.x );
  2465. if ( !AxisTestEdgeCrossY3( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p2, vecBoxExtents, flTolerance ) )
  2466. return false;
  2467. if ( !AxisTestEdgeCrossZ1( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p2, p3, vecBoxExtents, flTolerance ) )
  2468. return false;
  2469. // Test against the triangle face plane.
  2470. Vector vecMin, vecMax;
  2471. VectorSubtract( vecBoxCenter, vecBoxExtents, vecMin );
  2472. VectorAdd( vecBoxCenter, vecBoxExtents, vecMax );
  2473. if ( BoxOnPlaneSide( vecMin, vecMax, &plane ) != 3 )
  2474. return false;
  2475. return true;
  2476. }
  2477. // NOTE: JAY: This is untested code based on Real-time Collision Detection by Ericson
  2478. #if 0
  2479. Vector CalcClosestPointOnTriangle( const Vector &P, const Vector &v0, const Vector &v1, const Vector &v2 )
  2480. {
  2481. Vector e0 = v1 - v0;
  2482. Vector e1 = v2 - v0;
  2483. Vector p0 = P - v0;
  2484. // voronoi region of v0
  2485. float d1 = DotProduct( e0, p0 );
  2486. float d2 = DotProduct( e1, p0 );
  2487. if (d1 <= 0.0f && d2 <= 0.0f)
  2488. return v0;
  2489. // voronoi region of v1
  2490. Vector p1 = P - v1;
  2491. float d3 = DotProduct( e0, p1 );
  2492. float d4 = DotProduct( e1, p1 );
  2493. if (d3 >=0.0f && d4 <= d3)
  2494. return v1;
  2495. // voronoi region of e0 (v0-v1)
  2496. float ve2 = d1*d4 - d3*d2;
  2497. if ( ve2 <= 0.0f && d1 >= 0.0f && d3 <= 0.0f )
  2498. {
  2499. float v = d1 / (d1-d3);
  2500. return v0 + v * e0;
  2501. }
  2502. // voronoi region of v2
  2503. Vector p2 = P - v2;
  2504. float d5 = DotProduct( e0, p2 );
  2505. float d6 = DotProduct( e1, p2 );
  2506. if (d6 >= 0.0f && d5 <= d6)
  2507. return v2;
  2508. // voronoi region of e1
  2509. float ve1 = d5*d2 - d1*d6;
  2510. if (ve1 <= 0.0f && d2 >= 0.0f && d6 >= 0.0f)
  2511. {
  2512. float w = d2 / (d2-d6);
  2513. return v0 + w * e1;
  2514. }
  2515. // voronoi region on e2
  2516. float ve0 = d3*d6 - d5*d4;
  2517. if ( ve0 <= 0.0f && (d4-d3) >= 0.0f && (d5-d6) >= 0.0f )
  2518. {
  2519. float w = (d4-d3)/((d4-d3) + (d5-d6));
  2520. return v1 + w * (v2-v1);
  2521. }
  2522. // voronoi region of v0v1v2 triangle
  2523. float denom = 1.0f / (ve0+ve1+ve2);
  2524. float v = ve1*denom;
  2525. float w = ve2 * denom;
  2526. return v0 + e0 * v + e1 * w;
  2527. }
  2528. #endif
  2529. bool OBBHasFullyContainedIntersectionWithQuad( const Vector &vOBBExtent1_Scaled, const Vector &vOBBExtent2_Scaled, const Vector &vOBBExtent3_Scaled, const Vector &ptOBBCenter,
  2530. const Vector &vQuadNormal, float fQuadPlaneDist, const Vector &ptQuadCenter,
  2531. const Vector &vQuadExtent1_Normalized, float fQuadExtent1Length,
  2532. const Vector &vQuadExtent2_Normalized, float fQuadExtent2Length )
  2533. {
  2534. 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)
  2535. ptOBB[0] = ptOBBCenter - vOBBExtent1_Scaled - vOBBExtent2_Scaled - vOBBExtent3_Scaled;
  2536. ptOBB[1] = ptOBBCenter - vOBBExtent1_Scaled - vOBBExtent2_Scaled + vOBBExtent3_Scaled;
  2537. ptOBB[2] = ptOBBCenter - vOBBExtent1_Scaled + vOBBExtent2_Scaled + vOBBExtent3_Scaled;
  2538. ptOBB[3] = ptOBBCenter - vOBBExtent1_Scaled + vOBBExtent2_Scaled - vOBBExtent3_Scaled;
  2539. ptOBB[4] = ptOBBCenter + vOBBExtent1_Scaled - vOBBExtent2_Scaled - vOBBExtent3_Scaled;
  2540. ptOBB[5] = ptOBBCenter + vOBBExtent1_Scaled - vOBBExtent2_Scaled + vOBBExtent3_Scaled;
  2541. ptOBB[6] = ptOBBCenter + vOBBExtent1_Scaled + vOBBExtent2_Scaled + vOBBExtent3_Scaled;
  2542. ptOBB[7] = ptOBBCenter + vOBBExtent1_Scaled + vOBBExtent2_Scaled - vOBBExtent3_Scaled;
  2543. float fDists[8];
  2544. for( int i = 0; i != 8; ++i )
  2545. fDists[i] = vQuadNormal.Dot( ptOBB[i] ) - fQuadPlaneDist;
  2546. int iSides[8];
  2547. int iSideMask = 0;
  2548. for( int i = 0; i != 8; ++i )
  2549. {
  2550. if( fDists[i] > 0.0f )
  2551. {
  2552. iSides[i] = 1;
  2553. iSideMask |= 1;
  2554. }
  2555. else
  2556. {
  2557. iSides[i] = 2;
  2558. iSideMask |= 2;
  2559. }
  2560. }
  2561. if( iSideMask != 3 ) //points reside entirely on one side of the quad's plane
  2562. return false;
  2563. Vector ptPlaneIntersections[12]; //only have 12 lines, can only possibly generate 12 split points
  2564. int iPlaneIntersectionsCount = 0;
  2565. for( int i = 0; i != 8; ++i )
  2566. {
  2567. if( iSides[i] == 2 ) //point behind the plane
  2568. {
  2569. int iAxisCrossings[3];
  2570. iAxisCrossings[0] = i ^ 4; //upper 4 vs lower 4 crosses vOBBExtent1 axis
  2571. 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
  2572. 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
  2573. for( int j = 0; j != 3; ++j )
  2574. {
  2575. if( iSides[iAxisCrossings[j]] == 1 ) //point in front of the plane
  2576. {
  2577. //line between ptOBB[i] and ptOBB[iAxisCrossings[j]] intersects the plane, generate a point at the intersection for further testing
  2578. float fTotalDist = fDists[iAxisCrossings[j]] - fDists[i]; //remember that fDists[i] is a negative value
  2579. ptPlaneIntersections[iPlaneIntersectionsCount] = (ptOBB[iAxisCrossings[j]] * (-fDists[i]/fTotalDist)) + (ptOBB[i] * (fDists[iAxisCrossings[j]]/fTotalDist));
  2580. Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane
  2581. ++iPlaneIntersectionsCount;
  2582. }
  2583. }
  2584. }
  2585. }
  2586. Assert( iPlaneIntersectionsCount != 0 );
  2587. for( int i = 0; i != iPlaneIntersectionsCount; ++i )
  2588. {
  2589. //these points are guaranteed to be on the plane, now just check to see if they're within the quad's extents
  2590. Vector vToPointFromQuadCenter = ptPlaneIntersections[i] - ptQuadCenter;
  2591. float fExt1Dist = vQuadExtent1_Normalized.Dot( vToPointFromQuadCenter );
  2592. if( fabs( fExt1Dist ) > fQuadExtent1Length )
  2593. return false; //point is outside boundaries
  2594. //vToPointFromQuadCenter -= vQuadExtent1_Normalized * fExt1Dist; //to handle diamond shaped quads
  2595. float fExt2Dist = vQuadExtent2_Normalized.Dot( vToPointFromQuadCenter );
  2596. if( fabs( fExt2Dist ) > fQuadExtent2Length )
  2597. return false; //point is outside boundaries
  2598. }
  2599. 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
  2600. }
  2601. //-----------------------------------------------------------------------------
  2602. // Compute if the Ray intersects the quad plane, and whether the entire
  2603. // Ray/Quad intersection is contained within the quad itself
  2604. //
  2605. // False if no intersection exists, or if part of the intersection is
  2606. // outside the quad's extents
  2607. //-----------------------------------------------------------------------------
  2608. bool RayHasFullyContainedIntersectionWithQuad( const Ray_t &ray,
  2609. const Vector &vQuadNormal, float fQuadPlaneDist, const Vector &ptQuadCenter,
  2610. const Vector &vQuadExtent1_Normalized, float fQuadExtent1Length,
  2611. const Vector &vQuadExtent2_Normalized, float fQuadExtent2Length )
  2612. {
  2613. 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
  2614. //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
  2615. int iPlaneIntersectionsCount = 0;
  2616. if( ray.m_IsRay )
  2617. {
  2618. //just 1 line
  2619. if( ray.m_IsSwept )
  2620. {
  2621. Vector ptEndPoints[2];
  2622. ptEndPoints[0] = ray.m_Start;
  2623. ptEndPoints[1] = ptEndPoints[0] + ray.m_Delta;
  2624. int i;
  2625. float fDists[2];
  2626. for( i = 0; i != 2; ++i )
  2627. fDists[i] = vQuadNormal.Dot( ptEndPoints[i] ) - fQuadPlaneDist;
  2628. for( i = 0; i != 2; ++i )
  2629. {
  2630. if( fDists[i] <= 0.0f )
  2631. {
  2632. int j = 1-i;
  2633. if( fDists[j] >= 0.0f )
  2634. {
  2635. float fInvTotalDist = 1.0f / (fDists[j] - fDists[i]); //fDists[i] <= 0, ray is swept so no chance that the denom was 0
  2636. ptPlaneIntersections[0] = (ptEndPoints[i] * (fDists[j] * fInvTotalDist)) - (ptEndPoints[j] * (fDists[i] * fInvTotalDist)); //fDists[i] <= 0
  2637. Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane
  2638. iPlaneIntersectionsCount = 1;
  2639. }
  2640. else
  2641. {
  2642. return false;
  2643. }
  2644. break;
  2645. }
  2646. }
  2647. if( i == 2 )
  2648. return false;
  2649. }
  2650. else //not swept, so this is actually a point on quad question
  2651. {
  2652. if( fabs( vQuadNormal.Dot( ray.m_Start ) - fQuadPlaneDist ) < 1e-6 )
  2653. {
  2654. ptPlaneIntersections[0] = ray.m_Start;
  2655. iPlaneIntersectionsCount = 1;
  2656. }
  2657. else
  2658. {
  2659. return false;
  2660. }
  2661. }
  2662. }
  2663. else
  2664. {
  2665. Vector ptEndPoints[2][8];
  2666. //this specific ordering helps us web out from a point to its 3 connecting points with some bit math (most importantly, no if's)
  2667. 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;
  2668. 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;
  2669. 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;
  2670. 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;
  2671. 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;
  2672. 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;
  2673. 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;
  2674. 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;
  2675. float fDists[2][8];
  2676. int iSides[2][8];
  2677. int iSideMask[2] = { 0, 0 };
  2678. for( int i = 0; i != 8; ++i )
  2679. {
  2680. fDists[0][i] = vQuadNormal.Dot( ptEndPoints[0][i] ) - fQuadPlaneDist;
  2681. if( fDists[0][i] > 0.0f )
  2682. {
  2683. iSides[0][i] = 1;
  2684. iSideMask[0] |= 1;
  2685. }
  2686. else
  2687. {
  2688. iSides[0][i] = 2;
  2689. iSideMask[0] |= 2;
  2690. }
  2691. }
  2692. if( ray.m_IsSwept )
  2693. {
  2694. for( int i = 0; i != 8; ++i )
  2695. ptEndPoints[1][i] = ptEndPoints[0][i] + ray.m_Delta;
  2696. for( int i = 0; i != 8; ++i )
  2697. {
  2698. fDists[1][i] = vQuadNormal.Dot( ptEndPoints[1][i] ) - fQuadPlaneDist;
  2699. if( fDists[1][i] > 0.0f )
  2700. {
  2701. iSides[1][i] = 1;
  2702. iSideMask[1] |= 1;
  2703. }
  2704. else
  2705. {
  2706. iSides[1][i] = 2;
  2707. iSideMask[1] |= 2;
  2708. }
  2709. }
  2710. }
  2711. if( (iSideMask[0] | iSideMask[1]) != 3 )
  2712. {
  2713. //Assert( (iSideMask[0] | iSideMask[1]) != 2 );
  2714. return false; //all points resides entirely on one side of the quad
  2715. }
  2716. //generate intersections for boxes split by the plane at either end of the ray
  2717. for( int k = 0; k != 2; ++k )
  2718. {
  2719. if( iSideMask[k] == 3 ) //box is split by the plane
  2720. {
  2721. for( int i = 0; i != 8; ++i )
  2722. {
  2723. if( iSides[k][i] == 2 ) //point behind the plane
  2724. {
  2725. int iAxisCrossings[3];
  2726. iAxisCrossings[0] = i ^ 4; //upper 4 vs lower 4 crosses X axis
  2727. 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
  2728. 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
  2729. for( int j = 0; j != 3; ++j )
  2730. {
  2731. if( iSides[k][iAxisCrossings[j]] == 1 ) //point in front of the plane
  2732. {
  2733. //line between ptEndPoints[i] and ptEndPoints[iAxisCrossings[j]] intersects the plane, generate a point at the intersection for further testing
  2734. float fInvTotalDist = 1.0f / (fDists[k][iAxisCrossings[j]] - fDists[k][i]); //remember that fDists[k][i] is a negative value
  2735. ptPlaneIntersections[iPlaneIntersectionsCount] = (ptEndPoints[k][iAxisCrossings[j]] * (-fDists[k][i] * fInvTotalDist)) + (ptEndPoints[k][i] * (fDists[k][iAxisCrossings[j]] * fInvTotalDist));
  2736. Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane
  2737. ++iPlaneIntersectionsCount;
  2738. }
  2739. }
  2740. }
  2741. }
  2742. }
  2743. }
  2744. if( ray.m_IsSwept )
  2745. {
  2746. for( int i = 0; i != 8; ++i )
  2747. {
  2748. if( iSides[0][i] != iSides[1][i] )
  2749. {
  2750. int iPosSide, iNegSide;
  2751. if( iSides[0][i] == 1 )
  2752. {
  2753. iPosSide = 0;
  2754. iNegSide = 1;
  2755. }
  2756. else
  2757. {
  2758. iPosSide = 1;
  2759. iNegSide = 0;
  2760. }
  2761. Assert( (fDists[iPosSide][i] >= 0.0f) && (fDists[iNegSide][i] <= 0.0f) );
  2762. float fInvTotalDist = 1.0f / (fDists[iPosSide][i] - fDists[iNegSide][i]); //remember that fDists[iNegSide][i] is a negative value
  2763. ptPlaneIntersections[iPlaneIntersectionsCount] = (ptEndPoints[iPosSide][i] * (-fDists[iNegSide][i] * fInvTotalDist)) + (ptEndPoints[iNegSide][i] * (fDists[iPosSide][i] * fInvTotalDist));
  2764. Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane
  2765. ++iPlaneIntersectionsCount;
  2766. }
  2767. }
  2768. }
  2769. }
  2770. //down here, we should simply have a collection of plane intersections, now we see if they reside within the quad
  2771. Assert( iPlaneIntersectionsCount != 0 );
  2772. for( int i = 0; i != iPlaneIntersectionsCount; ++i )
  2773. {
  2774. //these points are guaranteed to be on the plane, now just check to see if they're within the quad's extents
  2775. Vector vToPointFromQuadCenter = ptPlaneIntersections[i] - ptQuadCenter;
  2776. float fExt1Dist = vQuadExtent1_Normalized.Dot( vToPointFromQuadCenter );
  2777. if( fabs( fExt1Dist ) > fQuadExtent1Length )
  2778. return false; //point is outside boundaries
  2779. //vToPointFromQuadCenter -= vQuadExtent1_Normalized * fExt1Dist; //to handle diamond shaped quads
  2780. float fExt2Dist = vQuadExtent2_Normalized.Dot( vToPointFromQuadCenter );
  2781. if( fabs( fExt2Dist ) > fQuadExtent2Length )
  2782. return false; //point is outside boundaries
  2783. }
  2784. 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
  2785. }
  2786. //-----------------------------------------------------------------------------
  2787. // Purpose: override how single player rays hit the player
  2788. //-----------------------------------------------------------------------------
  2789. bool LineCircleIntersection(const Vector2D &center,
  2790. const float radius,
  2791. const Vector2D &vLinePt,
  2792. const Vector2D &vLineDir,
  2793. float *fIntersection1,
  2794. float *fIntersection2)
  2795. {
  2796. // Line = P + Vt
  2797. // Sphere = r (assume we've translated to origin)
  2798. // (P + Vt)^2 = r^2
  2799. // VVt^2 + 2PVt + (PP - r^2)
  2800. // Solve as quadratic: (-b +/- sqrt(b^2 - 4ac)) / 2a
  2801. // If (b^2 - 4ac) is < 0 there is no solution.
  2802. // If (b^2 - 4ac) is = 0 there is one solution
  2803. // If (b^2 - 4ac) is > 0 there are two solutions.
  2804. // Translate circle to origin.
  2805. const Vector2D P( vLinePt - center );
  2806. const float a = vLineDir.Dot(vLineDir);
  2807. const float b = 2.0f * P.Dot(vLineDir);
  2808. const float c = P.Dot(P) - (radius * radius);
  2809. const float insideSqr = b*b - 4*a*c;
  2810. // No solution - (b^2 - 4ac) is < 0
  2811. if( insideSqr < -1.0e-6f )
  2812. {
  2813. return false;
  2814. }
  2815. else
  2816. {
  2817. const float sqr = (float)FastSqrt(insideSqr);
  2818. const float denom = 1.0 / (2.0f * a);
  2819. const float t0 = (-b - sqr) * denom;
  2820. const float t1 = (-b + sqr) * denom;
  2821. // One solution - (b^2 - 4ac) is = 0
  2822. if( insideSqr < 1.0e-6f )
  2823. {
  2824. // a = 0 if the line direction is the zero vector, in which case,
  2825. // the line starts inside the circle but will never exit. We fudge
  2826. // it for this case and say it intersects at the origin of the line.
  2827. // Otherwise, the result is the smallest positive result
  2828. *fIntersection1 = *fIntersection2 = ( a == 0.0f ) ? 0.0f : ( t0 < 0 ? t1 : t0 );
  2829. Assert( !IS_NAN(*fIntersection1) );
  2830. // Started inside of the sphere (the only way we get one solution, unless
  2831. // the ray direction is the zero vector)
  2832. return c < 0;
  2833. }
  2834. // Two solutions - (b^2 - 4ac) is > 0
  2835. else
  2836. {
  2837. *fIntersection1 = t0;
  2838. *fIntersection2 = t1;
  2839. }
  2840. return true;
  2841. }
  2842. }
  2843. bool IntersectRayWithAACylinder( const Ray_t &ray,
  2844. const Vector &center, float radius, float height, CBaseTrace *pTrace )
  2845. {
  2846. Assert( ray.m_IsRay );
  2847. Collision_ClearTrace( ray.m_Start, ray.m_Delta, pTrace );
  2848. // First intersect the ray with the top + bottom planes
  2849. float halfHeight = height * 0.5;
  2850. // Handle parallel case
  2851. Vector vStart = ray.m_Start - center;
  2852. Vector vEnd = vStart + ray.m_Delta;
  2853. float flEnterFrac, flLeaveFrac;
  2854. if (FloatMakePositive(ray.m_Delta.z) < 1e-8)
  2855. {
  2856. if ( (vStart.z < -halfHeight) || (vStart.z > halfHeight) )
  2857. {
  2858. return false; // no hit
  2859. }
  2860. flEnterFrac = 0.0f; flLeaveFrac = 1.0f;
  2861. }
  2862. else
  2863. {
  2864. // Clip the ray to the top and bottom of box
  2865. flEnterFrac = IntersectRayWithAAPlane( vStart, vEnd, 2, 1, halfHeight);
  2866. flLeaveFrac = IntersectRayWithAAPlane( vStart, vEnd, 2, 1, -halfHeight);
  2867. if ( flLeaveFrac < flEnterFrac )
  2868. {
  2869. float temp = flLeaveFrac;
  2870. flLeaveFrac = flEnterFrac;
  2871. flEnterFrac = temp;
  2872. }
  2873. if ( flLeaveFrac < 0 || flEnterFrac > 1)
  2874. {
  2875. return false;
  2876. }
  2877. }
  2878. // Intersect with circle
  2879. float flCircleEnterFrac, flCircleLeaveFrac;
  2880. if ( !LineCircleIntersection( vec3_origin.AsVector2D(), radius,
  2881. vStart.AsVector2D(), ray.m_Delta.AsVector2D(), &flCircleEnterFrac, &flCircleLeaveFrac ) )
  2882. {
  2883. return false; // no hit
  2884. }
  2885. Assert( flCircleEnterFrac <= flCircleLeaveFrac );
  2886. if ( flCircleLeaveFrac < 0 || flCircleEnterFrac > 1)
  2887. {
  2888. return false;
  2889. }
  2890. if ( flEnterFrac < flCircleEnterFrac )
  2891. flEnterFrac = flCircleEnterFrac;
  2892. if ( flLeaveFrac > flCircleLeaveFrac )
  2893. flLeaveFrac = flCircleLeaveFrac;
  2894. if ( flLeaveFrac < flEnterFrac )
  2895. return false;
  2896. VectorMA( ray.m_Start, flEnterFrac , ray.m_Delta, pTrace->endpos );
  2897. pTrace->fraction = flEnterFrac;
  2898. pTrace->contents = CONTENTS_SOLID;
  2899. // Calculate the point on our center line where we're nearest the intersection point
  2900. Vector collisionCenter;
  2901. CalcClosestPointOnLineSegment( pTrace->endpos, center + Vector( 0, 0, halfHeight ), center - Vector( 0, 0, halfHeight ), collisionCenter );
  2902. // Our normal is the direction from that center point to the intersection point
  2903. pTrace->plane.normal = pTrace->endpos - collisionCenter;
  2904. VectorNormalize( pTrace->plane.normal );
  2905. return true;
  2906. }
  2907. #endif // !_STATIC_LINKED || _SHARED_LIB