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.

5834 lines
157 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: Math primitives.
  4. //
  5. //===========================================================================//
  6. /// FIXME: As soon as all references to mathlib.c are gone, include it in here
  7. #include <math.h>
  8. #include <float.h> // needed for flt_epsilon
  9. #include "tier0/basetypes.h"
  10. //#include <memory.h>
  11. #include "tier0/dbg.h"
  12. #include "tier0/vprof.h"
  13. //#define _VPROF_MATHLIB
  14. #if !defined(__SPU__)
  15. #pragma warning(disable:4244) // "conversion from 'const int' to 'float', possible loss of data"
  16. #pragma warning(disable:4730) // "mixing _m64 and floating point expressions may result in incorrect code"
  17. #endif
  18. #include "mathlib/mathlib.h"
  19. #include "mathlib/vector.h"
  20. #include "mathlib/vplane.h"
  21. #if !defined(__SPU__)
  22. #include "mathlib/vmatrix.h"
  23. #endif
  24. #if !defined( _X360 )
  25. #include "sse.h"
  26. #endif
  27. #include "mathlib/ssemath.h"
  28. #include "mathlib/ssequaternion.h"
  29. // memdbgon must be the last include file in a .cpp file!!!
  30. #include "tier0/memdbgon.h"
  31. bool s_bMathlibInitialized = false;
  32. #ifdef PARANOID
  33. // User must provide an implementation of Sys_Error()
  34. void Sys_Error (char *error, ...);
  35. #endif
  36. const Vector vec3_origin(0,0,0);
  37. const QAngle vec3_angle(0,0,0);
  38. const Quaternion quat_identity(0,0,0,1);
  39. const Vector vec3_invalid( FLT_MAX, FLT_MAX, FLT_MAX );
  40. const int nanmask = 255<<23;
  41. const matrix3x4a_t g_MatrixIdentity(
  42. 1,0,0,0,
  43. 0,1,0,0,
  44. 0,0,1,0
  45. );
  46. #if !defined(__SPU__)
  47. //-----------------------------------------------------------------------------
  48. // Standard C implementations of optimized routines:
  49. //-----------------------------------------------------------------------------
  50. float _sqrtf(float _X)
  51. {
  52. Assert( s_bMathlibInitialized );
  53. return sqrtf(_X);
  54. }
  55. float _rsqrtf(float x)
  56. {
  57. Assert( s_bMathlibInitialized );
  58. return 1.f / _sqrtf( x );
  59. }
  60. #ifndef PLATFORM_PPC
  61. float VectorNormalize (Vector& vec)
  62. {
  63. #ifdef _VPROF_MATHLIB
  64. VPROF_BUDGET( "_VectorNormalize", "Mathlib" );
  65. #endif
  66. Assert( s_bMathlibInitialized );
  67. float radius = sqrtf(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z);
  68. // FLT_EPSILON is added to the radius to eliminate the possibility of divide by zero.
  69. float iradius = 1.f / ( radius + FLT_EPSILON );
  70. vec.x *= iradius;
  71. vec.y *= iradius;
  72. vec.z *= iradius;
  73. return radius;
  74. }
  75. #endif
  76. // TODO: Add fast C VectorNormalizeFast.
  77. // Perhaps use approximate rsqrt trick, if the accuracy isn't too bad.
  78. void FASTCALL _VectorNormalizeFast (Vector& vec)
  79. {
  80. Assert( s_bMathlibInitialized );
  81. // FLT_EPSILON is added to the radius to eliminate the possibility of divide by zero.
  82. float iradius = 1.f / ( sqrtf(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z) + FLT_EPSILON );
  83. vec.x *= iradius;
  84. vec.y *= iradius;
  85. vec.z *= iradius;
  86. }
  87. float _InvRSquared(const float* v)
  88. {
  89. Assert( s_bMathlibInitialized );
  90. float r2 = DotProduct(v, v);
  91. return r2 < 1.f ? 1.f : 1/r2;
  92. }
  93. #if !defined(__SPU__)
  94. //-----------------------------------------------------------------------------
  95. // Function pointers selecting the appropriate implementation
  96. //-----------------------------------------------------------------------------
  97. void (FASTCALL *pfVectorNormalizeFast)(Vector& v) = _VectorNormalizeFast;
  98. float SinCosTable[SIN_TABLE_SIZE];
  99. void InitSinCosTable()
  100. {
  101. for( int i = 0; i < SIN_TABLE_SIZE; i++ )
  102. {
  103. SinCosTable[i] = sin(i * 2.0 * M_PI / SIN_TABLE_SIZE);
  104. }
  105. }
  106. #endif // !defined(__SPU__)
  107. qboolean VectorsEqual( const float *v1, const float *v2 )
  108. {
  109. Assert( s_bMathlibInitialized );
  110. return ( ( v1[0] == v2[0] ) &&
  111. ( v1[1] == v2[1] ) &&
  112. ( v1[2] == v2[2] ) );
  113. }
  114. #endif // #if !defined(__SPU__)
  115. //-----------------------------------------------------------------------------
  116. // Purpose: Generates Euler angles given a left-handed orientation matrix. The
  117. // columns of the matrix contain the forward, left, and up vectors.
  118. // Input : matrix - Left-handed orientation matrix.
  119. // angles[PITCH, YAW, ROLL]. Receives right-handed counterclockwise
  120. // rotations in degrees around Y, Z, and X respectively.
  121. //-----------------------------------------------------------------------------
  122. void MatrixAngles( const matrix3x4_t& matrix, RadianEuler &angles, Vector &position )
  123. {
  124. MatrixGetColumn( matrix, 3, position );
  125. MatrixAngles( matrix, angles );
  126. }
  127. void MatrixAngles( const matrix3x4_t &matrix, Quaternion &q, Vector &pos )
  128. {
  129. #ifdef _VPROF_MATHLIB
  130. VPROF_BUDGET( "MatrixQuaternion", "Mathlib" );
  131. #endif
  132. float trace;
  133. trace = matrix[0][0] + matrix[1][1] + matrix[2][2] + 1.0f;
  134. if( trace > 1.0f + FLT_EPSILON )
  135. {
  136. // VPROF_INCREMENT_COUNTER("MatrixQuaternion A",1);
  137. q.x = ( matrix[2][1] - matrix[1][2] );
  138. q.y = ( matrix[0][2] - matrix[2][0] );
  139. q.z = ( matrix[1][0] - matrix[0][1] );
  140. q.w = trace;
  141. }
  142. else if ( matrix[0][0] > matrix[1][1] && matrix[0][0] > matrix[2][2] )
  143. {
  144. // VPROF_INCREMENT_COUNTER("MatrixQuaternion B",1);
  145. trace = 1.0f + matrix[0][0] - matrix[1][1] - matrix[2][2];
  146. q.x = trace;
  147. q.y = (matrix[1][0] + matrix[0][1] );
  148. q.z = (matrix[0][2] + matrix[2][0] );
  149. q.w = (matrix[2][1] - matrix[1][2] );
  150. }
  151. else if (matrix[1][1] > matrix[2][2])
  152. {
  153. // VPROF_INCREMENT_COUNTER("MatrixQuaternion C",1);
  154. trace = 1.0f + matrix[1][1] - matrix[0][0] - matrix[2][2];
  155. q.x = (matrix[0][1] + matrix[1][0] );
  156. q.y = trace;
  157. q.z = (matrix[2][1] + matrix[1][2] );
  158. q.w = (matrix[0][2] - matrix[2][0] );
  159. }
  160. else
  161. {
  162. // VPROF_INCREMENT_COUNTER("MatrixQuaternion D",1);
  163. trace = 1.0f + matrix[2][2] - matrix[0][0] - matrix[1][1];
  164. q.x = (matrix[0][2] + matrix[2][0] );
  165. q.y = (matrix[2][1] + matrix[1][2] );
  166. q.z = trace;
  167. q.w = (matrix[1][0] - matrix[0][1] );
  168. }
  169. QuaternionNormalize( q );
  170. #if 0
  171. // check against the angle version
  172. RadianEuler ang;
  173. MatrixAngles( matrix, ang );
  174. Quaternion test;
  175. AngleQuaternion( ang, test );
  176. float d = QuaternionDotProduct( q, test );
  177. Assert( fabs(d) > 0.99 && fabs(d) < 1.01 );
  178. #endif
  179. MatrixGetColumn( matrix, 3, pos );
  180. }
  181. void MatrixAngles( const matrix3x4_t& matrix, float *angles )
  182. {
  183. #ifdef _VPROF_MATHLIB
  184. VPROF_BUDGET( "MatrixAngles", "Mathlib" );
  185. #endif
  186. Assert( s_bMathlibInitialized );
  187. float forward[3];
  188. float left[3];
  189. float up[3];
  190. //
  191. // Extract the basis vectors from the matrix. Since we only need the Z
  192. // component of the up vector, we don't get X and Y.
  193. //
  194. forward[0] = matrix[0][0];
  195. forward[1] = matrix[1][0];
  196. forward[2] = matrix[2][0];
  197. left[0] = matrix[0][1];
  198. left[1] = matrix[1][1];
  199. left[2] = matrix[2][1];
  200. up[2] = matrix[2][2];
  201. float xyDist = sqrtf( forward[0] * forward[0] + forward[1] * forward[1] );
  202. // enough here to get angles?
  203. if ( xyDist > 0.001f )
  204. {
  205. // (yaw) y = ATAN( forward.y, forward.x ); -- in our space, forward is the X axis
  206. angles[1] = RAD2DEG( atan2f( forward[1], forward[0] ) );
  207. // (pitch) x = ATAN( -forward.z, sqrt(forward.x*forward.x+forward.y*forward.y) );
  208. angles[0] = RAD2DEG( atan2f( -forward[2], xyDist ) );
  209. // (roll) z = ATAN( left.z, up.z );
  210. angles[2] = RAD2DEG( atan2f( left[2], up[2] ) );
  211. }
  212. else // forward is mostly Z, gimbal lock-
  213. {
  214. // (yaw) y = ATAN( -left.x, left.y ); -- forward is mostly z, so use right for yaw
  215. angles[1] = RAD2DEG( atan2f( -left[0], left[1] ) );
  216. // (pitch) x = ATAN( -forward.z, sqrt(forward.x*forward.x+forward.y*forward.y) );
  217. angles[0] = RAD2DEG( atan2f( -forward[2], xyDist ) );
  218. // Assume no roll in this case as one degree of freedom has been lost (i.e. yaw == roll)
  219. angles[2] = 0;
  220. }
  221. }
  222. Vector MatrixNormalize( const matrix3x4_t &in, matrix3x4_t &out )
  223. {
  224. Vector vScale;
  225. vScale.x = sqrt( in[ 0 ][ 0 ] * in[ 0 ][ 0 ] + in[ 1 ][ 0 ] * in[ 1 ][ 0 ] + in[ 2 ][ 0 ] * in[ 2 ][ 0 ] );
  226. vScale.y = sqrt( in[ 0 ][ 1 ] * in[ 0 ][ 1 ] + in[ 1 ][ 1 ] * in[ 1 ][ 1 ] + in[ 2 ][ 1 ] * in[ 2 ][ 1 ] );
  227. vScale.z = sqrt( in[ 0 ][ 2 ] * in[ 0 ][ 2 ] + in[ 1 ][ 2 ] * in[ 1 ][ 2 ] + in[ 2 ][ 2 ] * in[ 2 ][ 2 ] );
  228. matrix3x4_t norm;
  229. float flInvScaleX = 1.0f / vScale.x;
  230. float flInvScaleY = 1.0f / vScale.y;
  231. float flInvScaleZ = 1.0f / vScale.z;
  232. out[ 0 ][ 0 ] = in[ 0 ][ 0 ] * flInvScaleX; out[ 1 ][ 0 ] = in[ 1 ][ 0 ] * flInvScaleX; out[ 2 ][ 0 ] = in[ 2 ][ 0 ] * flInvScaleX;
  233. out[ 0 ][ 1 ] = in[ 0 ][ 1 ] * flInvScaleY; out[ 1 ][ 1 ] = in[ 1 ][ 1 ] * flInvScaleY; out[ 2 ][ 1 ] = in[ 2 ][ 1 ] * flInvScaleY;
  234. out[ 0 ][ 2 ] = in[ 0 ][ 2 ] * flInvScaleZ; out[ 1 ][ 2 ] = in[ 1 ][ 2 ] * flInvScaleZ; out[ 2 ][ 2 ] = in[ 2 ][ 2 ] * flInvScaleZ;
  235. out[ 0 ][ 3 ] = in[ 0 ][ 3 ]; out[ 1 ][ 3 ] = in[ 1 ][ 3 ]; out[ 2 ][ 3 ] = in[ 2 ][ 3 ];
  236. return vScale;
  237. }
  238. #if !defined(__SPU__)
  239. // transform in1 by the matrix in2
  240. void VectorTransform (const float * RESTRICT in1, const matrix3x4_t& in2, float * RESTRICT out)
  241. {
  242. Assert( s_bMathlibInitialized );
  243. float x = DotProduct(in1, in2[0]) + in2[0][3];
  244. float y = DotProduct(in1, in2[1]) + in2[1][3];
  245. float z = DotProduct(in1, in2[2]) + in2[2][3];
  246. out[ 0 ] = x;
  247. out[ 1 ] = y;
  248. out[ 2 ] = z;
  249. }
  250. // assuming the matrix is orthonormal, transform in1 by the transpose (also the inverse in this case) of in2.
  251. void VectorITransform (const float *in1, const matrix3x4_t& in2, float *out)
  252. {
  253. Assert( s_bMathlibInitialized );
  254. float in1t[3];
  255. in1t[0] = in1[0] - in2[0][3];
  256. in1t[1] = in1[1] - in2[1][3];
  257. in1t[2] = in1[2] - in2[2][3];
  258. float x = in1t[0] * in2[0][0] + in1t[1] * in2[1][0] + in1t[2] * in2[2][0];
  259. float y = in1t[0] * in2[0][1] + in1t[1] * in2[1][1] + in1t[2] * in2[2][1];
  260. float z = in1t[0] * in2[0][2] + in1t[1] * in2[1][2] + in1t[2] * in2[2][2];
  261. out[ 0 ] = x;
  262. out[ 1 ] = y;
  263. out[ 2 ] = z;
  264. }
  265. #endif // #if !defined(__SPU__)
  266. // assume in2 is a rotation and rotate the input vector
  267. void VectorRotate( const float * RESTRICT in1, const matrix3x4_t& in2, float * RESTRICT out )
  268. {
  269. Assert( s_bMathlibInitialized );
  270. float x = DotProduct( in1, in2[ 0 ] );
  271. float y = DotProduct( in1, in2[ 1 ] );
  272. float z = DotProduct( in1, in2[ 2 ] );
  273. out[ 0 ] = x;
  274. out[ 1 ] = y;
  275. out[ 2 ] = z;
  276. }
  277. #if !defined(__SPU__)
  278. // assume in2 is a rotation and rotate the input vector
  279. void VectorRotate( const Vector &in1, const QAngle &in2, Vector &out )
  280. {
  281. matrix3x4_t matRotate;
  282. AngleMatrix( in2, matRotate );
  283. VectorRotate( in1, matRotate, out );
  284. }
  285. // assume in2 is a rotation and rotate the input vector
  286. void VectorRotate( const Vector &in1, const Quaternion &in2, Vector &out )
  287. {
  288. #if WE_WANT_OUR_CODE_TO_BE_POINTLESSLY_SLOW
  289. matrix3x4_t matRotate;
  290. QuaternionMatrix( in2, matRotate );
  291. VectorRotate( in1, matRotate, out );
  292. #else
  293. // rotation is q * v * q^-1
  294. Quaternion conjugate = in2.Conjugate();
  295. // do the rotation as unrolled flop code ( QuaternionMult is a function call, which murders instruction scheduling )
  296. // first q*v
  297. Quaternion temp;
  298. temp.x = in2.y * in1.z - in2.z * in1.y + in2.w * in1.x;
  299. temp.y = -in2.x * in1.z + in2.z * in1.x + in2.w * in1.y;
  300. temp.z = in2.x * in1.y - in2.y * in1.x + in2.w * in1.z;
  301. temp.w = -in2.x * in1.x - in2.y * in1.y - in2.z * in1.z;
  302. // now (qv)(q*)
  303. out.x = temp.x * conjugate.w + temp.y * conjugate.z - temp.z * conjugate.y + temp.w * conjugate.x;
  304. out.y = -temp.x * conjugate.z + temp.y * conjugate.w + temp.z * conjugate.x + temp.w * conjugate.y;
  305. out.z = temp.x * conjugate.y - temp.y * conjugate.x + temp.z * conjugate.w + temp.w * conjugate.z;
  306. Assert( fabs(-temp.x * conjugate.x - temp.y * conjugate.y - temp.z * conjugate.z + temp.w * conjugate.w) < 0.0001 );
  307. #endif
  308. }
  309. // rotate by the inverse of the matrix
  310. void VectorIRotate( const float * RESTRICT in1, const matrix3x4_t& in2, float * RESTRICT out )
  311. {
  312. Assert( s_bMathlibInitialized );
  313. Assert( in1 != out );
  314. out[0] = in1[0]*in2[0][0] + in1[1]*in2[1][0] + in1[2]*in2[2][0];
  315. out[1] = in1[0]*in2[0][1] + in1[1]*in2[1][1] + in1[2]*in2[2][1];
  316. out[2] = in1[0]*in2[0][2] + in1[1]*in2[1][2] + in1[2]*in2[2][2];
  317. }
  318. #ifndef VECTOR_NO_SLOW_OPERATIONS
  319. // transform a set of angles in the output space of parentMatrix to the input space
  320. QAngle TransformAnglesToLocalSpace( const QAngle &angles, const matrix3x4_t &parentMatrix )
  321. {
  322. matrix3x4_t angToWorld, worldToParent, localMatrix;
  323. MatrixInvert( parentMatrix, worldToParent );
  324. AngleMatrix( angles, angToWorld );
  325. ConcatTransforms( worldToParent, angToWorld, localMatrix );
  326. QAngle out;
  327. MatrixAngles( localMatrix, out );
  328. return out;
  329. }
  330. // transform a set of angles in the input space of parentMatrix to the output space
  331. QAngle TransformAnglesToWorldSpace( const QAngle &angles, const matrix3x4_t &parentMatrix )
  332. {
  333. matrix3x4_t angToParent, angToWorld;
  334. AngleMatrix( angles, angToParent );
  335. ConcatTransforms( parentMatrix, angToParent, angToWorld );
  336. QAngle out;
  337. MatrixAngles( angToWorld, out );
  338. return out;
  339. }
  340. #endif // VECTOR_NO_SLOW_OPERATIONS
  341. void MatrixInitialize( matrix3x4_t &mat, const Vector &vecOrigin, const Vector &vecXAxis, const Vector &vecYAxis, const Vector &vecZAxis )
  342. {
  343. MatrixSetColumn( vecXAxis, 0, mat );
  344. MatrixSetColumn( vecYAxis, 1, mat );
  345. MatrixSetColumn( vecZAxis, 2, mat );
  346. MatrixSetColumn( vecOrigin, 3, mat );
  347. }
  348. void MatrixCopy( const matrix3x4_t& in, matrix3x4_t& out )
  349. {
  350. Assert( s_bMathlibInitialized );
  351. memcpy( out.Base(), in.Base(), sizeof( float ) * 3 * 4 );
  352. }
  353. //-----------------------------------------------------------------------------
  354. // Matrix equality test
  355. //-----------------------------------------------------------------------------
  356. bool MatricesAreEqual( const matrix3x4_t &src1, const matrix3x4_t &src2, float flTolerance )
  357. {
  358. for ( int i = 0; i < 3; ++i )
  359. {
  360. for ( int j = 0; j < 4; ++j )
  361. {
  362. if ( fabs( src1[i][j] - src2[i][j] ) > flTolerance )
  363. return false;
  364. }
  365. }
  366. return true;
  367. }
  368. #endif // #if !defined(__SPU__)
  369. // NOTE: This is just the transpose not a general inverse
  370. void MatrixInvert( const matrix3x4_t& in, matrix3x4_t& out )
  371. {
  372. Assert( s_bMathlibInitialized );
  373. if ( &in == &out )
  374. {
  375. V_swap(out[0][1],out[1][0]);
  376. V_swap(out[0][2],out[2][0]);
  377. V_swap(out[1][2],out[2][1]);
  378. }
  379. else
  380. {
  381. // transpose the matrix
  382. out[0][0] = in[0][0];
  383. out[0][1] = in[1][0];
  384. out[0][2] = in[2][0];
  385. out[1][0] = in[0][1];
  386. out[1][1] = in[1][1];
  387. out[1][2] = in[2][1];
  388. out[2][0] = in[0][2];
  389. out[2][1] = in[1][2];
  390. out[2][2] = in[2][2];
  391. }
  392. // now fix up the translation to be in the other space
  393. float tmp[3];
  394. tmp[0] = in[0][3];
  395. tmp[1] = in[1][3];
  396. tmp[2] = in[2][3];
  397. out[0][3] = -DotProduct( tmp, out[0] );
  398. out[1][3] = -DotProduct( tmp, out[1] );
  399. out[2][3] = -DotProduct( tmp, out[2] );
  400. }
  401. void MatrixGetColumn( const matrix3x4_t& in, int column, Vector &out )
  402. {
  403. out.x = in[0][column];
  404. out.y = in[1][column];
  405. out.z = in[2][column];
  406. }
  407. void MatrixSetColumn( const Vector &in, int column, matrix3x4_t& out )
  408. {
  409. out[0][column] = in.x;
  410. out[1][column] = in.y;
  411. out[2][column] = in.z;
  412. }
  413. #if !defined(__SPU__)
  414. int VectorCompare (const float *v1, const float *v2)
  415. {
  416. Assert( s_bMathlibInitialized );
  417. int i;
  418. for (i=0 ; i<3 ; i++)
  419. if (v1[i] != v2[i])
  420. return 0;
  421. return 1;
  422. }
  423. void CrossProduct (const float* v1, const float* v2, float* cross)
  424. {
  425. Assert( s_bMathlibInitialized );
  426. Assert( v1 != cross );
  427. Assert( v2 != cross );
  428. cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
  429. cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
  430. cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
  431. }
  432. size_t Q_log2( unsigned int val )
  433. {
  434. #ifdef _X360 // use hardware
  435. // both zero and one return zero (per old implementation)
  436. return ( val == 0 ) ? 0 : 31 - _CountLeadingZeros( val );
  437. #else // use N. Compoop's algorithm ( inherited from days of yore )
  438. int answer=0;
  439. while (val>>=1)
  440. answer++;
  441. return answer;
  442. #endif
  443. }
  444. // Matrix is right-handed x=forward, y=left, z=up. We a left-handed convention for vectors in the game code (forward, right, up)
  445. void MatrixVectorsFLU( const matrix3x4_t &matrix, Vector* pForward, Vector *pLeft, Vector *pUp )
  446. {
  447. MatrixGetColumn( matrix, FORWARD_AXIS, *pForward );
  448. MatrixGetColumn( matrix, LEFT_AXIS, *pLeft );
  449. MatrixGetColumn( matrix, UP_AXIS, *pUp );
  450. }
  451. // Matrix is right-handed x=forward, y=left, z=up. We a left-handed convention for vectors in the game code (forward, right, up)
  452. void MatrixVectors( const matrix3x4_t &matrix, Vector* pForward, Vector *pRight, Vector *pUp )
  453. {
  454. MatrixGetColumn( matrix, 0, *pForward );
  455. MatrixGetColumn( matrix, 1, *pRight );
  456. MatrixGetColumn( matrix, 2, *pUp );
  457. *pRight *= -1.0f;
  458. }
  459. void VectorVectors( const Vector &forward, Vector &right, Vector &up )
  460. {
  461. Assert( s_bMathlibInitialized );
  462. Vector tmp;
  463. if ( fabs( forward[0] ) < 1e-6 && fabs( forward[1] ) < 1e-6 )
  464. {
  465. // pitch 90 degrees up/down from identity
  466. right[0] = 0;
  467. right[1] = -1;
  468. right[2] = 0;
  469. up[0] = -forward[2];
  470. up[1] = 0;
  471. up[2] = 0;
  472. }
  473. else
  474. {
  475. tmp[0] = 0; tmp[1] = 0; tmp[2] = 1.0;
  476. CrossProduct( forward, tmp, right );
  477. VectorNormalize( right );
  478. CrossProduct( right, forward, up );
  479. VectorNormalize( up );
  480. }
  481. }
  482. void VectorMatrix( const Vector &forward, matrix3x4_t& matrix)
  483. {
  484. Assert( s_bMathlibInitialized );
  485. Vector right, up;
  486. VectorVectors(forward, right, up);
  487. MatrixSetColumn( forward, 0, matrix );
  488. MatrixSetColumn( -right, 1, matrix );
  489. MatrixSetColumn( up, 2, matrix );
  490. }
  491. void VectorPerpendicularToVector( Vector const &in, Vector *pvecOut )
  492. {
  493. float flY = in.y * in.y;
  494. pvecOut->x = RemapVal( flY, 0, 1, in.z, 1 );
  495. pvecOut->y = 0;
  496. pvecOut->z = -in.x;
  497. pvecOut->NormalizeInPlace();
  498. float flDot = DotProduct( *pvecOut, in );
  499. *pvecOut -= flDot * in;
  500. pvecOut->NormalizeInPlace();
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Euler QAngle -> Basis Vectors. Each vector is optional
  504. //-----------------------------------------------------------------------------
  505. void AngleVectorsFLU( const QAngle &angles, Vector *pForward, Vector *pLeft, Vector *pUp )
  506. {
  507. Assert( s_bMathlibInitialized );
  508. float sr, sp, sy, cr, cp, cy;
  509. #ifdef _X360
  510. fltx4 radians, scale, sine, cosine;
  511. radians = LoadUnaligned3SIMD( angles.Base() );
  512. scale = ReplicateX4( M_PI_F / 180.f );
  513. radians = MulSIMD( radians, scale );
  514. SinCos3SIMD( sine, cosine, radians );
  515. sp = SubFloat( sine, 0 ); sy = SubFloat( sine, 1 ); sr = SubFloat( sine, 2 );
  516. cp = SubFloat( cosine, 0 ); cy = SubFloat( cosine, 1 ); cr = SubFloat( cosine, 2 );
  517. #else
  518. SinCos( DEG2RAD( angles[YAW] ), &sy, &cy );
  519. SinCos( DEG2RAD( angles[PITCH] ), &sp, &cp );
  520. SinCos( DEG2RAD( angles[ROLL] ), &sr, &cr );
  521. #endif
  522. if ( pForward )
  523. {
  524. (*pForward)[FORWARD_AXIS] = cp*cy;
  525. (*pForward)[LEFT_AXIS] = cp*sy;
  526. (*pForward)[UP_AXIS] = -sp;
  527. }
  528. if ( pLeft )
  529. {
  530. (*pLeft)[FORWARD_AXIS] = (sr*sp*cy+cr*-sy);
  531. (*pLeft)[LEFT_AXIS] = (sr*sp*sy+cr*cy);
  532. (*pLeft)[UP_AXIS] = sr*cp;
  533. }
  534. if ( pUp )
  535. {
  536. (*pUp)[FORWARD_AXIS] = (cr*sp*cy+-sr*-sy);
  537. (*pUp)[LEFT_AXIS] = (cr*sp*sy+-sr*cy);
  538. (*pUp)[UP_AXIS] = cr*cp;
  539. }
  540. }
  541. void VectorAngles( const float *forward, float *angles )
  542. {
  543. Assert( s_bMathlibInitialized );
  544. float tmp, yaw, pitch;
  545. if (forward[1] == 0 && forward[0] == 0)
  546. {
  547. yaw = 0;
  548. if (forward[2] > 0)
  549. pitch = 270;
  550. else
  551. pitch = 90;
  552. }
  553. else
  554. {
  555. yaw = (atan2(forward[1], forward[0]) * 180 / M_PI);
  556. if (yaw < 0)
  557. yaw += 360;
  558. tmp = sqrt (forward[0]*forward[0] + forward[1]*forward[1]);
  559. pitch = (atan2(-forward[2], tmp) * 180 / M_PI);
  560. if (pitch < 0)
  561. pitch += 360;
  562. }
  563. angles[0] = pitch;
  564. angles[1] = yaw;
  565. angles[2] = 0;
  566. }
  567. /*
  568. ================
  569. R_ConcatRotations
  570. ================
  571. */
  572. void ConcatRotations (const float in1[3][3], const float in2[3][3], float out[3][3])
  573. {
  574. Assert( s_bMathlibInitialized );
  575. Assert( in1 != out );
  576. Assert( in2 != out );
  577. out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
  578. in1[0][2] * in2[2][0];
  579. out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
  580. in1[0][2] * in2[2][1];
  581. out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
  582. in1[0][2] * in2[2][2];
  583. out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
  584. in1[1][2] * in2[2][0];
  585. out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
  586. in1[1][2] * in2[2][1];
  587. out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
  588. in1[1][2] * in2[2][2];
  589. out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
  590. in1[2][2] * in2[2][0];
  591. out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
  592. in1[2][2] * in2[2][1];
  593. out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
  594. in1[2][2] * in2[2][2];
  595. }
  596. #endif // #if !defined(__SPU__)
  597. void ConcatTransforms_Aligned( const matrix3x4a_t &m0, const matrix3x4a_t &m1, matrix3x4a_t &out )
  598. {
  599. AssertAligned( &m0 );
  600. AssertAligned( &m1 );
  601. AssertAligned( &out );
  602. fltx4 lastMask = *(fltx4 *)(&g_SIMD_ComponentMask[3]);
  603. fltx4 rowA0 = LoadAlignedSIMD( m0.m_flMatVal[0] );
  604. fltx4 rowA1 = LoadAlignedSIMD( m0.m_flMatVal[1] );
  605. fltx4 rowA2 = LoadAlignedSIMD( m0.m_flMatVal[2] );
  606. fltx4 rowB0 = LoadAlignedSIMD( m1.m_flMatVal[0] );
  607. fltx4 rowB1 = LoadAlignedSIMD( m1.m_flMatVal[1] );
  608. fltx4 rowB2 = LoadAlignedSIMD( m1.m_flMatVal[2] );
  609. // now we have the rows of m0 and the columns of m1
  610. // first output row
  611. fltx4 A0 = SplatXSIMD(rowA0);
  612. fltx4 A1 = SplatYSIMD(rowA0);
  613. fltx4 A2 = SplatZSIMD(rowA0);
  614. fltx4 mul00 = MulSIMD( A0, rowB0 );
  615. fltx4 mul01 = MulSIMD( A1, rowB1 );
  616. fltx4 mul02 = MulSIMD( A2, rowB2 );
  617. fltx4 out0 = AddSIMD( mul00, AddSIMD(mul01,mul02) );
  618. // second output row
  619. A0 = SplatXSIMD(rowA1);
  620. A1 = SplatYSIMD(rowA1);
  621. A2 = SplatZSIMD(rowA1);
  622. fltx4 mul10 = MulSIMD( A0, rowB0 );
  623. fltx4 mul11 = MulSIMD( A1, rowB1 );
  624. fltx4 mul12 = MulSIMD( A2, rowB2 );
  625. fltx4 out1 = AddSIMD( mul10, AddSIMD(mul11,mul12) );
  626. // third output row
  627. A0 = SplatXSIMD(rowA2);
  628. A1 = SplatYSIMD(rowA2);
  629. A2 = SplatZSIMD(rowA2);
  630. fltx4 mul20 = MulSIMD( A0, rowB0 );
  631. fltx4 mul21 = MulSIMD( A1, rowB1 );
  632. fltx4 mul22 = MulSIMD( A2, rowB2 );
  633. fltx4 out2 = AddSIMD( mul20, AddSIMD(mul21,mul22) );
  634. // add in translation vector
  635. A0 = AndSIMD(rowA0,lastMask);
  636. A1 = AndSIMD(rowA1,lastMask);
  637. A2 = AndSIMD(rowA2,lastMask);
  638. out0 = AddSIMD(out0, A0);
  639. out1 = AddSIMD(out1, A1);
  640. out2 = AddSIMD(out2, A2);
  641. StoreAlignedSIMD( out.m_flMatVal[0], out0 );
  642. StoreAlignedSIMD( out.m_flMatVal[1], out1 );
  643. StoreAlignedSIMD( out.m_flMatVal[2], out2 );
  644. }
  645. /*
  646. ================
  647. R_ConcatTransforms
  648. ================
  649. */
  650. void ConcatTransforms (const matrix3x4_t& in1, const matrix3x4_t& in2, matrix3x4_t& out)
  651. {
  652. #if 0
  653. // test for ones that'll be 2x faster
  654. if ( (((size_t)&in1) % 16) == 0 && (((size_t)&in2) % 16) == 0 && (((size_t)&out) % 16) == 0 )
  655. {
  656. ConcatTransforms_Aligned( in1, in2, out );
  657. return;
  658. }
  659. #endif
  660. fltx4 lastMask = *(fltx4 *)(&g_SIMD_ComponentMask[3]);
  661. fltx4 rowA0 = LoadUnalignedSIMD( in1.m_flMatVal[0] );
  662. fltx4 rowA1 = LoadUnalignedSIMD( in1.m_flMatVal[1] );
  663. fltx4 rowA2 = LoadUnalignedSIMD( in1.m_flMatVal[2] );
  664. fltx4 rowB0 = LoadUnalignedSIMD( in2.m_flMatVal[0] );
  665. fltx4 rowB1 = LoadUnalignedSIMD( in2.m_flMatVal[1] );
  666. fltx4 rowB2 = LoadUnalignedSIMD( in2.m_flMatVal[2] );
  667. // now we have the rows of m0 and the columns of m1
  668. // first output row
  669. fltx4 A0 = SplatXSIMD(rowA0);
  670. fltx4 A1 = SplatYSIMD(rowA0);
  671. fltx4 A2 = SplatZSIMD(rowA0);
  672. fltx4 mul00 = MulSIMD( A0, rowB0 );
  673. fltx4 mul01 = MulSIMD( A1, rowB1 );
  674. fltx4 mul02 = MulSIMD( A2, rowB2 );
  675. fltx4 out0 = AddSIMD( mul00, AddSIMD(mul01,mul02) );
  676. // second output row
  677. A0 = SplatXSIMD(rowA1);
  678. A1 = SplatYSIMD(rowA1);
  679. A2 = SplatZSIMD(rowA1);
  680. fltx4 mul10 = MulSIMD( A0, rowB0 );
  681. fltx4 mul11 = MulSIMD( A1, rowB1 );
  682. fltx4 mul12 = MulSIMD( A2, rowB2 );
  683. fltx4 out1 = AddSIMD( mul10, AddSIMD(mul11,mul12) );
  684. // third output row
  685. A0 = SplatXSIMD(rowA2);
  686. A1 = SplatYSIMD(rowA2);
  687. A2 = SplatZSIMD(rowA2);
  688. fltx4 mul20 = MulSIMD( A0, rowB0 );
  689. fltx4 mul21 = MulSIMD( A1, rowB1 );
  690. fltx4 mul22 = MulSIMD( A2, rowB2 );
  691. fltx4 out2 = AddSIMD( mul20, AddSIMD(mul21,mul22) );
  692. // add in translation vector
  693. A0 = AndSIMD(rowA0,lastMask);
  694. A1 = AndSIMD(rowA1,lastMask);
  695. A2 = AndSIMD(rowA2,lastMask);
  696. out0 = AddSIMD(out0, A0);
  697. out1 = AddSIMD(out1, A1);
  698. out2 = AddSIMD(out2, A2);
  699. // write to output
  700. StoreUnalignedSIMD( out.m_flMatVal[0], out0 );
  701. StoreUnalignedSIMD( out.m_flMatVal[1], out1 );
  702. StoreUnalignedSIMD( out.m_flMatVal[2], out2 );
  703. }
  704. /*
  705. ===================
  706. FloorDivMod
  707. Returns mathematically correct (floor-based) quotient and remainder for
  708. numer and denom, both of which should contain no fractional part. The
  709. quotient must fit in 32 bits.
  710. ====================
  711. */
  712. #if !defined(__SPU__)
  713. void FloorDivMod (double numer, double denom, int *quotient,
  714. int *rem)
  715. {
  716. Assert( s_bMathlibInitialized );
  717. int q, r;
  718. double x;
  719. #ifdef PARANOID
  720. if (denom <= 0.0)
  721. Sys_Error ("FloorDivMod: bad denominator %d\n", denom);
  722. // if ((floor(numer) != numer) || (floor(denom) != denom))
  723. // Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n",
  724. // numer, denom);
  725. #endif
  726. if (numer >= 0.0)
  727. {
  728. x = floor(numer / denom);
  729. q = (int)x;
  730. r = Floor2Int(numer - (x * denom));
  731. }
  732. else
  733. {
  734. //
  735. // perform operations with positive values, and fix mod to make floor-based
  736. //
  737. x = floor(-numer / denom);
  738. q = -(int)x;
  739. r = Floor2Int(-numer - (x * denom));
  740. if (r != 0)
  741. {
  742. q--;
  743. r = (int)denom - r;
  744. }
  745. }
  746. *quotient = q;
  747. *rem = r;
  748. }
  749. /*
  750. ===================
  751. GreatestCommonDivisor
  752. ====================
  753. */
  754. int GreatestCommonDivisor (int i1, int i2)
  755. {
  756. Assert( s_bMathlibInitialized );
  757. if (i1 > i2)
  758. {
  759. if (i2 == 0)
  760. return (i1);
  761. return GreatestCommonDivisor (i2, i1 % i2);
  762. }
  763. else
  764. {
  765. if (i1 == 0)
  766. return (i2);
  767. return GreatestCommonDivisor (i1, i2 % i1);
  768. }
  769. }
  770. bool IsDenormal( const float &val )
  771. {
  772. const int x = *reinterpret_cast <const int *> (&val); // needs 32-bit int
  773. const int abs_mantissa = x & 0x007FFFFF;
  774. const int biased_exponent = x & 0x7F800000;
  775. return ( biased_exponent == 0 && abs_mantissa != 0 );
  776. }
  777. int SignbitsForPlane (cplane_t *out)
  778. {
  779. Assert( s_bMathlibInitialized );
  780. int bits, j;
  781. // for fast box on planeside test
  782. bits = 0;
  783. for (j=0 ; j<3 ; j++)
  784. {
  785. if (out->normal[j] < 0)
  786. bits |= 1<<j;
  787. }
  788. return bits;
  789. }
  790. /*
  791. ==================
  792. BoxOnPlaneSide
  793. Returns 1, 2, or 1 + 2
  794. ==================
  795. */
  796. int __cdecl BoxOnPlaneSide (const float *emins, const float *emaxs, const cplane_t *p)
  797. {
  798. Assert( s_bMathlibInitialized );
  799. float dist1, dist2;
  800. int sides;
  801. // fast axial cases
  802. if (p->type < 3)
  803. {
  804. if (p->dist <= emins[p->type])
  805. return 1;
  806. if (p->dist >= emaxs[p->type])
  807. return 2;
  808. return 3;
  809. }
  810. // general case
  811. switch (p->signbits)
  812. {
  813. case 0:
  814. dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
  815. dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
  816. break;
  817. case 1:
  818. dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
  819. dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
  820. break;
  821. case 2:
  822. dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
  823. dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
  824. break;
  825. case 3:
  826. dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
  827. dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
  828. break;
  829. case 4:
  830. dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
  831. dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
  832. break;
  833. case 5:
  834. dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
  835. dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
  836. break;
  837. case 6:
  838. dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
  839. dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
  840. break;
  841. case 7:
  842. dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
  843. dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
  844. break;
  845. default:
  846. dist1 = dist2 = 0; // shut up compiler
  847. Assert( 0 );
  848. break;
  849. }
  850. sides = 0;
  851. if (dist1 >= p->dist)
  852. sides = 1;
  853. if (dist2 < p->dist)
  854. sides |= 2;
  855. Assert( sides != 0 );
  856. return sides;
  857. }
  858. //-----------------------------------------------------------------------------
  859. // Euler QAngle -> Basis Vectors
  860. //-----------------------------------------------------------------------------
  861. void AngleVectors (const QAngle &angles, Vector *forward)
  862. {
  863. Assert( s_bMathlibInitialized );
  864. Assert( forward );
  865. float sp, sy, cp, cy;
  866. SinCos( DEG2RAD( angles[YAW] ), &sy, &cy );
  867. SinCos( DEG2RAD( angles[PITCH] ), &sp, &cp );
  868. forward->x = cp*cy;
  869. forward->y = cp*sy;
  870. forward->z = -sp;
  871. }
  872. //-----------------------------------------------------------------------------
  873. // Euler QAngle -> Basis Vectors. Each vector is optional
  874. //-----------------------------------------------------------------------------
  875. void AngleVectors( const QAngle &angles, Vector *forward, Vector *right, Vector *up )
  876. {
  877. Assert( s_bMathlibInitialized );
  878. float sr, sp, sy, cr, cp, cy;
  879. #ifdef _X360
  880. fltx4 radians, scale, sine, cosine;
  881. radians = LoadUnaligned3SIMD( angles.Base() );
  882. scale = ReplicateX4( M_PI_F / 180.f );
  883. radians = MulSIMD( radians, scale );
  884. SinCos3SIMD( sine, cosine, radians );
  885. sp = SubFloat( sine, 0 ); sy = SubFloat( sine, 1 ); sr = SubFloat( sine, 2 );
  886. cp = SubFloat( cosine, 0 ); cy = SubFloat( cosine, 1 ); cr = SubFloat( cosine, 2 );
  887. #else
  888. SinCos( DEG2RAD( angles[YAW] ), &sy, &cy );
  889. SinCos( DEG2RAD( angles[PITCH] ), &sp, &cp );
  890. SinCos( DEG2RAD( angles[ROLL] ), &sr, &cr );
  891. #endif
  892. if (forward)
  893. {
  894. forward->x = cp*cy;
  895. forward->y = cp*sy;
  896. forward->z = -sp;
  897. }
  898. if (right)
  899. {
  900. right->x = (-1*sr*sp*cy+-1*cr*-sy);
  901. right->y = (-1*sr*sp*sy+-1*cr*cy);
  902. right->z = -1*sr*cp;
  903. }
  904. if (up)
  905. {
  906. up->x = (cr*sp*cy+-sr*-sy);
  907. up->y = (cr*sp*sy+-sr*cy);
  908. up->z = cr*cp;
  909. }
  910. }
  911. //-----------------------------------------------------------------------------
  912. // Euler QAngle -> Basis Vectors transposed
  913. //-----------------------------------------------------------------------------
  914. void AngleVectorsTranspose (const QAngle &angles, Vector *forward, Vector *right, Vector *up)
  915. {
  916. Assert( s_bMathlibInitialized );
  917. float sr, sp, sy, cr, cp, cy;
  918. SinCos( DEG2RAD( angles[YAW] ), &sy, &cy );
  919. SinCos( DEG2RAD( angles[PITCH] ), &sp, &cp );
  920. SinCos( DEG2RAD( angles[ROLL] ), &sr, &cr );
  921. if (forward)
  922. {
  923. forward->x = cp*cy;
  924. forward->y = (sr*sp*cy+cr*-sy);
  925. forward->z = (cr*sp*cy+-sr*-sy);
  926. }
  927. if (right)
  928. {
  929. right->x = cp*sy;
  930. right->y = (sr*sp*sy+cr*cy);
  931. right->z = (cr*sp*sy+-sr*cy);
  932. }
  933. if (up)
  934. {
  935. up->x = -sp;
  936. up->y = sr*cp;
  937. up->z = cr*cp;
  938. }
  939. }
  940. //-----------------------------------------------------------------------------
  941. // Forward direction vector -> Euler angles
  942. //-----------------------------------------------------------------------------
  943. void VectorAngles( const Vector& forward, QAngle &angles )
  944. {
  945. Assert( s_bMathlibInitialized );
  946. float tmp, yaw, pitch;
  947. if (forward[1] == 0 && forward[0] == 0)
  948. {
  949. yaw = 0;
  950. if (forward[2] > 0)
  951. pitch = 270;
  952. else
  953. pitch = 90;
  954. }
  955. else
  956. {
  957. yaw = (atan2(forward[1], forward[0]) * 180 / M_PI);
  958. if (yaw < 0)
  959. yaw += 360;
  960. tmp = FastSqrt (forward[0]*forward[0] + forward[1]*forward[1]);
  961. pitch = (atan2(-forward[2], tmp) * 180 / M_PI);
  962. if (pitch < 0)
  963. pitch += 360;
  964. }
  965. angles[0] = pitch;
  966. angles[1] = yaw;
  967. angles[2] = 0;
  968. }
  969. //-----------------------------------------------------------------------------
  970. // Forward direction vector with a reference up vector -> Euler angles
  971. //-----------------------------------------------------------------------------
  972. void VectorAngles( const Vector &forward, const Vector &pseudoup, QAngle &angles )
  973. {
  974. Assert( s_bMathlibInitialized );
  975. Vector left;
  976. CrossProduct( pseudoup, forward, left );
  977. VectorNormalizeFast( left );
  978. float xyDist = sqrtf( forward[0] * forward[0] + forward[1] * forward[1] );
  979. // enough here to get angles?
  980. if ( xyDist > 0.001f )
  981. {
  982. // (yaw) y = ATAN( forward.y, forward.x ); -- in our space, forward is the X axis
  983. angles[1] = RAD2DEG( atan2f( forward[1], forward[0] ) );
  984. // The engine does pitch inverted from this, but we always end up negating it in the DLL
  985. // UNDONE: Fix the engine to make it consistent
  986. // (pitch) x = ATAN( -forward.z, sqrt(forward.x*forward.x+forward.y*forward.y) );
  987. angles[0] = RAD2DEG( atan2f( -forward[2], xyDist ) );
  988. float up_z = (left[1] * forward[0]) - (left[0] * forward[1]);
  989. // (roll) z = ATAN( left.z, up.z );
  990. angles[2] = RAD2DEG( atan2f( left[2], up_z ) );
  991. }
  992. else // forward is mostly Z, gimbal lock-
  993. {
  994. // (yaw) y = ATAN( -left.x, left.y ); -- forward is mostly z, so use right for yaw
  995. angles[1] = RAD2DEG( atan2f( -left[0], left[1] ) ); //This was originally copied from the "void MatrixAngles( const matrix3x4_t& matrix, float *angles )" code, and it's 180 degrees off, negated the values and it all works now (Dave Kircher)
  996. // The engine does pitch inverted from this, but we always end up negating it in the DLL
  997. // UNDONE: Fix the engine to make it consistent
  998. // (pitch) x = ATAN( -forward.z, sqrt(forward.x*forward.x+forward.y*forward.y) );
  999. angles[0] = RAD2DEG( atan2f( -forward[2], xyDist ) );
  1000. // Assume no roll in this case as one degree of freedom has been lost (i.e. yaw == roll)
  1001. angles[2] = 0;
  1002. }
  1003. }
  1004. #endif // #if !defined(__SPU__)
  1005. void SetIdentityMatrix( matrix3x4_t& matrix )
  1006. {
  1007. memset( matrix.Base(), 0, sizeof(float)*3*4 );
  1008. matrix[0][0] = 1.0;
  1009. matrix[1][1] = 1.0;
  1010. matrix[2][2] = 1.0;
  1011. }
  1012. #if !defined(__SPU__)
  1013. //-----------------------------------------------------------------------------
  1014. // Builds a scale matrix
  1015. //-----------------------------------------------------------------------------
  1016. void SetScaleMatrix( float x, float y, float z, matrix3x4_t &dst )
  1017. {
  1018. dst[0][0] = x; dst[0][1] = 0.0f; dst[0][2] = 0.0f; dst[0][3] = 0.0f;
  1019. dst[1][0] = 0.0f; dst[1][1] = y; dst[1][2] = 0.0f; dst[1][3] = 0.0f;
  1020. dst[2][0] = 0.0f; dst[2][1] = 0.0f; dst[2][2] = z; dst[2][3] = 0.0f;
  1021. }
  1022. //-----------------------------------------------------------------------------
  1023. // Purpose: Builds the matrix for a counterclockwise rotation about an arbitrary axis.
  1024. //
  1025. // | ax2 + (1 - ax2)cosQ axay(1 - cosQ) - azsinQ azax(1 - cosQ) + aysinQ |
  1026. // Ra(Q) = | axay(1 - cosQ) + azsinQ ay2 + (1 - ay2)cosQ ayaz(1 - cosQ) - axsinQ |
  1027. // | azax(1 - cosQ) - aysinQ ayaz(1 - cosQ) + axsinQ az2 + (1 - az2)cosQ |
  1028. //
  1029. // Input : mat -
  1030. // vAxisOrRot -
  1031. // angle -
  1032. //-----------------------------------------------------------------------------
  1033. void MatrixBuildRotationAboutAxis( const Vector &vAxisOfRot, float angleDegrees, matrix3x4_t &dst )
  1034. {
  1035. float radians;
  1036. float axisXSquared;
  1037. float axisYSquared;
  1038. float axisZSquared;
  1039. float fSin;
  1040. float fCos;
  1041. radians = angleDegrees * ( M_PI / 180.0 );
  1042. fSin = sin( radians );
  1043. fCos = cos( radians );
  1044. axisXSquared = vAxisOfRot[0] * vAxisOfRot[0];
  1045. axisYSquared = vAxisOfRot[1] * vAxisOfRot[1];
  1046. axisZSquared = vAxisOfRot[2] * vAxisOfRot[2];
  1047. // Column 0:
  1048. dst[0][0] = axisXSquared + (1 - axisXSquared) * fCos;
  1049. dst[1][0] = vAxisOfRot[0] * vAxisOfRot[1] * (1 - fCos) + vAxisOfRot[2] * fSin;
  1050. dst[2][0] = vAxisOfRot[2] * vAxisOfRot[0] * (1 - fCos) - vAxisOfRot[1] * fSin;
  1051. // Column 1:
  1052. dst[0][1] = vAxisOfRot[0] * vAxisOfRot[1] * (1 - fCos) - vAxisOfRot[2] * fSin;
  1053. dst[1][1] = axisYSquared + (1 - axisYSquared) * fCos;
  1054. dst[2][1] = vAxisOfRot[1] * vAxisOfRot[2] * (1 - fCos) + vAxisOfRot[0] * fSin;
  1055. // Column 2:
  1056. dst[0][2] = vAxisOfRot[2] * vAxisOfRot[0] * (1 - fCos) + vAxisOfRot[1] * fSin;
  1057. dst[1][2] = vAxisOfRot[1] * vAxisOfRot[2] * (1 - fCos) - vAxisOfRot[0] * fSin;
  1058. dst[2][2] = axisZSquared + (1 - axisZSquared) * fCos;
  1059. // Column 3:
  1060. dst[0][3] = 0;
  1061. dst[1][3] = 0;
  1062. dst[2][3] = 0;
  1063. }
  1064. //-----------------------------------------------------------------------------
  1065. // Computes the transpose
  1066. //-----------------------------------------------------------------------------
  1067. void MatrixTranspose( matrix3x4_t& mat )
  1068. {
  1069. vec_t tmp;
  1070. tmp = mat[0][1]; mat[0][1] = mat[1][0]; mat[1][0] = tmp;
  1071. tmp = mat[0][2]; mat[0][2] = mat[2][0]; mat[2][0] = tmp;
  1072. tmp = mat[1][2]; mat[1][2] = mat[2][1]; mat[2][1] = tmp;
  1073. }
  1074. void MatrixTranspose( const matrix3x4_t& src, matrix3x4_t& dst )
  1075. {
  1076. dst[0][0] = src[0][0]; dst[0][1] = src[1][0]; dst[0][2] = src[2][0]; dst[0][3] = 0.0f;
  1077. dst[1][0] = src[0][1]; dst[1][1] = src[1][1]; dst[1][2] = src[2][1]; dst[1][3] = 0.0f;
  1078. dst[2][0] = src[0][2]; dst[2][1] = src[1][2]; dst[2][2] = src[2][2]; dst[2][3] = 0.0f;
  1079. }
  1080. #endif // #if !defined(__SPU__)
  1081. //-----------------------------------------------------------------------------
  1082. // Purpose: converts engine euler angles into a matrix
  1083. // Input : vec3_t angles - PITCH, YAW, ROLL
  1084. // Output : *matrix - left-handed column matrix
  1085. // the basis vectors for the rotations will be in the columns as follows:
  1086. // matrix[][0] is forward
  1087. // matrix[][1] is left
  1088. // matrix[][2] is up
  1089. //-----------------------------------------------------------------------------
  1090. void AngleMatrix( RadianEuler const &angles, const Vector &position, matrix3x4_t& matrix )
  1091. {
  1092. AngleMatrix( angles, matrix );
  1093. MatrixSetColumn( position, 3, matrix );
  1094. }
  1095. void AngleMatrix( const RadianEuler& angles, matrix3x4_t& matrix )
  1096. {
  1097. QAngle quakeEuler( RAD2DEG( angles.y ), RAD2DEG( angles.z ), RAD2DEG( angles.x ) );
  1098. AngleMatrix( quakeEuler, matrix );
  1099. }
  1100. void AngleMatrix( const QAngle &angles, const Vector &position, matrix3x4_t& matrix )
  1101. {
  1102. AngleMatrix( angles, matrix );
  1103. MatrixSetColumn( position, 3, matrix );
  1104. }
  1105. void AngleMatrix( const QAngle &angles, matrix3x4_t& matrix )
  1106. {
  1107. #ifdef _VPROF_MATHLIB
  1108. VPROF_BUDGET( "AngleMatrix", "Mathlib" );
  1109. #endif
  1110. Assert( s_bMathlibInitialized );
  1111. float sr, sp, sy, cr, cp, cy;
  1112. #ifdef _X360
  1113. fltx4 radians, scale, sine, cosine;
  1114. radians = LoadUnaligned3SIMD( angles.Base() );
  1115. scale = ReplicateX4( M_PI_F / 180.f );
  1116. radians = MulSIMD( radians, scale );
  1117. SinCos3SIMD( sine, cosine, radians );
  1118. sp = SubFloat( sine, 0 ); sy = SubFloat( sine, 1 ); sr = SubFloat( sine, 2 );
  1119. cp = SubFloat( cosine, 0 ); cy = SubFloat( cosine, 1 ); cr = SubFloat( cosine, 2 );
  1120. #else
  1121. SinCos( DEG2RAD( angles[YAW] ), &sy, &cy );
  1122. SinCos( DEG2RAD( angles[PITCH] ), &sp, &cp );
  1123. SinCos( DEG2RAD( angles[ROLL] ), &sr, &cr );
  1124. #endif
  1125. // matrix = (YAW * PITCH) * ROLL
  1126. matrix[0][0] = cp*cy;
  1127. matrix[1][0] = cp*sy;
  1128. matrix[2][0] = -sp;
  1129. // NOTE: Do not optimize this to reduce multiplies! optimizer bug will screw this up.
  1130. matrix[0][1] = sr*sp*cy+cr*-sy;
  1131. matrix[1][1] = sr*sp*sy+cr*cy;
  1132. matrix[2][1] = sr*cp;
  1133. matrix[0][2] = (cr*sp*cy+-sr*-sy);
  1134. matrix[1][2] = (cr*sp*sy+-sr*cy);
  1135. matrix[2][2] = cr*cp;
  1136. matrix[0][3] = 0.0f;
  1137. matrix[1][3] = 0.0f;
  1138. matrix[2][3] = 0.0f;
  1139. }
  1140. #if !defined(__SPU__)
  1141. void AngleIMatrix( const RadianEuler& angles, matrix3x4_t& matrix )
  1142. {
  1143. QAngle quakeEuler( RAD2DEG( angles.y ), RAD2DEG( angles.z ), RAD2DEG( angles.x ) );
  1144. AngleIMatrix( quakeEuler, matrix );
  1145. }
  1146. void AngleIMatrix (const QAngle& angles, matrix3x4_t& matrix )
  1147. {
  1148. Assert( s_bMathlibInitialized );
  1149. float sr, sp, sy, cr, cp, cy;
  1150. SinCos( DEG2RAD( angles[YAW] ), &sy, &cy );
  1151. SinCos( DEG2RAD( angles[PITCH] ), &sp, &cp );
  1152. SinCos( DEG2RAD( angles[ROLL] ), &sr, &cr );
  1153. // matrix = (YAW * PITCH) * ROLL
  1154. matrix[0][0] = cp*cy;
  1155. matrix[0][1] = cp*sy;
  1156. matrix[0][2] = -sp;
  1157. matrix[1][0] = sr*sp*cy+cr*-sy;
  1158. matrix[1][1] = sr*sp*sy+cr*cy;
  1159. matrix[1][2] = sr*cp;
  1160. matrix[2][0] = (cr*sp*cy+-sr*-sy);
  1161. matrix[2][1] = (cr*sp*sy+-sr*cy);
  1162. matrix[2][2] = cr*cp;
  1163. matrix[0][3] = 0.f;
  1164. matrix[1][3] = 0.f;
  1165. matrix[2][3] = 0.f;
  1166. }
  1167. void AngleIMatrix (const QAngle &angles, const Vector &position, matrix3x4_t &mat )
  1168. {
  1169. AngleIMatrix( angles, mat );
  1170. Vector vecTranslation;
  1171. VectorRotate( position, mat, vecTranslation );
  1172. vecTranslation *= -1.0f;
  1173. MatrixSetColumn( vecTranslation, 3, mat );
  1174. }
  1175. #endif // #if !defined(__SPU__)
  1176. #if !defined(__SPU__)
  1177. //-----------------------------------------------------------------------------
  1178. // Bounding box construction methods
  1179. //-----------------------------------------------------------------------------
  1180. void ClearBounds (Vector& mins, Vector& maxs)
  1181. {
  1182. Assert( s_bMathlibInitialized );
  1183. mins[0] = mins[1] = mins[2] = FLT_MAX;
  1184. maxs[0] = maxs[1] = maxs[2] = -FLT_MAX;
  1185. }
  1186. void AddPointToBounds (const Vector& v, Vector& mins, Vector& maxs)
  1187. {
  1188. Assert( s_bMathlibInitialized );
  1189. int i;
  1190. vec_t val;
  1191. for (i=0 ; i<3 ; i++)
  1192. {
  1193. val = v[i];
  1194. if (val < mins[i])
  1195. mins[i] = val;
  1196. if (val > maxs[i])
  1197. maxs[i] = val;
  1198. }
  1199. }
  1200. bool AreBoundsValid( const Vector &vMin, const Vector &vMax )
  1201. {
  1202. for ( int i = 0; i < 3; ++ i )
  1203. {
  1204. if ( vMin[i] > vMax[i] )
  1205. {
  1206. return false;
  1207. }
  1208. }
  1209. return true;
  1210. }
  1211. bool IsPointInBounds( const Vector &vPoint, const Vector &vMin, const Vector &vMax )
  1212. {
  1213. for ( int i = 0; i < 3; ++ i )
  1214. {
  1215. if ( vPoint[i] < vMin[i] || vPoint[i] > vMax[i] )
  1216. {
  1217. return false;
  1218. }
  1219. }
  1220. return true;
  1221. }
  1222. // solve a x^2 + b x + c = 0
  1223. bool SolveQuadratic( float a, float b, float c, float &root1, float &root2 )
  1224. {
  1225. Assert( s_bMathlibInitialized );
  1226. if (a == 0)
  1227. {
  1228. if (b != 0)
  1229. {
  1230. // no x^2 component, it's a linear system
  1231. root1 = root2 = -c / b;
  1232. return true;
  1233. }
  1234. if (c == 0)
  1235. {
  1236. // all zero's
  1237. root1 = root2 = 0;
  1238. return true;
  1239. }
  1240. return false;
  1241. }
  1242. float tmp = b * b - 4.0f * a * c;
  1243. if (tmp < 0)
  1244. {
  1245. // imaginary number, bah, no solution.
  1246. return false;
  1247. }
  1248. tmp = sqrt( tmp );
  1249. root1 = (-b + tmp) / (2.0f * a);
  1250. root2 = (-b - tmp) / (2.0f * a);
  1251. return true;
  1252. }
  1253. // solves for "a, b, c" where "a x^2 + b x + c = y", return true if solution exists
  1254. bool SolveInverseQuadratic( float x1, float y1, float x2, float y2, float x3, float y3, float &a, float &b, float &c )
  1255. {
  1256. float det = (x1 - x2)*(x1 - x3)*(x2 - x3);
  1257. // FIXME: check with some sort of epsilon
  1258. if (det == 0.0)
  1259. return false;
  1260. a = (x3*(-y1 + y2) + x2*(y1 - y3) + x1*(-y2 + y3)) / det;
  1261. b = (x3*x3*(y1 - y2) + x1*x1*(y2 - y3) + x2*x2*(-y1 + y3)) / det;
  1262. c = (x1*x3*(-x1 + x3)*y2 + x2*x2*(x3*y1 - x1*y3) + x2*(-(x3*x3*y1) + x1*x1*y3)) / det;
  1263. return true;
  1264. }
  1265. bool SolveInverseQuadraticMonotonic( float x1, float y1, float x2, float y2, float x3, float y3,
  1266. float &a, float &b, float &c )
  1267. {
  1268. // use SolveInverseQuadratic, but if the sigm of the derivative at the start point is the wrong
  1269. // sign, displace the mid point
  1270. // first, sort parameters
  1271. if (x1>x2)
  1272. {
  1273. V_swap(x1,x2);
  1274. V_swap(y1,y2);
  1275. }
  1276. if (x2>x3)
  1277. {
  1278. V_swap(x2,x3);
  1279. V_swap(y2,y3);
  1280. }
  1281. if (x1>x2)
  1282. {
  1283. V_swap(x1,x2);
  1284. V_swap(y1,y2);
  1285. }
  1286. // this code is not fast. what it does is when the curve would be non-monotonic, slowly shifts
  1287. // the center point closer to the linear line between the endpoints. Should anyone need htis
  1288. // function to be actually fast, it would be fairly easy to change it to be so.
  1289. for(float blend_to_linear_factor=0.0;blend_to_linear_factor<=1.0;blend_to_linear_factor+=0.05)
  1290. {
  1291. float tempy2=(1-blend_to_linear_factor)*y2+blend_to_linear_factor*FLerp(y1,y3,x1,x3,x2);
  1292. if (!SolveInverseQuadratic(x1,y1,x2,tempy2,x3,y3,a,b,c))
  1293. return false;
  1294. float derivative=2.0*a+b;
  1295. if ( (y1<y2) && (y2<y3)) // monotonically increasing
  1296. {
  1297. if (derivative>=0.0)
  1298. return true;
  1299. }
  1300. else
  1301. {
  1302. if ( (y1>y2) && (y2>y3)) // monotonically decreasing
  1303. {
  1304. if (derivative<=0.0)
  1305. return true;
  1306. }
  1307. else
  1308. return true;
  1309. }
  1310. }
  1311. return true;
  1312. }
  1313. // solves for "a, b, c" where "1/(a x^2 + b x + c ) = y", return true if solution exists
  1314. bool SolveInverseReciprocalQuadratic( float x1, float y1, float x2, float y2, float x3, float y3, float &a, float &b, float &c )
  1315. {
  1316. float det = (x1 - x2)*(x1 - x3)*(x2 - x3)*y1*y2*y3;
  1317. // FIXME: check with some sort of epsilon
  1318. if (det == 0.0)
  1319. return false;
  1320. a = (x1*y1*(y2 - y3) + x3*(y1 - y2)*y3 + x2*y2*(-y1 + y3)) / det;
  1321. b = (x2*x2*y2*(y1 - y3) + x3*x3*(-y1 + y2)*y3 + x1*x1*y1*(-y2 + y3)) / det;
  1322. c = (x2*(x2 - x3)*x3*y2*y3 + x1*x1*y1*(x2*y2 - x3*y3) + x1*(-(x2*x2*y1*y2) + x3*x3*y1*y3)) / det;
  1323. return true;
  1324. }
  1325. // Rotate a vector around the Z axis (YAW)
  1326. void VectorYawRotate( const Vector &in, float flYaw, Vector &out)
  1327. {
  1328. Assert( s_bMathlibInitialized );
  1329. if (&in == &out )
  1330. {
  1331. Vector tmp;
  1332. tmp = in;
  1333. VectorYawRotate( tmp, flYaw, out );
  1334. return;
  1335. }
  1336. float sy, cy;
  1337. SinCos( DEG2RAD(flYaw), &sy, &cy );
  1338. out.x = in.x * cy - in.y * sy;
  1339. out.y = in.x * sy + in.y * cy;
  1340. out.z = in.z;
  1341. }
  1342. float Bias( float x, float biasAmt )
  1343. {
  1344. // WARNING: not thread safe
  1345. static float lastAmt = -1;
  1346. static float lastExponent = 0;
  1347. if( lastAmt != biasAmt )
  1348. {
  1349. lastExponent = log( biasAmt ) * -1.4427f; // (-1.4427 = 1 / log(0.5))
  1350. }
  1351. return pow( x, lastExponent );
  1352. }
  1353. float Gain( float x, float biasAmt )
  1354. {
  1355. // WARNING: not thread safe
  1356. if( x < 0.5 )
  1357. return 0.5f * Bias( 2*x, 1-biasAmt );
  1358. else
  1359. return 1 - 0.5f * Bias( 2 - 2*x, 1-biasAmt );
  1360. }
  1361. float SmoothCurve( float x )
  1362. {
  1363. return (1 - cos( x * M_PI )) * 0.5f;
  1364. }
  1365. inline float MovePeak( float x, float flPeakPos )
  1366. {
  1367. // Todo: make this higher-order?
  1368. if( x < flPeakPos )
  1369. return x * 0.5f / flPeakPos;
  1370. else
  1371. return 0.5 + 0.5 * (x - flPeakPos) / (1 - flPeakPos);
  1372. }
  1373. float SmoothCurve_Tweak( float x, float flPeakPos, float flPeakSharpness )
  1374. {
  1375. float flMovedPeak = MovePeak( x, flPeakPos );
  1376. float flSharpened = Gain( flMovedPeak, flPeakSharpness );
  1377. return SmoothCurve( flSharpened );
  1378. }
  1379. #endif // !defined(__SPU__)
  1380. //-----------------------------------------------------------------------------
  1381. // make sure quaternions are within 180 degrees of one another, if not, reverse q
  1382. //-----------------------------------------------------------------------------
  1383. void QuaternionAlign( const Quaternion &p, const Quaternion &q, Quaternion &qt )
  1384. {
  1385. Assert( s_bMathlibInitialized );
  1386. // FIXME: can this be done with a quat dot product?
  1387. int i;
  1388. // decide if one of the quaternions is backwards
  1389. float a = 0;
  1390. float b = 0;
  1391. for (i = 0; i < 4; i++)
  1392. {
  1393. a += (p[i]-q[i])*(p[i]-q[i]);
  1394. b += (p[i]+q[i])*(p[i]+q[i]);
  1395. }
  1396. if (a > b)
  1397. {
  1398. for (i = 0; i < 4; i++)
  1399. {
  1400. qt[i] = -q[i];
  1401. }
  1402. }
  1403. else if (&qt != &q)
  1404. {
  1405. for (i = 0; i < 4; i++)
  1406. {
  1407. qt[i] = q[i];
  1408. }
  1409. }
  1410. }
  1411. //-----------------------------------------------------------------------------
  1412. // Do a piecewise addition of the quaternion elements. This actually makes little
  1413. // mathematical sense, but it's a cheap way to simulate a slerp.
  1414. //-----------------------------------------------------------------------------
  1415. void QuaternionBlend( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt )
  1416. {
  1417. Assert( s_bMathlibInitialized );
  1418. #if ALLOW_SIMD_QUATERNION_MATH
  1419. fltx4 psimd, qsimd, qtsimd;
  1420. psimd = LoadUnalignedSIMD( p.Base() );
  1421. qsimd = LoadUnalignedSIMD( q.Base() );
  1422. qtsimd = QuaternionBlendSIMD( psimd, qsimd, t );
  1423. StoreUnalignedSIMD( qt.Base(), qtsimd );
  1424. #else
  1425. // decide if one of the quaternions is backwards
  1426. Quaternion q2;
  1427. QuaternionAlign( p, q, q2 );
  1428. QuaternionBlendNoAlign( p, q2, t, qt );
  1429. #endif
  1430. }
  1431. void QuaternionBlendNoAlign( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt )
  1432. {
  1433. Assert( s_bMathlibInitialized );
  1434. float sclp, sclq;
  1435. int i;
  1436. // 0.0 returns p, 1.0 return q.
  1437. sclp = 1.0f - t;
  1438. sclq = t;
  1439. for (i = 0; i < 4; i++) {
  1440. qt[i] = sclp * p[i] + sclq * q[i];
  1441. }
  1442. QuaternionNormalize( qt );
  1443. }
  1444. void QuaternionIdentityBlend( const Quaternion &p, float t, Quaternion &qt )
  1445. {
  1446. Assert( s_bMathlibInitialized );
  1447. float sclp;
  1448. sclp = 1.0f - t;
  1449. qt.x = p.x * sclp;
  1450. qt.y = p.y * sclp;
  1451. qt.z = p.z * sclp;
  1452. if (qt.w < 0.0)
  1453. {
  1454. qt.w = p.w * sclp - t;
  1455. }
  1456. else
  1457. {
  1458. qt.w = p.w * sclp + t;
  1459. }
  1460. QuaternionNormalize( qt );
  1461. }
  1462. //-----------------------------------------------------------------------------
  1463. // Quaternion sphereical linear interpolation
  1464. //-----------------------------------------------------------------------------
  1465. void QuaternionSlerp( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt )
  1466. {
  1467. Quaternion q2;
  1468. // 0.0 returns p, 1.0 return q.
  1469. // decide if one of the quaternions is backwards
  1470. QuaternionAlign( p, q, q2 );
  1471. QuaternionSlerpNoAlign( p, q2, t, qt );
  1472. }
  1473. void QuaternionSlerpNoAlign( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt )
  1474. {
  1475. Assert( s_bMathlibInitialized );
  1476. float omega, cosom, sinom, sclp, sclq;
  1477. int i;
  1478. // 0.0 returns p, 1.0 return q.
  1479. cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3];
  1480. if ((1.0f + cosom) > 0.000001f) {
  1481. if ((1.0f - cosom) > 0.000001f) {
  1482. omega = acos( cosom );
  1483. sinom = sin( omega );
  1484. sclp = sin( (1.0f - t)*omega) / sinom;
  1485. sclq = sin( t*omega ) / sinom;
  1486. }
  1487. else {
  1488. // TODO: add short circuit for cosom == 1.0f?
  1489. sclp = 1.0f - t;
  1490. sclq = t;
  1491. }
  1492. for (i = 0; i < 4; i++) {
  1493. qt[i] = sclp * p[i] + sclq * q[i];
  1494. }
  1495. }
  1496. else {
  1497. Assert( &qt != &q );
  1498. qt[0] = -q[1];
  1499. qt[1] = q[0];
  1500. qt[2] = -q[3];
  1501. qt[3] = q[2];
  1502. sclp = sin( (1.0f - t) * (0.5f * M_PI));
  1503. sclq = sin( t * (0.5f * M_PI));
  1504. for (i = 0; i < 3; i++) {
  1505. qt[i] = sclp * p[i] + sclq * qt[i];
  1506. }
  1507. }
  1508. Assert( qt.IsValid() );
  1509. }
  1510. #if !defined(__SPU__)
  1511. //-----------------------------------------------------------------------------
  1512. // Purpose: Returns the angular delta between the two normalized quaternions in degrees.
  1513. //-----------------------------------------------------------------------------
  1514. float QuaternionAngleDiff( const Quaternion &p, const Quaternion &q )
  1515. {
  1516. #if 1
  1517. // this code path is here for 2 reasons:
  1518. // 1 - acos maps 1-epsilon to values much larger than epsilon (vs asin, which maps epsilon to itself)
  1519. // this means that in floats, anything below ~0.05 degrees truncates to 0
  1520. // 2 - normalized quaternions are frequently slightly non-normalized due to float precision issues,
  1521. // and the epsilon off of normalized can be several percents of a degree
  1522. Quaternion qInv, diff;
  1523. QuaternionConjugate( q, qInv );
  1524. QuaternionMult( p, qInv, diff );
  1525. // Note if the quaternion is slightly non-normalized the square root below may be more than 1,
  1526. // the value is clamped to one otherwise it may result in asin() returning an undefined result.
  1527. float sinang = MIN( 1.0f, sqrt( diff.x * diff.x + diff.y * diff.y + diff.z * diff.z ) );
  1528. float angle = RAD2DEG( 2 * asin( sinang ) );
  1529. return angle;
  1530. #else
  1531. Quaternion q2;
  1532. QuaternionAlign( p, q, q2 );
  1533. Assert( s_bMathlibInitialized );
  1534. float cosom = p.x * q2.x + p.y * q2.y + p.z * q2.z + p.w * q2.w;
  1535. if ( cosom > -1.0f )
  1536. {
  1537. if ( cosom < 1.0f )
  1538. {
  1539. float omega = 2 * fabs( acos( cosom ) );
  1540. return RAD2DEG( omega );
  1541. }
  1542. return 0.0f;
  1543. }
  1544. return 180.0f;
  1545. #endif
  1546. }
  1547. void QuaternionConjugate( const Quaternion &p, Quaternion &q )
  1548. {
  1549. Assert( s_bMathlibInitialized );
  1550. Assert( q.IsValid() );
  1551. q.x = -p.x;
  1552. q.y = -p.y;
  1553. q.z = -p.z;
  1554. q.w = p.w;
  1555. }
  1556. void QuaternionInvert( const Quaternion &p, Quaternion &q )
  1557. {
  1558. Assert( s_bMathlibInitialized );
  1559. Assert( q.IsValid() );
  1560. QuaternionConjugate( p, q );
  1561. float magnitudeSqr = QuaternionDotProduct( p, p );
  1562. Assert( magnitudeSqr );
  1563. if ( magnitudeSqr )
  1564. {
  1565. float inv = 1.0f / magnitudeSqr;
  1566. q.x *= inv;
  1567. q.y *= inv;
  1568. q.z *= inv;
  1569. q.w *= inv;
  1570. }
  1571. }
  1572. void QuaternionMultiply( const Quaternion &q, const Vector &v, Vector &result )
  1573. {
  1574. Vector t, t2;
  1575. CrossProduct( q.ImaginaryPart(), v, t );
  1576. t *= 2.0f;
  1577. VectorMA( v, q.RealPart(), t, result );
  1578. CrossProduct( q.ImaginaryPart(), t, t2 );
  1579. result += t2;
  1580. }
  1581. #endif // #if !defined(__SPU__)
  1582. //-----------------------------------------------------------------------------
  1583. // Make sure the quaternion is of unit length
  1584. //-----------------------------------------------------------------------------
  1585. float QuaternionNormalize( Quaternion &q )
  1586. {
  1587. Assert( s_bMathlibInitialized );
  1588. float radius, iradius;
  1589. Assert( q.IsValid() );
  1590. radius = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
  1591. if ( radius ) // > FLT_EPSILON && ((radius < 1.0f - 4*FLT_EPSILON) || (radius > 1.0f + 4*FLT_EPSILON))
  1592. {
  1593. radius = sqrt(radius);
  1594. iradius = 1.0f/radius;
  1595. q[3] *= iradius;
  1596. q[2] *= iradius;
  1597. q[1] *= iradius;
  1598. q[0] *= iradius;
  1599. }
  1600. return radius;
  1601. }
  1602. void QuaternionScale( const Quaternion &p, float t, Quaternion &q )
  1603. {
  1604. Assert( s_bMathlibInitialized );
  1605. #if 0
  1606. Quaternion p0;
  1607. Quaternion q;
  1608. p0.Init( 0.0, 0.0, 0.0, 1.0 );
  1609. // slerp in "reverse order" so that p doesn't get realigned
  1610. QuaternionSlerp( p, p0, 1.0 - fabs( t ), q );
  1611. if (t < 0.0)
  1612. {
  1613. q.w = -q.w;
  1614. }
  1615. #else
  1616. float r;
  1617. // FIXME: nick, this isn't overly sensitive to accuracy, and it may be faster to
  1618. // use the cos part (w) of the quaternion (sin(omega)*N,cos(omega)) to figure the new scale.
  1619. float sinom = sqrt( DotProduct( &p.x, &p.x ) );
  1620. sinom = MIN( sinom, 1.f );
  1621. float sinsom = sin( asin( sinom ) * t );
  1622. t = sinsom / (sinom + FLT_EPSILON);
  1623. VectorScale( &p.x, t, &q.x );
  1624. // rescale rotation
  1625. r = 1.0f - sinsom * sinsom;
  1626. // Assert( r >= 0 );
  1627. if (r < 0.0f)
  1628. r = 0.0f;
  1629. r = sqrt( r );
  1630. // keep sign of rotation
  1631. if (p.w < 0)
  1632. q.w = -r;
  1633. else
  1634. q.w = r;
  1635. #endif
  1636. Assert( q.IsValid() );
  1637. return;
  1638. }
  1639. void QuaternionAdd( const Quaternion &p, const Quaternion &q, Quaternion &qt )
  1640. {
  1641. Assert( s_bMathlibInitialized );
  1642. Assert( p.IsValid() );
  1643. Assert( q.IsValid() );
  1644. // decide if one of the quaternions is backwards
  1645. Quaternion q2;
  1646. QuaternionAlign( p, q, q2 );
  1647. // is this right???
  1648. qt[0] = p[0] + q2[0];
  1649. qt[1] = p[1] + q2[1];
  1650. qt[2] = p[2] + q2[2];
  1651. qt[3] = p[3] + q2[3];
  1652. return;
  1653. }
  1654. float QuaternionDotProduct( const Quaternion &p, const Quaternion &q )
  1655. {
  1656. Assert( s_bMathlibInitialized );
  1657. Assert( p.IsValid() );
  1658. Assert( q.IsValid() );
  1659. return p.x * q.x + p.y * q.y + p.z * q.z + p.w * q.w;
  1660. }
  1661. // qt = p * q
  1662. void QuaternionMult( const Quaternion &p, const Quaternion &q, Quaternion &qt )
  1663. {
  1664. Assert( s_bMathlibInitialized );
  1665. Assert( p.IsValid() );
  1666. Assert( q.IsValid() );
  1667. if (&p == &qt)
  1668. {
  1669. Quaternion p2 = p;
  1670. QuaternionMult( p2, q, qt );
  1671. return;
  1672. }
  1673. // decide if one of the quaternions is backwards
  1674. Quaternion q2;
  1675. QuaternionAlign( p, q, q2 );
  1676. qt.x = p.x * q2.w + p.y * q2.z - p.z * q2.y + p.w * q2.x;
  1677. qt.y = -p.x * q2.z + p.y * q2.w + p.z * q2.x + p.w * q2.y;
  1678. qt.z = p.x * q2.y - p.y * q2.x + p.z * q2.w + p.w * q2.z;
  1679. qt.w = -p.x * q2.x - p.y * q2.y - p.z * q2.z + p.w * q2.w;
  1680. }
  1681. #if !defined(__SPU__)
  1682. void QuaternionExp( const Quaternion &p, Quaternion &q )
  1683. {
  1684. float r = sqrt(p[0]*p[0]+p[1]*p[1]+p[2]*p[2]);
  1685. float et = exp(p[3]);
  1686. float s = r>=0.00001f? et*sin(r)/r: 0.f;
  1687. q.Init( s*p[0],s*p[1],s*p[2], et*cos( r ) );
  1688. }
  1689. void QuaternionLn( const Quaternion &p, Quaternion &q )
  1690. {
  1691. float r = sqrt(p[0]*p[0]+p[1]*p[1]+p[2]*p[2]);
  1692. float t = r>0.00001f? atan2(r,p[3])/r: 0.f;
  1693. float norm = p[0]*p[0] + p[1]*p[1] + p[2]*p[2] + p[3]*p[3];
  1694. q.Init( t*p[0],t*p[1],t*p[2],0.5*log(norm) );
  1695. }
  1696. // Average using exponential method
  1697. // Qave = exp( 1 / n * log( Q1 ) + ... + 1 / n * log( Qn ) ) where
  1698. // if pflWeights passed in 1/n is replaced by normalized weighting
  1699. void QuaternionAverageExponential( Quaternion &q, int nCount, const Quaternion *pQuaternions, const float *pflWeights /*=NULL*/ )
  1700. {
  1701. Assert( nCount >= 1 );
  1702. Assert( pQuaternions );
  1703. // Nothing to do if only one input quaternions
  1704. if ( nCount == 1 )
  1705. {
  1706. q = pQuaternions[ 0 ];
  1707. return;
  1708. }
  1709. float ooWeightSum = 1.0f;
  1710. float flWeightSum = 0.0f;
  1711. for ( int i = 0 ; i < nCount; ++i )
  1712. {
  1713. if ( pflWeights )
  1714. {
  1715. flWeightSum += pflWeights[ i ];
  1716. }
  1717. else
  1718. {
  1719. flWeightSum += 1.0f;
  1720. }
  1721. }
  1722. if ( flWeightSum > 0.0f )
  1723. {
  1724. ooWeightSum = 1.0f / flWeightSum;
  1725. }
  1726. Quaternion sum( 0, 0, 0, 0 );
  1727. // Now sum the ln of the quaternions
  1728. for ( int i = 0; i < nCount; ++i )
  1729. {
  1730. float weight = ooWeightSum;
  1731. if ( pflWeights )
  1732. {
  1733. weight *= pflWeights[ i ];
  1734. }
  1735. // Make sure all quaternions are aligned with the
  1736. // first to avoid blending the wrong direction.
  1737. Quaternion alignedQuat;
  1738. QuaternionAlign( pQuaternions[ 0 ], pQuaternions[ i ], alignedQuat );
  1739. Quaternion qLn;
  1740. QuaternionLn( alignedQuat, qLn );
  1741. for ( int j = 0; j < 4; ++j )
  1742. {
  1743. sum[ j ] += ( qLn[ j ] * weight );
  1744. }
  1745. }
  1746. // then exponentiate to get final value
  1747. QuaternionExp( sum, q );
  1748. }
  1749. // Given a vector and a pseudo-up reference vector, create a quaternion which represents
  1750. // the orientation of the forward vector. Note, will be unstable if vecForward is close
  1751. // to referenceUp
  1752. void QuaternionLookAt( const Vector &vecForward, const Vector &referenceUp, Quaternion &q )
  1753. {
  1754. Vector forward = vecForward;
  1755. forward.NormalizeInPlace();
  1756. float ratio = DotProduct( forward, referenceUp );
  1757. Vector up = referenceUp - ( forward * ratio );
  1758. up.NormalizeInPlace();
  1759. Vector right = forward.Cross( up );
  1760. right.NormalizeInPlace();
  1761. const Vector &x = right;
  1762. const Vector &y = forward;
  1763. const Vector &z = up;
  1764. float tr = x.x + y.y + z.z;
  1765. q.Init( y.z - z.y , z.x - x.z, x.y - y.x, tr + 1.0f );
  1766. QuaternionNormalize( q );
  1767. /*
  1768. Vector z = vecForward;
  1769. z.NormalizeInPlace();
  1770. Vector x = referenceUp.Cross( z );
  1771. x.NormalizeInPlace();
  1772. Vector y = z.Cross( x );
  1773. y.NormalizeInPlace();
  1774. float tr = x.x + y.y + z.z;
  1775. q.Init( y.z - z.y , z.x - x.z, x.y - y.x, tr + 1.0f );
  1776. QuaternionNormalize( q );
  1777. */
  1778. }
  1779. #endif // !defined(__SPU__)
  1780. void QuaternionMatrix( const Quaternion &q, const Vector &pos, matrix3x4_t& matrix )
  1781. {
  1782. Assert( pos.IsValid() );
  1783. QuaternionMatrix( q, matrix );
  1784. matrix[0][3] = pos.x;
  1785. matrix[1][3] = pos.y;
  1786. matrix[2][3] = pos.z;
  1787. }
  1788. void QuaternionMatrix( const Quaternion &q, const Vector &pos, const Vector &vScale, matrix3x4_t& mat )
  1789. {
  1790. Assert( pos.IsValid() );
  1791. Assert( q.IsValid() );
  1792. Assert( vScale.IsValid() );
  1793. QuaternionMatrix( q, mat );
  1794. mat[ 0 ][ 0 ] *= vScale.x; mat[ 1 ][ 0 ] *= vScale.x; mat[ 2 ][ 0 ] *= vScale.x;
  1795. mat[ 0 ][ 1 ] *= vScale.y; mat[ 1 ][ 1 ] *= vScale.y; mat[ 2 ][ 1 ] *= vScale.y;
  1796. mat[ 0 ][ 2 ] *= vScale.z; mat[ 1 ][ 2 ] *= vScale.z; mat[ 2 ][ 2 ] *= vScale.z;
  1797. mat[ 0 ][ 3 ] = pos.x; mat[ 1 ][ 3 ] = pos.y; mat[ 2 ][ 3 ] = pos.z;
  1798. }
  1799. void QuaternionMatrix( const Quaternion &q, matrix3x4_t& matrix )
  1800. {
  1801. Assert( s_bMathlibInitialized );
  1802. Assert( q.IsValid() );
  1803. #ifdef _VPROF_MATHLIB
  1804. VPROF_BUDGET( "QuaternionMatrix", "Mathlib" );
  1805. #endif
  1806. // Original code
  1807. // This should produce the same code as below with optimization, but looking at the assmebly,
  1808. // it doesn't. There are 7 extra multiplies in the release build of this, go figure.
  1809. #if 1
  1810. matrix[0][0] = 1.0 - 2.0 * q.y * q.y - 2.0 * q.z * q.z;
  1811. matrix[1][0] = 2.0 * q.x * q.y + 2.0 * q.w * q.z;
  1812. matrix[2][0] = 2.0 * q.x * q.z - 2.0 * q.w * q.y;
  1813. matrix[0][1] = 2.0f * q.x * q.y - 2.0f * q.w * q.z;
  1814. matrix[1][1] = 1.0f - 2.0f * q.x * q.x - 2.0f * q.z * q.z;
  1815. matrix[2][1] = 2.0f * q.y * q.z + 2.0f * q.w * q.x;
  1816. matrix[0][2] = 2.0f * q.x * q.z + 2.0f * q.w * q.y;
  1817. matrix[1][2] = 2.0f * q.y * q.z - 2.0f * q.w * q.x;
  1818. matrix[2][2] = 1.0f - 2.0f * q.x * q.x - 2.0f * q.y * q.y;
  1819. matrix[0][3] = 0.0f;
  1820. matrix[1][3] = 0.0f;
  1821. matrix[2][3] = 0.0f;
  1822. #else
  1823. float wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
  1824. // precalculate common multiplitcations
  1825. x2 = q.x + q.x;
  1826. y2 = q.y + q.y;
  1827. z2 = q.z + q.z;
  1828. xx = q.x * x2;
  1829. xy = q.x * y2;
  1830. xz = q.x * z2;
  1831. yy = q.y * y2;
  1832. yz = q.y * z2;
  1833. zz = q.z * z2;
  1834. wx = q.w * x2;
  1835. wy = q.w * y2;
  1836. wz = q.w * z2;
  1837. matrix[0][0] = 1.0 - (yy + zz);
  1838. matrix[0][1] = xy - wz;
  1839. matrix[0][2] = xz + wy;
  1840. matrix[0][3] = 0.0f;
  1841. matrix[1][0] = xy + wz;
  1842. matrix[1][1] = 1.0 - (xx + zz);
  1843. matrix[1][2] = yz - wx;
  1844. matrix[1][3] = 0.0f;
  1845. matrix[2][0] = xz - wy;
  1846. matrix[2][1] = yz + wx;
  1847. matrix[2][2] = 1.0 - (xx + yy);
  1848. matrix[2][3] = 0.0f;
  1849. #endif
  1850. }
  1851. const Vector Quaternion::GetForward()const
  1852. {
  1853. Vector vAxisX;
  1854. vAxisX.x = 1.0 - 2.0 * y * y - 2.0 * z * z;
  1855. vAxisX.y = 2.0 * x * y + 2.0 * w * z;
  1856. vAxisX.z = 2.0 * x * z - 2.0 * w * y;
  1857. return vAxisX;
  1858. }
  1859. const Vector Quaternion::GetLeft()const
  1860. {
  1861. Vector vAxisY;
  1862. vAxisY.x = 2.0f * x * y - 2.0f * w * z;
  1863. vAxisY.y = 1.0f - 2.0f * x * x - 2.0f * z * z;
  1864. vAxisY.z = 2.0f * y * z + 2.0f * w * x;
  1865. return vAxisY;
  1866. }
  1867. const Vector Quaternion::GetUp()const
  1868. {
  1869. Vector vAxisZ;
  1870. vAxisZ.x = 2.0f * x * z + 2.0f * w * y;
  1871. vAxisZ.y = 2.0f * y * z - 2.0f * w * x;
  1872. vAxisZ.z = 1.0f - 2.0f * x * x - 2.0f * y * y;
  1873. return vAxisZ;
  1874. }
  1875. const Quaternion RotateBetween( const Vector& v1, const Vector& v2 )
  1876. {
  1877. // Find quaternion that rotates v1 into v2
  1878. Quaternion qOut;
  1879. Vector vBisector = 0.5f * ( v1 + v2 );
  1880. if ( vBisector.LengthSqr() > 1e-9f )
  1881. {
  1882. qOut.Init( CrossProduct( v1, vBisector ), DotProduct( v1, vBisector ) );
  1883. }
  1884. else
  1885. {
  1886. // Anti-parallel: Use a perpendicular vector
  1887. if ( fabsf( v1.x ) > 0.5f )
  1888. {
  1889. qOut.x = v1.y;
  1890. qOut.y = -v1.x;
  1891. qOut.z = 0.0f;
  1892. }
  1893. else
  1894. {
  1895. qOut.x = 0.0f;
  1896. qOut.y = v1.z;
  1897. qOut.z = -v1.y;
  1898. }
  1899. qOut.w = 0.0f;
  1900. }
  1901. // The algorithm is simplified and made more accurate by normalizing at the end
  1902. QuaternionNormalize( qOut );
  1903. Assert( ( VectorTransform( v1, QuaternionMatrix( qOut ) ) - v2 ).Length() < 2e-3f );
  1904. return qOut;
  1905. }
  1906. void UnitTestQuatExpLog()
  1907. {
  1908. for ( int i = 0; i < 300000; ++i )
  1909. {
  1910. Quaternion q = RandomQuaternion();
  1911. Vector l = QuaternionLog( q );
  1912. Quaternion q2 = Exp( l );
  1913. Assert( QuaternionLength( q - q2 ) < 0.0001f );
  1914. }
  1915. }
  1916. void UnitTestRotateBetween()
  1917. {
  1918. RandomSeed( 1 );
  1919. float flMaxError = 0;
  1920. int nMaxError;
  1921. for ( int i = 0; i < 3000000; ++i )
  1922. {
  1923. Vector u = RandomVectorOnUnitSphere(), v = RandomVectorOnUnitSphere();
  1924. Quaternion q = RotateBetween( u, v );
  1925. float flError = ( VectorTransform( u, QuaternionMatrix( q ) ) - v ).Length();
  1926. if ( flMaxError < flError )
  1927. {
  1928. flMaxError = flError;
  1929. nMaxError = i;
  1930. }
  1931. }
  1932. Assert( flMaxError < 0.001f );
  1933. }
  1934. //-----------------------------------------------------------------------------
  1935. // Purpose: Converts a quaternion into engine angles
  1936. // Input : *quaternion - q3 + q0.i + q1.j + q2.k
  1937. // *outAngles - PITCH, YAW, ROLL
  1938. //-----------------------------------------------------------------------------
  1939. void QuaternionAngles( const Quaternion &q, QAngle &angles )
  1940. {
  1941. Assert( s_bMathlibInitialized );
  1942. Assert( q.IsValid() );
  1943. #ifdef _VPROF_MATHLIB
  1944. VPROF_BUDGET( "QuaternionAngles", "Mathlib" );
  1945. #endif
  1946. #if 1
  1947. // FIXME: doing it this way calculates too much data, needs to do an optimized version...
  1948. matrix3x4_t matrix;
  1949. QuaternionMatrix( q, matrix );
  1950. MatrixAngles( matrix, angles );
  1951. #else
  1952. float m11, m12, m13, m23, m33;
  1953. m11 = ( 2.0f * q.w * q.w ) + ( 2.0f * q.x * q.x ) - 1.0f;
  1954. m12 = ( 2.0f * q.x * q.y ) + ( 2.0f * q.w * q.z );
  1955. m13 = ( 2.0f * q.x * q.z ) - ( 2.0f * q.w * q.y );
  1956. m23 = ( 2.0f * q.y * q.z ) + ( 2.0f * q.w * q.x );
  1957. m33 = ( 2.0f * q.w * q.w ) + ( 2.0f * q.z * q.z ) - 1.0f;
  1958. // FIXME: this code has a singularity near PITCH +-90
  1959. angles[YAW] = RAD2DEG( atan2(m12, m11) );
  1960. angles[PITCH] = RAD2DEG( asin(-m13) );
  1961. angles[ROLL] = RAD2DEG( atan2(m23, m33) );
  1962. #endif
  1963. Assert( angles.IsValid() );
  1964. }
  1965. float QuaternionionGetYaw( const Quaternion &q )
  1966. {
  1967. // FIXME: doing it this way calculates too much data, need to do an optimized version...
  1968. QAngle angles;
  1969. matrix3x4_t matrix;
  1970. QuaternionMatrix( q, matrix );
  1971. MatrixAngles( matrix, angles );
  1972. return angles[ YAW ];
  1973. }
  1974. float QuaternionionGetPitch( const Quaternion &q )
  1975. {
  1976. // FIXME: doing it this way calculates too much data, need to do an optimized version...
  1977. QAngle angles;
  1978. matrix3x4_t matrix;
  1979. QuaternionMatrix( q, matrix );
  1980. MatrixAngles( matrix, angles );
  1981. return angles[ PITCH ];
  1982. }
  1983. float QuaternionionGetRoll( const Quaternion &q )
  1984. {
  1985. // FIXME: doing it this way calculates too much data, need to do an optimized version...
  1986. QAngle angles;
  1987. matrix3x4_t matrix;
  1988. QuaternionMatrix( q, matrix );
  1989. MatrixAngles( matrix, angles );
  1990. return angles[ ROLL ];
  1991. }
  1992. //-----------------------------------------------------------------------------
  1993. // Purpose: Converts a quaternion into FLU vectors
  1994. // Input : *quaternion - q3 + q0.i + q1.j + q2.k
  1995. // basis vectors, each vector is optional
  1996. //-----------------------------------------------------------------------------
  1997. void QuaternionVectorsFLU( Quaternion const &q, Vector *pForward, Vector *pLeft, Vector *pUp )
  1998. {
  1999. Assert( s_bMathlibInitialized );
  2000. Assert( q.IsValid() );
  2001. #ifdef _VPROF_MATHLIB
  2002. // @TODO: VPROF_BUDGET( "QuaternionVectorsFLU", "Mathlib" );
  2003. #endif
  2004. // Note: it's pretty much identical to just computing the quaternion matrix and assigning its columns to the vectors
  2005. *pForward = q.GetForward();
  2006. *pLeft = q.GetLeft();
  2007. *pUp = q.GetUp();
  2008. #ifdef DBGFLAG_ASSERT
  2009. matrix3x4_t matrix;
  2010. QuaternionMatrix( q, matrix );
  2011. Vector forward, left, up;
  2012. MatrixVectorsFLU( matrix, &forward, &left, &up );
  2013. Assert( ( forward - *pForward ).Length() + ( left - *pLeft ).Length() + ( up - *pUp ).Length() < 1e-4f );
  2014. #endif
  2015. }
  2016. void QuaternionVectorsForward( const Quaternion& q, Vector *pForward )
  2017. {
  2018. Assert( s_bMathlibInitialized );
  2019. Assert( q.IsValid() );
  2020. #ifdef _VPROF_MATHLIB
  2021. // @TODO: VPROF_BUDGET( "QuaternionVectorsForward", "Mathlib" );
  2022. #endif
  2023. *pForward = q.GetForward();
  2024. #ifdef DBGFLAG_ASSERT
  2025. matrix3x4_t matrix;
  2026. QuaternionMatrix( q, matrix );
  2027. Assert( ( MatrixGetColumn( matrix, FORWARD_AXIS ) - *pForward ).Length() < 1e-4f );
  2028. #endif
  2029. }
  2030. void UnitTestVectorFLU()
  2031. {
  2032. for ( int i = 0; i < 100000; ++i )
  2033. {
  2034. Quaternion q = RandomQuaternion();
  2035. Vector forward, left, up;
  2036. QuaternionVectorsForward( q, &forward );
  2037. QuaternionVectorsFLU( q, &forward, &left, &up );
  2038. }
  2039. }
  2040. #if !defined(__SPU__)
  2041. //-----------------------------------------------------------------------------
  2042. // Purpose: Converts a quaternion to an axis / angle in degrees
  2043. // (exponential map)
  2044. //-----------------------------------------------------------------------------
  2045. void QuaternionAxisAngle( const Quaternion &q, Vector &axis, float &angle )
  2046. {
  2047. angle = RAD2DEG(2 * acos(q.w));
  2048. if ( angle > 180 )
  2049. {
  2050. angle -= 360;
  2051. }
  2052. axis.x = q.x;
  2053. axis.y = q.y;
  2054. axis.z = q.z;
  2055. VectorNormalize( axis );
  2056. }
  2057. //-----------------------------------------------------------------------------
  2058. // Purpose: Converts an exponential map (ang/axis) to a quaternion
  2059. //-----------------------------------------------------------------------------
  2060. void AxisAngleQuaternion( const Vector &axis, float angle, Quaternion &q )
  2061. {
  2062. float sa, ca;
  2063. SinCos( DEG2RAD(angle) * 0.5f, &sa, &ca );
  2064. q.x = axis.x * sa;
  2065. q.y = axis.y * sa;
  2066. q.z = axis.z * sa;
  2067. q.w = ca;
  2068. }
  2069. #endif // #if !defined(__SPU__)
  2070. //-----------------------------------------------------------------------------
  2071. // Purpose: Converts radian-euler axis aligned angles to a quaternion
  2072. // Input : *pfAngles - Right-handed Euler angles in radians
  2073. // *outQuat - quaternion of form (i,j,k,real)
  2074. //-----------------------------------------------------------------------------
  2075. void AngleQuaternion( const RadianEuler &angles, Quaternion &outQuat )
  2076. {
  2077. Assert( s_bMathlibInitialized );
  2078. // Assert( angles.IsValid() );
  2079. #ifdef _VPROF_MATHLIB
  2080. VPROF_BUDGET( "AngleQuaternion", "Mathlib" );
  2081. #endif
  2082. float sr, sp, sy, cr, cp, cy;
  2083. #ifdef _X360
  2084. fltx4 radians, scale, sine, cosine;
  2085. radians = LoadUnaligned3SIMD( &angles.x );
  2086. scale = ReplicateX4( 0.5f );
  2087. radians = MulSIMD( radians, scale );
  2088. SinCos3SIMD( sine, cosine, radians );
  2089. // NOTE: The ordering here is *different* from the AngleQuaternion below
  2090. // because p, y, r are not in the same locations in QAngle + RadianEuler. Yay!
  2091. sr = SubFloat( sine, 0 ); sp = SubFloat( sine, 1 ); sy = SubFloat( sine, 2 );
  2092. cr = SubFloat( cosine, 0 ); cp = SubFloat( cosine, 1 ); cy = SubFloat( cosine, 2 );
  2093. #else
  2094. SinCos( angles.z * 0.5f, &sy, &cy );
  2095. SinCos( angles.y * 0.5f, &sp, &cp );
  2096. SinCos( angles.x * 0.5f, &sr, &cr );
  2097. #endif
  2098. // NJS: for some reason VC6 wasn't recognizing the common subexpressions:
  2099. float srXcp = sr * cp, crXsp = cr * sp;
  2100. outQuat.x = srXcp*cy-crXsp*sy; // X
  2101. outQuat.y = crXsp*cy+srXcp*sy; // Y
  2102. float crXcp = cr * cp, srXsp = sr * sp;
  2103. outQuat.z = crXcp*sy-srXsp*cy; // Z
  2104. outQuat.w = crXcp*cy+srXsp*sy; // W (real component)
  2105. }
  2106. #ifdef _X360
  2107. //-----------------------------------------------------------------------------
  2108. // Purpose: Converts radian-euler axis aligned angles to a quaternion, returning
  2109. // it on a vector register.
  2110. // Input : *vAngles - Right-handed Euler angles in radians (roll pitch yaw)
  2111. //
  2112. // Algorithm based on that found in the XDK (which really uses RPY order, as
  2113. // opposed to this which takes the parameters in RPY order but catenates them
  2114. // in PYR order).
  2115. //-----------------------------------------------------------------------------
  2116. fltx4 AngleQuaternionSIMD( FLTX4 vAngles )
  2117. {
  2118. Assert( s_bMathlibInitialized );
  2119. // Assert( angles.IsValid() );
  2120. #ifdef _VPROF_MATHLIB
  2121. VPROF_BUDGET( "AngleQuaternion", "Mathlib" );
  2122. #endif
  2123. // we compute the sin and cos of half all the angles.
  2124. // in the comments I'll call these components
  2125. // sr = sin(r/2), cp = cos(p/2), sy = sin(y/2), etc.
  2126. fltx4 OneHalf = __vspltisw(1);
  2127. OneHalf = __vcfsx(OneHalf, 1);
  2128. fltx4 HalfAngles = MulSIMD(vAngles, OneHalf);
  2129. fltx4 sine,cosine;
  2130. SinCos3SIMD(sine, cosine, HalfAngles);
  2131. fltx4 SignMask = __vspltisw(-1);
  2132. fltx4 Zero = __vspltisw(0);
  2133. SignMask = __vslw(SignMask, SignMask); // shift left so 1 is only in the sign bit
  2134. SignMask = __vrlimi(SignMask, Zero, 0x5, 0); // { -1, 0, -1, 0 }
  2135. fltx4 Rc, Pc, Yc, Rs, Ps, Ys, retsum, retval;
  2136. Rc = __vspltw(cosine, 0); // cr cr cr cr
  2137. Pc = __vspltw(cosine, 1); // cp cp cp cp
  2138. Yc = __vspltw(cosine, 2); // cy cy cy cy
  2139. Rs = __vspltw(sine, 0); // sr sr sr sr
  2140. Ps = __vspltw(sine, 1); // sp sp sp sp
  2141. Ys = __vspltw(sine, 2); // sy sy sy sy
  2142. Rc = __vrlimi(Rc, sine, 0x8, 0); // sr cr cr cr
  2143. Rs = __vrlimi(Rs, cosine, 0x8, 0); // cr sr sr sr
  2144. Pc = __vrlimi(Pc, sine, 0x4, 0); // cp sp cp cp
  2145. Ps = __vrlimi(Ps, cosine, 0x4, 0); // sp cp sp sp
  2146. Yc = __vrlimi(Yc, sine, 0x2, 0); // cy cy sy cy
  2147. Ys = __vrlimi(Ys, cosine, 0x2, 0); // sy sy cy sy
  2148. retsum = __vxor(Rs, SignMask); // -cr sr -sr sr
  2149. retval = __vmulfp(Pc, Yc); // cp*cy sp*cy cp*sy cp*cy
  2150. retsum = __vmulfp(retsum, Ys); // -cr*sy sr*sy -sr*cy sr*sy
  2151. retval = __vmulfp(retval, Rc); // cp*cy*sr sp*cy*cr cp*sy*cr cp*cy*cr
  2152. retval = __vmaddfp(retsum, Ps, retval); // cp*cy*sr + -cr*sy*sp ...
  2153. return retval;
  2154. }
  2155. inline fltx4 AngleQuaternionSIMD( const RadianEuler &angles )
  2156. {
  2157. return AngleQuaternionSIMD(LoadUnaligned3SIMD(angles.Base()));
  2158. }
  2159. #endif
  2160. //-----------------------------------------------------------------------------
  2161. // Purpose: Converts engine-format euler angles to a quaternion
  2162. // Input : angles - Right-handed Euler angles in degrees as follows:
  2163. // [0]: PITCH: Clockwise rotation around the Y axis.
  2164. // [1]: YAW: Counterclockwise rotation around the Z axis.
  2165. // [2]: ROLL: Counterclockwise rotation around the X axis.
  2166. // *outQuat - quaternion of form (i,j,k,real)
  2167. //-----------------------------------------------------------------------------
  2168. void AngleQuaternion( const QAngle &angles, Quaternion &outQuat )
  2169. {
  2170. #ifdef _VPROF_MATHLIB
  2171. VPROF_BUDGET( "AngleQuaternion", "Mathlib" );
  2172. #endif
  2173. float sr, sp, sy, cr, cp, cy;
  2174. #ifdef _X360
  2175. fltx4 radians, scale, sine, cosine;
  2176. radians = LoadUnaligned3SIMD( angles.Base() );
  2177. scale = ReplicateX4( 0.5f * M_PI_F / 180.f );
  2178. radians = MulSIMD( radians, scale );
  2179. SinCos3SIMD( sine, cosine, radians );
  2180. // NOTE: The ordering here is *different* from the AngleQuaternion above
  2181. // because p, y, r are not in the same locations in QAngle + RadianEuler. Yay!
  2182. sp = SubFloat( sine, 0 ); sy = SubFloat( sine, 1 ); sr = SubFloat( sine, 2 );
  2183. cp = SubFloat( cosine, 0 ); cy = SubFloat( cosine, 1 ); cr = SubFloat( cosine, 2 );
  2184. #else
  2185. SinCos( DEG2RAD( angles.y ) * 0.5f, &sy, &cy );
  2186. SinCos( DEG2RAD( angles.x ) * 0.5f, &sp, &cp );
  2187. SinCos( DEG2RAD( angles.z ) * 0.5f, &sr, &cr );
  2188. #endif
  2189. // NJS: for some reason VC6 wasn't recognizing the common subexpressions:
  2190. float srXcp = sr * cp, crXsp = cr * sp;
  2191. outQuat.x = srXcp*cy-crXsp*sy; // X
  2192. outQuat.y = crXsp*cy+srXcp*sy; // Y
  2193. float crXcp = cr * cp, srXsp = sr * sp;
  2194. outQuat.z = crXcp*sy-srXsp*cy; // Z
  2195. outQuat.w = crXcp*cy+srXsp*sy; // W (real component)
  2196. }
  2197. #if !defined(__SPU__)
  2198. //-----------------------------------------------------------------------------
  2199. // Purpose: Converts a basis to a quaternion
  2200. //-----------------------------------------------------------------------------
  2201. void BasisToQuaternion( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp, Quaternion &q )
  2202. {
  2203. Assert( fabs( vecForward.LengthSqr() - 1.0f ) < 1e-3 );
  2204. Assert( fabs( vecRight.LengthSqr() - 1.0f ) < 1e-3 );
  2205. Assert( fabs( vecUp.LengthSqr() - 1.0f ) < 1e-3 );
  2206. Vector vecLeft;
  2207. VectorMultiply( vecRight, -1.0f, vecLeft );
  2208. // FIXME: Don't know why, but this doesn't match at all with other result
  2209. // so we can't use this super-fast way.
  2210. /*
  2211. // Find the trace of the matrix:
  2212. float flTrace = vecForward.x + vecLeft.y + vecUp.z + 1.0f;
  2213. if ( flTrace > 1e-6 )
  2214. {
  2215. float flSqrtTrace = FastSqrt( flTrace );
  2216. float s = 0.5f / flSqrtTrace;
  2217. q.x = ( vecUp.y - vecLeft.z ) * s;
  2218. q.y = ( vecForward.z - vecUp.x ) * s;
  2219. q.z = ( vecLeft.x - vecForward.y ) * s;
  2220. q.w = 0.5f * flSqrtTrace;
  2221. }
  2222. else
  2223. {
  2224. if (( vecForward.x > vecLeft.y ) && ( vecForward.x > vecUp.z ) )
  2225. {
  2226. float flSqrtTrace = FastSqrt( 1.0f + vecForward.x - vecLeft.y - vecUp.z );
  2227. float s = 0.5f / flSqrtTrace;
  2228. q.x = 0.5f * flSqrtTrace;
  2229. q.y = ( vecForward.y + vecLeft.x ) * s;
  2230. q.z = ( vecUp.x + vecForward.z ) * s;
  2231. q.w = ( vecUp.y - vecLeft.z ) * s;
  2232. }
  2233. else if ( vecLeft.y > vecUp.z )
  2234. {
  2235. float flSqrtTrace = FastSqrt( 1.0f + vecLeft.y - vecForward.x - vecUp.z );
  2236. float s = 0.5f / flSqrtTrace;
  2237. q.x = ( vecForward.y + vecLeft.x ) * s;
  2238. q.y = 0.5f * flSqrtTrace;
  2239. q.z = ( vecUp.y + vecLeft.z ) * s;
  2240. q.w = ( vecForward.z - vecUp.x ) * s;
  2241. }
  2242. else
  2243. {
  2244. float flSqrtTrace = FastSqrt( 1.0 + vecUp.z - vecForward.x - vecLeft.y );
  2245. float s = 0.5f / flSqrtTrace;
  2246. q.x = ( vecUp.x + vecForward.z ) * s;
  2247. q.y = ( vecUp.y + vecLeft.z ) * s;
  2248. q.z = 0.5f * flSqrtTrace;
  2249. q.w = ( vecLeft.x - vecForward.y ) * s;
  2250. }
  2251. }
  2252. QuaternionNormalize( q );
  2253. */
  2254. // Version 2: Go through angles
  2255. matrix3x4_t mat;
  2256. MatrixSetColumn( vecForward, 0, mat );
  2257. MatrixSetColumn( vecLeft, 1, mat );
  2258. MatrixSetColumn( vecUp, 2, mat );
  2259. QAngle angles;
  2260. MatrixAngles( mat, angles );
  2261. // Quaternion q2;
  2262. AngleQuaternion( angles, q );
  2263. // Assert( fabs(q.x - q2.x) < 1e-3 );
  2264. // Assert( fabs(q.y - q2.y) < 1e-3 );
  2265. // Assert( fabs(q.z - q2.z) < 1e-3 );
  2266. // Assert( fabs(q.w - q2.w) < 1e-3 );
  2267. }
  2268. // FIXME: Optimize!
  2269. void MatrixQuaternion( const matrix3x4_t &mat, Quaternion &q )
  2270. {
  2271. QAngle angles;
  2272. MatrixAngles( mat, angles );
  2273. AngleQuaternion( angles, q );
  2274. }
  2275. #endif // #if !defined(__SPU__)
  2276. void MatrixQuaternionFast( const matrix3x4_t &mat, Quaternion &q )
  2277. {
  2278. float t;
  2279. if ( mat[ 2 ][ 2 ] < 0 )
  2280. {
  2281. if ( mat[ 0 ][ 0 ] > mat[ 1 ][ 1 ] )
  2282. {
  2283. t = 1 + mat[ 0 ][ 0 ] - mat[ 1 ][ 1 ] - mat[ 2 ][ 2 ];
  2284. q.Init( t, mat[ 0 ][ 1 ] + mat[ 1 ][ 0 ], mat[ 2 ][ 0 ] + mat[ 0 ][ 2 ], mat[ 2 ][ 1 ] - mat[ 1 ][ 2 ] );
  2285. }
  2286. else
  2287. {
  2288. t = 1 - mat[ 0 ][ 0 ] + mat[ 1 ][ 1 ] - mat[ 2 ][ 2 ];
  2289. q.Init( mat[ 0 ][ 1 ] + mat[ 1 ][ 0 ], t, mat[ 1 ][ 2 ] + mat[ 2 ][ 1 ], mat[ 0 ][ 2 ] - mat[ 2 ][ 0 ] );
  2290. }
  2291. }
  2292. else
  2293. {
  2294. if ( mat[ 0 ][ 0 ] < -mat[ 1 ][ 1 ] )
  2295. {
  2296. t = 1 - mat[ 0 ][ 0 ] - mat[ 1 ][ 1 ] + mat[ 2 ][ 2 ];
  2297. q.Init( mat[ 2 ][ 0 ] + mat[ 0 ][ 2 ], mat[ 1 ][ 2 ] + mat[ 2 ][ 1 ], t, mat[ 1 ][ 0 ] - mat[ 0 ][ 1 ] );
  2298. }
  2299. else
  2300. {
  2301. t = 1 + mat[ 0 ][ 0 ] + mat[ 1 ][ 1 ] + mat[ 2 ][ 2 ];
  2302. q.Init( mat[ 2 ][ 1 ] - mat[ 1 ][ 2 ], mat[ 0 ][ 2 ] - mat[ 2 ][ 0 ], mat[ 1 ][ 0 ] - mat[ 0 ][ 1 ], t );
  2303. }
  2304. }
  2305. q = q * ( 0.5f / sqrtf( t ) );
  2306. }
  2307. float MatrixQuaternionTest( uint nCount )
  2308. {
  2309. float flMaxError = 0, flSumError = 0;
  2310. for ( uint i = 0; i < nCount; ++i )
  2311. {
  2312. Quaternion q = RandomQuaternion(), r;
  2313. Assert( fabsf( q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w - 1 ) < 1e-5f );
  2314. matrix3x4_t mat;
  2315. QuaternionMatrix( q, mat );
  2316. MatrixQuaternion( mat, r );
  2317. if ( QuaternionDotProduct( q, r ) < 0 )
  2318. {
  2319. r = -r;
  2320. }
  2321. float flError = Sqr( q.x - r.x ) + Sqr( q.y - r.y ) + Sqr( q.z - r.z ) + Sqr( q.w - r.w );
  2322. flSumError += flError;
  2323. if ( flError > flMaxError )
  2324. {
  2325. flMaxError = flError;
  2326. }
  2327. }
  2328. NOTE_UNUSED( flMaxError ); NOTE_UNUSED( flSumError );
  2329. return flSumError / nCount;
  2330. }
  2331. float MatrixQuaternionFastTest( uint nCount )
  2332. {
  2333. float flMaxError = 0, flSumError = 0;
  2334. for ( uint i = 0; i < nCount; ++i )
  2335. {
  2336. Quaternion q = RandomQuaternion(), r;
  2337. Assert( fabsf( q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w - 1 ) < 1e-5f );
  2338. matrix3x4_t mat;
  2339. QuaternionMatrix( q, mat );
  2340. MatrixQuaternionFast( mat, r );
  2341. if ( QuaternionDotProduct( q, r ) < 0 )
  2342. {
  2343. r = -r;
  2344. }
  2345. float flError = Sqr( q.x - r.x ) + Sqr( q.y - r.y ) + Sqr( q.z - r.z ) + Sqr( q.w - r.w );
  2346. flSumError += flError;
  2347. if ( flError > flMaxError )
  2348. {
  2349. flMaxError = flError;
  2350. }
  2351. }
  2352. NOTE_UNUSED( flMaxError ); NOTE_UNUSED( flSumError );
  2353. return flSumError / nCount;
  2354. }
  2355. // the same as MatrixQuaternionTest, but uses inline helper functions that return matrix and quaternion instead of using return-by-reference versions
  2356. // on MSVC10, this generates the same code as MatrixQuaternionTest, but it's easier to read, write and maintain code
  2357. float MatrixQuaternionTest2( uint nCount )
  2358. {
  2359. float flMaxError = 0, flSumError = 0;
  2360. for ( uint i = 0; i < nCount; ++i )
  2361. {
  2362. Quaternion q = RandomQuaternion(), r;
  2363. Assert( fabsf( q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w - 1 ) < 1e-5f );
  2364. matrix3x4_t mat = QuaternionMatrix( q );
  2365. r = MatrixQuaternion( mat );
  2366. if ( QuaternionDotProduct( q, r ) < 0 )
  2367. {
  2368. r = -r;
  2369. }
  2370. float flError = Sqr( q.x - r.x ) + Sqr( q.y - r.y ) + Sqr( q.z - r.z ) + Sqr( q.w - r.w );
  2371. flSumError += flError;
  2372. if ( flError > flMaxError )
  2373. {
  2374. flMaxError = flError;
  2375. }
  2376. }
  2377. NOTE_UNUSED( flMaxError ); NOTE_UNUSED( flSumError );
  2378. return flSumError / nCount;
  2379. }
  2380. //-----------------------------------------------------------------------------
  2381. // Purpose: Converts a quaternion into engine angles
  2382. // Input : *quaternion - q3 + q0.i + q1.j + q2.k
  2383. // *outAngles - PITCH, YAW, ROLL
  2384. //-----------------------------------------------------------------------------
  2385. void QuaternionAngles( const Quaternion &q, RadianEuler &angles )
  2386. {
  2387. Assert( s_bMathlibInitialized );
  2388. Assert( q.IsValid() );
  2389. // FIXME: doing it this way calculates too much data, needs to do an optimized version...
  2390. matrix3x4_t matrix;
  2391. QuaternionMatrix( q, matrix );
  2392. MatrixAngles( matrix, angles );
  2393. Assert( angles.IsValid() );
  2394. }
  2395. #if !defined(__SPU__)
  2396. //-----------------------------------------------------------------------------
  2397. // Purpose: A helper function to normalize p2.x->p1.x and p3.x->p4.x to
  2398. // be the same length as p2.x->p3.x
  2399. // Input : &p2 -
  2400. // &p4 -
  2401. // p4n -
  2402. //-----------------------------------------------------------------------------
  2403. void Spline_Normalize(
  2404. const Vector &p1,
  2405. const Vector &p2,
  2406. const Vector &p3,
  2407. const Vector &p4,
  2408. Vector& p1n,
  2409. Vector& p4n )
  2410. {
  2411. float dt = p3.x - p2.x;
  2412. p1n = p1;
  2413. p4n = p4;
  2414. if ( dt != 0.0 )
  2415. {
  2416. if (p1.x != p2.x)
  2417. {
  2418. // Equivalent to p1n = p2 - (p2 - p1) * (dt / (p2.x - p1.x));
  2419. VectorLerp( p2, p1, dt / (p2.x - p1.x), p1n );
  2420. }
  2421. if (p4.x != p3.x)
  2422. {
  2423. // Equivalent to p4n = p3 + (p4 - p3) * (dt / (p4.x - p3.x));
  2424. VectorLerp( p3, p4, dt / (p4.x - p3.x), p4n );
  2425. }
  2426. }
  2427. }
  2428. #endif // #if !defined(__SPU__)
  2429. #if !defined(__SPU__)
  2430. //-----------------------------------------------------------------------------
  2431. // Purpose:
  2432. // Input :
  2433. //-----------------------------------------------------------------------------
  2434. void Catmull_Rom_Spline(
  2435. const Vector &p1,
  2436. const Vector &p2,
  2437. const Vector &p3,
  2438. const Vector &p4,
  2439. float t,
  2440. Vector& output )
  2441. {
  2442. Assert( s_bMathlibInitialized );
  2443. float tSqr = t*t*0.5f;
  2444. float tSqrSqr = t*tSqr;
  2445. t *= 0.5f;
  2446. Assert( &output != &p1 );
  2447. Assert( &output != &p2 );
  2448. Assert( &output != &p3 );
  2449. Assert( &output != &p4 );
  2450. output.Init();
  2451. Vector a, b, c, d;
  2452. // matrix row 1
  2453. VectorScale( p1, -tSqrSqr, a ); // 0.5 t^3 * [ (-1*p1) + ( 3*p2) + (-3*p3) + p4 ]
  2454. VectorScale( p2, tSqrSqr*3, b );
  2455. VectorScale( p3, tSqrSqr*-3, c );
  2456. VectorScale( p4, tSqrSqr, d );
  2457. VectorAdd( a, output, output );
  2458. VectorAdd( b, output, output );
  2459. VectorAdd( c, output, output );
  2460. VectorAdd( d, output, output );
  2461. // matrix row 2
  2462. VectorScale( p1, tSqr*2, a ); // 0.5 t^2 * [ ( 2*p1) + (-5*p2) + ( 4*p3) - p4 ]
  2463. VectorScale( p2, tSqr*-5, b );
  2464. VectorScale( p3, tSqr*4, c );
  2465. VectorScale( p4, -tSqr, d );
  2466. VectorAdd( a, output, output );
  2467. VectorAdd( b, output, output );
  2468. VectorAdd( c, output, output );
  2469. VectorAdd( d, output, output );
  2470. // matrix row 3
  2471. VectorScale( p1, -t, a ); // 0.5 t * [ (-1*p1) + p3 ]
  2472. VectorScale( p3, t, b );
  2473. VectorAdd( a, output, output );
  2474. VectorAdd( b, output, output );
  2475. // matrix row 4
  2476. VectorAdd( p2, output, output ); // p2
  2477. }
  2478. void Catmull_Rom_Spline_Tangent(
  2479. const Vector &p1,
  2480. const Vector &p2,
  2481. const Vector &p3,
  2482. const Vector &p4,
  2483. float t,
  2484. Vector& output )
  2485. {
  2486. Assert( s_bMathlibInitialized );
  2487. float tOne = 3*t*t*0.5f;
  2488. float tTwo = 2*t*0.5f;
  2489. float tThree = 0.5;
  2490. Assert( &output != &p1 );
  2491. Assert( &output != &p2 );
  2492. Assert( &output != &p3 );
  2493. Assert( &output != &p4 );
  2494. output.Init();
  2495. Vector a, b, c, d;
  2496. // matrix row 1
  2497. VectorScale( p1, -tOne, a ); // 0.5 t^3 * [ (-1*p1) + ( 3*p2) + (-3*p3) + p4 ]
  2498. VectorScale( p2, tOne*3, b );
  2499. VectorScale( p3, tOne*-3, c );
  2500. VectorScale( p4, tOne, d );
  2501. VectorAdd( a, output, output );
  2502. VectorAdd( b, output, output );
  2503. VectorAdd( c, output, output );
  2504. VectorAdd( d, output, output );
  2505. // matrix row 2
  2506. VectorScale( p1, tTwo*2, a ); // 0.5 t^2 * [ ( 2*p1) + (-5*p2) + ( 4*p3) - p4 ]
  2507. VectorScale( p2, tTwo*-5, b );
  2508. VectorScale( p3, tTwo*4, c );
  2509. VectorScale( p4, -tTwo, d );
  2510. VectorAdd( a, output, output );
  2511. VectorAdd( b, output, output );
  2512. VectorAdd( c, output, output );
  2513. VectorAdd( d, output, output );
  2514. // matrix row 3
  2515. VectorScale( p1, -tThree, a ); // 0.5 t * [ (-1*p1) + p3 ]
  2516. VectorScale( p3, tThree, b );
  2517. VectorAdd( a, output, output );
  2518. VectorAdd( b, output, output );
  2519. }
  2520. // area under the curve [0..t]
  2521. void Catmull_Rom_Spline_Integral(
  2522. const Vector &p1,
  2523. const Vector &p2,
  2524. const Vector &p3,
  2525. const Vector &p4,
  2526. float t,
  2527. Vector& output )
  2528. {
  2529. output = p2*t
  2530. -0.25f*(p1 - p3)*t*t
  2531. + (1.0f/6.0f)*(2.0f*p1 - 5.0f*p2 + 4.0f*p3 - p4)*t*t*t
  2532. - 0.125f*(p1 - 3.0f*p2 + 3.0f*p3 - p4)*t*t*t*t;
  2533. }
  2534. // area under the curve [0..1]
  2535. void Catmull_Rom_Spline_Integral(
  2536. const Vector &p1,
  2537. const Vector &p2,
  2538. const Vector &p3,
  2539. const Vector &p4,
  2540. Vector& output )
  2541. {
  2542. output = (-0.25f * p1 + 3.25f * p2 + 3.25f * p3 - 0.25f * p4) * (1.0f / 6.0f);
  2543. }
  2544. void Catmull_Rom_Spline_Normalize(
  2545. const Vector &p1,
  2546. const Vector &p2,
  2547. const Vector &p3,
  2548. const Vector &p4,
  2549. float t,
  2550. Vector& output )
  2551. {
  2552. // Normalize p2->p1 and p3->p4 to be the same length as p2->p3
  2553. float dt = p3.DistTo(p2);
  2554. Vector p1n, p4n;
  2555. VectorSubtract( p1, p2, p1n );
  2556. VectorSubtract( p4, p3, p4n );
  2557. VectorNormalize( p1n );
  2558. VectorNormalize( p4n );
  2559. VectorMA( p2, dt, p1n, p1n );
  2560. VectorMA( p3, dt, p4n, p4n );
  2561. Catmull_Rom_Spline( p1n, p2, p3, p4n, t, output );
  2562. }
  2563. void Catmull_Rom_Spline_Integral_Normalize(
  2564. const Vector &p1,
  2565. const Vector &p2,
  2566. const Vector &p3,
  2567. const Vector &p4,
  2568. float t,
  2569. Vector& output )
  2570. {
  2571. // Normalize p2->p1 and p3->p4 to be the same length as p2->p3
  2572. float dt = p3.DistTo(p2);
  2573. Vector p1n, p4n;
  2574. VectorSubtract( p1, p2, p1n );
  2575. VectorSubtract( p4, p3, p4n );
  2576. VectorNormalize( p1n );
  2577. VectorNormalize( p4n );
  2578. VectorMA( p2, dt, p1n, p1n );
  2579. VectorMA( p3, dt, p4n, p4n );
  2580. Catmull_Rom_Spline_Integral( p1n, p2, p3, p4n, t, output );
  2581. }
  2582. void Catmull_Rom_Spline_NormalizeX(
  2583. const Vector &p1,
  2584. const Vector &p2,
  2585. const Vector &p3,
  2586. const Vector &p4,
  2587. float t,
  2588. Vector& output )
  2589. {
  2590. Vector p1n, p4n;
  2591. Spline_Normalize( p1, p2, p3, p4, p1n, p4n );
  2592. Catmull_Rom_Spline( p1n, p2, p3, p4n, t, output );
  2593. }
  2594. #endif // !defined(__SPU__)
  2595. //-----------------------------------------------------------------------------
  2596. // Purpose: basic hermite spline. t = 0 returns p1, t = 1 returns p2,
  2597. // d1 and d2 are used to entry and exit slope of curve
  2598. // Input :
  2599. //-----------------------------------------------------------------------------
  2600. void Hermite_Spline(
  2601. const Vector &p1,
  2602. const Vector &p2,
  2603. const Vector &d1,
  2604. const Vector &d2,
  2605. float t,
  2606. Vector& output )
  2607. {
  2608. Assert( s_bMathlibInitialized );
  2609. float tSqr = t*t;
  2610. float tCube = t*tSqr;
  2611. Assert( &output != &p1 );
  2612. Assert( &output != &p2 );
  2613. Assert( &output != &d1 );
  2614. Assert( &output != &d2 );
  2615. float b1 = 2.0f*tCube-3.0f*tSqr+1.0f;
  2616. float b2 = 1.0f - b1; // -2*tCube+3*tSqr;
  2617. float b3 = tCube-2*tSqr+t;
  2618. float b4 = tCube-tSqr;
  2619. VectorScale( p1, b1, output );
  2620. VectorMA( output, b2, p2, output );
  2621. VectorMA( output, b3, d1, output );
  2622. VectorMA( output, b4, d2, output );
  2623. }
  2624. float Hermite_Spline(
  2625. float p1,
  2626. float p2,
  2627. float d1,
  2628. float d2,
  2629. float t )
  2630. {
  2631. Assert( s_bMathlibInitialized );
  2632. float output;
  2633. float tSqr = t*t;
  2634. float tCube = t*tSqr;
  2635. float b1 = 2.0f*tCube-3.0f*tSqr+1.0f;
  2636. float b2 = 1.0f - b1; // -2*tCube+3*tSqr;
  2637. float b3 = tCube-2*tSqr+t;
  2638. float b4 = tCube-tSqr;
  2639. output = p1 * b1;
  2640. output += p2 * b2;
  2641. output += d1 * b3;
  2642. output += d2 * b4;
  2643. return output;
  2644. }
  2645. void Hermite_SplineBasis( float t, float basis[4] )
  2646. {
  2647. float tSqr = t*t;
  2648. float tCube = t*tSqr;
  2649. basis[0] = 2.0f*tCube-3.0f*tSqr+1.0f;
  2650. basis[1] = 1.0f - basis[0]; // -2*tCube+3*tSqr;
  2651. basis[2] = tCube-2*tSqr+t;
  2652. basis[3] = tCube-tSqr;
  2653. }
  2654. //-----------------------------------------------------------------------------
  2655. // Purpose: simple three data point hermite spline.
  2656. // t = 0 returns p1, t = 1 returns p2,
  2657. // slopes are generated from the p0->p1 and p1->p2 segments
  2658. // this is reasonable C1 method when there's no "p3" data yet.
  2659. // Input :
  2660. //-----------------------------------------------------------------------------
  2661. // BUG: the VectorSubtract()'s calls go away if the global optimizer is enabled
  2662. #if !defined(__SPU__)
  2663. #pragma optimize( "g", off )
  2664. #endif
  2665. void Hermite_Spline( const Vector &p0, const Vector &p1, const Vector &p2, float t, Vector& output )
  2666. {
  2667. Vector e10, e21;
  2668. VectorSubtract( p1, p0, e10 );
  2669. VectorSubtract( p2, p1, e21 );
  2670. Hermite_Spline( p1, p2, e10, e21, t, output );
  2671. }
  2672. #if !defined(__SPU__)
  2673. #pragma optimize( "", on )
  2674. #endif
  2675. float Hermite_Spline( float p0, float p1, float p2, float t )
  2676. {
  2677. return Hermite_Spline( p1, p2, p1 - p0, p2 - p1, t );
  2678. }
  2679. void Hermite_Spline( const Quaternion &q0, const Quaternion &q1, const Quaternion &q2, float t, Quaternion &output )
  2680. {
  2681. // cheap, hacked version of quaternions
  2682. Quaternion q0a;
  2683. Quaternion q1a;
  2684. QuaternionAlign( q2, q0, q0a );
  2685. QuaternionAlign( q2, q1, q1a );
  2686. output.x = Hermite_Spline( q0a.x, q1a.x, q2.x, t );
  2687. output.y = Hermite_Spline( q0a.y, q1a.y, q2.y, t );
  2688. output.z = Hermite_Spline( q0a.z, q1a.z, q2.z, t );
  2689. output.w = Hermite_Spline( q0a.w, q1a.w, q2.w, t );
  2690. QuaternionNormalize( output );
  2691. }
  2692. #if !defined(__SPU__)
  2693. // See http://en.wikipedia.org/wiki/Kochanek-Bartels_curves
  2694. //
  2695. // Tension: -1 = Round -> 1 = Tight
  2696. // Bias: -1 = Pre-shoot (bias left) -> 1 = Post-shoot (bias right)
  2697. // Continuity: -1 = Box corners -> 1 = Inverted corners
  2698. //
  2699. // If T=B=C=0 it's the same matrix as Catmull-Rom.
  2700. // If T=1 & B=C=0 it's the same as Cubic.
  2701. // If T=B=0 & C=-1 it's just linear interpolation
  2702. //
  2703. // See http://news.povray.org/povray.binaries.tutorials/attachment/%[email protected]%3E/Splines.bas.txt
  2704. // for example code and descriptions of various spline types...
  2705. //
  2706. void Kochanek_Bartels_Spline(
  2707. float tension,
  2708. float bias,
  2709. float continuity,
  2710. const Vector &p1,
  2711. const Vector &p2,
  2712. const Vector &p3,
  2713. const Vector &p4,
  2714. float t,
  2715. Vector& output )
  2716. {
  2717. Assert( s_bMathlibInitialized );
  2718. float ffa, ffb, ffc, ffd;
  2719. ffa = ( 1.0f - tension ) * ( 1.0f + continuity ) * ( 1.0f + bias );
  2720. ffb = ( 1.0f - tension ) * ( 1.0f - continuity ) * ( 1.0f - bias );
  2721. ffc = ( 1.0f - tension ) * ( 1.0f - continuity ) * ( 1.0f + bias );
  2722. ffd = ( 1.0f - tension ) * ( 1.0f + continuity ) * ( 1.0f - bias );
  2723. float tSqr = t*t*0.5f;
  2724. float tSqrSqr = t*tSqr;
  2725. t *= 0.5f;
  2726. Assert( &output != &p1 );
  2727. Assert( &output != &p2 );
  2728. Assert( &output != &p3 );
  2729. Assert( &output != &p4 );
  2730. output.Init();
  2731. Vector a, b, c, d;
  2732. // matrix row 1
  2733. VectorScale( p1, tSqrSqr * -ffa, a );
  2734. VectorScale( p2, tSqrSqr * ( 4.0f + ffa - ffb - ffc ), b );
  2735. VectorScale( p3, tSqrSqr * ( -4.0f + ffb + ffc - ffd ), c );
  2736. VectorScale( p4, tSqrSqr * ffd, d );
  2737. VectorAdd( a, output, output );
  2738. VectorAdd( b, output, output );
  2739. VectorAdd( c, output, output );
  2740. VectorAdd( d, output, output );
  2741. // matrix row 2
  2742. VectorScale( p1, tSqr* 2 * ffa, a );
  2743. VectorScale( p2, tSqr * ( -6 - 2 * ffa + 2 * ffb + ffc ), b );
  2744. VectorScale( p3, tSqr * ( 6 - 2 * ffb - ffc + ffd ), c );
  2745. VectorScale( p4, tSqr * -ffd, d );
  2746. VectorAdd( a, output, output );
  2747. VectorAdd( b, output, output );
  2748. VectorAdd( c, output, output );
  2749. VectorAdd( d, output, output );
  2750. // matrix row 3
  2751. VectorScale( p1, t * -ffa, a );
  2752. VectorScale( p2, t * ( ffa - ffb ), b );
  2753. VectorScale( p3, t * ffb, c );
  2754. // p4 unchanged
  2755. VectorAdd( a, output, output );
  2756. VectorAdd( b, output, output );
  2757. VectorAdd( c, output, output );
  2758. // matrix row 4
  2759. // p1, p3, p4 unchanged
  2760. // p2 is multiplied by 1 and added, so just added it directly
  2761. VectorAdd( p2, output, output );
  2762. }
  2763. void Kochanek_Bartels_Spline_NormalizeX(
  2764. float tension,
  2765. float bias,
  2766. float continuity,
  2767. const Vector &p1,
  2768. const Vector &p2,
  2769. const Vector &p3,
  2770. const Vector &p4,
  2771. float t,
  2772. Vector& output )
  2773. {
  2774. Vector p1n, p4n;
  2775. Spline_Normalize( p1, p2, p3, p4, p1n, p4n );
  2776. Kochanek_Bartels_Spline( tension, bias, continuity, p1n, p2, p3, p4n, t, output );
  2777. }
  2778. void Cubic_Spline(
  2779. const Vector &p1,
  2780. const Vector &p2,
  2781. const Vector &p3,
  2782. const Vector &p4,
  2783. float t,
  2784. Vector& output )
  2785. {
  2786. Assert( s_bMathlibInitialized );
  2787. float tSqr = t*t;
  2788. float tSqrSqr = t*tSqr;
  2789. Assert( &output != &p1 );
  2790. Assert( &output != &p2 );
  2791. Assert( &output != &p3 );
  2792. Assert( &output != &p4 );
  2793. output.Init();
  2794. Vector a, b, c, d;
  2795. // matrix row 1
  2796. VectorScale( p2, tSqrSqr * 2, b );
  2797. VectorScale( p3, tSqrSqr * -2, c );
  2798. VectorAdd( b, output, output );
  2799. VectorAdd( c, output, output );
  2800. // matrix row 2
  2801. VectorScale( p2, tSqr * -3, b );
  2802. VectorScale( p3, tSqr * 3, c );
  2803. VectorAdd( b, output, output );
  2804. VectorAdd( c, output, output );
  2805. // matrix row 3
  2806. // no influence
  2807. // p4 unchanged
  2808. // matrix row 4
  2809. // p1, p3, p4 unchanged
  2810. VectorAdd( p2, output, output );
  2811. }
  2812. void Cubic_Spline_NormalizeX(
  2813. const Vector &p1,
  2814. const Vector &p2,
  2815. const Vector &p3,
  2816. const Vector &p4,
  2817. float t,
  2818. Vector& output )
  2819. {
  2820. Vector p1n, p4n;
  2821. Spline_Normalize( p1, p2, p3, p4, p1n, p4n );
  2822. Cubic_Spline( p1n, p2, p3, p4n, t, output );
  2823. }
  2824. void BSpline(
  2825. const Vector &p1,
  2826. const Vector &p2,
  2827. const Vector &p3,
  2828. const Vector &p4,
  2829. float t,
  2830. Vector& output )
  2831. {
  2832. Assert( s_bMathlibInitialized );
  2833. float oneOver6 = 1.0f / 6.0f;
  2834. float tSqr = t * t * oneOver6;
  2835. float tSqrSqr = t*tSqr;
  2836. t *= oneOver6;
  2837. Assert( &output != &p1 );
  2838. Assert( &output != &p2 );
  2839. Assert( &output != &p3 );
  2840. Assert( &output != &p4 );
  2841. output.Init();
  2842. Vector a, b, c, d;
  2843. // matrix row 1
  2844. VectorScale( p1, -tSqrSqr, a );
  2845. VectorScale( p2, tSqrSqr * 3.0f, b );
  2846. VectorScale( p3, tSqrSqr * -3.0f, c );
  2847. VectorScale( p4, tSqrSqr, d );
  2848. VectorAdd( a, output, output );
  2849. VectorAdd( b, output, output );
  2850. VectorAdd( c, output, output );
  2851. VectorAdd( d, output, output );
  2852. // matrix row 2
  2853. VectorScale( p1, tSqr * 3.0f, a );
  2854. VectorScale( p2, tSqr * -6.0f, b );
  2855. VectorScale( p3, tSqr * 3.0f, c );
  2856. VectorAdd( a, output, output );
  2857. VectorAdd( b, output, output );
  2858. VectorAdd( c, output, output );
  2859. // matrix row 3
  2860. VectorScale( p1, t * -3.0f, a );
  2861. VectorScale( p3, t * 3.0f, c );
  2862. // p4 unchanged
  2863. VectorAdd( a, output, output );
  2864. VectorAdd( c, output, output );
  2865. // matrix row 4
  2866. // p1 and p3 scaled by 1.0f, so done below
  2867. VectorScale( p1, oneOver6, a );
  2868. VectorScale( p2, 4.0f * oneOver6, b );
  2869. VectorScale( p3, oneOver6, c );
  2870. VectorAdd( a, output, output );
  2871. VectorAdd( b, output, output );
  2872. VectorAdd( c, output, output );
  2873. }
  2874. void BSpline_NormalizeX(
  2875. const Vector &p1,
  2876. const Vector &p2,
  2877. const Vector &p3,
  2878. const Vector &p4,
  2879. float t,
  2880. Vector& output )
  2881. {
  2882. Vector p1n, p4n;
  2883. Spline_Normalize( p1, p2, p3, p4, p1n, p4n );
  2884. BSpline( p1n, p2, p3, p4n, t, output );
  2885. }
  2886. void Parabolic_Spline(
  2887. const Vector &p1,
  2888. const Vector &p2,
  2889. const Vector &p3,
  2890. const Vector &p4,
  2891. float t,
  2892. Vector& output )
  2893. {
  2894. Assert( s_bMathlibInitialized );
  2895. float tSqr = t*t*0.5f;
  2896. t *= 0.5f;
  2897. Assert( &output != &p1 );
  2898. Assert( &output != &p2 );
  2899. Assert( &output != &p3 );
  2900. Assert( &output != &p4 );
  2901. output.Init();
  2902. Vector a, b, c, d;
  2903. // matrix row 1
  2904. // no influence from t cubed
  2905. // matrix row 2
  2906. VectorScale( p1, tSqr, a );
  2907. VectorScale( p2, tSqr * -2.0f, b );
  2908. VectorScale( p3, tSqr, c );
  2909. VectorAdd( a, output, output );
  2910. VectorAdd( b, output, output );
  2911. VectorAdd( c, output, output );
  2912. // matrix row 3
  2913. VectorScale( p1, t * -2.0f, a );
  2914. VectorScale( p2, t * 2.0f, b );
  2915. // p4 unchanged
  2916. VectorAdd( a, output, output );
  2917. VectorAdd( b, output, output );
  2918. // matrix row 4
  2919. VectorScale( p1, 0.5f, a );
  2920. VectorScale( p2, 0.5f, b );
  2921. VectorAdd( a, output, output );
  2922. VectorAdd( b, output, output );
  2923. }
  2924. void Parabolic_Spline_NormalizeX(
  2925. const Vector &p1,
  2926. const Vector &p2,
  2927. const Vector &p3,
  2928. const Vector &p4,
  2929. float t,
  2930. Vector& output )
  2931. {
  2932. Vector p1n, p4n;
  2933. Spline_Normalize( p1, p2, p3, p4, p1n, p4n );
  2934. Parabolic_Spline( p1n, p2, p3, p4n, t, output );
  2935. }
  2936. //-----------------------------------------------------------------------------
  2937. // Cubic Bernstein basis functions
  2938. // http://mathworld.wolfram.com/BernsteinPolynomial.html
  2939. //
  2940. // Purpose: Evaluate the cubic Bernstein basis for the input parametric coordinate.
  2941. // Output is the coefficient for that basis polynomial.
  2942. //-----------------------------------------------------------------------------
  2943. float CubicBasis0( float t )
  2944. {
  2945. float invT = 1.0f-t;
  2946. return invT*invT*invT;
  2947. }
  2948. float CubicBasis1( float t )
  2949. {
  2950. float invT = 1.0f-t;
  2951. return 3.0f*t*invT*invT;
  2952. }
  2953. float CubicBasis2( float t )
  2954. {
  2955. float invT = 1.0f-t;
  2956. return 3.0f*t*t*invT;
  2957. }
  2958. float CubicBasis3( float t )
  2959. {
  2960. return t*t*t;
  2961. }
  2962. //-----------------------------------------------------------------------------
  2963. // Purpose: Compress the input values for a ranged result such that from 75% to 200% smoothly of the range maps
  2964. //-----------------------------------------------------------------------------
  2965. float RangeCompressor( float flValue, float flMin, float flMax, float flBase )
  2966. {
  2967. // clamp base
  2968. if (flBase < flMin)
  2969. flBase = flMin;
  2970. if (flBase > flMax)
  2971. flBase = flMax;
  2972. flValue += flBase;
  2973. // convert to 0 to 1 value
  2974. float flMid = (flValue - flMin) / (flMax - flMin);
  2975. // convert to -1 to 1 value
  2976. float flTarget = flMid * 2 - 1;
  2977. if (fabs(flTarget) > 0.75)
  2978. {
  2979. float t = (fabs(flTarget) - 0.75) / (1.25);
  2980. if (t < 1.0)
  2981. {
  2982. if (flTarget > 0)
  2983. {
  2984. flTarget = Hermite_Spline( 0.75, 1, 0.75, 0, t );
  2985. }
  2986. else
  2987. {
  2988. flTarget = -Hermite_Spline( 0.75, 1, 0.75, 0, t );
  2989. }
  2990. }
  2991. else
  2992. {
  2993. flTarget = (flTarget > 0) ? 1.0f : -1.0f;
  2994. }
  2995. }
  2996. flMid = (flTarget + 1 ) / 2.0;
  2997. flValue = flMin * (1 - flMid) + flMax * flMid;
  2998. flValue -= flBase;
  2999. return flValue;
  3000. }
  3001. //#pragma optimize( "", on )
  3002. //-----------------------------------------------------------------------------
  3003. // Transforms a AABB into another space; which will inherently grow the box.
  3004. //-----------------------------------------------------------------------------
  3005. void TransformAABB( const matrix3x4_t& transform, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut )
  3006. {
  3007. Vector localCenter;
  3008. VectorAdd( vecMinsIn, vecMaxsIn, localCenter );
  3009. localCenter *= 0.5f;
  3010. Vector localExtents;
  3011. VectorSubtract( vecMaxsIn, localCenter, localExtents );
  3012. Vector worldCenter;
  3013. VectorTransform( localCenter, transform, worldCenter );
  3014. Vector worldExtents;
  3015. worldExtents.x = DotProductAbs( localExtents, transform[0] );
  3016. worldExtents.y = DotProductAbs( localExtents, transform[1] );
  3017. worldExtents.z = DotProductAbs( localExtents, transform[2] );
  3018. VectorSubtract( worldCenter, worldExtents, vecMinsOut );
  3019. VectorAdd( worldCenter, worldExtents, vecMaxsOut );
  3020. // sanity chec
  3021. Assert( vecMinsOut.LengthSqr() + vecMaxsOut.LengthSqr() < 1e+12 );
  3022. }
  3023. //-----------------------------------------------------------------------------
  3024. // Uses the inverse transform of in1
  3025. //-----------------------------------------------------------------------------
  3026. void ITransformAABB( const matrix3x4_t& transform, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut )
  3027. {
  3028. Vector worldCenter;
  3029. VectorAdd( vecMinsIn, vecMaxsIn, worldCenter );
  3030. worldCenter *= 0.5f;
  3031. Vector worldExtents;
  3032. VectorSubtract( vecMaxsIn, worldCenter, worldExtents );
  3033. Vector localCenter;
  3034. VectorITransform( worldCenter, transform, localCenter );
  3035. Vector localExtents;
  3036. localExtents.x = FloatMakePositive( worldExtents.x * transform[0][0] ) +
  3037. FloatMakePositive( worldExtents.y * transform[1][0] ) +
  3038. FloatMakePositive( worldExtents.z * transform[2][0] );
  3039. localExtents.y = FloatMakePositive( worldExtents.x * transform[0][1] ) +
  3040. FloatMakePositive( worldExtents.y * transform[1][1] ) +
  3041. FloatMakePositive( worldExtents.z * transform[2][1] );
  3042. localExtents.z = FloatMakePositive( worldExtents.x * transform[0][2] ) +
  3043. FloatMakePositive( worldExtents.y * transform[1][2] ) +
  3044. FloatMakePositive( worldExtents.z * transform[2][2] );
  3045. VectorSubtract( localCenter, localExtents, vecMinsOut );
  3046. VectorAdd( localCenter, localExtents, vecMaxsOut );
  3047. }
  3048. //-----------------------------------------------------------------------------
  3049. // Rotates a AABB into another space; which will inherently grow the box.
  3050. // (same as TransformAABB, but doesn't take the translation into account)
  3051. //-----------------------------------------------------------------------------
  3052. void RotateAABB( const matrix3x4_t &transform, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut )
  3053. {
  3054. Vector localCenter;
  3055. VectorAdd( vecMinsIn, vecMaxsIn, localCenter );
  3056. localCenter *= 0.5f;
  3057. Vector localExtents;
  3058. VectorSubtract( vecMaxsIn, localCenter, localExtents );
  3059. Vector newCenter;
  3060. VectorRotate( localCenter, transform, newCenter );
  3061. Vector newExtents;
  3062. newExtents.x = DotProductAbs( localExtents, transform[0] );
  3063. newExtents.y = DotProductAbs( localExtents, transform[1] );
  3064. newExtents.z = DotProductAbs( localExtents, transform[2] );
  3065. VectorSubtract( newCenter, newExtents, vecMinsOut );
  3066. VectorAdd( newCenter, newExtents, vecMaxsOut );
  3067. }
  3068. //-----------------------------------------------------------------------------
  3069. // Uses the inverse transform of in1
  3070. //-----------------------------------------------------------------------------
  3071. void IRotateAABB( const matrix3x4_t &transform, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut )
  3072. {
  3073. Vector oldCenter;
  3074. VectorAdd( vecMinsIn, vecMaxsIn, oldCenter );
  3075. oldCenter *= 0.5f;
  3076. Vector oldExtents;
  3077. VectorSubtract( vecMaxsIn, oldCenter, oldExtents );
  3078. Vector newCenter;
  3079. VectorIRotate( oldCenter, transform, newCenter );
  3080. Vector newExtents;
  3081. newExtents.x = FloatMakePositive( oldExtents.x * transform[0][0] ) +
  3082. FloatMakePositive( oldExtents.y * transform[1][0] ) +
  3083. FloatMakePositive( oldExtents.z * transform[2][0] );
  3084. newExtents.y = FloatMakePositive( oldExtents.x * transform[0][1] ) +
  3085. FloatMakePositive( oldExtents.y * transform[1][1] ) +
  3086. FloatMakePositive( oldExtents.z * transform[2][1] );
  3087. newExtents.z = FloatMakePositive( oldExtents.x * transform[0][2] ) +
  3088. FloatMakePositive( oldExtents.y * transform[1][2] ) +
  3089. FloatMakePositive( oldExtents.z * transform[2][2] );
  3090. VectorSubtract( newCenter, newExtents, vecMinsOut );
  3091. VectorAdd( newCenter, newExtents, vecMaxsOut );
  3092. }
  3093. float CalcSqrDistanceToAABB( const Vector &mins, const Vector &maxs, const Vector &point )
  3094. {
  3095. float flDelta;
  3096. float flDistSqr = 0.0f;
  3097. if ( point.x < mins.x )
  3098. {
  3099. flDelta = (mins.x - point.x);
  3100. flDistSqr += flDelta * flDelta;
  3101. }
  3102. else if ( point.x > maxs.x )
  3103. {
  3104. flDelta = (point.x - maxs.x);
  3105. flDistSqr += flDelta * flDelta;
  3106. }
  3107. if ( point.y < mins.y )
  3108. {
  3109. flDelta = (mins.y - point.y);
  3110. flDistSqr += flDelta * flDelta;
  3111. }
  3112. else if ( point.y > maxs.y )
  3113. {
  3114. flDelta = (point.y - maxs.y);
  3115. flDistSqr += flDelta * flDelta;
  3116. }
  3117. if ( point.z < mins.z )
  3118. {
  3119. flDelta = (mins.z - point.z);
  3120. flDistSqr += flDelta * flDelta;
  3121. }
  3122. else if ( point.z > maxs.z )
  3123. {
  3124. flDelta = (point.z - maxs.z);
  3125. flDistSqr += flDelta * flDelta;
  3126. }
  3127. return flDistSqr;
  3128. }
  3129. void CalcClosestPointOnAABB( const Vector &mins, const Vector &maxs, const Vector &point, Vector &closestOut )
  3130. {
  3131. closestOut.x = clamp( point.x, mins.x, maxs.x );
  3132. closestOut.y = clamp( point.y, mins.y, maxs.y );
  3133. closestOut.z = clamp( point.z, mins.z, maxs.z );
  3134. }
  3135. void CalcSqrDistAndClosestPointOnAABB( const Vector &mins, const Vector &maxs, const Vector &point, Vector &closestOut, float &distSqrOut )
  3136. {
  3137. distSqrOut = 0.0f;
  3138. for ( int i = 0; i < 3; i++ )
  3139. {
  3140. if ( point[i] < mins[i] )
  3141. {
  3142. closestOut[i] = mins[i];
  3143. float flDelta = closestOut[i] - mins[i];
  3144. distSqrOut += flDelta * flDelta;
  3145. }
  3146. else if ( point[i] > maxs[i] )
  3147. {
  3148. closestOut[i] = maxs[i];
  3149. float flDelta = closestOut[i] - maxs[i];
  3150. distSqrOut += flDelta * flDelta;
  3151. }
  3152. else
  3153. {
  3154. closestOut[i] = point[i];
  3155. }
  3156. }
  3157. }
  3158. float CalcClosestPointToLineT( const Vector &P, const Vector &vLineA, const Vector &vLineB, Vector &vDir )
  3159. {
  3160. Assert( s_bMathlibInitialized );
  3161. VectorSubtract( vLineB, vLineA, vDir );
  3162. // D dot [P - (A + D*t)] = 0
  3163. // t = ( DP - DA) / DD
  3164. float div = vDir.Dot( vDir );
  3165. if( div < 0.00001f )
  3166. {
  3167. return 0;
  3168. }
  3169. else
  3170. {
  3171. return (vDir.Dot( P ) - vDir.Dot( vLineA )) / div;
  3172. }
  3173. }
  3174. void CalcClosestPointOnLine( const Vector &P, const Vector &vLineA, const Vector &vLineB, Vector &vClosest, float *outT )
  3175. {
  3176. Assert( s_bMathlibInitialized );
  3177. Vector vDir;
  3178. float t = CalcClosestPointToLineT( P, vLineA, vLineB, vDir );
  3179. if ( outT ) *outT = t;
  3180. vClosest.MulAdd( vLineA, vDir, t );
  3181. }
  3182. float CalcDistanceToLine( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *outT )
  3183. {
  3184. Assert( s_bMathlibInitialized );
  3185. Vector vClosest;
  3186. CalcClosestPointOnLine( P, vLineA, vLineB, vClosest, outT );
  3187. return P.DistTo(vClosest);
  3188. }
  3189. float CalcDistanceSqrToLine( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *outT )
  3190. {
  3191. Assert( s_bMathlibInitialized );
  3192. Vector vClosest;
  3193. CalcClosestPointOnLine( P, vLineA, vLineB, vClosest, outT );
  3194. return P.DistToSqr(vClosest);
  3195. }
  3196. void CalcClosestPointOnLineSegment( const Vector &P, const Vector &vLineA, const Vector &vLineB, Vector &vClosest, float *outT )
  3197. {
  3198. Vector vDir;
  3199. float t = CalcClosestPointToLineT( P, vLineA, vLineB, vDir );
  3200. t = clamp( t, 0, 1 );
  3201. if ( outT )
  3202. {
  3203. *outT = t;
  3204. }
  3205. vClosest.MulAdd( vLineA, vDir, t );
  3206. }
  3207. float CalcDistanceToLineSegment( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *outT )
  3208. {
  3209. Assert( s_bMathlibInitialized );
  3210. Vector vClosest;
  3211. CalcClosestPointOnLineSegment( P, vLineA, vLineB, vClosest, outT );
  3212. return P.DistTo( vClosest );
  3213. }
  3214. float CalcDistanceSqrToLineSegment( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *outT )
  3215. {
  3216. Assert( s_bMathlibInitialized );
  3217. Vector vClosest;
  3218. CalcClosestPointOnLineSegment( P, vLineA, vLineB, vClosest, outT );
  3219. return P.DistToSqr(vClosest);
  3220. }
  3221. float CalcClosestPointToLineT2D( const Vector2D &P, const Vector2D &vLineA, const Vector2D &vLineB, Vector2D &vDir )
  3222. {
  3223. Assert( s_bMathlibInitialized );
  3224. Vector2DSubtract( vLineB, vLineA, vDir );
  3225. // D dot [P - (A + D*t)] = 0
  3226. // t = (DP - DA) / DD
  3227. float div = vDir.Dot( vDir );
  3228. if( div < 0.00001f )
  3229. {
  3230. return 0;
  3231. }
  3232. else
  3233. {
  3234. return (vDir.Dot( P ) - vDir.Dot( vLineA )) / div;
  3235. }
  3236. }
  3237. void CalcClosestPointOnLine2D( const Vector2D &P, const Vector2D &vLineA, const Vector2D &vLineB, Vector2D &vClosest, float *outT )
  3238. {
  3239. Assert( s_bMathlibInitialized );
  3240. Vector2D vDir;
  3241. float t = CalcClosestPointToLineT2D( P, vLineA, vLineB, vDir );
  3242. if ( outT ) *outT = t;
  3243. vClosest.MulAdd( vLineA, vDir, t );
  3244. }
  3245. float CalcDistanceToLine2D( const Vector2D &P, const Vector2D &vLineA, const Vector2D &vLineB, float *outT )
  3246. {
  3247. Assert( s_bMathlibInitialized );
  3248. Vector2D vClosest;
  3249. CalcClosestPointOnLine2D( P, vLineA, vLineB, vClosest, outT );
  3250. return P.DistTo( vClosest );
  3251. }
  3252. float CalcDistanceSqrToLine2D( const Vector2D &P, const Vector2D &vLineA, const Vector2D &vLineB, float *outT )
  3253. {
  3254. Assert( s_bMathlibInitialized );
  3255. Vector2D vClosest;
  3256. CalcClosestPointOnLine2D( P, vLineA, vLineB, vClosest, outT );
  3257. return P.DistToSqr(vClosest);
  3258. }
  3259. void CalcClosestPointOnLineSegment2D( const Vector2D &P, const Vector2D &vLineA, const Vector2D &vLineB, Vector2D &vClosest, float *outT )
  3260. {
  3261. Vector2D vDir;
  3262. float t = CalcClosestPointToLineT2D( P, vLineA, vLineB, vDir );
  3263. t = clamp( t, 0, 1 );
  3264. if ( outT )
  3265. {
  3266. *outT = t;
  3267. }
  3268. vClosest.MulAdd( vLineA, vDir, t );
  3269. }
  3270. float CalcDistanceToLineSegment2D( const Vector2D &P, const Vector2D &vLineA, const Vector2D &vLineB, float *outT )
  3271. {
  3272. Assert( s_bMathlibInitialized );
  3273. Vector2D vClosest;
  3274. CalcClosestPointOnLineSegment2D( P, vLineA, vLineB, vClosest, outT );
  3275. return P.DistTo( vClosest );
  3276. }
  3277. float CalcDistanceSqrToLineSegment2D( const Vector2D &P, const Vector2D &vLineA, const Vector2D &vLineB, float *outT )
  3278. {
  3279. Assert( s_bMathlibInitialized );
  3280. Vector2D vClosest;
  3281. CalcClosestPointOnLineSegment2D( P, vLineA, vLineB, vClosest, outT );
  3282. return P.DistToSqr( vClosest );
  3283. }
  3284. // Do we have another epsilon we could use
  3285. #define LINE_EPS ( 0.000001f )
  3286. //-----------------------------------------------------------------------------
  3287. // Purpose: Given lines p1->p2 and p3->p4, computes a line segment (pa->pb) and returns the parameters 0->1 multipliers
  3288. // along each segment for the returned points
  3289. // Input : p1 -
  3290. // p2 -
  3291. // p3 -
  3292. // p4 -
  3293. // *s1 -
  3294. // *s2 -
  3295. // Output : Returns true on success, false on failure.
  3296. //-----------------------------------------------------------------------------
  3297. bool CalcLineToLineIntersectionSegment(
  3298. const Vector& p1,const Vector& p2,const Vector& p3,const Vector& p4,Vector *s1,Vector *s2,
  3299. float *t1, float *t2)
  3300. {
  3301. Vector p13,p43,p21;
  3302. float d1343,d4321,d1321,d4343,d2121;
  3303. float numer,denom;
  3304. p13.x = p1.x - p3.x;
  3305. p13.y = p1.y - p3.y;
  3306. p13.z = p1.z - p3.z;
  3307. p43.x = p4.x - p3.x;
  3308. p43.y = p4.y - p3.y;
  3309. p43.z = p4.z - p3.z;
  3310. if (fabs(p43.x) < LINE_EPS && fabs(p43.y) < LINE_EPS && fabs(p43.z) < LINE_EPS)
  3311. return false;
  3312. p21.x = p2.x - p1.x;
  3313. p21.y = p2.y - p1.y;
  3314. p21.z = p2.z - p1.z;
  3315. if (fabs(p21.x) < LINE_EPS && fabs(p21.y) < LINE_EPS && fabs(p21.z) < LINE_EPS)
  3316. return false;
  3317. d1343 = p13.x * p43.x + p13.y * p43.y + p13.z * p43.z;
  3318. d4321 = p43.x * p21.x + p43.y * p21.y + p43.z * p21.z;
  3319. d1321 = p13.x * p21.x + p13.y * p21.y + p13.z * p21.z;
  3320. d4343 = p43.x * p43.x + p43.y * p43.y + p43.z * p43.z;
  3321. d2121 = p21.x * p21.x + p21.y * p21.y + p21.z * p21.z;
  3322. denom = d2121 * d4343 - d4321 * d4321;
  3323. if (fabs(denom) < LINE_EPS)
  3324. return false;
  3325. numer = d1343 * d4321 - d1321 * d4343;
  3326. *t1 = numer / denom;
  3327. *t2 = (d1343 + d4321 * (*t1)) / d4343;
  3328. if ( s1 != NULL && s2 != NULL )
  3329. {
  3330. s1->x = p1.x + *t1 * p21.x;
  3331. s1->y = p1.y + *t1 * p21.y;
  3332. s1->z = p1.z + *t1 * p21.z;
  3333. s2->x = p3.x + *t2 * p43.x;
  3334. s2->y = p3.y + *t2 * p43.y;
  3335. s2->z = p3.z + *t2 * p43.z;
  3336. }
  3337. return true;
  3338. }
  3339. #pragma optimize( "", off )
  3340. #ifndef EXCEPTION_EXECUTE_HANDLER
  3341. #define EXCEPTION_EXECUTE_HANDLER 1
  3342. #endif
  3343. #pragma optimize( "", on )
  3344. #ifndef NDEBUG
  3345. volatile static char const *pDebugString;
  3346. #endif
  3347. void MathLib_Init( float gamma, float texGamma, float brightness, int overbright, bool bAllow3DNow, bool bAllowSSE, bool bAllowSSE2, bool bAllowMMX )
  3348. {
  3349. if ( s_bMathlibInitialized )
  3350. return;
  3351. #ifdef _WIN32
  3352. Assert( _rotl( 0xC7654321, 1 ) == 0x8ECA8643 );
  3353. Assert( _rotl64( 0xC7654321ABCDEF00ull, 1 ) == 0x8ECA8643579BDE01ull );
  3354. #endif
  3355. #ifndef NDEBUG
  3356. pDebugString = "mathlib.lib built debug!";
  3357. #endif
  3358. // FIXME: Hook SSE into VectorAligned + Vector4DAligned
  3359. #if !defined( _GAMECONSOLE )
  3360. // Grab the processor information:
  3361. const CPUInformation& pi = GetCPUInformation();
  3362. if ( ! ( pi.m_bSSE && pi.m_bSSE2 ) )
  3363. {
  3364. Assert( 0 );
  3365. Error( "SSE and SSE2 are required." );
  3366. }
  3367. #endif //!360
  3368. s_bMathlibInitialized = true;
  3369. InitSinCosTable();
  3370. BuildGammaTable( gamma, texGamma, brightness, overbright );
  3371. SeedRandSIMD( 0x31415926 );
  3372. }
  3373. bool MathLib_MMXEnabled( void )
  3374. {
  3375. Assert( s_bMathlibInitialized );
  3376. return true;
  3377. }
  3378. bool MathLib_SSEEnabled( void )
  3379. {
  3380. Assert( s_bMathlibInitialized );
  3381. return true;
  3382. }
  3383. bool MathLib_SSE2Enabled( void )
  3384. {
  3385. Assert( s_bMathlibInitialized );
  3386. return true;
  3387. }
  3388. // BUGBUG: Why doesn't this call angle diff?!?!?
  3389. float ApproachAngle( float target, float value, float speed )
  3390. {
  3391. target = anglemod( target );
  3392. value = anglemod( value );
  3393. float delta = target - value;
  3394. // Speed is assumed to be positive
  3395. if ( speed < 0 )
  3396. speed = -speed;
  3397. if ( delta < -180 )
  3398. delta += 360;
  3399. else if ( delta > 180 )
  3400. delta -= 360;
  3401. if ( delta > speed )
  3402. value += speed;
  3403. else if ( delta < -speed )
  3404. value -= speed;
  3405. else
  3406. value = target;
  3407. return value;
  3408. }
  3409. // BUGBUG: Why do we need both of these?
  3410. float AngleDiff( float destAngle, float srcAngle )
  3411. {
  3412. float delta;
  3413. delta = fmodf(destAngle - srcAngle, 360.0f);
  3414. if ( destAngle > srcAngle )
  3415. {
  3416. if ( delta >= 180 )
  3417. delta -= 360;
  3418. }
  3419. else
  3420. {
  3421. if ( delta <= -180 )
  3422. delta += 360;
  3423. }
  3424. return delta;
  3425. }
  3426. float AngleDistance( float next, float cur )
  3427. {
  3428. float delta = next - cur;
  3429. if ( delta < -180 )
  3430. delta += 360;
  3431. else if ( delta > 180 )
  3432. delta -= 360;
  3433. return delta;
  3434. }
  3435. float AngleNormalize( float angle )
  3436. {
  3437. angle = fmodf(angle, 360.0f);
  3438. if (angle > 180)
  3439. {
  3440. angle -= 360;
  3441. }
  3442. if (angle < -180)
  3443. {
  3444. angle += 360;
  3445. }
  3446. return angle;
  3447. }
  3448. //--------------------------------------------------------------------------------------------------------------
  3449. // ensure that 0 <= angle <= 360
  3450. float AngleNormalizePositive( float angle )
  3451. {
  3452. angle = fmodf( angle, 360.0f );
  3453. if (angle < 0.0f)
  3454. {
  3455. angle += 360.0f;
  3456. }
  3457. return angle;
  3458. }
  3459. //--------------------------------------------------------------------------------------------------------------
  3460. bool AnglesAreEqual( float a, float b, float tolerance )
  3461. {
  3462. return (fabs( AngleDiff( a, b ) ) < tolerance);
  3463. }
  3464. void RotationDeltaAxisAngle( const QAngle &srcAngles, const QAngle &destAngles, Vector &deltaAxis, float &deltaAngle )
  3465. {
  3466. Quaternion srcQuat, destQuat, srcQuatInv, out;
  3467. AngleQuaternion( srcAngles, srcQuat );
  3468. AngleQuaternion( destAngles, destQuat );
  3469. QuaternionScale( srcQuat, -1, srcQuatInv );
  3470. QuaternionMult( destQuat, srcQuatInv, out );
  3471. QuaternionNormalize( out );
  3472. QuaternionAxisAngle( out, deltaAxis, deltaAngle );
  3473. }
  3474. void RotationDelta( const QAngle &srcAngles, const QAngle &destAngles, QAngle *out )
  3475. {
  3476. matrix3x4_t src, srcInv;
  3477. matrix3x4_t dest;
  3478. AngleMatrix( srcAngles, src );
  3479. AngleMatrix( destAngles, dest );
  3480. // xform = src(-1) * dest
  3481. MatrixInvert( src, srcInv );
  3482. matrix3x4_t xform;
  3483. ConcatTransforms( dest, srcInv, xform );
  3484. QAngle xformAngles;
  3485. MatrixAngles( xform, xformAngles );
  3486. if ( out )
  3487. {
  3488. *out = xformAngles;
  3489. }
  3490. }
  3491. void ClipLineSegmentToPlane( const Vector &vNormal, const Vector &vPlanePoint, Vector *p1, Vector *p2, float flBias )
  3492. {
  3493. float flDot1, flDot2;
  3494. flDot1 = ( *p1 - vPlanePoint ).Dot( vNormal ) + flBias;
  3495. flDot2 = ( *p2 - vPlanePoint ).Dot( vNormal ) + flBias;
  3496. if ( flDot1 >= 0 && flDot2 >= 0 )
  3497. {
  3498. return;
  3499. }
  3500. if ( flDot1 >= 0 )
  3501. {
  3502. Vector vRay = *p2 - *p1;
  3503. *p2 = *p1 + vRay * flDot1 / ( flDot1 - flDot2 );
  3504. }
  3505. else if ( flDot2 >= 0 )
  3506. {
  3507. Vector vRay = *p1 - *p2;
  3508. *p1 = *p2 + vRay * flDot2 / ( flDot2 - flDot1 );
  3509. }
  3510. else
  3511. {
  3512. *p1 = vec3_invalid;
  3513. *p2 = vec3_invalid;
  3514. }
  3515. }
  3516. //-----------------------------------------------------------------------------
  3517. // Purpose: Computes a triangle normal
  3518. //-----------------------------------------------------------------------------
  3519. void ComputeTrianglePlane( const Vector& v1, const Vector& v2, const Vector& v3, Vector& normal, float& intercept )
  3520. {
  3521. Vector e1, e2;
  3522. VectorSubtract( v2, v1, e1 );
  3523. VectorSubtract( v3, v1, e2 );
  3524. CrossProduct( e1, e2, normal );
  3525. VectorNormalize( normal );
  3526. intercept = DotProduct( normal, v1 );
  3527. }
  3528. //-----------------------------------------------------------------------------
  3529. // Purpose: Calculate the volume of a tetrahedron with these vertices
  3530. // Input : p0 - points of tetrahedron
  3531. // p1 -
  3532. // p2 -
  3533. // p3 -
  3534. // Output : float (volume in units^3)
  3535. //-----------------------------------------------------------------------------
  3536. float TetrahedronVolume( const Vector &p0, const Vector &p1, const Vector &p2, const Vector &p3 )
  3537. {
  3538. Vector a, b, c, cross;
  3539. float volume = 1.0f / 6.0f;
  3540. a = p1 - p0;
  3541. b = p2 - p0;
  3542. c = p3 - p0;
  3543. cross = CrossProduct( b, c );
  3544. volume *= DotProduct( a, cross );
  3545. if ( volume < 0 )
  3546. return -volume;
  3547. return volume;
  3548. }
  3549. // computes the area of a triangle given three verts
  3550. float TriangleArea( const Vector &v0, const Vector &v1, const Vector &v2 )
  3551. {
  3552. Vector vecEdge0, vecEdge1, vecCross;
  3553. VectorSubtract( v1, v0, vecEdge0 );
  3554. VectorSubtract( v2, v0, vecEdge1 );
  3555. CrossProduct( vecEdge0, vecEdge1, vecCross );
  3556. return ( VectorLength( vecCross ) * 0.5f );
  3557. }
  3558. //-----------------------------------------------------------------------------
  3559. // Purpose: This is a clone of BaseWindingForPlane()
  3560. // Input : *pOutVerts - an array of preallocated verts to build the polygon in
  3561. // normal - the plane normal
  3562. // dist - the plane constant
  3563. // Output : int - vert count (always 4)
  3564. //-----------------------------------------------------------------------------
  3565. int PolyFromPlane( Vector *pOutVerts, const Vector& normal, float dist, float fHalfScale )
  3566. {
  3567. int i, x;
  3568. vec_t max, v;
  3569. Vector org, vright, vup;
  3570. // find the major axis
  3571. max = -16384; //MAX_COORD_INTEGER
  3572. x = -1;
  3573. for (i=0 ; i<3; i++)
  3574. {
  3575. v = fabs(normal[i]);
  3576. if (v > max)
  3577. {
  3578. x = i;
  3579. max = v;
  3580. }
  3581. }
  3582. if (x==-1)
  3583. return 0;
  3584. // Build a unit vector along something other than the major axis
  3585. VectorCopy (vec3_origin, vup);
  3586. switch (x)
  3587. {
  3588. case 0:
  3589. case 1:
  3590. vup[2] = 1;
  3591. break;
  3592. case 2:
  3593. vup[0] = 1;
  3594. break;
  3595. }
  3596. // Remove the component of this vector along the normal
  3597. v = DotProduct (vup, normal);
  3598. VectorMA (vup, -v, normal, vup);
  3599. // Make it a unit (perpendicular)
  3600. VectorNormalize (vup);
  3601. // Center of the poly is at normal * dist
  3602. VectorScale (normal, dist, org);
  3603. // Calculate the third orthonormal basis vector for our plane space (this one and vup are in the plane)
  3604. CrossProduct (vup, normal, vright);
  3605. // Make the plane's basis vectors big (these are the half-sides of the polygon we're making)
  3606. VectorScale (vup, fHalfScale, vup);
  3607. VectorScale (vright, fHalfScale, vright);
  3608. // Move diagonally away from org to create the corner verts
  3609. VectorSubtract (org, vright, pOutVerts[0]); // left
  3610. VectorAdd (pOutVerts[0], vup, pOutVerts[0]); // up
  3611. VectorAdd (org, vright, pOutVerts[1]); // right
  3612. VectorAdd (pOutVerts[1], vup, pOutVerts[1]); // up
  3613. VectorAdd (org, vright, pOutVerts[2]); // right
  3614. VectorSubtract (pOutVerts[2], vup, pOutVerts[2]); // down
  3615. VectorSubtract (org, vright, pOutVerts[3]); // left
  3616. VectorSubtract (pOutVerts[3], vup, pOutVerts[3]); // down
  3617. // The four corners form a planar quadrilateral normal to "normal"
  3618. return 4;
  3619. }
  3620. // Returns void as it was impossible for the function to returns anything other than 4.
  3621. // Any absolute of a floating value will always return a number greater than -16384. That test seemed bogus.
  3622. void PolyFromPlane_SIMD( fltx4 *pOutVerts, const fltx4 & plane, float fHalfScale )
  3623. {
  3624. // So we need to find the biggest component of all three,
  3625. // And depending of the value, we need to build a unit vector along something that is not the major axis.
  3626. fltx4 f4Abs = AbsSIMD( plane );
  3627. fltx4 x = SplatXSIMD( f4Abs );
  3628. fltx4 y = SplatYSIMD( f4Abs );
  3629. fltx4 z = SplatZSIMD( f4Abs );
  3630. fltx4 max = MaxSIMD( x, y );
  3631. max = MaxSIMD( max, z );
  3632. // Simplify the code, if Z is the biggest component, we will use 1 0 0.
  3633. // If X or Y are the biggest, we will use 0 0 1.
  3634. bi32x4 fIsMax = CmpEqSIMD( max, f4Abs ); // isMax will be set for the components that are the max
  3635. fltx4 fIsZMax = SplatZSIMD( (fltx4)fIsMax ); // 0 if Z is not the max, 0xffffffff is Z is the max
  3636. // And depending if Z is max or not, we are going to select one unit vector or the other
  3637. fltx4 vup = MaskedAssign( (bi32x4)fIsZMax, g_SIMD_Identity[0], g_SIMD_Identity[2] );
  3638. fltx4 normal = SetWToZeroSIMD( plane );
  3639. fltx4 dist = SplatWSIMD( plane );
  3640. // Remove the component of this vector along the normal
  3641. fltx4 v = Dot3SIMD( vup, normal );
  3642. vup = MaddSIMD( -v, normal, vup);
  3643. // Make it a unit (perpendicular)
  3644. vup = Normalized3SIMD( vup );
  3645. // Center of the poly is at normal * dist
  3646. fltx4 org = MulSIMD( dist, normal );
  3647. // Calculate the third orthonormal basis vector for our plane space (this one and vup are in the plane)
  3648. fltx4 vright = CrossProductSIMD( vup, normal);
  3649. // Make the plane's basis vectors big (these are the half-sides of the polygon we're making)
  3650. fltx4 f4HalfScale = ReplicateX4( fHalfScale );
  3651. vup = MulSIMD( f4HalfScale, vup );
  3652. vright = MulSIMD( f4HalfScale, vright );
  3653. // Move diagonally away from org to create the corner verts
  3654. fltx4 vleft = SubSIMD( org, vright );
  3655. vright = AddSIMD( org, vright );
  3656. pOutVerts[0] = AddSIMD( vleft, vup ); // left + up
  3657. pOutVerts[1] = AddSIMD( vright, vup ); // right + up
  3658. pOutVerts[2] = SubSIMD( vright, vup ); // right + down
  3659. pOutVerts[3] = SubSIMD( vleft, vup ); // left + down
  3660. }
  3661. //-----------------------------------------------------------------------------
  3662. // Purpose: clip a poly to the plane and return the poly on the front side of the plane
  3663. // Input : *inVerts - input polygon
  3664. // vertCount - # verts in input poly
  3665. // *outVerts - destination poly
  3666. // normal - plane normal
  3667. // dist - plane constant
  3668. // Output : int - # verts in output poly
  3669. //-----------------------------------------------------------------------------
  3670. int ClipPolyToPlane( Vector *inVerts, int vertCount, Vector *outVerts, const Vector& normal, float dist, float fOnPlaneEpsilon )
  3671. {
  3672. vec_t *dists = (vec_t *)stackalloc( sizeof(vec_t) * vertCount * 4 ); //4x vertcount should cover all cases
  3673. int *sides = (int *)stackalloc( sizeof(vec_t) * vertCount * 4 );
  3674. int counts[3];
  3675. vec_t dot;
  3676. int i, j;
  3677. Vector mid = vec3_origin;
  3678. int outCount;
  3679. counts[0] = counts[1] = counts[2] = 0;
  3680. // determine sides for each point
  3681. for ( i = 0; i < vertCount; i++ )
  3682. {
  3683. dot = DotProduct( inVerts[i], normal) - dist;
  3684. dists[i] = dot;
  3685. if ( dot > fOnPlaneEpsilon )
  3686. {
  3687. sides[i] = SIDE_FRONT;
  3688. }
  3689. else if ( dot < -fOnPlaneEpsilon )
  3690. {
  3691. sides[i] = SIDE_BACK;
  3692. }
  3693. else
  3694. {
  3695. sides[i] = SIDE_ON;
  3696. }
  3697. counts[sides[i]]++;
  3698. }
  3699. sides[i] = sides[0];
  3700. dists[i] = dists[0];
  3701. if (!counts[0])
  3702. return 0;
  3703. if (!counts[1])
  3704. {
  3705. // Copy to output verts
  3706. for ( i = 0; i < vertCount; i++ )
  3707. {
  3708. VectorCopy( inVerts[i], outVerts[i] );
  3709. }
  3710. return vertCount;
  3711. }
  3712. outCount = 0;
  3713. for ( i = 0; i < vertCount; i++ )
  3714. {
  3715. Vector& p1 = inVerts[i];
  3716. if (sides[i] == SIDE_ON)
  3717. {
  3718. VectorCopy( p1, outVerts[outCount]);
  3719. outCount++;
  3720. continue;
  3721. }
  3722. if (sides[i] == SIDE_FRONT)
  3723. {
  3724. VectorCopy( p1, outVerts[outCount]);
  3725. outCount++;
  3726. }
  3727. if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
  3728. continue;
  3729. // generate a split point
  3730. Vector& p2 = inVerts[(i+1)%vertCount];
  3731. dot = dists[i] / (dists[i]-dists[i+1]);
  3732. for (j=0 ; j<3 ; j++)
  3733. { // avoid round off error when possible
  3734. if (normal[j] == 1)
  3735. mid[j] = dist;
  3736. else if (normal[j] == -1)
  3737. mid[j] = -dist;
  3738. else
  3739. mid[j] = p1[j] + dot*(p2[j]-p1[j]);
  3740. }
  3741. VectorCopy (mid, outVerts[outCount]);
  3742. outCount++;
  3743. }
  3744. return outCount;
  3745. }
  3746. int ClipPolyToPlane_SIMD( fltx4 *pInVerts, int nVertCount, fltx4 *pOutVerts, const fltx4& plane, float fOnPlaneEpsilon )
  3747. {
  3748. vec_t *dists = (vec_t *)stackalloc( sizeof(vec_t) * nVertCount * 4 ); //4* nVertCount should cover all cases
  3749. uint8 *sides = (uint8 *)stackalloc( sizeof(uint8) * nVertCount * 4 );
  3750. int i;
  3751. /*
  3752. * It seems something could be done here... Especially in relation with the code below i, i + 1, etc...
  3753. fltx4 f4OnPlaneEpsilonP = ReplicateX4( fOnPlaneEpsilon );
  3754. fltx4 f4OnPlaneEpsilonM = -f4OnPlaneEpsilonP;
  3755. Also we could store the full fltx4 instead of a single float. It would avoid doing a SubFloat() here,
  3756. and a ReplicateX4() later. Trading off potential LHS against L2 cache misses?
  3757. */
  3758. // determine sides for each point
  3759. int nAllSides = 0;
  3760. fltx4 f4Dist = SplatWSIMD( plane );
  3761. for ( i = 0; i < nVertCount; i++ )
  3762. {
  3763. // dot = DotProduct( pInVerts[i], normal) - dist;
  3764. fltx4 dot = Dot3SIMD( pInVerts[i], plane );
  3765. dot = SubSIMD( dot, f4Dist );
  3766. float fDot = SubFloat( dot, 0 );
  3767. dists[i] = fDot;
  3768. // Look how to update sides with a branch-less version
  3769. int nSide = OR_SIDE_ON;
  3770. if ( fDot > fOnPlaneEpsilon )
  3771. {
  3772. nSide = OR_SIDE_FRONT;
  3773. }
  3774. else if ( fDot < -fOnPlaneEpsilon )
  3775. {
  3776. nSide = OR_SIDE_BACK;
  3777. }
  3778. sides[i] = nSide;
  3779. nAllSides |= nSide;
  3780. }
  3781. sides[i] = sides[0];
  3782. dists[i] = dists[0];
  3783. // Shortcuts (either completely clipped or not clipped at all)
  3784. if ( ( nAllSides & OR_SIDE_FRONT ) == 0 )
  3785. {
  3786. return 0; // Completely clipped
  3787. }
  3788. if ( ( nAllSides & OR_SIDE_BACK ) == 0 )
  3789. {
  3790. // Not clipped at all, copy to output verts
  3791. Assert ( i == nVertCount );
  3792. int nIndex = 0;
  3793. while ( i >= 4 )
  3794. {
  3795. pOutVerts[nIndex] = pInVerts[nIndex];
  3796. pOutVerts[nIndex + 1] = pInVerts[nIndex + 1];
  3797. pOutVerts[nIndex + 2] = pInVerts[nIndex + 2];
  3798. pOutVerts[nIndex + 3] = pInVerts[nIndex + 3];
  3799. nIndex += 4;
  3800. i -= 4;
  3801. }
  3802. while ( i > 0 )
  3803. {
  3804. pOutVerts[nIndex] = pInVerts[nIndex];
  3805. ++nIndex;
  3806. --i;
  3807. }
  3808. return nVertCount;
  3809. }
  3810. fltx4 f4one = Four_Ones;
  3811. fltx4 f4MOne = -f4one;
  3812. fltx4 f4OneMask = (fltx4)CmpEqSIMD( plane, f4one );
  3813. fltx4 f4mOneMask = (fltx4)CmpEqSIMD( plane, f4MOne );
  3814. fltx4 f4AllMask = OrSIMD( f4OneMask, f4mOneMask ); // 0xffffffff where normal was 1 or -1, 0 otherwise
  3815. f4OneMask = AndSIMD( f4OneMask, f4Dist ); // Dist where normal.* was 1
  3816. f4mOneMask = AndSIMD( f4mOneMask, -f4Dist ); // -Dist where normal.* was -1
  3817. fltx4 f4AllValue = OrSIMD( f4OneMask, f4mOneMask ); // Dist and -Dist where normal.* was 1 and -1
  3818. // f4AllMask and f4AllValue will be used together (to override the default calculation).
  3819. int nOutCount = 0;
  3820. for ( i = 0; i < nVertCount; i++ )
  3821. {
  3822. const fltx4& p1 = pInVerts[i];
  3823. if (sides[i] == OR_SIDE_ON)
  3824. {
  3825. pOutVerts[nOutCount++] = p1;
  3826. continue;
  3827. }
  3828. if (sides[i] == OR_SIDE_FRONT)
  3829. {
  3830. pOutVerts[nOutCount++] = p1;
  3831. }
  3832. if (sides[i+1] == OR_SIDE_ON || sides[i+1] == sides[i])
  3833. continue;
  3834. // generate a split point
  3835. fltx4& p2 = pInVerts[(i+1)%nVertCount];
  3836. float fDot = dists[i] / (dists[i]-dists[i+1]);
  3837. fltx4 f4Dot = ReplicateX4( fDot );
  3838. // mid[j] = v1[j] + dot*(v2[j]-v1[j]); - For j=0...2
  3839. fltx4 f4Result = MaddSIMD( f4Dot, SubSIMD( p2, p1) , p1);
  3840. // If normal.* is 1, it should be dist, if -1, it should be -dist, otherwise it should be mid[j] = v1[j] + dot*(v2[j]-v1[j]);
  3841. fltx4 mid = MaskedAssign( (bi32x4)f4AllMask, f4AllValue, f4Result );
  3842. pOutVerts[nOutCount++] = mid;
  3843. }
  3844. return nOutCount;
  3845. }
  3846. int ClipPolyToPlane_Precise( double *inVerts, int vertCount, double *outVerts, const double *normal, double dist, double fOnPlaneEpsilon )
  3847. {
  3848. double *dists = (double *)stackalloc( sizeof(double) * vertCount * 4 ); //4x vertcount should cover all cases
  3849. int *sides = (int *)stackalloc( sizeof(double) * vertCount * 4 );
  3850. int counts[3];
  3851. double dot;
  3852. int i, j;
  3853. //Vector mid = vec3_origin;
  3854. double mid[3];
  3855. mid[0] = 0.0;
  3856. mid[1] = 0.0;
  3857. mid[2] = 0.0;
  3858. int outCount;
  3859. counts[0] = counts[1] = counts[2] = 0;
  3860. // determine sides for each point
  3861. for ( i = 0; i < vertCount; i++ )
  3862. {
  3863. //dot = DotProduct( inVerts[i], normal) - dist;
  3864. dot = ((inVerts[i*3 + 0] * normal[0]) + (inVerts[i*3 + 1] * normal[1]) + (inVerts[i*3 + 2] * normal[2])) - dist;
  3865. dists[i] = dot;
  3866. if ( dot > fOnPlaneEpsilon )
  3867. {
  3868. sides[i] = SIDE_FRONT;
  3869. }
  3870. else if ( dot < -fOnPlaneEpsilon )
  3871. {
  3872. sides[i] = SIDE_BACK;
  3873. }
  3874. else
  3875. {
  3876. sides[i] = SIDE_ON;
  3877. }
  3878. counts[sides[i]]++;
  3879. }
  3880. sides[i] = sides[0];
  3881. dists[i] = dists[0];
  3882. if (!counts[0])
  3883. return 0;
  3884. if (!counts[1])
  3885. {
  3886. // Copy to output verts
  3887. //for ( i = 0; i < vertCount; i++ )
  3888. for ( i = 0; i < vertCount * 3; i++ )
  3889. {
  3890. //VectorCopy( inVerts[i], outVerts[i] );
  3891. outVerts[i] = inVerts[i];
  3892. }
  3893. return vertCount;
  3894. }
  3895. outCount = 0;
  3896. for ( i = 0; i < vertCount; i++ )
  3897. {
  3898. //Vector& p1 = inVerts[i];
  3899. double *p1 = &inVerts[i*3];
  3900. //p1[0] = inVerts[i*3 + 0];
  3901. //p1[1] = inVerts[i*3 + 1];
  3902. //p1[2] = inVerts[i*3 + 2];
  3903. if (sides[i] == SIDE_ON)
  3904. {
  3905. //VectorCopy( p1, outVerts[outCount]);
  3906. outVerts[outCount*3 + 0] = p1[0];
  3907. outVerts[outCount*3 + 1] = p1[1];
  3908. outVerts[outCount*3 + 2] = p1[2];
  3909. outCount++;
  3910. continue;
  3911. }
  3912. if (sides[i] == SIDE_FRONT)
  3913. {
  3914. //VectorCopy( p1, outVerts[outCount]);
  3915. outVerts[outCount*3 + 0] = p1[0];
  3916. outVerts[outCount*3 + 1] = p1[1];
  3917. outVerts[outCount*3 + 2] = p1[2];
  3918. outCount++;
  3919. }
  3920. if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
  3921. continue;
  3922. // generate a split point
  3923. //Vector& p2 = inVerts[(i+1)%vertCount];
  3924. int wrappedindex = (i+1)%vertCount;
  3925. double *p2 = &inVerts[wrappedindex*3];
  3926. //p2[0] = inVerts[wrappedindex*3 + 0];
  3927. //p2[1] = inVerts[wrappedindex*3 + 1];
  3928. //p2[2] = inVerts[wrappedindex*3 + 2];
  3929. dot = dists[i] / (dists[i]-dists[i+1]);
  3930. for (j=0 ; j<3 ; j++)
  3931. {
  3932. mid[j] = (double)p1[j] + dot*((double)p2[j]-(double)p1[j]);
  3933. }
  3934. //VectorCopy (mid, outVerts[outCount]);
  3935. outVerts[outCount*3 + 0] = mid[0];
  3936. outVerts[outCount*3 + 1] = mid[1];
  3937. outVerts[outCount*3 + 2] = mid[2];
  3938. outCount++;
  3939. }
  3940. return outCount;
  3941. }
  3942. int CeilPow2( int in )
  3943. {
  3944. int retval;
  3945. retval = 1;
  3946. while( retval < in )
  3947. retval <<= 1;
  3948. return retval;
  3949. }
  3950. int FloorPow2( int in )
  3951. {
  3952. int retval;
  3953. retval = 1;
  3954. while( retval < in )
  3955. retval <<= 1;
  3956. return retval >> 1;
  3957. }
  3958. //-----------------------------------------------------------------------------
  3959. // Computes Y fov from an X fov and a screen aspect ratio
  3960. //-----------------------------------------------------------------------------
  3961. float CalcFovY( float flFovX, float flAspect )
  3962. {
  3963. if ( flFovX < 1 || flFovX > 179)
  3964. {
  3965. flFovX = 90; // error, set to 90
  3966. }
  3967. // The long, but illustrative version (more closely matches CShaderAPIDX8::PerspectiveX, which
  3968. // is what it's based on).
  3969. //
  3970. //float width = 2 * zNear * tan( DEG2RAD( fov_x / 2.0 ) );
  3971. //float height = width / screenaspect;
  3972. //float yRadians = atan( (height/2.0) / zNear );
  3973. //return RAD2DEG( yRadians ) * 2;
  3974. // The short and sweet version.
  3975. float val = atan( tan( DEG2RAD( flFovX ) * 0.5f ) / flAspect );
  3976. val = RAD2DEG( val ) * 2.0f;
  3977. return val;
  3978. }
  3979. float CalcFovX( float flFovY, float flAspect )
  3980. {
  3981. return RAD2DEG( atan( tan( DEG2RAD( flFovY ) * 0.5f ) * flAspect ) ) * 2.0f;
  3982. }
  3983. #endif // !defined(__SPU__)
  3984. #if !defined(__SPU__)
  3985. //-----------------------------------------------------------------------------
  3986. // Generate a frustum based on perspective view parameters
  3987. //-----------------------------------------------------------------------------
  3988. void GeneratePerspectiveFrustum( const Vector& origin, const Vector &forward,
  3989. const Vector &right, const Vector &up, float flZNear, float flZFar,
  3990. float flFovX, float flFovY, VPlane *pPlanesOut )
  3991. {
  3992. float flIntercept = DotProduct( origin, forward );
  3993. // Setup the near and far planes.
  3994. pPlanesOut[FRUSTUM_FARZ].Init( -forward, -flZFar - flIntercept );
  3995. pPlanesOut[FRUSTUM_NEARZ].Init( forward, flZNear + flIntercept );
  3996. flFovX *= 0.5f;
  3997. flFovY *= 0.5f;
  3998. float flTanX = tan( DEG2RAD( flFovX ) );
  3999. float flTanY = tan( DEG2RAD( flFovY ) );
  4000. // OPTIMIZE: Normalizing these planes is not necessary for culling
  4001. Vector normalPos, normalNeg;
  4002. VectorMA( right, flTanX, forward, normalPos );
  4003. VectorMA( normalPos, -2.0f, right, normalNeg );
  4004. VectorNormalize( normalPos );
  4005. VectorNormalize( normalNeg );
  4006. pPlanesOut[FRUSTUM_LEFT].Init( normalPos, normalPos.Dot( origin ) );
  4007. pPlanesOut[FRUSTUM_RIGHT].Init( normalNeg, normalNeg.Dot( origin ) );
  4008. VectorMA( up, flTanY, forward, normalPos );
  4009. VectorMA( normalPos, -2.0f, up, normalNeg );
  4010. VectorNormalize( normalPos );
  4011. VectorNormalize( normalNeg );
  4012. pPlanesOut[FRUSTUM_BOTTOM].Init( normalPos, normalPos.Dot( origin ) );
  4013. pPlanesOut[FRUSTUM_TOP].Init( normalNeg, normalNeg.Dot( origin ) );
  4014. }
  4015. //-----------------------------------------------------------------------------
  4016. // Generate a frustum based on orthographic parameters
  4017. //-----------------------------------------------------------------------------
  4018. void GenerateOrthoFrustum( const Vector &origin, const Vector &forward, const Vector &right, const Vector &up, float flLeft, float flRight, float flBottom, float flTop, float flZNear, float flZFar, VPlane *pPlanesOut )
  4019. {
  4020. float flIntercept = DotProduct( origin, forward );
  4021. pPlanesOut[FRUSTUM_NEARZ].Init( forward, flZNear + flIntercept );
  4022. pPlanesOut[FRUSTUM_FARZ].Init( -forward, -flZFar - flIntercept );
  4023. flIntercept = DotProduct( origin, right );
  4024. pPlanesOut[FRUSTUM_RIGHT].Init( -right, -flRight - flIntercept );
  4025. pPlanesOut[FRUSTUM_LEFT].Init( right, flLeft + flIntercept );
  4026. flIntercept = DotProduct( origin, up );
  4027. pPlanesOut[FRUSTUM_BOTTOM].Init( up, flBottom + flIntercept );
  4028. pPlanesOut[FRUSTUM_TOP].Init( -up, -flTop - flIntercept );
  4029. }
  4030. //-----------------------------------------------------------------------------
  4031. // Version that accepts angles instead of vectors
  4032. //-----------------------------------------------------------------------------
  4033. void GeneratePerspectiveFrustum( const Vector& origin, const QAngle &angles, float flZNear, float flZFar, float flFovX, float flAspectRatio, Frustum_t &frustum )
  4034. {
  4035. VPlane planes[FRUSTUM_NUMPLANES];
  4036. Vector vecForward, vecRight, vecUp;
  4037. AngleVectors( angles, &vecForward, &vecRight, &vecUp );
  4038. float flFovY = CalcFovY( flFovX, flAspectRatio );
  4039. GeneratePerspectiveFrustum( origin, vecForward, vecRight, vecUp, flZNear, flZFar, flFovX, flFovY, planes );
  4040. frustum.SetPlanes( planes );
  4041. }
  4042. void fourplanes_t::ComputeSignbits()
  4043. {
  4044. xSign = CmpLtSIMD( nX, Four_Zeros );
  4045. ySign = CmpLtSIMD( nY, Four_Zeros );
  4046. zSign = CmpLtSIMD( nZ, Four_Zeros );
  4047. nXAbs = fabs(nX);
  4048. nYAbs = fabs(nY);
  4049. nZAbs = fabs(nZ);
  4050. }
  4051. void fourplanes_t::GetPlane( int index, Vector *pNormalOut, float *pDistOut ) const
  4052. {
  4053. pNormalOut->x = SubFloat(nX,index);
  4054. pNormalOut->y = SubFloat(nY,index);
  4055. pNormalOut->z = SubFloat(nZ,index);
  4056. *pDistOut = SubFloat(dist,index);
  4057. }
  4058. void fourplanes_t::SetPlane( int index, const Vector &vecNormal, float planeDist )
  4059. {
  4060. SubFloat(nX,index) = vecNormal.x;
  4061. SubFloat(nY,index) = vecNormal.y;
  4062. SubFloat(nZ,index) = vecNormal.z;
  4063. SubFloat(dist,index) = planeDist;
  4064. ComputeSignbits();
  4065. }
  4066. void fourplanes_t::Set4Planes( const VPlane *pPlanes )
  4067. {
  4068. nX = LoadUnalignedSIMD( &pPlanes[0].m_Normal.x );
  4069. nY = LoadUnalignedSIMD( &pPlanes[1].m_Normal.x );
  4070. nZ = LoadUnalignedSIMD( &pPlanes[2].m_Normal.x );
  4071. dist = LoadUnalignedSIMD( &pPlanes[3].m_Normal.x );
  4072. TransposeSIMD(nX, nY, nZ, dist);
  4073. ComputeSignbits();
  4074. }
  4075. void fourplanes_t::Set2Planes( const VPlane *pPlanes )
  4076. {
  4077. nX = LoadUnalignedSIMD( &pPlanes[0].m_Normal.x );
  4078. nY = LoadUnalignedSIMD( &pPlanes[1].m_Normal.x );
  4079. nZ = Four_Zeros;
  4080. dist = Four_Zeros;
  4081. TransposeSIMD(nX, nY, nZ, dist);
  4082. ComputeSignbits();
  4083. }
  4084. void fourplanes_t::Get4Planes( VPlane *pPlanesOut ) const
  4085. {
  4086. fltx4 p0 = nX;
  4087. fltx4 p1 = nY;
  4088. fltx4 p2 = nZ;
  4089. fltx4 p3 = dist;
  4090. TransposeSIMD(p0, p1, p2, p3);
  4091. StoreUnalignedSIMD( &pPlanesOut[0].m_Normal.x, p0 );
  4092. StoreUnalignedSIMD( &pPlanesOut[1].m_Normal.x, p1 );
  4093. StoreUnalignedSIMD( &pPlanesOut[2].m_Normal.x, p2 );
  4094. StoreUnalignedSIMD( &pPlanesOut[3].m_Normal.x, p3 );
  4095. }
  4096. void fourplanes_t::Get2Planes( VPlane *pPlanesOut ) const
  4097. {
  4098. fltx4 p0 = nX;
  4099. fltx4 p1 = nY;
  4100. fltx4 p2 = nZ;
  4101. fltx4 p3 = dist;
  4102. TransposeSIMD(p0, p1, p2, p3);
  4103. StoreUnalignedSIMD( &pPlanesOut[0].m_Normal.x, p0 );
  4104. StoreUnalignedSIMD( &pPlanesOut[1].m_Normal.x, p1 );
  4105. }
  4106. Frustum_t::Frustum_t()
  4107. {
  4108. memset(this, 0, sizeof(*this));
  4109. }
  4110. void Frustum_t::SetPlane( int i, const Vector &vecNormal, float dist )
  4111. {
  4112. if ( i < 4 )
  4113. {
  4114. planes[0].SetPlane( i, vecNormal, dist );
  4115. }
  4116. else
  4117. {
  4118. planes[1].SetPlane( i-4, vecNormal, dist );
  4119. }
  4120. }
  4121. void Frustum_t::GetPlane( int i, Vector *pNormalOut, float *pDistOut ) const
  4122. {
  4123. if ( i < 4 )
  4124. {
  4125. planes[0].GetPlane( i, pNormalOut, pDistOut );
  4126. }
  4127. else
  4128. {
  4129. planes[1].GetPlane( i-4, pNormalOut, pDistOut );
  4130. }
  4131. }
  4132. void Frustum_t::SetPlanes( const VPlane *pPlanes )
  4133. {
  4134. planes[0].Set4Planes(pPlanes);
  4135. planes[1].Set2Planes(pPlanes+4);
  4136. }
  4137. void Frustum_t::GetPlanes( VPlane *pPlanesOut ) const
  4138. {
  4139. planes[0].Get4Planes(pPlanesOut);
  4140. planes[1].Get2Planes(pPlanesOut+4);
  4141. }
  4142. bool Frustum_t::CullBox( const Vector &mins, const Vector &maxs ) const
  4143. {
  4144. fltx4 mins4 = LoadUnalignedSIMD( &mins.x );
  4145. fltx4 minx = SplatXSIMD(mins4);
  4146. fltx4 miny = SplatYSIMD(mins4);
  4147. fltx4 minz = SplatZSIMD(mins4);
  4148. fltx4 maxs4 = LoadUnalignedSIMD( &maxs.x );
  4149. fltx4 maxx = SplatXSIMD(maxs4);
  4150. fltx4 maxy = SplatYSIMD(maxs4);
  4151. fltx4 maxz = SplatZSIMD(maxs4);
  4152. // compute the dot product of the normal and the farthest corner
  4153. // dotBack0 = DotProduct( normal, normals.x < 0 ? mins.x : maxs.x );
  4154. for ( int i = 0; i < 2; i++ )
  4155. {
  4156. fltx4 xTotalBack = MulSIMD( planes[i].nX, MaskedAssign( planes[i].xSign, minx, maxx ) );
  4157. fltx4 yTotalBack = MulSIMD( planes[i].nY, MaskedAssign( planes[i].ySign, miny, maxy ) );
  4158. fltx4 zTotalBack = MulSIMD( planes[i].nZ, MaskedAssign( planes[i].zSign, minz, maxz ) );
  4159. fltx4 dotBack = AddSIMD( xTotalBack, AddSIMD(yTotalBack, zTotalBack) );
  4160. // if plane of the farthest corner is behind the plane, then the box is completely outside this plane
  4161. if ( IsVector4LessThan( dotBack, planes[i].dist ) )
  4162. return true;
  4163. }
  4164. return false;
  4165. }
  4166. bool Frustum_t::CullBox( const fltx4 &mins4, const fltx4 &maxs4 ) const
  4167. {
  4168. fltx4 minx = SplatXSIMD(mins4);
  4169. fltx4 miny = SplatYSIMD(mins4);
  4170. fltx4 minz = SplatZSIMD(mins4);
  4171. fltx4 maxx = SplatXSIMD(maxs4);
  4172. fltx4 maxy = SplatYSIMD(maxs4);
  4173. fltx4 maxz = SplatZSIMD(maxs4);
  4174. // compute the dot product of the normal and the farthest corner
  4175. // dotBack0 = DotProduct( normal, normals.x < 0 ? mins.x : maxs.x );
  4176. for ( int i = 0; i < 2; i++ )
  4177. {
  4178. fltx4 xTotalBack = MulSIMD( planes[i].nX, MaskedAssign( planes[i].xSign, minx, maxx ) );
  4179. fltx4 yTotalBack = MulSIMD( planes[i].nY, MaskedAssign( planes[i].ySign, miny, maxy ) );
  4180. fltx4 zTotalBack = MulSIMD( planes[i].nZ, MaskedAssign( planes[i].zSign, minz, maxz ) );
  4181. fltx4 dotBack = AddSIMD( xTotalBack, AddSIMD(yTotalBack, zTotalBack) );
  4182. // if plane of the farthest corner is behind the plane, then the box is completely outside this plane
  4183. if ( IsVector4LessThan( dotBack, planes[i].dist ) )
  4184. return true;
  4185. }
  4186. return false;
  4187. }
  4188. bool Frustum_t::CullBoxCenterExtents( const Vector &center, const Vector &extents ) const
  4189. {
  4190. fltx4 center4 = LoadUnalignedSIMD( &center.x );
  4191. fltx4 centerx = SplatXSIMD(center4);
  4192. fltx4 centery = SplatYSIMD(center4);
  4193. fltx4 centerz = SplatZSIMD(center4);
  4194. fltx4 extents4 = LoadUnalignedSIMD( &extents.x );
  4195. fltx4 extx = SplatXSIMD(extents4);
  4196. fltx4 exty = SplatYSIMD(extents4);
  4197. fltx4 extz = SplatZSIMD(extents4);
  4198. // compute the dot product of the normal and the farthest corner
  4199. for ( int i = 0; i < 2; i++ )
  4200. {
  4201. fltx4 xTotalBack = AddSIMD( MulSIMD( planes[i].nX, centerx ), MulSIMD(planes[i].nXAbs, extx ) );
  4202. fltx4 yTotalBack = AddSIMD( MulSIMD( planes[i].nY, centery ), MulSIMD(planes[i].nYAbs, exty ) );
  4203. fltx4 zTotalBack = AddSIMD( MulSIMD( planes[i].nZ, centerz ), MulSIMD(planes[i].nZAbs, extz ) );
  4204. fltx4 dotBack = AddSIMD( xTotalBack, AddSIMD(yTotalBack, zTotalBack) );
  4205. // if plane of the farthest corner is behind the plane, then the box is completely outside this plane
  4206. if ( IsVector4LessThan( dotBack, planes[i].dist ) )
  4207. return true;
  4208. }
  4209. return false;
  4210. }
  4211. bool Frustum_t::CullBoxCenterExtents( const fltx4 &fl4Center, const fltx4 &fl4Extents ) const
  4212. {
  4213. fltx4 centerx = SplatXSIMD(fl4Center);
  4214. fltx4 centery = SplatYSIMD(fl4Center);
  4215. fltx4 centerz = SplatZSIMD(fl4Center);
  4216. fltx4 extx = SplatXSIMD(fl4Extents);
  4217. fltx4 exty = SplatYSIMD(fl4Extents);
  4218. fltx4 extz = SplatZSIMD(fl4Extents);
  4219. // compute the dot product of the normal and the farthest corner
  4220. for ( int i = 0; i < 2; i++ )
  4221. {
  4222. fltx4 xTotalBack = AddSIMD( MulSIMD( planes[i].nX, centerx ), MulSIMD(planes[i].nXAbs, extx ) );
  4223. fltx4 yTotalBack = AddSIMD( MulSIMD( planes[i].nY, centery ), MulSIMD(planes[i].nYAbs, exty ) );
  4224. fltx4 zTotalBack = AddSIMD( MulSIMD( planes[i].nZ, centerz ), MulSIMD(planes[i].nZAbs, extz ) );
  4225. fltx4 dotBack = AddSIMD( xTotalBack, AddSIMD(yTotalBack, zTotalBack) );
  4226. // if plane of the farthest corner is behind the plane, then the box is completely outside this plane
  4227. if ( IsVector4LessThan( dotBack, planes[i].dist ) )
  4228. return true;
  4229. }
  4230. return false;
  4231. }
  4232. // Return true if this bounding volume is contained in the frustum, false if it is not
  4233. // TODO SIMDIFY
  4234. bool Frustum_t::Contains( const Vector &mins, const Vector &maxs ) const
  4235. {
  4236. // Get box corners
  4237. Vector vCorners[8];
  4238. vCorners[0] = mins;
  4239. vCorners[1] = Vector( mins.x, mins.y, maxs.z );
  4240. vCorners[2] = Vector( mins.x, maxs.y, mins.z );
  4241. vCorners[3] = Vector( mins.x, maxs.y, maxs.z );
  4242. vCorners[4] = Vector( maxs.x, mins.y, mins.z );
  4243. vCorners[5] = Vector( maxs.x, mins.y, maxs.z );
  4244. vCorners[6] = Vector( maxs.x, maxs.y, mins.z );
  4245. vCorners[7] = maxs;
  4246. // if we are in with all points, then we are fully in
  4247. for ( int j = 0; j < FRUSTUM_NUMPLANES; ++j )
  4248. {
  4249. for( int i = 0; i < 8; ++i )
  4250. {
  4251. // compute the dot product of the normal and the corner
  4252. Vector vNormal;
  4253. float dist;
  4254. GetPlane( i, &vNormal, &dist );
  4255. if ( DotProduct( vCorners[j], vNormal ) <= 0 )
  4256. {
  4257. return false;
  4258. }
  4259. }
  4260. }
  4261. return true; // all pts were inside
  4262. }
  4263. // Brute force SAT frustum intersection between two frustums
  4264. bool Frustum_t::Intersects( Frustum_t &otherFrustum ) const
  4265. {
  4266. Vector pPointsA[8];
  4267. bool bResult = false;
  4268. bResult = GetCorners( pPointsA );
  4269. Assert( bResult );
  4270. VPlane pPlanesA[FRUSTUM_NUMPLANES];
  4271. GetPlanes( pPlanesA );
  4272. Vector pPointsB[8];
  4273. bResult = otherFrustum.GetCorners( pPointsB );
  4274. Assert( bResult );
  4275. VPlane pPlanesB[FRUSTUM_NUMPLANES];
  4276. otherFrustum.GetPlanes( pPlanesB );
  4277. // See if all points in B are on one side of any plane in A
  4278. for ( int p=0; p<6; ++p )
  4279. {
  4280. bool bPointsOnOutside = true;
  4281. for ( int i=0; i<8; ++i )
  4282. {
  4283. float flDist = pPlanesA[ p ].DistTo( pPointsB[ i ] );
  4284. // If dist is pos, we are not on the outside
  4285. if ( flDist > 0 )
  4286. {
  4287. bPointsOnOutside = false;
  4288. break;
  4289. }
  4290. }
  4291. // We never hit a negative case, we have a separating axis
  4292. if ( bPointsOnOutside )
  4293. {
  4294. return false;
  4295. }
  4296. }
  4297. // See if all points in A are on one side of any plane in B
  4298. for ( int p=0; p<6; ++p )
  4299. {
  4300. bool bPointsOnOutside = true;
  4301. for ( int i=0; i<8; ++i )
  4302. {
  4303. float flDist = pPlanesB[ p ].DistTo( pPointsA[ i ] );
  4304. // If dist is pos, we are not on the outside
  4305. if ( flDist > 0 )
  4306. {
  4307. bPointsOnOutside = false;
  4308. break;
  4309. }
  4310. }
  4311. // We never hit a negative case, we have a separating axis
  4312. if ( bPointsOnOutside )
  4313. {
  4314. return false;
  4315. }
  4316. }
  4317. // They intersect
  4318. return true;
  4319. }
  4320. // Return true if this bounding volume intersects the frustum, false if it is outside
  4321. bool Frustum_t::Intersects( const Vector &mins, const Vector &maxs ) const
  4322. {
  4323. fltx4 mins4 = LoadUnalignedSIMD( &mins.x );
  4324. fltx4 minx = SplatXSIMD(mins4);
  4325. fltx4 miny = SplatYSIMD(mins4);
  4326. fltx4 minz = SplatZSIMD(mins4);
  4327. fltx4 maxs4 = LoadUnalignedSIMD( &maxs.x );
  4328. fltx4 maxx = SplatXSIMD(maxs4);
  4329. fltx4 maxy = SplatYSIMD(maxs4);
  4330. fltx4 maxz = SplatZSIMD(maxs4);
  4331. // compute the dot product of the normal and the farthest corner
  4332. // dotBack0 = DotProduct( normal, normals.x < 0 ? mins.x : maxs.x );
  4333. for ( int i = 0; i < 2; i++ )
  4334. {
  4335. fltx4 xTotalBack = MulSIMD( planes[i].nX, MaskedAssign( planes[i].xSign, minx, maxx ) );
  4336. fltx4 yTotalBack = MulSIMD( planes[i].nY, MaskedAssign( planes[i].ySign, miny, maxy ) );
  4337. fltx4 zTotalBack = MulSIMD( planes[i].nZ, MaskedAssign( planes[i].zSign, minz, maxz ) );
  4338. fltx4 dotBack = AddSIMD( xTotalBack, AddSIMD(yTotalBack, zTotalBack) );
  4339. // if plane of the farthest corner is behind the plane, then the box is completely outside this plane
  4340. #if _X360
  4341. if ( !XMVector3GreaterOrEqual( dotBack, planes[i].dist ) )
  4342. return false;
  4343. #elif defined( _PS3 )
  4344. bi32x4 isOut = CmpLtSIMD( dotBack, planes[i].dist );
  4345. if ( IsAnyNegative(isOut) )
  4346. return false;
  4347. #else
  4348. fltx4 isOut = CmpLtSIMD( dotBack, planes[i].dist );
  4349. if ( IsAnyNegative(isOut) )
  4350. return false;
  4351. #endif
  4352. }
  4353. return true;
  4354. }
  4355. bool Frustum_t::Intersects( const fltx4 &mins4, const fltx4 &maxs4 ) const
  4356. {
  4357. fltx4 minx = SplatXSIMD(mins4);
  4358. fltx4 miny = SplatYSIMD(mins4);
  4359. fltx4 minz = SplatZSIMD(mins4);
  4360. fltx4 maxx = SplatXSIMD(maxs4);
  4361. fltx4 maxy = SplatYSIMD(maxs4);
  4362. fltx4 maxz = SplatZSIMD(maxs4);
  4363. // compute the dot product of the normal and the farthest corner
  4364. // dotBack0 = DotProduct( normal, normals.x < 0 ? mins.x : maxs.x );
  4365. for ( int i = 0; i < 2; i++ )
  4366. {
  4367. fltx4 xTotalBack = MulSIMD( planes[i].nX, MaskedAssign( planes[i].xSign, minx, maxx ) );
  4368. fltx4 yTotalBack = MulSIMD( planes[i].nY, MaskedAssign( planes[i].ySign, miny, maxy ) );
  4369. fltx4 zTotalBack = MulSIMD( planes[i].nZ, MaskedAssign( planes[i].zSign, minz, maxz ) );
  4370. fltx4 dotBack = AddSIMD( xTotalBack, AddSIMD(yTotalBack, zTotalBack) );
  4371. // if plane of the farthest corner is behind the plane, then the box is completely outside this plane
  4372. #if _X360
  4373. if ( !XMVector4GreaterOrEqual( dotBack, planes[i].dist ) )
  4374. return false;
  4375. #elif defined( _PS3 )
  4376. bi32x4 isOut = CmpLtSIMD( dotBack, planes[i].dist );
  4377. if ( IsAnyNegative(isOut) )
  4378. return false;
  4379. #else
  4380. fltx4 isOut = CmpLtSIMD( dotBack, planes[i].dist );
  4381. if ( IsAnyNegative(isOut) )
  4382. return false;
  4383. #endif
  4384. }
  4385. return true;
  4386. }
  4387. bool Frustum_t::IntersectsCenterExtents( const Vector &center, const Vector &extents ) const
  4388. {
  4389. fltx4 center4 = LoadUnalignedSIMD( &center.x );
  4390. fltx4 centerx = SplatXSIMD(center4);
  4391. fltx4 centery = SplatYSIMD(center4);
  4392. fltx4 centerz = SplatZSIMD(center4);
  4393. fltx4 extents4 = LoadUnalignedSIMD( &extents.x );
  4394. fltx4 extx = SplatXSIMD(extents4);
  4395. fltx4 exty = SplatYSIMD(extents4);
  4396. fltx4 extz = SplatZSIMD(extents4);
  4397. // compute the dot product of the normal and the farthest corner
  4398. for ( int i = 0; i < 2; i++ )
  4399. {
  4400. fltx4 xTotalBack = AddSIMD( MulSIMD( planes[i].nX, centerx ), MulSIMD(planes[i].nXAbs, extx ) );
  4401. fltx4 yTotalBack = AddSIMD( MulSIMD( planes[i].nY, centery ), MulSIMD(planes[i].nYAbs, exty ) );
  4402. fltx4 zTotalBack = AddSIMD( MulSIMD( planes[i].nZ, centerz ), MulSIMD(planes[i].nZAbs, extz ) );
  4403. fltx4 dotBack = AddSIMD( xTotalBack, AddSIMD(yTotalBack, zTotalBack) );
  4404. // if plane of the farthest corner is behind the plane, then the box is completely outside this plane
  4405. #if _X360
  4406. if ( !XMVector4GreaterOrEqual( dotBack, planes[i].dist ) )
  4407. return false;
  4408. #elif defined( _PS3 )
  4409. bi32x4 isOut = CmpLtSIMD( dotBack, planes[i].dist );
  4410. if ( IsAnyNegative(isOut) )
  4411. return false;
  4412. #else
  4413. fltx4 isOut = CmpLtSIMD( dotBack, planes[i].dist );
  4414. if ( IsAnyNegative(isOut) )
  4415. return false;
  4416. #endif
  4417. }
  4418. return true;
  4419. }
  4420. bool Frustum_t::IntersectsCenterExtents( const fltx4 &fl4Center, const fltx4 &fl4Extents ) const
  4421. {
  4422. fltx4 centerx = SplatXSIMD(fl4Center);
  4423. fltx4 centery = SplatYSIMD(fl4Center);
  4424. fltx4 centerz = SplatZSIMD(fl4Center);
  4425. fltx4 extx = SplatXSIMD(fl4Extents);
  4426. fltx4 exty = SplatYSIMD(fl4Extents);
  4427. fltx4 extz = SplatZSIMD(fl4Extents);
  4428. // compute the dot product of the normal and the farthest corner
  4429. for ( int i = 0; i < 2; i++ )
  4430. {
  4431. fltx4 xTotalBack = AddSIMD( MulSIMD( planes[i].nX, centerx ), MulSIMD(planes[i].nXAbs, extx ) );
  4432. fltx4 yTotalBack = AddSIMD( MulSIMD( planes[i].nY, centery ), MulSIMD(planes[i].nYAbs, exty ) );
  4433. fltx4 zTotalBack = AddSIMD( MulSIMD( planes[i].nZ, centerz ), MulSIMD(planes[i].nZAbs, extz ) );
  4434. fltx4 dotBack = AddSIMD( xTotalBack, AddSIMD(yTotalBack, zTotalBack) );
  4435. // if plane of the farthest corner is behind the plane, then the box is completely outside this plane
  4436. #if _X360
  4437. if ( !XMVector3GreaterOrEqual( dotBack, planes[i].dist ) )
  4438. return false;
  4439. #elif defined( _PS3 )
  4440. bi32x4 isOut = CmpLtSIMD( dotBack, planes[i].dist );
  4441. if ( IsAnyNegative(isOut) )
  4442. return false;
  4443. #else
  4444. fltx4 isOut = CmpLtSIMD( dotBack, planes[i].dist );
  4445. if ( IsAnyNegative(isOut) )
  4446. return false;
  4447. #endif
  4448. }
  4449. return true;
  4450. }
  4451. //-----------------------------------------------------------------------------
  4452. // Generate a frustum based on orthographic parameters
  4453. //-----------------------------------------------------------------------------
  4454. void GenerateOrthoFrustumFLU( const Vector &origin, const Vector &forward, const Vector &vLeft, const Vector &up, float flLeft, float flRight, float flBottom, float flTop, float flZNear, float flZFar, VPlane *pPlanesOut )
  4455. {
  4456. // YUP_ACTIVE: FIXME : This is actually producing incorrect planes (see the VectorMA below)
  4457. Vector vRight = vLeft;
  4458. vRight *= -1.0f;
  4459. float flIntercept = DotProduct( origin, forward );
  4460. pPlanesOut[FRUSTUM_NEARZ].Init( forward, flZNear + flIntercept );
  4461. pPlanesOut[FRUSTUM_FARZ].Init( -forward, -flZFar - flIntercept );
  4462. flIntercept = DotProduct( origin, vRight );
  4463. pPlanesOut[FRUSTUM_RIGHT].Init( -vRight, -flRight - flIntercept );
  4464. pPlanesOut[FRUSTUM_LEFT].Init( vRight, flLeft + flIntercept );
  4465. flIntercept = DotProduct( origin, up );
  4466. pPlanesOut[FRUSTUM_BOTTOM].Init( up, flBottom + flIntercept );
  4467. pPlanesOut[FRUSTUM_TOP].Init( -up, -flTop - flIntercept );
  4468. }
  4469. //-----------------------------------------------------------------------------
  4470. // Generate a frustum based on perspective view parameters
  4471. //-----------------------------------------------------------------------------
  4472. void GeneratePerspectiveFrustumFLU( const Vector& origin, const Vector &forward,
  4473. const Vector &vLeft, const Vector &up, float flZNear, float flZFar,
  4474. float flFovX, float flAspect, VPlane *pPlanesOut )
  4475. {
  4476. // YUP_ACTIVE: FIXME : This is actually producing incorrect planes (see the VectorMA below)
  4477. Vector vRight = vLeft;
  4478. vRight *= -1.0f;
  4479. float flIntercept = DotProduct( origin, forward );
  4480. // Setup the near and far planes.
  4481. pPlanesOut[FRUSTUM_FARZ].Init( -forward, -flZFar - flIntercept );
  4482. pPlanesOut[FRUSTUM_NEARZ].Init( forward, flZNear + flIntercept );
  4483. flFovX *= 0.5f;
  4484. float flTanX = tan( DEG2RAD( flFovX ) );
  4485. float flTanY = flTanX / flAspect;
  4486. // OPTIMIZE: Normalizing these planes is not necessary for culling
  4487. Vector normalPos, normalNeg;
  4488. // NOTE: This should be using left and not right to produce correct planes, not changing it quite yet
  4489. // because I'm not able to test whether fixing this breaks anything.
  4490. VectorMA( vRight, flTanX, forward, normalPos );
  4491. VectorMA( normalPos, -2.0f, vRight, normalNeg );
  4492. VectorNormalize( normalPos );
  4493. VectorNormalize( normalNeg );
  4494. pPlanesOut[FRUSTUM_LEFT].Init( normalPos, normalPos.Dot( origin ) );
  4495. pPlanesOut[FRUSTUM_RIGHT].Init( normalNeg, normalNeg.Dot( origin ) );
  4496. VectorMA( up, flTanY, forward, normalPos );
  4497. VectorMA( normalPos, -2.0f, up, normalNeg );
  4498. VectorNormalize( normalPos );
  4499. VectorNormalize( normalNeg );
  4500. pPlanesOut[FRUSTUM_BOTTOM].Init( normalPos, normalPos.Dot( origin ) );
  4501. pPlanesOut[FRUSTUM_TOP].Init( normalNeg, normalNeg.Dot( origin ) );
  4502. }
  4503. // Generate a frustum based on perspective view parameters
  4504. void Frustum_t::CreatePerspectiveFrustumFLU( const Vector &vOrigin, const Vector &vForward,
  4505. const Vector &vLeft, const Vector &vUp, float flZNear, float flZFar,
  4506. float flFovX, float flAspect )
  4507. {
  4508. VPlane planes[FRUSTUM_NUMPLANES];
  4509. GeneratePerspectiveFrustumFLU( vOrigin, vForward, vLeft, vUp, flZNear, flZFar, flFovX, flAspect, planes );
  4510. SetPlanes( planes );
  4511. }
  4512. //#ifndef YUP_ACTIVE
  4513. void Frustum_t::CreatePerspectiveFrustum( const Vector& origin, const Vector &forward,
  4514. const Vector &right, const Vector &up, float flZNear, float flZFar,
  4515. float flFovX, float flAspect )
  4516. {
  4517. Vector vLeft = right;
  4518. vLeft *= -1.0f;
  4519. CreatePerspectiveFrustumFLU( origin, forward, vLeft, up, flZNear, flZFar, flFovX, flAspect );
  4520. }
  4521. //#endif
  4522. // Version that accepts angles instead of vectors
  4523. void Frustum_t::CreatePerspectiveFrustum( const Vector& origin, const QAngle &angles, float flZNear, float flZFar, float flFovX, float flAspectRatio )
  4524. {
  4525. VPlane planes[FRUSTUM_NUMPLANES];
  4526. Vector vecForward, vecLeft, vecUp;
  4527. AngleVectorsFLU( angles, &vecForward, &vecLeft, &vecUp );
  4528. GeneratePerspectiveFrustumFLU( origin, vecForward, vecLeft, vecUp, flZNear, flZFar, flFovX, flAspectRatio, planes );
  4529. SetPlanes( planes );
  4530. }
  4531. // Generate a frustum based on orthographic parameters
  4532. void Frustum_t::CreateOrthoFrustumFLU( const Vector &origin, const Vector &forward, const Vector &vLeft, const Vector &up, float flLeft, float flRight, float flBottom, float flTop, float flZNear, float flZFar )
  4533. {
  4534. VPlane planes[FRUSTUM_NUMPLANES];
  4535. GenerateOrthoFrustumFLU( origin, forward, vLeft, up, flLeft, flRight, flBottom, flTop, flZNear, flZFar, planes );
  4536. SetPlanes( planes );
  4537. }
  4538. //#ifndef YUP_ACTIVE
  4539. void Frustum_t::CreateOrthoFrustum( const Vector &origin, const Vector &forward, const Vector &right, const Vector &up, float flLeft, float flRight, float flBottom, float flTop, float flZNear, float flZFar )
  4540. {
  4541. Vector vLeft = right;
  4542. vLeft *= -1.0f;
  4543. CreateOrthoFrustumFLU( origin, forward, vLeft, up, flLeft, flRight, flBottom, flTop, flZNear, flZFar );
  4544. }
  4545. // The points returned correspond to the corners of the frustum faces
  4546. // Points 0 to 3 correspond to the near face
  4547. // Points 4 to 7 correspond to the far face
  4548. // Returns points in a face in this order:
  4549. // 2--3
  4550. // | |
  4551. // 0--1
  4552. bool Frustum_t::GetCorners( Vector *pPoints ) const
  4553. {
  4554. VPlane planes[FRUSTUM_NUMPLANES];
  4555. GetPlanes( planes );
  4556. // Near face
  4557. // Bottom Left
  4558. if ( !PlaneIntersection( planes[FRUSTUM_NEARZ], planes[FRUSTUM_LEFT], planes[FRUSTUM_BOTTOM], pPoints[0] ) )
  4559. return false;
  4560. // Bottom right
  4561. if ( !PlaneIntersection( planes[FRUSTUM_NEARZ], planes[FRUSTUM_RIGHT], planes[FRUSTUM_BOTTOM], pPoints[1] ) )
  4562. return false;
  4563. // Upper Left
  4564. if ( !PlaneIntersection( planes[FRUSTUM_NEARZ], planes[FRUSTUM_LEFT], planes[FRUSTUM_TOP], pPoints[2] ) )
  4565. return false;
  4566. // Upper right
  4567. if ( !PlaneIntersection( planes[FRUSTUM_NEARZ], planes[FRUSTUM_RIGHT], planes[FRUSTUM_TOP], pPoints[3] ) )
  4568. return false;
  4569. // Far face
  4570. // Bottom Left
  4571. if ( !PlaneIntersection( planes[FRUSTUM_FARZ], planes[FRUSTUM_LEFT], planes[FRUSTUM_BOTTOM], pPoints[4] ) )
  4572. return false;
  4573. // Bottom right
  4574. if ( !PlaneIntersection( planes[FRUSTUM_FARZ], planes[FRUSTUM_RIGHT], planes[FRUSTUM_BOTTOM], pPoints[5] ) )
  4575. return false;
  4576. // Upper Left
  4577. if ( !PlaneIntersection( planes[FRUSTUM_FARZ], planes[FRUSTUM_LEFT], planes[FRUSTUM_TOP], pPoints[6] ) )
  4578. return false;
  4579. // Upper right
  4580. if ( !PlaneIntersection( planes[FRUSTUM_FARZ], planes[FRUSTUM_RIGHT], planes[FRUSTUM_TOP], pPoints[7] ) )
  4581. return false;
  4582. return true;
  4583. }
  4584. // NOTE: This routine was taken (and modified) from NVidia's BlinnReflection demo
  4585. // Creates basis vectors, based on a vertex and index list.
  4586. // See the NVidia white paper 'GDC2K PerPixel Lighting' for a description
  4587. // of how this computation works
  4588. #define SMALL_FLOAT 1e-12
  4589. void CalcTriangleTangentSpace( const Vector &p0, const Vector &p1, const Vector &p2,
  4590. const Vector2D &t0, const Vector2D &t1, const Vector2D& t2,
  4591. Vector &sVect, Vector &tVect )
  4592. {
  4593. /* Compute the partial derivatives of X, Y, and Z with respect to S and T. */
  4594. sVect.Init( 0.0f, 0.0f, 0.0f );
  4595. tVect.Init( 0.0f, 0.0f, 0.0f );
  4596. // x, s, t
  4597. Vector edge01( p1.x - p0.x, t1.x - t0.x, t1.y - t0.y );
  4598. Vector edge02( p2.x - p0.x, t2.x - t0.x, t2.y - t0.y );
  4599. Vector cross;
  4600. CrossProduct( edge01, edge02, cross );
  4601. if ( fabs( cross.x ) > SMALL_FLOAT )
  4602. {
  4603. sVect.x += -cross.y / cross.x;
  4604. tVect.x += -cross.z / cross.x;
  4605. }
  4606. // y, s, t
  4607. edge01.Init( p1.y - p0.y, t1.x - t0.x, t1.y - t0.y );
  4608. edge02.Init( p2.y - p0.y, t2.x - t0.x, t2.y - t0.y );
  4609. CrossProduct( edge01, edge02, cross );
  4610. if ( fabs( cross.x ) > SMALL_FLOAT )
  4611. {
  4612. sVect.y += -cross.y / cross.x;
  4613. tVect.y += -cross.z / cross.x;
  4614. }
  4615. // z, s, t
  4616. edge01.Init( p1.z - p0.z, t1.x - t0.x, t1.y - t0.y );
  4617. edge02.Init( p2.z - p0.z, t2.x - t0.x, t2.y - t0.y );
  4618. CrossProduct( edge01, edge02, cross );
  4619. if( fabs( cross.x ) > SMALL_FLOAT )
  4620. {
  4621. sVect.z += -cross.y / cross.x;
  4622. tVect.z += -cross.z / cross.x;
  4623. }
  4624. // Normalize sVect and tVect
  4625. VectorNormalize( sVect );
  4626. VectorNormalize( tVect );
  4627. }
  4628. //-----------------------------------------------------------------------------
  4629. // Convert RGB to HSV
  4630. //-----------------------------------------------------------------------------
  4631. void RGBtoHSV( const Vector &rgb, Vector &hsv )
  4632. {
  4633. float flMax = MAX( rgb.x, rgb.y );
  4634. flMax = MAX( flMax, rgb.z );
  4635. float flMin = MIN( rgb.x, rgb.y );
  4636. flMin = MIN( flMin, rgb.z );
  4637. // hsv.z is the value
  4638. hsv.z = flMax;
  4639. // hsv.y is the saturation
  4640. if (flMax != 0.0F)
  4641. {
  4642. hsv.y = (flMax - flMin) / flMax;
  4643. }
  4644. else
  4645. {
  4646. hsv.y = 0.0F;
  4647. }
  4648. // hsv.x is the hue
  4649. if (hsv.y == 0.0F)
  4650. {
  4651. hsv.x = -1.0f;
  4652. }
  4653. else
  4654. {
  4655. float32 d = flMax - flMin;
  4656. if (rgb.x == flMax)
  4657. {
  4658. hsv.x = (rgb.y - rgb.z) / d;
  4659. }
  4660. else if (rgb.y == flMax)
  4661. {
  4662. hsv.x = 2.0F + (rgb.z - rgb.x) / d;
  4663. }
  4664. else
  4665. {
  4666. hsv.x = 4.0F + (rgb.x - rgb.y) / d;
  4667. }
  4668. hsv.x *= 60.0F;
  4669. if ( hsv.x < 0.0F )
  4670. {
  4671. hsv.x += 360.0F;
  4672. }
  4673. }
  4674. }
  4675. //-----------------------------------------------------------------------------
  4676. // Convert HSV to RGB
  4677. //-----------------------------------------------------------------------------
  4678. void HSVtoRGB( const Vector &hsv, Vector &rgb )
  4679. {
  4680. if ( hsv.y == 0.0F )
  4681. {
  4682. rgb.Init( hsv.z, hsv.z, hsv.z );
  4683. return;
  4684. }
  4685. float32 hue = hsv.x;
  4686. if (hue == 360.0F)
  4687. {
  4688. hue = 0.0F;
  4689. }
  4690. hue /= 60.0F;
  4691. int i = Float2Int( hue ); // integer part
  4692. float32 f = hue - i; // fractional part
  4693. float32 p = hsv.z * (1.0F - hsv.y);
  4694. float32 q = hsv.z * (1.0F - hsv.y * f);
  4695. float32 t = hsv.z * (1.0F - hsv.y * (1.0F - f));
  4696. switch(i)
  4697. {
  4698. case 0: rgb.Init( hsv.z, t, p ); break;
  4699. case 1: rgb.Init( q, hsv.z, p ); break;
  4700. case 2: rgb.Init( p, hsv.z, t ); break;
  4701. case 3: rgb.Init( p, q, hsv.z ); break;
  4702. case 4: rgb.Init( t, p, hsv.z ); break;
  4703. case 5: rgb.Init( hsv.z, p, q ); break;
  4704. }
  4705. }
  4706. void GetInterpolationData( float const *pKnotPositions,
  4707. float const *pKnotValues,
  4708. int nNumValuesinList,
  4709. int nInterpolationRange,
  4710. float flPositionToInterpolateAt,
  4711. bool bWrap,
  4712. float *pValueA,
  4713. float *pValueB,
  4714. float *pInterpolationValue)
  4715. {
  4716. // first, find the bracketting knots by looking for the first knot >= our index
  4717. int idx;
  4718. for(idx = 0; idx < nNumValuesinList; idx++ )
  4719. {
  4720. if ( pKnotPositions[idx] >= flPositionToInterpolateAt )
  4721. break;
  4722. }
  4723. int nKnot1, nKnot2;
  4724. float flOffsetFromStartOfGap, flSizeOfGap;
  4725. if ( idx == 0)
  4726. {
  4727. if ( bWrap )
  4728. {
  4729. nKnot1 = nNumValuesinList-1;
  4730. nKnot2 = 0;
  4731. flSizeOfGap =
  4732. ( pKnotPositions[nKnot2] + ( nInterpolationRange-pKnotPositions[nKnot1] ) );
  4733. flOffsetFromStartOfGap =
  4734. flPositionToInterpolateAt + ( nInterpolationRange-pKnotPositions[nKnot1] );
  4735. }
  4736. else
  4737. {
  4738. *pValueA = *pValueB = pKnotValues[0];
  4739. *pInterpolationValue = 1.0;
  4740. return;
  4741. }
  4742. }
  4743. else if ( idx == nNumValuesinList ) // ran out of values
  4744. {
  4745. if ( bWrap )
  4746. {
  4747. nKnot1 = nNumValuesinList -1;
  4748. nKnot2 = 0;
  4749. flSizeOfGap = ( pKnotPositions[nKnot2] +
  4750. ( nInterpolationRange-pKnotPositions[nKnot1] ) );
  4751. flOffsetFromStartOfGap = flPositionToInterpolateAt - pKnotPositions[nKnot1];
  4752. }
  4753. else
  4754. {
  4755. *pValueA = *pValueB = pKnotValues[nNumValuesinList-1];
  4756. *pInterpolationValue = 1.0;
  4757. return;
  4758. }
  4759. }
  4760. else
  4761. {
  4762. nKnot1 = idx-1;
  4763. nKnot2 = idx;
  4764. flSizeOfGap = pKnotPositions[nKnot2]-pKnotPositions[nKnot1];
  4765. flOffsetFromStartOfGap = flPositionToInterpolateAt-pKnotPositions[nKnot1];
  4766. }
  4767. *pValueA = pKnotValues[nKnot1];
  4768. *pValueB = pKnotValues[nKnot2];
  4769. *pInterpolationValue = FLerp( 0, 1, 0, flSizeOfGap, flOffsetFromStartOfGap );
  4770. return;
  4771. }
  4772. static Vector RandomVectorOnUnitSphere( float u, float v )
  4773. {
  4774. float flPhi = acos( 1 - 2 * u );
  4775. float flTheta = 2 * M_PI * v;
  4776. float flSinPhi, flCosPhi;
  4777. float flSinTheta, flCosTheta;
  4778. SinCos( flPhi, &flSinPhi, &flCosPhi );
  4779. SinCos( flTheta, &flSinTheta, &flCosTheta );
  4780. return Vector( flSinPhi * flCosTheta, flSinPhi * flSinTheta, flCosPhi );
  4781. }
  4782. Vector RandomVectorOnUnitSphere()
  4783. {
  4784. // Guarantee uniform random distribution on a sphere
  4785. // Graphics gems III contains this algorithm ("Nonuniform random point sets via warping")
  4786. float u = RandomFloat( 0., 1. );
  4787. float v = RandomFloat( 0., 1. );
  4788. return RandomVectorOnUnitSphere( u, v );
  4789. }
  4790. Vector RandomVectorOnUnitSphere( IUniformRandomStream *pRnd )
  4791. {
  4792. return RandomVectorOnUnitSphere( pRnd->RandomFloat(), pRnd->RandomFloat() );
  4793. }
  4794. float RandomVectorInUnitSphere( Vector *pVector )
  4795. {
  4796. // Guarantee uniform random distribution within a sphere
  4797. // Graphics gems III contains this algorithm ("Nonuniform random point sets via warping")
  4798. float u = ((float)rand() / VALVE_RAND_MAX);
  4799. float v = ((float)rand() / VALVE_RAND_MAX);
  4800. float w = ((float)rand() / VALVE_RAND_MAX);
  4801. float flPhi = acos( 1 - 2 * u );
  4802. float flTheta = 2 * M_PI * v;
  4803. float flRadius = powf( w, 1.0f / 3.0f );
  4804. float flSinPhi, flCosPhi;
  4805. float flSinTheta, flCosTheta;
  4806. SinCos( flPhi, &flSinPhi, &flCosPhi );
  4807. SinCos( flTheta, &flSinTheta, &flCosTheta );
  4808. pVector->x = flRadius * flSinPhi * flCosTheta;
  4809. pVector->y = flRadius * flSinPhi * flSinTheta;
  4810. pVector->z = flRadius * flCosPhi;
  4811. return flRadius;
  4812. }
  4813. Vector RandomVectorInUnitSphere()
  4814. {
  4815. Vector vOut;
  4816. RandomVectorInUnitSphere( &vOut );
  4817. return vOut;
  4818. }
  4819. Vector RandomVectorInUnitSphere( IUniformRandomStream *pRnd )
  4820. {
  4821. float w = pRnd->RandomFloat();
  4822. float flRadius = powf( w, 1.0f / 3.0f );
  4823. Vector v = RandomVectorOnUnitSphere( pRnd ) * flRadius;
  4824. return v;
  4825. }
  4826. float RandomVectorInUnitCircle( Vector2D *pVector )
  4827. {
  4828. // Guarantee uniform random distribution within a sphere
  4829. // Graphics gems III contains this algorithm ("Nonuniform random point sets via warping")
  4830. float u = ((float)rand() / VALVE_RAND_MAX);
  4831. float v = ((float)rand() / VALVE_RAND_MAX);
  4832. float flTheta = 2 * M_PI * v;
  4833. float flRadius = powf( u, 1.0f / 2.0f );
  4834. float flSinTheta, flCosTheta;
  4835. SinCos( flTheta, &flSinTheta, &flCosTheta );
  4836. pVector->x = flRadius * flCosTheta;
  4837. pVector->y = flRadius * flSinTheta;
  4838. return flRadius;
  4839. }
  4840. const Quaternion RandomQuaternion()
  4841. {
  4842. // Guarantee uniform distribution within S^3. Found on the internet, looked through the proof very briefly, looks sound enough to tentatively trust it before testing or checking the proof for real.
  4843. // http://mathproofs.blogspot.com/2005/05/uniformly-distributed-random-unit.html
  4844. float u = RandomFloat( 0, 2 * M_PI ), flSinU = sinf( u );
  4845. float v = acosf( RandomFloat( -1, 1 ) ), flSinV = sinf( v );
  4846. float w = 0.5f * ( RandomFloat( 0, M_PI ) + acosf( RandomFloat( 0, 1 ) ) + M_PI / 2 ), flSinW = sinf( w );
  4847. return Quaternion( cosf( u ), flSinU * cosf( v ), flSinU * flSinV * cosf( w ), flSinU * flSinV * flSinW );
  4848. }
  4849. const Quaternion RandomQuaternion( IUniformRandomStream *pRnd )
  4850. {
  4851. // Guarantee uniform distribution within S^3. Found on the internet, looked through the proof very briefly, looks sound enough to tentatively trust it before testing or checking the proof for real.
  4852. // http://mathproofs.blogspot.com/2005/05/uniformly-distributed-random-unit.html
  4853. float u = pRnd->RandomFloat( 0, 2 * M_PI ), flSinU = sinf( u );
  4854. float v = acosf( pRnd->RandomFloat( -1, 1 ) ), flSinV = sinf( v );
  4855. float w = 0.5f * ( pRnd->RandomFloat( 0, M_PI ) + acosf( pRnd->RandomFloat( 0, 1 ) ) + M_PI / 2 ), flSinW = sinf( w );
  4856. return Quaternion( cosf( u ), flSinU * cosf( v ), flSinU * flSinV * cosf( w ), flSinU * flSinV * flSinW );
  4857. }
  4858. // Originally from hammer_mathlib.cpp
  4859. //
  4860. // Generate the corner points of a box:
  4861. // +y _+z
  4862. // ^ /|
  4863. // | /
  4864. // | 3---7
  4865. // /| /|
  4866. // / | / |
  4867. // 2---6 |
  4868. // | 1|--5
  4869. // | / | /
  4870. // |/ |/
  4871. // 0---4 --> +x
  4872. //
  4873. void PointsFromBox( const Vector &mins, const Vector &maxs, Vector *points )
  4874. {
  4875. points[ 0 ][ 0 ] = mins[ 0 ];
  4876. points[ 0 ][ 1 ] = mins[ 1 ];
  4877. points[ 0 ][ 2 ] = mins[ 2 ];
  4878. points[ 1 ][ 0 ] = mins[ 0 ];
  4879. points[ 1 ][ 1 ] = mins[ 1 ];
  4880. points[ 1 ][ 2 ] = maxs[ 2 ];
  4881. points[ 2 ][ 0 ] = mins[ 0 ];
  4882. points[ 2 ][ 1 ] = maxs[ 1 ];
  4883. points[ 2 ][ 2 ] = mins[ 2 ];
  4884. points[ 3 ][ 0 ] = mins[ 0 ];
  4885. points[ 3 ][ 1 ] = maxs[ 1 ];
  4886. points[ 3 ][ 2 ] = maxs[ 2 ];
  4887. points[ 4 ][ 0 ] = maxs[ 0 ];
  4888. points[ 4 ][ 1 ] = mins[ 1 ];
  4889. points[ 4 ][ 2 ] = mins[ 2 ];
  4890. points[ 5 ][ 0 ] = maxs[ 0 ];
  4891. points[ 5 ][ 1 ] = mins[ 1 ];
  4892. points[ 5 ][ 2 ] = maxs[ 2 ];
  4893. points[ 6 ][ 0 ] = maxs[ 0 ];
  4894. points[ 6 ][ 1 ] = maxs[ 1 ];
  4895. points[ 6 ][ 2 ] = mins[ 2 ];
  4896. points[ 7 ][ 0 ] = maxs[ 0 ];
  4897. points[ 7 ][ 1 ] = maxs[ 1 ];
  4898. points[ 7 ][ 2 ] = maxs[ 2 ];
  4899. }
  4900. void BuildTransformedBox( Vector *v2, Vector const &bbmin, Vector const &bbmax, const matrix3x4_t& m )
  4901. {
  4902. Vector v[ 8 ];
  4903. PointsFromBox( bbmin, bbmax, v );
  4904. VectorTransform( v[ 0 ], m, v2[ 0 ] );
  4905. VectorTransform( v[ 1 ], m, v2[ 1 ] );
  4906. VectorTransform( v[ 2 ], m, v2[ 2 ] );
  4907. VectorTransform( v[ 3 ], m, v2[ 3 ] );
  4908. VectorTransform( v[ 4 ], m, v2[ 4 ] );
  4909. VectorTransform( v[ 5 ], m, v2[ 5 ] );
  4910. VectorTransform( v[ 6 ], m, v2[ 6 ] );
  4911. VectorTransform( v[ 7 ], m, v2[ 7 ] );
  4912. }
  4913. #endif // !defined(__SPU__)