170 lines
5.9 KiB
C++
170 lines
5.9 KiB
C++
#include <basetypes.h>
|
|
#include <float.h>
|
|
#include "tier1/utlvector.h"
|
|
#include "eigen.h"
|
|
#include "mathlib/aabb.h"
|
|
|
|
static matrix3x4_t Transpose(const matrix3x4_t &a)
|
|
{
|
|
return matrix3x4_t(a[0][0],a[1][0],a[2][0],0,
|
|
a[0][1],a[1][1],a[2][1],0,
|
|
a[0][2],a[1][2],a[2][2],0 );
|
|
}
|
|
|
|
|
|
|
|
Quaternion Diagonalizer( const matrix3x4_t &A, Vector &d )
|
|
{
|
|
// A must be a symmetric matrix.
|
|
// returns quaternion q such that its corresponding matrix Q
|
|
// can be used to Diagonalize A
|
|
// Note, this routine has been adapted to valve's matrix conventions
|
|
// which is C-style row-major matrix using common mathtext (openglish) column conventions for
|
|
// representing transforms (rotation and position): v' = Mv
|
|
// Valve's quaternion conventions are the same that everybody uses novodex, d3d, ogl.
|
|
// Diagonal matrix D = Transpose(Q) * A * Q; and A = QT*D*Q
|
|
// The columns of Q are the eigenvectors D's diagonal is the eigenvalues
|
|
// As per 'column' convention if Q = q.getmatrix(); then Q*v = q*v*conj(q)
|
|
int maxsteps = 24; // certainly wont need that many.
|
|
int i;
|
|
Quaternion q( 0, 0, 0, 1 );
|
|
for ( i = 0; i < maxsteps; i++ )
|
|
{
|
|
matrix3x4_t Q ; // Q*v == q*v*conj(q)
|
|
QuaternionMatrix( q, Q );
|
|
matrix3x4_t D = Transpose( Q ) * A * Q; // A = Q*D*Q^T
|
|
Vector offdiag( D[1][2], D[0][2], D[0][1] ); // elements not on the diagonal
|
|
d = Vector( D[0][0], D[1][1], D[2][2] );
|
|
Vector om( fabsf( offdiag.x ), fabsf( offdiag.y ), fabsf( offdiag.z ) ); // mag of each offdiag elem
|
|
int k = ( om.x > om.y && om.x > om.z ) ? 0 : ( om.y > om.z ) ? 1 : 2; // index of largest element of offdiag
|
|
int k1 = ( k + 1 ) % 3;
|
|
int k2 = ( k + 2 ) % 3;
|
|
if ( offdiag[k] == 0.0f ) break; // diagonal already
|
|
float thet = ( D[k2][k2] - D[k1][k1] ) / ( 2.0f * offdiag[k] );
|
|
float sgn = ( thet > 0.0f ) ? 1.0f : -1.0f;
|
|
thet *= sgn; // make it positive
|
|
float t = sgn / ( thet + ( ( thet < 1.E6f ) ? sqrtf( thet * thet + 1.0f ) : thet ) ) ; // sign(T)/(|T|+sqrt(T^2+1))
|
|
float c = 1.0f / sqrtf( t * t + 1.0f ); // c= 1/(t^2+1) , t=s/c
|
|
if ( c == 1.0f ) break; // no room for improvement - reached machine precision.
|
|
Quaternion jr( 0, 0, 0, 0 ); // jacobi rotation for this iteration.
|
|
jr[k] = sgn * sqrtf( ( 1.0f - c ) / 2.0f ); // using 1/2 angle identity sin(a/2) = sqrt((1-cos(a))/2)
|
|
jr[k] *= -1.0f; // ??since our quat-to-matrix convention was for v*M instead of M*v
|
|
jr.w = sqrtf( 1.0f - jr[k] * jr[k] );
|
|
if ( jr.w == 1.0f ) break; // reached limits of floating point precision
|
|
QuaternionMult( q, jr, q ); //q = q*jr;
|
|
QuaternionNormalize( q );
|
|
}
|
|
return q;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes the mean point of a set of points, used by ComputeCovariantMatrix
|
|
//-----------------------------------------------------------------------------
|
|
extern Vector ComputeMeanPoint( const Vector *pPointList, int nPointCount )
|
|
{
|
|
Vector vMean( 0.0f, 0.0f, 0.0f );
|
|
|
|
for ( int ii = 0; ii < nPointCount; ++ii )
|
|
{
|
|
vMean += pPointList[ii];
|
|
}
|
|
|
|
vMean /= static_cast< float >( nPointCount );
|
|
|
|
return vMean;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes a covariance matrix for a set of points which measures spatial
|
|
// dispersion of the points against the mean of the points,
|
|
// the covariance matrix is symmetric and suitable for use in Diagonalizer()
|
|
//-----------------------------------------------------------------------------
|
|
void ComputeCovarianceMatrix( matrix3x4_t &covarianceMatrix, const Vector *pPointList, int nPointCount )
|
|
{
|
|
SetIdentityMatrix( covarianceMatrix );
|
|
|
|
if ( nPointCount <= 0 )
|
|
return;
|
|
|
|
const Vector vMean = ComputeMeanPoint( pPointList, nPointCount );
|
|
|
|
CUtlVector< Vector > skewedPointList;
|
|
skewedPointList.CopyArray( pPointList, nPointCount );
|
|
|
|
for ( int ii = 0; ii < nPointCount; ++ii )
|
|
{
|
|
skewedPointList[ ii ] -= vMean;
|
|
}
|
|
|
|
const float flPointCount = static_cast< float >( nPointCount );
|
|
|
|
for ( int ii = 0; ii < 3; ++ii )
|
|
{
|
|
for ( int jj = 0; jj < 3; ++jj )
|
|
{
|
|
float flCovariance = 0.0f;
|
|
|
|
for ( int kk = 0; kk < nPointCount; ++kk )
|
|
{
|
|
flCovariance += skewedPointList[kk][ii] * skewedPointList[kk][jj];
|
|
}
|
|
|
|
covarianceMatrix[ii][jj] = flCovariance / flPointCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes the center and scale using qEigenVectors as the orientation to
|
|
// transform a unit cube at the origin to contain the specified point list,
|
|
// calls ComputeCovarianceMatrix(), Diagonalizer()
|
|
//-----------------------------------------------------------------------------
|
|
void ComputeExtents( Vector &vCenter, Vector &vScale, const Quaternion &qEigen, const Vector *pPointList, int nPointCount )
|
|
{
|
|
if ( nPointCount <= 0 )
|
|
return;
|
|
|
|
AABB_t bbox;
|
|
|
|
// Compute bounding box in inverse eigen space
|
|
const Quaternion qEigenInverse = QuaternionInvert( qEigen );
|
|
const matrix3x4_t mEigenInverse = QuaternionMatrix( qEigenInverse ); // VectorRotate with a quaternion does this each call
|
|
|
|
Vector vTmp;
|
|
VectorRotate( pPointList[0], mEigenInverse, vTmp );
|
|
|
|
bbox.SetToPoint( vTmp );
|
|
|
|
for ( int ii = 1; ii < nPointCount; ++ii )
|
|
{
|
|
VectorRotate( pPointList[ii], mEigenInverse, vTmp );
|
|
bbox |= vTmp;
|
|
}
|
|
|
|
VectorRotate( bbox.GetCenter(), qEigen, vCenter ); // Transform center back to eigen space
|
|
vScale = bbox.GetSize();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
extern void ComputeBoundingBoxMatrix( matrix3x4_t &boundingBoxMatrix, const Vector *pPointList, int nPointCount )
|
|
{
|
|
matrix3x4_t covarianceMatrix;
|
|
ComputeCovarianceMatrix( covarianceMatrix, pPointList, nPointCount );
|
|
|
|
Vector vEigenValues;
|
|
const Quaternion qEigenVectors = Diagonalizer( covarianceMatrix, vEigenValues );
|
|
|
|
Vector vCenter( 0.0f, 0.0f, 0.0f );
|
|
Vector vScale( 1.0f, 1.0f, 1.0f );
|
|
|
|
ComputeExtents( vCenter, vScale, qEigenVectors, pPointList, nPointCount );
|
|
|
|
QuaternionMatrix( qEigenVectors, vCenter, vScale, boundingBoxMatrix );
|
|
}
|