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.

169 lines
5.9 KiB

  1. #include <basetypes.h>
  2. #include <float.h>
  3. #include "tier1/utlvector.h"
  4. #include "eigen.h"
  5. #include "mathlib/aabb.h"
  6. static matrix3x4_t Transpose(const matrix3x4_t &a)
  7. {
  8. return matrix3x4_t(a[0][0],a[1][0],a[2][0],0,
  9. a[0][1],a[1][1],a[2][1],0,
  10. a[0][2],a[1][2],a[2][2],0 );
  11. }
  12. Quaternion Diagonalizer( const matrix3x4_t &A, Vector &d )
  13. {
  14. // A must be a symmetric matrix.
  15. // returns quaternion q such that its corresponding matrix Q
  16. // can be used to Diagonalize A
  17. // Note, this routine has been adapted to valve's matrix conventions
  18. // which is C-style row-major matrix using common mathtext (openglish) column conventions for
  19. // representing transforms (rotation and position): v' = Mv
  20. // Valve's quaternion conventions are the same that everybody uses novodex, d3d, ogl.
  21. // Diagonal matrix D = Transpose(Q) * A * Q; and A = QT*D*Q
  22. // The columns of Q are the eigenvectors D's diagonal is the eigenvalues
  23. // As per 'column' convention if Q = q.getmatrix(); then Q*v = q*v*conj(q)
  24. int maxsteps = 24; // certainly wont need that many.
  25. int i;
  26. Quaternion q( 0, 0, 0, 1 );
  27. for ( i = 0; i < maxsteps; i++ )
  28. {
  29. matrix3x4_t Q ; // Q*v == q*v*conj(q)
  30. QuaternionMatrix( q, Q );
  31. matrix3x4_t D = Transpose( Q ) * A * Q; // A = Q*D*Q^T
  32. Vector offdiag( D[1][2], D[0][2], D[0][1] ); // elements not on the diagonal
  33. d = Vector( D[0][0], D[1][1], D[2][2] );
  34. Vector om( fabsf( offdiag.x ), fabsf( offdiag.y ), fabsf( offdiag.z ) ); // mag of each offdiag elem
  35. int k = ( om.x > om.y && om.x > om.z ) ? 0 : ( om.y > om.z ) ? 1 : 2; // index of largest element of offdiag
  36. int k1 = ( k + 1 ) % 3;
  37. int k2 = ( k + 2 ) % 3;
  38. if ( offdiag[k] == 0.0f ) break; // diagonal already
  39. float thet = ( D[k2][k2] - D[k1][k1] ) / ( 2.0f * offdiag[k] );
  40. float sgn = ( thet > 0.0f ) ? 1.0f : -1.0f;
  41. thet *= sgn; // make it positive
  42. float t = sgn / ( thet + ( ( thet < 1.E6f ) ? sqrtf( thet * thet + 1.0f ) : thet ) ) ; // sign(T)/(|T|+sqrt(T^2+1))
  43. float c = 1.0f / sqrtf( t * t + 1.0f ); // c= 1/(t^2+1) , t=s/c
  44. if ( c == 1.0f ) break; // no room for improvement - reached machine precision.
  45. Quaternion jr( 0, 0, 0, 0 ); // jacobi rotation for this iteration.
  46. jr[k] = sgn * sqrtf( ( 1.0f - c ) / 2.0f ); // using 1/2 angle identity sin(a/2) = sqrt((1-cos(a))/2)
  47. jr[k] *= -1.0f; // ??since our quat-to-matrix convention was for v*M instead of M*v
  48. jr.w = sqrtf( 1.0f - jr[k] * jr[k] );
  49. if ( jr.w == 1.0f ) break; // reached limits of floating point precision
  50. QuaternionMult( q, jr, q ); //q = q*jr;
  51. QuaternionNormalize( q );
  52. }
  53. return q;
  54. }
  55. //-----------------------------------------------------------------------------
  56. // Computes the mean point of a set of points, used by ComputeCovariantMatrix
  57. //-----------------------------------------------------------------------------
  58. extern Vector ComputeMeanPoint( const Vector *pPointList, int nPointCount )
  59. {
  60. Vector vMean( 0.0f, 0.0f, 0.0f );
  61. for ( int ii = 0; ii < nPointCount; ++ii )
  62. {
  63. vMean += pPointList[ii];
  64. }
  65. vMean /= static_cast< float >( nPointCount );
  66. return vMean;
  67. }
  68. //-----------------------------------------------------------------------------
  69. // Computes a covariance matrix for a set of points which measures spatial
  70. // dispersion of the points against the mean of the points,
  71. // the covariance matrix is symmetric and suitable for use in Diagonalizer()
  72. //-----------------------------------------------------------------------------
  73. void ComputeCovarianceMatrix( matrix3x4_t &covarianceMatrix, const Vector *pPointList, int nPointCount )
  74. {
  75. SetIdentityMatrix( covarianceMatrix );
  76. if ( nPointCount <= 0 )
  77. return;
  78. const Vector vMean = ComputeMeanPoint( pPointList, nPointCount );
  79. CUtlVector< Vector > skewedPointList;
  80. skewedPointList.CopyArray( pPointList, nPointCount );
  81. for ( int ii = 0; ii < nPointCount; ++ii )
  82. {
  83. skewedPointList[ ii ] -= vMean;
  84. }
  85. const float flPointCount = static_cast< float >( nPointCount );
  86. for ( int ii = 0; ii < 3; ++ii )
  87. {
  88. for ( int jj = 0; jj < 3; ++jj )
  89. {
  90. float flCovariance = 0.0f;
  91. for ( int kk = 0; kk < nPointCount; ++kk )
  92. {
  93. flCovariance += skewedPointList[kk][ii] * skewedPointList[kk][jj];
  94. }
  95. covarianceMatrix[ii][jj] = flCovariance / flPointCount;
  96. }
  97. }
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Computes the center and scale using qEigenVectors as the orientation to
  101. // transform a unit cube at the origin to contain the specified point list,
  102. // calls ComputeCovarianceMatrix(), Diagonalizer()
  103. //-----------------------------------------------------------------------------
  104. void ComputeExtents( Vector &vCenter, Vector &vScale, const Quaternion &qEigen, const Vector *pPointList, int nPointCount )
  105. {
  106. if ( nPointCount <= 0 )
  107. return;
  108. AABB_t bbox;
  109. // Compute bounding box in inverse eigen space
  110. const Quaternion qEigenInverse = QuaternionInvert( qEigen );
  111. const matrix3x4_t mEigenInverse = QuaternionMatrix( qEigenInverse ); // VectorRotate with a quaternion does this each call
  112. Vector vTmp;
  113. VectorRotate( pPointList[0], mEigenInverse, vTmp );
  114. bbox.SetToPoint( vTmp );
  115. for ( int ii = 1; ii < nPointCount; ++ii )
  116. {
  117. VectorRotate( pPointList[ii], mEigenInverse, vTmp );
  118. bbox |= vTmp;
  119. }
  120. VectorRotate( bbox.GetCenter(), qEigen, vCenter ); // Transform center back to eigen space
  121. vScale = bbox.GetSize();
  122. }
  123. //-----------------------------------------------------------------------------
  124. //
  125. //-----------------------------------------------------------------------------
  126. extern void ComputeBoundingBoxMatrix( matrix3x4_t &boundingBoxMatrix, const Vector *pPointList, int nPointCount )
  127. {
  128. matrix3x4_t covarianceMatrix;
  129. ComputeCovarianceMatrix( covarianceMatrix, pPointList, nPointCount );
  130. Vector vEigenValues;
  131. const Quaternion qEigenVectors = Diagonalizer( covarianceMatrix, vEigenValues );
  132. Vector vCenter( 0.0f, 0.0f, 0.0f );
  133. Vector vScale( 1.0f, 1.0f, 1.0f );
  134. ComputeExtents( vCenter, vScale, qEigenVectors, pPointList, nPointCount );
  135. QuaternionMatrix( qEigenVectors, vCenter, vScale, boundingBoxMatrix );
  136. }