/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #ifndef __MATH_MATX_H__ #define __MATH_MATX_H__ /* =============================================================================== idMatX - arbitrary sized dense real matrix The matrix lives on 16 byte aligned and 16 byte padded memory. NOTE: due to the temporary memory pool idMatX cannot be used by multiple threads. =============================================================================== */ #define MATX_MAX_TEMP 1024 #define MATX_QUAD( x ) ( ( ( ( x ) + 3 ) & ~3 ) * sizeof( float ) ) #define MATX_CLEAREND() int s = numRows * numColumns; while( s < ( ( s + 3 ) & ~3 ) ) { mat[s++] = 0.0f; } #define MATX_ALLOCA( n ) ( (float *) _alloca16( MATX_QUAD( n ) ) ) #define MATX_ALLOCA_CACHE_LINES( n ) ( (float *) _alloca128( ( ( n ) * sizeof( float ) + CACHE_LINE_SIZE - 1 ) & ~ ( CACHE_LINE_SIZE - 1 ) ) ) #define MATX_SIMD class idMatX { public: ID_INLINE idMatX(); ID_INLINE idMatX( const idMatX & other ); ID_INLINE explicit idMatX( int rows, int columns ); ID_INLINE explicit idMatX( int rows, int columns, float *src ); ID_INLINE ~idMatX(); ID_INLINE void Set( int rows, int columns, const float *src ); ID_INLINE void Set( const idMat3 &m1, const idMat3 &m2 ); ID_INLINE void Set( const idMat3 &m1, const idMat3 &m2, const idMat3 &m3, const idMat3 &m4 ); ID_INLINE const float * operator[]( int index ) const; ID_INLINE float * operator[]( int index ); ID_INLINE idMatX & operator=( const idMatX &a ); ID_INLINE idMatX operator*( const float a ) const; ID_INLINE idVecX operator*( const idVecX &vec ) const; ID_INLINE idMatX operator*( const idMatX &a ) const; ID_INLINE idMatX operator+( const idMatX &a ) const; ID_INLINE idMatX operator-( const idMatX &a ) const; ID_INLINE idMatX & operator*=( const float a ); ID_INLINE idMatX & operator*=( const idMatX &a ); ID_INLINE idMatX & operator+=( const idMatX &a ); ID_INLINE idMatX & operator-=( const idMatX &a ); friend ID_INLINE idMatX operator*( const float a, const idMatX &m ); friend ID_INLINE idVecX operator*( const idVecX &vec, const idMatX &m ); friend ID_INLINE idVecX &operator*=( idVecX &vec, const idMatX &m ); ID_INLINE bool Compare( const idMatX &a ) const; // exact compare, no epsilon ID_INLINE bool Compare( const idMatX &a, const float epsilon ) const; // compare with epsilon ID_INLINE bool operator==( const idMatX &a ) const; // exact compare, no epsilon ID_INLINE bool operator!=( const idMatX &a ) const; // exact compare, no epsilon ID_INLINE void SetSize( int rows, int columns ); // set the number of rows/columns void ChangeSize( int rows, int columns, bool makeZero = false ); // change the size keeping data intact where possible ID_INLINE void ChangeNumRows( int rows ) { ChangeSize( rows, numColumns ); } // set the number of rows/columns int GetNumRows() const { return numRows; } // get the number of rows int GetNumColumns() const { return numColumns; } // get the number of columns ID_INLINE void SetData( int rows, int columns, float *data ); // set float array pointer ID_INLINE void SetDataCacheLines( int rows, int columns, float *data, bool clear );// set float array pointer ID_INLINE void Zero(); // clear matrix ID_INLINE void Zero( int rows, int columns ); // set size and clear matrix ID_INLINE void Identity(); // clear to identity matrix ID_INLINE void Identity( int rows, int columns ); // set size and clear to identity matrix ID_INLINE void Diag( const idVecX &v ); // create diagonal matrix from vector ID_INLINE void Random( int seed, float l = 0.0f, float u = 1.0f ); // fill matrix with random values ID_INLINE void Random( int rows, int columns, int seed, float l = 0.0f, float u = 1.0f ); ID_INLINE void Negate(); // (*this) = - (*this) ID_INLINE void Clamp( float min, float max ); // clamp all values ID_INLINE idMatX & SwapRows( int r1, int r2 ); // swap rows ID_INLINE idMatX & SwapColumns( int r1, int r2 ); // swap columns ID_INLINE idMatX & SwapRowsColumns( int r1, int r2 ); // swap rows and columns idMatX & RemoveRow( int r ); // remove a row idMatX & RemoveColumn( int r ); // remove a column idMatX & RemoveRowColumn( int r ); // remove a row and column ID_INLINE void ClearUpperTriangle(); // clear the upper triangle ID_INLINE void ClearLowerTriangle(); // clear the lower triangle void CopyLowerToUpperTriangle(); // copy the lower triangle to the upper triangle ID_INLINE void SquareSubMatrix( const idMatX &m, int size ); // get square sub-matrix from 0,0 to size,size ID_INLINE float MaxDifference( const idMatX &m ) const; // return maximum element difference between this and m ID_INLINE bool IsSquare() const { return ( numRows == numColumns ); } ID_INLINE bool IsZero( const float epsilon = MATRIX_EPSILON ) const; ID_INLINE bool IsIdentity( const float epsilon = MATRIX_EPSILON ) const; ID_INLINE bool IsDiagonal( const float epsilon = MATRIX_EPSILON ) const; ID_INLINE bool IsTriDiagonal( const float epsilon = MATRIX_EPSILON ) const; ID_INLINE bool IsSymmetric( const float epsilon = MATRIX_EPSILON ) const; bool IsOrthogonal( const float epsilon = MATRIX_EPSILON ) const; bool IsOrthonormal( const float epsilon = MATRIX_EPSILON ) const; bool IsPMatrix( const float epsilon = MATRIX_EPSILON ) const; bool IsZMatrix( const float epsilon = MATRIX_EPSILON ) const; bool IsPositiveDefinite( const float epsilon = MATRIX_EPSILON ) const; bool IsSymmetricPositiveDefinite( const float epsilon = MATRIX_EPSILON ) const; bool IsPositiveSemiDefinite( const float epsilon = MATRIX_EPSILON ) const; bool IsSymmetricPositiveSemiDefinite( const float epsilon = MATRIX_EPSILON ) const; ID_INLINE float Trace() const; // returns product of diagonal elements ID_INLINE float Determinant() const; // returns determinant of matrix ID_INLINE idMatX Transpose() const; // returns transpose ID_INLINE idMatX & TransposeSelf(); // transposes the matrix itself ID_INLINE void Transpose( idMatX & dst ) const; // stores transpose in 'dst' ID_INLINE idMatX Inverse() const; // returns the inverse ( m * m.Inverse() = identity ) ID_INLINE bool InverseSelf(); // returns false if determinant is zero ID_INLINE idMatX InverseFast() const; // returns the inverse ( m * m.Inverse() = identity ) ID_INLINE bool InverseFastSelf(); // returns false if determinant is zero ID_INLINE void Inverse( idMatX & dst ) const; // stores the inverse in 'dst' ( m * m.Inverse() = identity ) bool LowerTriangularInverse(); // in-place inversion, returns false if determinant is zero bool UpperTriangularInverse(); // in-place inversion, returns false if determinant is zero ID_INLINE void Subtract( const idMatX & a ); // (*this) -= a; ID_INLINE idVecX Multiply( const idVecX &vec ) const; // (*this) * vec ID_INLINE idVecX TransposeMultiply( const idVecX &vec ) const; // this->Transpose() * vec ID_INLINE idMatX Multiply( const idMatX &a ) const; // (*this) * a ID_INLINE idMatX TransposeMultiply( const idMatX &a ) const; // this->Transpose() * a ID_INLINE void Multiply( idVecX &dst, const idVecX &vec ) const; // dst = (*this) * vec ID_INLINE void MultiplyAdd( idVecX &dst, const idVecX &vec ) const; // dst += (*this) * vec ID_INLINE void MultiplySub( idVecX &dst, const idVecX &vec ) const; // dst -= (*this) * vec ID_INLINE void TransposeMultiply( idVecX &dst, const idVecX &vec ) const; // dst = this->Transpose() * vec ID_INLINE void TransposeMultiplyAdd( idVecX &dst, const idVecX &vec ) const; // dst += this->Transpose() * vec ID_INLINE void TransposeMultiplySub( idVecX &dst, const idVecX &vec ) const; // dst -= this->Transpose() * vec ID_INLINE void Multiply( idMatX &dst, const idMatX &a ) const; // dst = (*this) * a ID_INLINE void TransposeMultiply( idMatX &dst, const idMatX &a ) const; // dst = this->Transpose() * a ID_INLINE int GetDimension() const; // returns total number of values in matrix ID_INLINE const idVec6 & SubVec6( int row ) const; // interpret beginning of row as a const idVec6 ID_INLINE idVec6 & SubVec6( int row ); // interpret beginning of row as an idVec6 ID_INLINE const idVecX SubVecX( int row ) const; // interpret complete row as a const idVecX ID_INLINE idVecX SubVecX( int row ); // interpret complete row as an idVecX ID_INLINE const float * ToFloatPtr() const; // pointer to const matrix float array ID_INLINE float * ToFloatPtr(); // pointer to matrix float array const char * ToString( int precision = 2 ) const; void Update_RankOne( const idVecX &v, const idVecX &w, float alpha ); void Update_RankOneSymmetric( const idVecX &v, float alpha ); void Update_RowColumn( const idVecX &v, const idVecX &w, int r ); void Update_RowColumnSymmetric( const idVecX &v, int r ); void Update_Increment( const idVecX &v, const idVecX &w ); void Update_IncrementSymmetric( const idVecX &v ); void Update_Decrement( int r ); bool Inverse_GaussJordan(); // invert in-place with Gauss-Jordan elimination bool Inverse_UpdateRankOne( const idVecX &v, const idVecX &w, float alpha ); bool Inverse_UpdateRowColumn( const idVecX &v, const idVecX &w, int r ); bool Inverse_UpdateIncrement( const idVecX &v, const idVecX &w ); bool Inverse_UpdateDecrement( const idVecX &v, const idVecX &w, int r ); void Inverse_Solve( idVecX &x, const idVecX &b ) const; bool LU_Factor( int *index, float *det = NULL ); // factor in-place: L * U bool LU_UpdateRankOne( const idVecX &v, const idVecX &w, float alpha, int *index ); bool LU_UpdateRowColumn( const idVecX &v, const idVecX &w, int r, int *index ); bool LU_UpdateIncrement( const idVecX &v, const idVecX &w, int *index ); bool LU_UpdateDecrement( const idVecX &v, const idVecX &w, const idVecX &u, int r, int *index ); void LU_Solve( idVecX &x, const idVecX &b, const int *index ) const; void LU_Inverse( idMatX &inv, const int *index ) const; void LU_UnpackFactors( idMatX &L, idMatX &U ) const; void LU_MultiplyFactors( idMatX &m, const int *index ) const; bool QR_Factor( idVecX &c, idVecX &d ); // factor in-place: Q * R bool QR_UpdateRankOne( idMatX &R, const idVecX &v, const idVecX &w, float alpha ); bool QR_UpdateRowColumn( idMatX &R, const idVecX &v, const idVecX &w, int r ); bool QR_UpdateIncrement( idMatX &R, const idVecX &v, const idVecX &w ); bool QR_UpdateDecrement( idMatX &R, const idVecX &v, const idVecX &w, int r ); void QR_Solve( idVecX &x, const idVecX &b, const idVecX &c, const idVecX &d ) const; void QR_Solve( idVecX &x, const idVecX &b, const idMatX &R ) const; void QR_Inverse( idMatX &inv, const idVecX &c, const idVecX &d ) const; void QR_UnpackFactors( idMatX &Q, idMatX &R, const idVecX &c, const idVecX &d ) const; void QR_MultiplyFactors( idMatX &m, const idVecX &c, const idVecX &d ) const; bool SVD_Factor( idVecX &w, idMatX &V ); // factor in-place: U * Diag(w) * V.Transpose() void SVD_Solve( idVecX &x, const idVecX &b, const idVecX &w, const idMatX &V ) const; void SVD_Inverse( idMatX &inv, const idVecX &w, const idMatX &V ) const; void SVD_MultiplyFactors( idMatX &m, const idVecX &w, const idMatX &V ) const; bool Cholesky_Factor(); // factor in-place: L * L.Transpose() bool Cholesky_UpdateRankOne( const idVecX &v, float alpha, int offset = 0 ); bool Cholesky_UpdateRowColumn( const idVecX &v, int r ); bool Cholesky_UpdateIncrement( const idVecX &v ); bool Cholesky_UpdateDecrement( const idVecX &v, int r ); void Cholesky_Solve( idVecX &x, const idVecX &b ) const; void Cholesky_Inverse( idMatX &inv ) const; void Cholesky_MultiplyFactors( idMatX &m ) const; bool LDLT_Factor(); // factor in-place: L * D * L.Transpose() bool LDLT_UpdateRankOne( const idVecX &v, float alpha, int offset = 0 ); bool LDLT_UpdateRowColumn( const idVecX &v, int r ); bool LDLT_UpdateIncrement( const idVecX &v ); bool LDLT_UpdateDecrement( const idVecX &v, int r ); void LDLT_Solve( idVecX &x, const idVecX &b ) const; void LDLT_Inverse( idMatX &inv ) const; void LDLT_UnpackFactors( idMatX &L, idMatX &D ) const; void LDLT_MultiplyFactors( idMatX &m ) const; void TriDiagonal_ClearTriangles(); bool TriDiagonal_Solve( idVecX &x, const idVecX &b ) const; void TriDiagonal_Inverse( idMatX &inv ) const; bool Eigen_SolveSymmetricTriDiagonal( idVecX &eigenValues ); bool Eigen_SolveSymmetric( idVecX &eigenValues ); bool Eigen_Solve( idVecX &realEigenValues, idVecX &imaginaryEigenValues ); void Eigen_SortIncreasing( idVecX &eigenValues ); void Eigen_SortDecreasing( idVecX &eigenValues ); static void Test(); private: int numRows; // number of rows int numColumns; // number of columns int alloced; // floats allocated, if -1 then mat points to data set with SetData float * mat; // memory the matrix is stored static float temp[MATX_MAX_TEMP+4]; // used to store intermediate results static float * tempPtr; // pointer to 16 byte aligned temporary memory static int tempIndex; // index into memory pool, wraps around private: void SetTempSize( int rows, int columns ); float DeterminantGeneric() const; bool InverseSelfGeneric(); void QR_Rotate( idMatX &R, int i, float a, float b ); float Pythag( float a, float b ) const; void SVD_BiDiag( idVecX &w, idVecX &rv1, float &anorm ); void SVD_InitialWV( idVecX &w, idMatX &V, idVecX &rv1 ); void HouseholderReduction( idVecX &diag, idVecX &subd ); bool QL( idVecX &diag, idVecX &subd ); void HessenbergReduction( idMatX &H ); void ComplexDivision( float xr, float xi, float yr, float yi, float &cdivr, float &cdivi ); bool HessenbergToRealSchur( idMatX &H, idVecX &realEigenValues, idVecX &imaginaryEigenValues ); }; /* ======================== idMatX::idMatX ======================== */ ID_INLINE idMatX::idMatX() { numRows = numColumns = alloced = 0; mat = NULL; } /* ======================== idMatX::~idMatX ======================== */ ID_INLINE idMatX::~idMatX() { // if not temp memory if ( mat != NULL && ( mat < idMatX::tempPtr || mat > idMatX::tempPtr + MATX_MAX_TEMP ) && alloced != -1 ) { Mem_Free16( mat ); } } /* ======================== idMatX::idMatX ======================== */ ID_INLINE idMatX::idMatX( int rows, int columns ) { numRows = numColumns = alloced = 0; mat = NULL; SetSize( rows, columns ); } /* ======================== idMatX::idMatX ======================== */ ID_INLINE idMatX::idMatX( const idMatX & other ) { numRows = numColumns = alloced = 0; mat = NULL; Set( other.GetNumRows(), other.GetNumColumns(), other.ToFloatPtr() ); } /* ======================== idMatX::idMatX ======================== */ ID_INLINE idMatX::idMatX( int rows, int columns, float *src ) { numRows = numColumns = alloced = 0; mat = NULL; SetData( rows, columns, src ); } /* ======================== idMatX::Set ======================== */ ID_INLINE void idMatX::Set( int rows, int columns, const float *src ) { SetSize( rows, columns ); memcpy( this->mat, src, rows * columns * sizeof( float ) ); } /* ======================== idMatX::Set ======================== */ ID_INLINE void idMatX::Set( const idMat3 &m1, const idMat3 &m2 ) { SetSize( 3, 6 ); for ( int i = 0; i < 3; i++ ) { for ( int j = 0; j < 3; j++ ) { mat[(i+0) * numColumns + (j+0)] = m1[i][j]; mat[(i+0) * numColumns + (j+3)] = m2[i][j]; } } } /* ======================== idMatX::Set ======================== */ ID_INLINE void idMatX::Set( const idMat3 &m1, const idMat3 &m2, const idMat3 &m3, const idMat3 &m4 ) { SetSize( 6, 6 ); for ( int i = 0; i < 3; i++ ) { for ( int j = 0; j < 3; j++ ) { mat[(i+0) * numColumns + (j+0)] = m1[i][j]; mat[(i+0) * numColumns + (j+3)] = m2[i][j]; mat[(i+3) * numColumns + (j+0)] = m3[i][j]; mat[(i+3) * numColumns + (j+3)] = m4[i][j]; } } } /* ======================== idMatX::operator[] ======================== */ ID_INLINE const float *idMatX::operator[]( int index ) const { assert( ( index >= 0 ) && ( index < numRows ) ); return mat + index * numColumns; } /* ======================== idMatX::operator[] ======================== */ ID_INLINE float *idMatX::operator[]( int index ) { assert( ( index >= 0 ) && ( index < numRows ) ); return mat + index * numColumns; } /* ======================== idMatX::operator= ======================== */ ID_INLINE idMatX &idMatX::operator=( const idMatX &a ) { SetSize( a.numRows, a.numColumns ); int s = a.numRows * a.numColumns; #if defined(ID_WIN_X86_SSE_INTRIN) && defined(MATX_SIMD) for ( int i = 0; i < s; i += 4 ) { _mm_store_ps( mat + i, _mm_load_ps( a.mat + i ) ); } #else memcpy( mat, a.mat, s * sizeof( float ) ); #endif idMatX::tempIndex = 0; return *this; } /* ======================== idMatX::operator* ======================== */ ID_INLINE idMatX idMatX::operator*( const float a ) const { idMatX m; m.SetTempSize( numRows, numColumns ); int s = numRows * numColumns; #if defined(ID_WIN_X86_SSE_INTRIN) && defined(MATX_SIMD) __m128 va = _mm_load1_ps( & a ); for ( int i = 0; i < s; i += 4 ) { _mm_store_ps( m.mat + i, _mm_mul_ps( _mm_load_ps( mat + i ), va ) ); } #else for ( int i = 0; i < s; i++ ) { m.mat[i] = mat[i] * a; } #endif return m; } /* ======================== idMatX::operator* ======================== */ ID_INLINE idVecX idMatX::operator*( const idVecX &vec ) const { assert( numColumns == vec.GetSize() ); idVecX dst; dst.SetTempSize( numRows ); Multiply( dst, vec ); return dst; } /* ======================== idMatX::operator* ======================== */ ID_INLINE idMatX idMatX::operator*( const idMatX &a ) const { assert( numColumns == a.numRows ); idMatX dst; dst.SetTempSize( numRows, a.numColumns ); Multiply( dst, a ); return dst; } /* ======================== idMatX::operator+ ======================== */ ID_INLINE idMatX idMatX::operator+( const idMatX &a ) const { idMatX m; assert( numRows == a.numRows && numColumns == a.numColumns ); m.SetTempSize( numRows, numColumns ); int s = numRows * numColumns; #if defined(ID_WIN_X86_SSE_INTRIN) && defined(MATX_SIMD) for ( int i = 0; i < s; i += 4 ) { _mm_store_ps( m.mat + i, _mm_add_ps( _mm_load_ps( mat + i ), _mm_load_ps( a.mat + i ) ) ); } #else for ( int i = 0; i < s; i++ ) { m.mat[i] = mat[i] + a.mat[i]; } #endif return m; } /* ======================== idMatX::operator- ======================== */ ID_INLINE idMatX idMatX::operator-( const idMatX &a ) const { idMatX m; assert( numRows == a.numRows && numColumns == a.numColumns ); m.SetTempSize( numRows, numColumns ); int s = numRows * numColumns; #if defined(ID_WIN_X86_SSE_INTRIN) && defined(MATX_SIMD) for ( int i = 0; i < s; i += 4 ) { _mm_store_ps( m.mat + i, _mm_sub_ps( _mm_load_ps( mat + i ), _mm_load_ps( a.mat + i ) ) ); } #else for ( int i = 0; i < s; i++ ) { m.mat[i] = mat[i] - a.mat[i]; } #endif return m; } /* ======================== idMatX::operator*= ======================== */ ID_INLINE idMatX &idMatX::operator*=( const float a ) { int s = numRows * numColumns; #if defined(ID_WIN_X86_SSE_INTRIN) && defined(MATX_SIMD) __m128 va = _mm_load1_ps( & a ); for ( int i = 0; i < s; i += 4 ) { _mm_store_ps( mat + i, _mm_mul_ps( _mm_load_ps( mat + i ), va ) ); } #else for ( int i = 0; i < s; i++ ) { mat[i] *= a; } #endif idMatX::tempIndex = 0; return *this; } /* ======================== idMatX::operator*= ======================== */ ID_INLINE idMatX &idMatX::operator*=( const idMatX &a ) { *this = *this * a; idMatX::tempIndex = 0; return *this; } /* ======================== idMatX::operator+= ======================== */ ID_INLINE idMatX &idMatX::operator+=( const idMatX &a ) { assert( numRows == a.numRows && numColumns == a.numColumns ); int s = numRows * numColumns; #if defined(ID_WIN_X86_SSE_INTRIN) && defined(MATX_SIMD) for ( int i = 0; i < s; i += 4 ) { _mm_store_ps( mat + i, _mm_add_ps( _mm_load_ps( mat + i ), _mm_load_ps( a.mat + i ) ) ); } #else for ( int i = 0; i < s; i++ ) { mat[i] += a.mat[i]; } #endif idMatX::tempIndex = 0; return *this; } /* ======================== idMatX::operator-= ======================== */ ID_INLINE idMatX &idMatX::operator-=( const idMatX &a ) { assert( numRows == a.numRows && numColumns == a.numColumns ); int s = numRows * numColumns; #if defined(ID_WIN_X86_SSE_INTRIN) && defined(MATX_SIMD) for ( int i = 0; i < s; i += 4 ) { _mm_store_ps( mat + i, _mm_sub_ps( _mm_load_ps( mat + i ), _mm_load_ps( a.mat + i ) ) ); } #else for ( int i = 0; i < s; i++ ) { mat[i] -= a.mat[i]; } #endif idMatX::tempIndex = 0; return *this; } /* ======================== operator* ======================== */ ID_INLINE idMatX operator*( const float a, idMatX const &m ) { return m * a; } /* ======================== operator* ======================== */ ID_INLINE idVecX operator*( const idVecX &vec, const idMatX &m ) { return m * vec; } /* ======================== operator*= ======================== */ ID_INLINE idVecX &operator*=( idVecX &vec, const idMatX &m ) { vec = m * vec; return vec; } /* ======================== idMatX::Compare ======================== */ ID_INLINE bool idMatX::Compare( const idMatX &a ) const { assert( numRows == a.numRows && numColumns == a.numColumns ); int s = numRows * numColumns; for ( int i = 0; i < s; i++ ) { if ( mat[i] != a.mat[i] ) { return false; } } return true; } /* ======================== idMatX::Compare ======================== */ ID_INLINE bool idMatX::Compare( const idMatX &a, const float epsilon ) const { assert( numRows == a.numRows && numColumns == a.numColumns ); int s = numRows * numColumns; for ( int i = 0; i < s; i++ ) { if ( idMath::Fabs( mat[i] - a.mat[i] ) > epsilon ) { return false; } } return true; } /* ======================== idMatX::operator== ======================== */ ID_INLINE bool idMatX::operator==( const idMatX &a ) const { return Compare( a ); } /* ======================== idMatX::operator!= ======================== */ ID_INLINE bool idMatX::operator!=( const idMatX &a ) const { return !Compare( a ); } /* ======================== idMatX::SetSize ======================== */ ID_INLINE void idMatX::SetSize( int rows, int columns ) { if ( rows != numRows || columns != numColumns || mat == NULL ) { assert( mat < idMatX::tempPtr || mat > idMatX::tempPtr + MATX_MAX_TEMP ); int alloc = ( rows * columns + 3 ) & ~3; if ( alloc > alloced && alloced != -1 ) { if ( mat != NULL ) { Mem_Free16( mat ); } mat = (float *) Mem_Alloc16( alloc * sizeof( float ), TAG_MATH ); alloced = alloc; } numRows = rows; numColumns = columns; MATX_CLEAREND(); } } /* ======================== idMatX::SetTempSize ======================== */ ID_INLINE void idMatX::SetTempSize( int rows, int columns ) { int newSize; newSize = ( rows * columns + 3 ) & ~3; assert( newSize < MATX_MAX_TEMP ); if ( idMatX::tempIndex + newSize > MATX_MAX_TEMP ) { idMatX::tempIndex = 0; } mat = idMatX::tempPtr + idMatX::tempIndex; idMatX::tempIndex += newSize; alloced = newSize; numRows = rows; numColumns = columns; MATX_CLEAREND(); } /* ======================== idMatX::SetData ======================== */ ID_INLINE void idMatX::SetData( int rows, int columns, float *data ) { assert( mat < idMatX::tempPtr || mat > idMatX::tempPtr + MATX_MAX_TEMP ); if ( mat != NULL && alloced != -1 ) { Mem_Free16( mat ); } assert( ( ( (UINT_PTR) data ) & 15 ) == 0 ); // data must be 16 byte aligned mat = data; alloced = -1; numRows = rows; numColumns = columns; MATX_CLEAREND(); } /* ======================== idMatX::SetDataCacheLines ======================== */ ID_INLINE void idMatX::SetDataCacheLines( int rows, int columns, float *data, bool clear ) { if ( mat != NULL && alloced != -1 ) { Mem_Free( mat ); } assert( ( ( (UINT_PTR) data ) & 127 ) == 0 ); // data must be 128 byte aligned mat = data; alloced = -1; numRows = rows; numColumns = columns; if ( clear ) { int size = numRows * numColumns * sizeof( float ); for ( int i = 0; i < size; i += CACHE_LINE_SIZE ) { ZeroCacheLine( mat, i ); } } else { MATX_CLEAREND(); } } /* ======================== idMatX::Zero ======================== */ ID_INLINE void idMatX::Zero() { int s = numRows * numColumns; #if defined(ID_WIN_X86_SSE_INTRIN) && defined(MATX_SIMD) for ( int i = 0; i < s; i += 4 ) { _mm_store_ps( mat + i, _mm_setzero_ps() ); } #else s; memset( mat, 0, numRows * numColumns * sizeof( float ) ); #endif } /* ======================== idMatX::Zero ======================== */ ID_INLINE void idMatX::Zero( int rows, int columns ) { SetSize( rows, columns ); Zero(); } /* ======================== idMatX::Identity ======================== */ ID_INLINE void idMatX::Identity() { assert( numRows == numColumns ); Zero(); for ( int i = 0; i < numRows; i++ ) { mat[i * numColumns + i] = 1.0f; } } /* ======================== idMatX::Identity ======================== */ ID_INLINE void idMatX::Identity( int rows, int columns ) { assert( rows == columns ); SetSize( rows, columns ); idMatX::Identity(); } /* ======================== idMatX::Diag ======================== */ ID_INLINE void idMatX::Diag( const idVecX &v ) { Zero( v.GetSize(), v.GetSize() ); for ( int i = 0; i < v.GetSize(); i++ ) { mat[i * numColumns + i] = v[i]; } } /* ======================== idMatX::Random ======================== */ ID_INLINE void idMatX::Random( int seed, float l, float u ) { idRandom rnd(seed); float c = u - l; int s = numRows * numColumns; for ( int i = 0; i < s; i++ ) { mat[i] = l + rnd.RandomFloat() * c; } } /* ======================== idMatX::Random ======================== */ ID_INLINE void idMatX::Random( int rows, int columns, int seed, float l, float u ) { idRandom rnd(seed); SetSize( rows, columns ); float c = u - l; int s = numRows * numColumns; for ( int i = 0; i < s; i++ ) { mat[i] = l + rnd.RandomFloat() * c; } } /* ======================== idMatX::Negate ======================== */ ID_INLINE void idMatX::Negate() { int s = numRows * numColumns; #if defined(ID_WIN_X86_SSE_INTRIN) && defined(MATX_SIMD) ALIGN16( const unsigned int signBit[4] ) = { IEEE_FLT_SIGN_MASK, IEEE_FLT_SIGN_MASK, IEEE_FLT_SIGN_MASK, IEEE_FLT_SIGN_MASK }; for ( int i = 0; i < s; i += 4 ) { _mm_store_ps( mat + i, _mm_xor_ps( _mm_load_ps( mat + i ), (__m128 &) signBit[0] ) ); } #else for ( int i = 0; i < s; i++ ) { mat[i] = -mat[i]; } #endif } /* ======================== idMatX::Clamp ======================== */ ID_INLINE void idMatX::Clamp( float min, float max ) { int s = numRows * numColumns; for ( int i = 0; i < s; i++ ) { if ( mat[i] < min ) { mat[i] = min; } else if ( mat[i] > max ) { mat[i] = max; } } } /* ======================== idMatX::SwapRows ======================== */ ID_INLINE idMatX &idMatX::SwapRows( int r1, int r2 ) { float * ptr1 = mat + r1 * numColumns; float * ptr2 = mat + r2 * numColumns; for ( int i = 0; i < numColumns; i++ ) { SwapValues( ptr1[i], ptr2[i] ); } return *this; } /* ======================== idMatX::SwapColumns ======================== */ ID_INLINE idMatX &idMatX::SwapColumns( int r1, int r2 ) { float * ptr = mat; for ( int i = 0; i < numRows; i++, ptr += numColumns ) { SwapValues( ptr[r1], ptr[r2] ); } return *this; } /* ======================== idMatX::SwapRowsColumns ======================== */ ID_INLINE idMatX &idMatX::SwapRowsColumns( int r1, int r2 ) { SwapRows( r1, r2 ); SwapColumns( r1, r2 ); return *this; } /* ======================== idMatX::ClearUpperTriangle ======================== */ ID_INLINE void idMatX::ClearUpperTriangle() { assert( numRows == numColumns ); for ( int i = numRows-2; i >= 0; i-- ) { memset( mat + i * numColumns + i + 1, 0, (numColumns - 1 - i) * sizeof(float) ); } } /* ======================== idMatX::ClearLowerTriangle ======================== */ ID_INLINE void idMatX::ClearLowerTriangle() { assert( numRows == numColumns ); for ( int i = 1; i < numRows; i++ ) { memset( mat + i * numColumns, 0, i * sizeof(float) ); } } /* ======================== idMatX::SquareSubMatrix ======================== */ ID_INLINE void idMatX::SquareSubMatrix( const idMatX &m, int size ) { assert( size <= m.numRows && size <= m.numColumns ); SetSize( size, size ); for ( int i = 0; i < size; i++ ) { memcpy( mat + i * numColumns, m.mat + i * m.numColumns, size * sizeof( float ) ); } } /* ======================== idMatX::MaxDifference ======================== */ ID_INLINE float idMatX::MaxDifference( const idMatX &m ) const { assert( numRows == m.numRows && numColumns == m.numColumns ); float maxDiff = -1.0f; for ( int i = 0; i < numRows; i++ ) { for ( int j = 0; j < numColumns; j++ ) { float diff = idMath::Fabs( mat[ i * numColumns + j ] - m[i][j] ); if ( maxDiff < 0.0f || diff > maxDiff ) { maxDiff = diff; } } } return maxDiff; } /* ======================== idMatX::IsZero ======================== */ ID_INLINE bool idMatX::IsZero( const float epsilon ) const { // returns true if (*this) == Zero for ( int i = 0; i < numRows; i++ ) { for ( int j = 0; j < numColumns; j++ ) { if ( idMath::Fabs( mat[i * numColumns + j] ) > epsilon ) { return false; } } } return true; } /* ======================== idMatX::IsIdentity ======================== */ ID_INLINE bool idMatX::IsIdentity( const float epsilon ) const { // returns true if (*this) == Identity assert( numRows == numColumns ); for ( int i = 0; i < numRows; i++ ) { for ( int j = 0; j < numColumns; j++ ) { if ( idMath::Fabs( mat[i * numColumns + j] - (float)( i == j ) ) > epsilon ) { return false; } } } return true; } /* ======================== idMatX::IsDiagonal ======================== */ ID_INLINE bool idMatX::IsDiagonal( const float epsilon ) const { // returns true if all elements are zero except for the elements on the diagonal assert( numRows == numColumns ); for ( int i = 0; i < numRows; i++ ) { for ( int j = 0; j < numColumns; j++ ) { if ( i != j && idMath::Fabs( mat[i * numColumns + j] ) > epsilon ) { return false; } } } return true; } /* ======================== idMatX::IsTriDiagonal ======================== */ ID_INLINE bool idMatX::IsTriDiagonal( const float epsilon ) const { // returns true if all elements are zero except for the elements on the diagonal plus or minus one column if ( numRows != numColumns ) { return false; } for ( int i = 0; i < numRows-2; i++ ) { for ( int j = i+2; j < numColumns; j++ ) { if ( idMath::Fabs( (*this)[i][j] ) > epsilon ) { return false; } if ( idMath::Fabs( (*this)[j][i] ) > epsilon ) { return false; } } } return true; } /* ======================== idMatX::IsSymmetric ======================== */ ID_INLINE bool idMatX::IsSymmetric( const float epsilon ) const { // (*this)[i][j] == (*this)[j][i] if ( numRows != numColumns ) { return false; } for ( int i = 0; i < numRows; i++ ) { for ( int j = 0; j < numColumns; j++ ) { if ( idMath::Fabs( mat[ i * numColumns + j ] - mat[ j * numColumns + i ] ) > epsilon ) { return false; } } } return true; } /* ======================== idMatX::Trace ======================== */ ID_INLINE float idMatX::Trace() const { float trace = 0.0f; assert( numRows == numColumns ); // sum of elements on the diagonal for ( int i = 0; i < numRows; i++ ) { trace += mat[i * numRows + i]; } return trace; } /* ======================== idMatX::Determinant ======================== */ ID_INLINE float idMatX::Determinant() const { assert( numRows == numColumns ); switch( numRows ) { case 1: return mat[0]; case 2: return reinterpret_cast( mat )->Determinant(); case 3: return reinterpret_cast( mat )->Determinant(); case 4: return reinterpret_cast( mat )->Determinant(); case 5: return reinterpret_cast( mat )->Determinant(); case 6: return reinterpret_cast( mat )->Determinant(); default: return DeterminantGeneric(); } } /* ======================== idMatX::Transpose ======================== */ ID_INLINE idMatX idMatX::Transpose() const { idMatX transpose; transpose.SetTempSize( numColumns, numRows ); for ( int i = 0; i < numRows; i++ ) { for ( int j = 0; j < numColumns; j++ ) { transpose.mat[j * transpose.numColumns + i] = mat[i * numColumns + j]; } } return transpose; } /* ======================== idMatX::TransposeSelf ======================== */ ID_INLINE idMatX &idMatX::TransposeSelf() { *this = Transpose(); return *this; } /* ======================== idMatX::Transpose ======================== */ ID_INLINE void idMatX::Transpose( idMatX & dst ) const { dst = Transpose(); } /* ======================== idMatX::Inverse ======================== */ ID_INLINE idMatX idMatX::Inverse() const { idMatX invMat; invMat.SetTempSize( numRows, numColumns ); memcpy( invMat.mat, mat, numRows * numColumns * sizeof( float ) ); verify( invMat.InverseSelf() ); return invMat; } /* ======================== idMatX::InverseSelf ======================== */ ID_INLINE bool idMatX::InverseSelf() { assert( numRows == numColumns ); switch( numRows ) { case 1: if ( idMath::Fabs( mat[0] ) < MATRIX_INVERSE_EPSILON ) { return false; } mat[0] = 1.0f / mat[0]; return true; case 2: return reinterpret_cast(mat)->InverseSelf(); case 3: return reinterpret_cast(mat)->InverseSelf(); case 4: return reinterpret_cast(mat)->InverseSelf(); case 5: return reinterpret_cast(mat)->InverseSelf(); case 6: return reinterpret_cast(mat)->InverseSelf(); default: return InverseSelfGeneric(); } } /* ======================== idMatX::InverseFast ======================== */ ID_INLINE idMatX idMatX::InverseFast() const { idMatX invMat; invMat.SetTempSize( numRows, numColumns ); memcpy( invMat.mat, mat, numRows * numColumns * sizeof( float ) ); verify( invMat.InverseFastSelf() ); return invMat; } /* ======================== idMatX::InverseFastSelf ======================== */ ID_INLINE bool idMatX::InverseFastSelf() { assert( numRows == numColumns ); switch( numRows ) { case 1: if ( idMath::Fabs( mat[0] ) < MATRIX_INVERSE_EPSILON ) { return false; } mat[0] = 1.0f / mat[0]; return true; case 2: return reinterpret_cast(mat)->InverseFastSelf(); case 3: return reinterpret_cast(mat)->InverseFastSelf(); case 4: return reinterpret_cast(mat)->InverseFastSelf(); case 5: return reinterpret_cast(mat)->InverseFastSelf(); case 6: return reinterpret_cast(mat)->InverseFastSelf(); default: return InverseSelfGeneric(); } } /* ======================== idMatX::Inverse ======================== */ ID_INLINE void idMatX::Inverse( idMatX & dst ) const { dst = InverseFast(); } /* ======================== idMatX::Subtract ======================== */ ID_INLINE void idMatX::Subtract( const idMatX & a ) { (*this) -= a; } /* ======================== idMatX::Multiply ======================== */ ID_INLINE idVecX idMatX::Multiply( const idVecX &vec ) const { assert( numColumns == vec.GetSize() ); idVecX dst; dst.SetTempSize( numRows ); Multiply( dst, vec ); return dst; } /* ======================== idMatX::Multiply ======================== */ ID_INLINE idMatX idMatX::Multiply( const idMatX &a ) const { assert( numColumns == a.numRows ); idMatX dst; dst.SetTempSize( numRows, a.numColumns ); Multiply( dst, a ); return dst; } /* ======================== idMatX::TransposeMultiply ======================== */ ID_INLINE idVecX idMatX::TransposeMultiply( const idVecX &vec ) const { assert( numRows == vec.GetSize() ); idVecX dst; dst.SetTempSize( numColumns ); TransposeMultiply( dst, vec ); return dst; } /* ======================== idMatX::TransposeMultiply ======================== */ ID_INLINE idMatX idMatX::TransposeMultiply( const idMatX &a ) const { assert( numRows == a.numRows ); idMatX dst; dst.SetTempSize( numColumns, a.numColumns ); TransposeMultiply( dst, a ); return dst; } /* ======================== idMatX::Multiply ======================== */ ID_INLINE void idMatX::Multiply( idVecX &dst, const idVecX &vec ) const { dst.SetSize( numRows ); const float * mPtr = mat; const float * vPtr = vec.ToFloatPtr(); float * dstPtr = dst.ToFloatPtr(); float * temp = (float *)_alloca16( numRows * sizeof( float ) ); for ( int i = 0; i < numRows; i++ ) { float sum = mPtr[0] * vPtr[0]; for ( int j = 1; j < numColumns; j++ ) { sum += mPtr[j] * vPtr[j]; } temp[i] = sum; mPtr += numColumns; } for ( int i = 0; i < numRows; i++ ) { dstPtr[i] = temp[i]; } } /* ======================== idMatX::MultiplyAdd ======================== */ ID_INLINE void idMatX::MultiplyAdd( idVecX &dst, const idVecX &vec ) const { assert( dst.GetSize() == numRows ); const float * mPtr = mat; const float * vPtr = vec.ToFloatPtr(); float * dstPtr = dst.ToFloatPtr(); float * temp = (float *)_alloca16( numRows * sizeof( float ) ); for ( int i = 0; i < numRows; i++ ) { float sum = mPtr[0] * vPtr[0]; for ( int j = 1; j < numColumns; j++ ) { sum += mPtr[j] * vPtr[j]; } temp[i] = dstPtr[i] + sum; mPtr += numColumns; } for ( int i = 0; i < numRows; i++ ) { dstPtr[i] = temp[i]; } } /* ======================== idMatX::MultiplySub ======================== */ ID_INLINE void idMatX::MultiplySub( idVecX &dst, const idVecX &vec ) const { assert( dst.GetSize() == numRows ); const float * mPtr = mat; const float * vPtr = vec.ToFloatPtr(); float * dstPtr = dst.ToFloatPtr(); float * temp = (float *)_alloca16( numRows * sizeof( float ) ); for ( int i = 0; i < numRows; i++ ) { float sum = mPtr[0] * vPtr[0]; for ( int j = 1; j < numColumns; j++ ) { sum += mPtr[j] * vPtr[j]; } temp[i] = dstPtr[i] - sum; mPtr += numColumns; } for ( int i = 0; i < numRows; i++ ) { dstPtr[i] = temp[i]; } } /* ======================== idMatX::TransposeMultiply ======================== */ ID_INLINE void idMatX::TransposeMultiply( idVecX &dst, const idVecX &vec ) const { dst.SetSize( numColumns ); const float * vPtr = vec.ToFloatPtr(); float * dstPtr = dst.ToFloatPtr(); float * temp = (float *)_alloca16( numColumns * sizeof( float ) ); for ( int i = 0; i < numColumns; i++ ) { const float * mPtr = mat + i; float sum = mPtr[0] * vPtr[0]; for ( int j = 1; j < numRows; j++ ) { mPtr += numColumns; sum += mPtr[0] * vPtr[j]; } temp[i] = sum; } for ( int i = 0; i < numColumns; i++ ) { dstPtr[i] = temp[i]; } } /* ======================== idMatX::TransposeMultiplyAdd ======================== */ ID_INLINE void idMatX::TransposeMultiplyAdd( idVecX &dst, const idVecX &vec ) const { assert( dst.GetSize() == numColumns ); const float * vPtr = vec.ToFloatPtr(); float * dstPtr = dst.ToFloatPtr(); float * temp = (float *)_alloca16( numColumns * sizeof( float ) ); for ( int i = 0; i < numColumns; i++ ) { const float * mPtr = mat + i; float sum = mPtr[0] * vPtr[0]; for ( int j = 1; j < numRows; j++ ) { mPtr += numColumns; sum += mPtr[0] * vPtr[j]; } temp[i] = dstPtr[i] + sum; } for ( int i = 0; i < numColumns; i++ ) { dstPtr[i] = temp[i]; } } /* ======================== idMatX::TransposeMultiplySub ======================== */ ID_INLINE void idMatX::TransposeMultiplySub( idVecX &dst, const idVecX &vec ) const { assert( dst.GetSize() == numColumns ); const float * vPtr = vec.ToFloatPtr(); float * dstPtr = dst.ToFloatPtr(); float * temp = (float *)_alloca16( numColumns * sizeof( float ) ); for ( int i = 0; i < numColumns; i++ ) { const float * mPtr = mat + i; float sum = mPtr[0] * vPtr[0]; for ( int j = 1; j < numRows; j++ ) { mPtr += numColumns; sum += mPtr[0] * vPtr[j]; } temp[i] = dstPtr[i] - sum; } for ( int i = 0; i < numColumns; i++ ) { dstPtr[i] = temp[i]; } } /* ======================== idMatX::Multiply ======================== */ ID_INLINE void idMatX::Multiply( idMatX &dst, const idMatX &a ) const { assert( numColumns == a.numRows ); assert( &dst != &a && &dst != this ); dst.SetSize( numRows, a.numColumns ); float * dstPtr = dst.ToFloatPtr(); const float * m1Ptr = ToFloatPtr(); int k = numRows; int l = a.GetNumColumns(); for ( int i = 0; i < k; i++ ) { for ( int j = 0; j < l; j++ ) { const float * m2Ptr = a.ToFloatPtr() + j; float sum = m1Ptr[0] * m2Ptr[0]; for ( int n = 1; n < numColumns; n++ ) { m2Ptr += l; sum += m1Ptr[n] * m2Ptr[0]; } *dstPtr++ = sum; } m1Ptr += numColumns; } } /* ======================== idMatX::TransposeMultiply ======================== */ ID_INLINE void idMatX::TransposeMultiply( idMatX &dst, const idMatX &a ) const { assert( numRows == a.numRows ); assert( &dst != &a && &dst != this ); dst.SetSize( numColumns, a.numColumns ); float * dstPtr = dst.ToFloatPtr(); int k = numColumns; int l = a.numColumns; for ( int i = 0; i < k; i++ ) { for ( int j = 0; j < l; j++ ) { const float * m1Ptr = ToFloatPtr() + i; const float * m2Ptr = a.ToFloatPtr() + j; float sum = m1Ptr[0] * m2Ptr[0]; for ( int n = 1; n < numRows; n++ ) { m1Ptr += numColumns; m2Ptr += a.numColumns; sum += m1Ptr[0] * m2Ptr[0]; } *dstPtr++ = sum; } } } /* ======================== idMatX::GetDimension ======================== */ ID_INLINE int idMatX::GetDimension() const { return numRows * numColumns; } /* ======================== idMatX::SubVec6 ======================== */ ID_INLINE const idVec6 &idMatX::SubVec6( int row ) const { assert( numColumns >= 6 && row >= 0 && row < numRows ); return *reinterpret_cast(mat + row * numColumns); } /* ======================== idMatX::SubVec6 ======================== */ ID_INLINE idVec6 &idMatX::SubVec6( int row ) { assert( numColumns >= 6 && row >= 0 && row < numRows ); return *reinterpret_cast(mat + row * numColumns); } /* ======================== idMatX::SubVecX ======================== */ ID_INLINE const idVecX idMatX::SubVecX( int row ) const { idVecX v; assert( row >= 0 && row < numRows ); v.SetData( numColumns, mat + row * numColumns ); return v; } /* ======================== idMatX::SubVecX ======================== */ ID_INLINE idVecX idMatX::SubVecX( int row ) { idVecX v; assert( row >= 0 && row < numRows ); v.SetData( numColumns, mat + row * numColumns ); return v; } /* ======================== idMatX::ToFloatPtr ======================== */ ID_INLINE const float *idMatX::ToFloatPtr() const { return mat; } /* ======================== idMatX::ToFloatPtr ======================== */ ID_INLINE float *idMatX::ToFloatPtr() { return mat; } #endif // !__MATH_MATRIXX_H__