// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdmisc.h" #include "nel/misc/matrix.h" #include "nel/misc/plane.h" #include "nel/misc/debug.h" using namespace std; namespace NLMISC { // ====================================================================================================== const CMatrix CMatrix::Identity; // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== // State Bits. #define MAT_TRANS 1 #define MAT_ROT 2 #define MAT_SCALEUNI 4 #define MAT_SCALEANY 8 #define MAT_PROJ 16 // Validity bits. These means that the part may be yet identity, but is valid in the floats. // NB: MAT_VALIDTRANS no more used for faster Pos access #define MAT_VALIDROT 64 #define MAT_VALIDPROJ 128 #define MAT_VALIDALL (MAT_VALIDROT | MAT_VALIDPROJ) // The identity is nothing. #define MAT_IDENTITY 0 // Matrix elements. #define a11 M[0] #define a21 M[1] #define a31 M[2] #define a41 M[3] #define a12 M[4] #define a22 M[5] #define a32 M[6] #define a42 M[7] #define a13 M[8] #define a23 M[9] #define a33 M[10] #define a43 M[11] #define a14 M[12] #define a24 M[13] #define a34 M[14] #define a44 M[15] // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== bool CMatrix::hasScalePart() const { return (StateBit&(MAT_SCALEUNI|MAT_SCALEANY))!=0; } bool CMatrix::hasProjectionPart() const { return (StateBit&MAT_PROJ)!=0; } bool CMatrix::hasScaleUniform() const { return (StateBit & (MAT_SCALEUNI|MAT_SCALEANY))== MAT_SCALEUNI; } float CMatrix::getScaleUniform() const { if(hasScaleUniform()) return Scale33; else return 1; } // ====================================================================================================== inline bool CMatrix::hasRot() const { return (StateBit&(MAT_ROT|MAT_SCALEUNI|MAT_SCALEANY))!=0; } inline bool CMatrix::hasTrans() const { return (StateBit&MAT_TRANS)!=0; } inline bool CMatrix::hasProj() const { return (StateBit&MAT_PROJ)!=0; } inline bool CMatrix::hasAll() const { return (hasRot() && hasTrans() && hasProj()); } inline void CMatrix::testExpandRot() const { if(hasRot()) return; if(!(StateBit&MAT_VALIDROT)) { CMatrix *self= const_cast(this); self->StateBit|=MAT_VALIDROT; self->a11= 1; self->a12=0; self->a13=0; self->a21= 0; self->a22=1; self->a23=0; self->a31= 0; self->a32=0; self->a33=1; self->Scale33= 1; } } inline void CMatrix::testExpandProj() const { if(hasProj()) return; if(!(StateBit&MAT_VALIDPROJ)) { CMatrix *self= const_cast(this); self->StateBit|=MAT_VALIDPROJ; self->a41=0; self->a42=0; self->a43=0; self->a44=1; } } // ====================================================================================================== CMatrix::CMatrix(const CMatrix &m) { (*this)= m; } // ====================================================================================================== CMatrix &CMatrix::operator=(const CMatrix &m) { StateBit= m.StateBit & ~MAT_VALIDALL; if(hasAll()) { memcpy(M, m.M, 16*sizeof(float)); Scale33= m.Scale33; } else { if(hasRot()) { memcpy(&a11, &m.a11, 3*sizeof(float)); memcpy(&a12, &m.a12, 3*sizeof(float)); memcpy(&a13, &m.a13, 3*sizeof(float)); Scale33= m.Scale33; } if(hasProj()) { a41= m.a41; a42= m.a42; a43= m.a43; a44= m.a44; } // Must always copy Trans part. memcpy(&a14, &m.a14, 3*sizeof(float)); } return *this; } // ====================================================================================================== void CMatrix::identity() { StateBit= MAT_IDENTITY; // Reset just Pos because must always be valid for faster getPos() a14= a24= a34= 0; // For optimisation it would be useful to keep MAT_VALID states. // But this slows identity(), and this may not be interesting... } // ====================================================================================================== void CMatrix::setRot(const CVector &i, const CVector &j, const CVector &k, bool hintNoScale) { StateBit|= MAT_ROT | MAT_SCALEANY; if(hintNoScale) StateBit&= ~(MAT_SCALEANY|MAT_SCALEUNI); a11= i.x; a12= j.x; a13= k.x; a21= i.y; a22= j.y; a23= k.y; a31= i.z; a32= j.z; a33= k.z; Scale33= 1.0f; } // ====================================================================================================== void CMatrix::setRot(const float m33[9], bool hintNoScale) { StateBit|= MAT_ROT | MAT_SCALEANY; if(hintNoScale) StateBit&= ~(MAT_SCALEANY|MAT_SCALEUNI); a11= m33[0]; a12= m33[3]; a13= m33[6]; a21= m33[1]; a22= m33[4]; a23= m33[7]; a31= m33[2]; a32= m33[5]; a33= m33[8]; Scale33= 1.0f; } // ====================================================================================================== void CMatrix::setRot(const CVector &v, TRotOrder ro) { CMatrix rot; rot.identity(); rot.rotate(v, ro); float m33[9]; rot.getRot(m33); setRot(m33, true); } // ====================================================================================================== void CMatrix::setRot(const CMatrix &matrix) { // copy rotpart statebit from other. StateBit&= ~(MAT_ROT | MAT_SCALEUNI | MAT_SCALEANY); StateBit|= matrix.StateBit & (MAT_ROT | MAT_SCALEUNI | MAT_SCALEANY); // copy values. if(hasRot()) { a11= matrix.a11; a12= matrix.a12; a13= matrix.a13; a21= matrix.a21; a22= matrix.a22; a23= matrix.a23; a31= matrix.a31; a32= matrix.a32; a33= matrix.a33; // if has scale uniform, copy from matrix. if(hasScaleUniform()) Scale33= matrix.Scale33; } else { // we are rot identity, with undefined values. StateBit&= ~MAT_VALIDROT; } } // ====================================================================================================== void CMatrix::setPos(const CVector &v) { a14= v.x; a24= v.y; a34= v.z; if(a14!=0 || a24!=0 || a34!=0) StateBit|= MAT_TRANS; else // The trans is identity StateBit&= ~MAT_TRANS; } // ====================================================================================================== void CMatrix::movePos(const CVector &v) { a14+= v.x; a24+= v.y; a34+= v.z; if(a14!=0 || a24!=0 || a34!=0) StateBit|= MAT_TRANS; else // The trans is identity StateBit&= ~MAT_TRANS; } // ====================================================================================================== void CMatrix::setProj(const float proj[4]) { a41= proj[0]; a42= proj[1]; a43= proj[2]; a44= proj[3]; // Check Proj state. if(a41!=0 || a42!=0 || a43!=0 || a44!=1) StateBit|= MAT_PROJ; else { // The proj is identity, and is correcly setup! StateBit&= ~MAT_PROJ; StateBit|= MAT_VALIDPROJ; } } // ====================================================================================================== void CMatrix::resetProj() { a41= 0; a42= 0; a43= 0; a44= 1; // The proj is identity, and is correcly setup! StateBit&= ~MAT_PROJ; StateBit|= MAT_VALIDPROJ; } // ====================================================================================================== void CMatrix::set(const float m44[16]) { StateBit= MAT_IDENTITY; StateBit|= MAT_ROT | MAT_SCALEANY; memcpy(M, m44, 16*sizeof(float)); Scale33= 1.0f; // Check Trans state. if(a14!=0 || a24!=0 || a34!=0) StateBit|= MAT_TRANS; else // The trans is identity StateBit&= ~MAT_TRANS; // Check Proj state. if(a41!=0 || a42!=0 || a43!=0 || a44!=1) StateBit|= MAT_PROJ; else { // The proj is identity, and is correcly setup! StateBit&= ~MAT_PROJ; StateBit|= MAT_VALIDPROJ; } } // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== void CMatrix::getRot(CVector &i, CVector &j, CVector &k) const { if(hasRot()) { i.set(a11, a21, a31); j.set(a12, a22, a32); k.set(a13, a23, a33); } else { i.set(1, 0, 0); j.set(0, 1, 0); k.set(0, 0, 1); } } // ====================================================================================================== void CMatrix::getRot(float m33[9]) const { if(hasRot()) { m33[0]= a11; m33[1]= a21; m33[2]= a31; m33[3]= a12; m33[4]= a22; m33[5]= a32; m33[6]= a13; m33[7]= a23; m33[8]= a33; } else { m33[0]= 1; m33[1]= 0; m33[2]= 0; m33[3]= 0; m33[4]= 1; m33[5]= 0; m33[6]= 0; m33[7]= 0; m33[8]= 1; } } // ====================================================================================================== void CMatrix::getProj(float proj[4]) const { if(hasProj()) { proj[0]= a41; proj[1]= a42; proj[2]= a43; proj[3]= a44; } else { proj[0]= 0; proj[1]= 0; proj[2]= 0; proj[3]= 1; } } // ====================================================================================================== CVector CMatrix::getI() const { if(hasRot()) return CVector(a11, a21, a31); else return CVector(1, 0, 0); } // ====================================================================================================== CVector CMatrix::getJ() const { if(hasRot()) return CVector(a12, a22, a32); else return CVector(0, 1, 0); } // ====================================================================================================== CVector CMatrix::getK() const { if(hasRot()) return CVector(a13, a23, a33); else return CVector(0, 0, 1); } // ====================================================================================================== void CMatrix::get(float m44[16]) const { testExpandRot(); testExpandProj(); memcpy(m44, M, 16*sizeof(float)); } // ====================================================================================================== const float *CMatrix::get() const { testExpandRot(); testExpandProj(); return M; } /*// ====================================================================================================== CVector CMatrix::toEuler(TRotOrder ro) const { }*/ // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== void CMatrix::translate(const CVector &v) { // SetTrans. if( hasRot() ) { a14+= a11*v.x + a12*v.y + a13*v.z; a24+= a21*v.x + a22*v.y + a23*v.z; a34+= a31*v.x + a32*v.y + a33*v.z; } else { a14+= v.x; a24+= v.y; a34+= v.z; } // SetProj. if( hasProj() ) a44+= a41*v.x + a42*v.y + a43*v.z; // Check Trans. if(a14!=0 || a24!=0 || a34!=0) StateBit|= MAT_TRANS; else // The trans is identity, and is correcly setup! StateBit&= ~MAT_TRANS; } // ====================================================================================================== void CMatrix::rotateX(float a) { if(a==0) return; double ca,sa; ca=cos(a); sa=sin(a); // SetRot. if( hasRot() ) { float b12=a12, b22=a22, b32=a32; float b13=a13, b23=a23, b33=a33; a12= (float)(b12*ca + b13*sa); a22= (float)(b22*ca + b23*sa); a32= (float)(b32*ca + b33*sa); a13= (float)(b13*ca - b12*sa); a23= (float)(b23*ca - b22*sa); a33= (float)(b33*ca - b32*sa); } else { testExpandRot(); a12= 0.0f; a22= (float)ca; a32= (float)sa; a13= 0.0f; a23= (float)-sa; a33= (float)ca; } // SetProj. if( hasProj() ) { float b42=a42, b43=a43; a42= (float)(b42*ca + b43*sa); a43= (float)(b43*ca - b42*sa); } // set Rot. StateBit|= MAT_ROT; } // ====================================================================================================== void CMatrix::rotateY(float a) { if(a==0) return; double ca,sa; ca=cos(a); sa=sin(a); // SetRot. if( hasRot() ) { float b11=a11, b21=a21, b31=a31; float b13=a13, b23=a23, b33=a33; a11= (float)(b11*ca - b13*sa); a21= (float)(b21*ca - b23*sa); a31= (float)(b31*ca - b33*sa); a13= (float)(b13*ca + b11*sa); a23= (float)(b23*ca + b21*sa); a33= (float)(b33*ca + b31*sa); } else { testExpandRot(); a11= (float)ca; a21=0.0f; a31= (float)-sa; a13= (float)sa; a23=0.0f; a33= (float)ca; } // SetProj. if( hasProj() ) { float b41=a41, b43=a43; a41= (float)(b41*ca - b43*sa); a43= (float)(b43*ca + b41*sa); } // set Rot. StateBit|= MAT_ROT; } // ====================================================================================================== void CMatrix::rotateZ(float a) { if(a==0) return; double ca,sa; ca=cos(a); sa=sin(a); // SetRot. if( StateBit & (MAT_ROT|MAT_SCALEUNI|MAT_SCALEANY) ) { float b11=a11, b21=a21, b31=a31; float b12=a12, b22=a22, b32=a32; a11= (float)(b11*ca + b12*sa); a21= (float)(b21*ca + b22*sa); a31= (float)(b31*ca + b32*sa); a12= (float)(b12*ca - b11*sa); a22= (float)(b22*ca - b21*sa); a32= (float)(b32*ca - b31*sa); } else { testExpandRot(); a11= (float)ca; a21= (float)sa; a31=0.0f; a12= (float)-sa; a22= (float)ca; a32=0.0f; } // SetProj. if( hasProj() ) { float b41=a41, b42=a42; a41= (float)(b41*ca + b42*sa); a42= (float)(b42*ca - b41*sa); } // set Rot. StateBit|= MAT_ROT; } // ====================================================================================================== void CMatrix::rotate(const CVector &v, TRotOrder ro) { CMatrix rot; rot.identity(); switch(ro) { case XYZ: rot.rotateX(v.x); rot.rotateY(v.y); rot.rotateZ(v.z); break; case XZY: rot.rotateX(v.x); rot.rotateZ(v.z); rot.rotateY(v.y); break; case YXZ: rot.rotateY(v.y); rot.rotateX(v.x); rot.rotateZ(v.z); break; case YZX: rot.rotateY(v.y); rot.rotateZ(v.z); rot.rotateX(v.x); break; case ZXY: rot.rotateZ(v.z); rot.rotateX(v.x); rot.rotateY(v.y); break; case ZYX: rot.rotateZ(v.z); rot.rotateY(v.y); rot.rotateX(v.x); break; } (*this)*= rot; } // ====================================================================================================== void CMatrix::rotate(const CQuat &quat) { CMatrix rot; rot.setRot(quat); (*this)*= rot; } // ====================================================================================================== void CMatrix::scale(float f) { if(f==1.0f) return; if(StateBit & MAT_SCALEANY) { scale(CVector(f,f,f)); } else { testExpandRot(); StateBit|= MAT_SCALEUNI; Scale33*=f; a11*= f; a12*=f; a13*=f; a21*= f; a22*=f; a23*=f; a31*= f; a32*=f; a33*=f; // SetProj. if( hasProj() ) { a41*=f; a42*=f; a43*=f; } } } // ====================================================================================================== void CMatrix::scale(const CVector &v) { if( v==CVector(1,1,1) ) return; if( !(StateBit & MAT_SCALEANY) && v.x==v.y && v.x==v.z) { scale(v.x); } else { testExpandRot(); StateBit|=MAT_SCALEANY; a11*= v.x; a12*=v.y; a13*=v.z; a21*= v.x; a22*=v.y; a23*=v.z; a31*= v.x; a32*=v.y; a33*=v.z; // SetProj. if( hasProj() ) { a41*=v.x; a42*=v.y; a43*=v.z; } } } // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== // *************************************************************************** void CMatrix::setMulMatrixNoProj(const CMatrix &m1, const CMatrix &m2) { /* For a fast MulMatrix, it appears to be better to not take State bits into account (no test/if() overhead) Just do heavy mul all the time (common case, and not so slow) */ // Ensure the src matrix have correct values in rot part m1.testExpandRot(); m2.testExpandRot(); // Rot Mul a11= m1.a11*m2.a11 + m1.a12*m2.a21 + m1.a13*m2.a31; a12= m1.a11*m2.a12 + m1.a12*m2.a22 + m1.a13*m2.a32; a13= m1.a11*m2.a13 + m1.a12*m2.a23 + m1.a13*m2.a33; a21= m1.a21*m2.a11 + m1.a22*m2.a21 + m1.a23*m2.a31; a22= m1.a21*m2.a12 + m1.a22*m2.a22 + m1.a23*m2.a32; a23= m1.a21*m2.a13 + m1.a22*m2.a23 + m1.a23*m2.a33; a31= m1.a31*m2.a11 + m1.a32*m2.a21 + m1.a33*m2.a31; a32= m1.a31*m2.a12 + m1.a32*m2.a22 + m1.a33*m2.a32; a33= m1.a31*m2.a13 + m1.a32*m2.a23 + m1.a33*m2.a33; // Trans mul a14= m1.a11*m2.a14 + m1.a12*m2.a24 + m1.a13*m2.a34 + m1.a14; a24= m1.a21*m2.a14 + m1.a22*m2.a24 + m1.a23*m2.a34 + m1.a24; a34= m1.a31*m2.a14 + m1.a32*m2.a24 + m1.a33*m2.a34 + m1.a34; // Setup no proj at all, and force valid rot (still may be identity, but 0/1 are filled) StateBit= (m1.StateBit | m2.StateBit | MAT_VALIDROT) & ~(MAT_PROJ|MAT_VALIDPROJ); // Modify Scale. This test is important because Scale33 may be a #NAN if SCALEANY => avoid very slow mul. if( hasScaleUniform() ) Scale33= m1.Scale33*m2.Scale33; else Scale33=1; } // *************************************************************************** void CMatrix::setMulMatrix(const CMatrix &m1, const CMatrix &m2) { // Do *this= m1*m2 identity(); StateBit= m1.StateBit | m2.StateBit; StateBit&= ~MAT_VALIDALL; // Build Rot part. //=============== bool M1Identity= ! m1.hasRot(); bool M2Identity= ! m2.hasRot(); bool M1ScaleOnly= ! (m1.StateBit & MAT_ROT); bool M2ScaleOnly= ! (m2.StateBit & MAT_ROT); bool MGeneralCase= !M1Identity && !M2Identity && !M1ScaleOnly && !M2ScaleOnly; // Manage the most common general case first (optim the if ): blending of two rotations. if( MGeneralCase ) { a11= m1.a11*m2.a11 + m1.a12*m2.a21 + m1.a13*m2.a31; a12= m1.a11*m2.a12 + m1.a12*m2.a22 + m1.a13*m2.a32; a13= m1.a11*m2.a13 + m1.a12*m2.a23 + m1.a13*m2.a33; a21= m1.a21*m2.a11 + m1.a22*m2.a21 + m1.a23*m2.a31; a22= m1.a21*m2.a12 + m1.a22*m2.a22 + m1.a23*m2.a32; a23= m1.a21*m2.a13 + m1.a22*m2.a23 + m1.a23*m2.a33; a31= m1.a31*m2.a11 + m1.a32*m2.a21 + m1.a33*m2.a31; a32= m1.a31*m2.a12 + m1.a32*m2.a22 + m1.a33*m2.a32; a33= m1.a31*m2.a13 + m1.a32*m2.a23 + m1.a33*m2.a33; } // If one of the 3x3 matrix is an identity, just do a copy else if( M1Identity || M2Identity ) { // If both identity, then me too. if( M1Identity && M2Identity ) { // just expand me (important because validated below) testExpandRot(); } else { // Copy the non identity matrix. const CMatrix *c= M2Identity? &m1 : &m2; a11= c->a11; a12= c->a12; a13= c->a13; a21= c->a21; a22= c->a22; a23= c->a23; a31= c->a31; a32= c->a32; a33= c->a33; } } // If two 3x3 matrix are just scaleOnly matrix, do a scaleFact. else if( M1ScaleOnly && M2ScaleOnly ) { // same process for scaleUni or scaleAny. a11= m1.a11*m2.a11; a12= 0; a13= 0; a21= 0; a22= m1.a22*m2.a22; a23= 0; a31= 0; a32= 0; a33= m1.a33*m2.a33; } // If one of the matrix is a scaleOnly matrix, do a scale*Rot. else if( M1ScaleOnly && !M2ScaleOnly ) { a11= m1.a11*m2.a11; a12= m1.a11*m2.a12; a13= m1.a11*m2.a13; a21= m1.a22*m2.a21; a22= m1.a22*m2.a22; a23= m1.a22*m2.a23; a31= m1.a33*m2.a31; a32= m1.a33*m2.a32; a33= m1.a33*m2.a33; } else { // This must be this case nlassert(!M1ScaleOnly && M2ScaleOnly); a11= m1.a11*m2.a11; a12= m1.a12*m2.a22; a13= m1.a13*m2.a33; a21= m1.a21*m2.a11; a22= m1.a22*m2.a22; a23= m1.a23*m2.a33; a31= m1.a31*m2.a11; a32= m1.a32*m2.a22; a33= m1.a33*m2.a33; } // If M1 has translate and M2 has projective, rotation is modified. if( m1.hasTrans() && m2.hasProj()) { StateBit|= MAT_ROT|MAT_SCALEANY; a11+= m1.a14*m2.a41; a12+= m1.a14*m2.a42; a13+= m1.a14*m2.a43; a21+= m1.a24*m2.a41; a22+= m1.a24*m2.a42; a23+= m1.a24*m2.a43; a31+= m1.a34*m2.a41; a32+= m1.a34*m2.a42; a33+= m1.a34*m2.a43; } // Modify Scale. if( (StateBit & MAT_SCALEUNI) && !(StateBit & MAT_SCALEANY) ) { // Must have correct Scale33 m1.testExpandRot(); m2.testExpandRot(); Scale33= m1.Scale33*m2.Scale33; } else Scale33=1; // In every case, I am valid now! StateBit|=MAT_VALIDROT; // Build Trans part. //================= if( StateBit & MAT_TRANS ) { // Compose M2 part. if( M1Identity ) { a14= m2.a14; a24= m2.a24; a34= m2.a34; } else if (M1ScaleOnly ) { a14= m1.a11*m2.a14; a24= m1.a22*m2.a24; a34= m1.a33*m2.a34; } else { a14= m1.a11*m2.a14 + m1.a12*m2.a24 + m1.a13*m2.a34; a24= m1.a21*m2.a14 + m1.a22*m2.a24 + m1.a23*m2.a34; a34= m1.a31*m2.a14 + m1.a32*m2.a24 + m1.a33*m2.a34; } // Compose M1 part. if(m1.StateBit & MAT_TRANS) { if(m2.StateBit & MAT_PROJ) { a14+= m1.a14*m2.a44; a24+= m1.a24*m2.a44; a34+= m1.a34*m2.a44; } else { a14+= m1.a14; a24+= m1.a24; a34+= m1.a34; } } } // Build Proj part. //================= if( StateBit & MAT_PROJ ) { // optimize nothing... (projection matrix are rare). m1.testExpandRot(); m1.testExpandProj(); m2.testExpandRot(); m2.testExpandProj(); a41= m1.a41*m2.a11 + m1.a42*m2.a21 + m1.a43*m2.a31 + m1.a44*m2.a41; a42= m1.a41*m2.a12 + m1.a42*m2.a22 + m1.a43*m2.a32 + m1.a44*m2.a42; a43= m1.a41*m2.a13 + m1.a42*m2.a23 + m1.a43*m2.a33 + m1.a44*m2.a43; a44= m1.a41*m2.a14 + m1.a42*m2.a24 + m1.a43*m2.a34 + m1.a44*m2.a44; // The proj is valid now StateBit|= MAT_VALIDPROJ; } else { // Don't copy proj part, and leave MAT_VALIDPROJ not set } } // ====================================================================================================== void CMatrix::invert() { *this= inverted(); } // ====================================================================================================== void CMatrix::transpose3x3() { if(hasRot()) { // swap values. swap(a12, a21); swap(a13, a31); swap(a32, a23); // Scale mode (none, uni, or any) is conserved. Scale33 too... } } // ====================================================================================================== void CMatrix::transpose() { transpose3x3(); if(hasTrans() || hasProj()) { // if necessary, Get valid 0 on proj part. testExpandProj(); // swap values swap(a41, a14); swap(a42, a24); swap(a43, a34); // swap StateBit flags, if not both were sets... if(!hasTrans() || !hasProj()) { // swap StateBit flags (swap trans with proj). if(hasTrans()) { StateBit&= ~MAT_TRANS; StateBit|= MAT_PROJ; } else { StateBit&= ~MAT_PROJ; StateBit|= MAT_TRANS; } } // reset validity. NB, maybe not useful, but simpler, and bugfree. StateBit&= ~(MAT_VALIDPROJ); } // NB: if no Trans or no Proj, do nothing, so don't need to modify VALIDTRANS and VALIDPROJ too. } // ====================================================================================================== bool CMatrix::fastInvert33(CMatrix &ret) const { // Fast invert of 3x3 rot matrix. // Work if no scale and if MAT_SCALEUNI. doesn't work if MAT_SCALEANY. if(StateBit & MAT_SCALEUNI) { if (Scale33 == 0.f) return false; double s,S; // important for precision. // Must divide the matrix by 1/Scale 2 times, to set unit, and to have a Scale=1/Scale. S=1.0/Scale33; ret.Scale33= (float)S; s=S*S; // The matrix is a base, so just transpose it. ret.a11= (float)(a11*s); ret.a12= (float)(a21*s); ret.a13= (float)(a31*s); ret.a21= (float)(a12*s); ret.a22= (float)(a22*s); ret.a23= (float)(a32*s); ret.a31= (float)(a13*s); ret.a32= (float)(a23*s); ret.a33= (float)(a33*s); } else { ret.Scale33=1; // The matrix is a base, so just transpose it. ret.a11= a11; ret.a12= a21; ret.a13=a31; ret.a21= a12; ret.a22= a22; ret.a23=a32; ret.a31= a13; ret.a32= a23; ret.a33=a33; } return true; // 15 cycles if no scale. // 35 cycles if scale. } // ====================================================================================================== bool CMatrix::slowInvert33(CMatrix &ret) const { CVector invi,invj,invk; CVector i,j,k; double s; i= getI(); j= getJ(); k= getK(); // Compute cofactors (minors *(-1)^(i+j)). invi.x= j.y*k.z - k.y*j.z; invi.y= j.z*k.x - k.z*j.x; invi.z= j.x*k.y - k.x*j.y; invj.x= k.y*i.z - i.y*k.z; invj.y= k.z*i.x - i.z*k.x; invj.z= k.x*i.y - i.x*k.y; invk.x= i.y*j.z - j.y*i.z; invk.y= i.z*j.x - j.z*i.x; invk.z= i.x*j.y - j.x*i.y; // compute determinant. s= invi.x*i.x + invj.x*j.x + invk.x*k.x; if(s==0) return false; // Transpose the Comatrice, and divide by determinant. s=1.0/s; ret.a11= (float)(invi.x*s); ret.a12= (float)(invi.y*s); ret.a13= (float)(invi.z*s); ret.a21= (float)(invj.x*s); ret.a22= (float)(invj.y*s); ret.a23= (float)(invj.z*s); ret.a31= (float)(invk.x*s); ret.a32= (float)(invk.y*s); ret.a33= (float)(invk.z*s); return true; // Roundly 82 cycles. (1Div=10 cycles). } // ====================================================================================================== bool CMatrix::slowInvert44(CMatrix &ret) const { sint i,j; double s; // Compute Cofactors //================== for(i=0;i<=3;i++) { for(j=0;j<=3;j++) { sint l1=0,l2=0,l3=0; sint c1,c2,c3; getCofactIndex(i,l1,l2,l3); getCofactIndex(j,c1,c2,c3); ret.mat(i,j)= 0; ret.mat(i,j)+= mat(l1,c1) * mat(l2,c2) * mat(l3,c3); ret.mat(i,j)+= mat(l1,c2) * mat(l2,c3) * mat(l3,c1); ret.mat(i,j)+= mat(l1,c3) * mat(l2,c1) * mat(l3,c2); ret.mat(i,j)-= mat(l1,c1) * mat(l2,c3) * mat(l3,c2); ret.mat(i,j)-= mat(l1,c2) * mat(l2,c1) * mat(l3,c3); ret.mat(i,j)-= mat(l1,c3) * mat(l2,c2) * mat(l3,c1); if( (i+j)&1 ) ret.mat(i,j)=-ret.mat(i,j); } } // Compute determinant. //===================== s= ret.mat(0,0) * mat(0,0) + ret.mat(0,1) * mat(0,1) + ret.mat(0,2) * mat(0,2) + ret.mat(0,3) * mat(0,3); if(s==0) return false; // Divide by determinant. //======================= s=1.0/s; for(i=0;i<=3;i++) { for(j=0;j<=3;j++) ret.mat(i,j)= (float)(ret.mat(i,j)*s); } // Transpose the comatrice. //========================= for(i=0;i<=3;i++) { for(j=i+1;j<=3;j++) { swap(ret.mat(i,j), ret.mat(j,i)); } } return true; } // ====================================================================================================== CMatrix CMatrix::inverted() const { CMatrix ret; testExpandRot(); testExpandProj(); // Do a conventionnal 44 inversion. //================================= if(StateBit & MAT_PROJ) { if(!slowInvert44(ret)) { ret.identity(); return ret; } // Well, don't know what happens to matrix, so set all StateBit :). ret.StateBit= MAT_TRANS|MAT_ROT|MAT_SCALEANY|MAT_PROJ; // Check Trans state. if(ret.a14!=0 || ret.a24!=0 || ret.a34!=0) ret.StateBit|= MAT_TRANS; else ret.StateBit&= ~MAT_TRANS; // Check Proj state. if(ret.a41!=0 || ret.a42!=0 || ret.a43!=0 || ret.a44!=1) ret.StateBit|= MAT_PROJ; else ret.StateBit&= ~MAT_PROJ; } // Do a speed 34 inversion. //========================= else { // Invert the rotation part. if(StateBit & MAT_SCALEANY) { if(!slowInvert33(ret)) { ret.identity(); return ret; } } else { if (!fastInvert33(ret)) { ret.identity(); return ret; } } // Scale33 is updated in fastInvert33(). // Invert the translation part. if(StateBit & MAT_TRANS) { // Invert the translation part. // This can only work if 4th line is 0 0 0 1. // formula: InvVp= InvVi*(-Vp.x) + InvVj*(-Vp.y) + InvVk*(-Vp.z) ret.a14= ret.a11*(-a14) + ret.a12*(-a24) + ret.a13*(-a34); ret.a24= ret.a21*(-a14) + ret.a22*(-a24) + ret.a23*(-a34); ret.a34= ret.a31*(-a14) + ret.a32*(-a24) + ret.a33*(-a34); } else { ret.a14= 0; ret.a24= 0; ret.a34= 0; } // The projection part is unmodified. ret.a41= 0; ret.a42= 0; ret.a43= 0; ret.a44= 1; // The matrix inverted keep the same state bits. ret.StateBit= StateBit; } return ret; } // ====================================================================================================== bool CMatrix::normalize(TRotOrder ro) { CVector ti,tj,tk; ti= getI(); tj= getJ(); tk= getK(); testExpandRot(); // Normalize with help of ro switch(ro) { case XYZ: ti.normalize(); tk= ti^tj; tk.normalize(); tj= tk^ti; break; case XZY: ti.normalize(); tj= tk^ti; tj.normalize(); tk= ti^tj; break; case YXZ: tj.normalize(); tk= ti^tj; tk.normalize(); ti= tj^tk; break; case YZX: tj.normalize(); ti= tj^tk; ti.normalize(); tk= ti^tj; break; case ZXY: tk.normalize(); tj= tk^ti; tj.normalize(); ti= tj^tk; break; case ZYX: tk.normalize(); ti= tj^tk; ti.normalize(); tj= tk^ti; break; } // Check, and set result. if( ti.isNull() || tj.isNull() || tk.isNull() ) return false; a11= ti.x; a12= tj.x; a13= tk.x; a21= ti.y; a22= tj.y; a23= tk.y; a31= ti.z; a32= tj.z; a33= tk.z; // Scale is reseted. StateBit&= ~(MAT_SCALEUNI|MAT_SCALEANY); // Rot is setup... StateBit|= MAT_ROT; Scale33=1; return true; } // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== CVector CMatrix::mulVector(const CVector &v) const { CVector ret; if( hasRot() ) { ret.x= a11*v.x + a12*v.y + a13*v.z; ret.y= a21*v.x + a22*v.y + a23*v.z; ret.z= a31*v.x + a32*v.y + a33*v.z; return ret; } else return v; } // ====================================================================================================== CVector CMatrix::mulPoint(const CVector &v) const { CVector ret; if( hasRot() ) { ret.x= a11*v.x + a12*v.y + a13*v.z; ret.y= a21*v.x + a22*v.y + a23*v.z; ret.z= a31*v.x + a32*v.y + a33*v.z; } else { ret= v; } if( hasTrans() ) { ret.x+= a14; ret.y+= a24; ret.z+= a34; } return ret; } /* * Multiply */ CVectorH CMatrix::operator*(const CVectorH& v) const { CVectorH ret; testExpandRot(); testExpandProj(); ret.x= a11*v.x + a12*v.y + a13*v.z + a14*v.w; ret.y= a21*v.x + a22*v.y + a23*v.z + a24*v.w; ret.z= a31*v.x + a32*v.y + a33*v.z + a34*v.w; ret.w= a41*v.x + a42*v.y + a43*v.z + a44*v.w; return ret; } // ====================================================================================================== CPlane operator*(const CPlane &p, const CMatrix &m) { m.testExpandRot(); m.testExpandProj(); CPlane ret; if( m.StateBit & (MAT_ROT|MAT_SCALEUNI|MAT_SCALEANY|MAT_PROJ) ) { // Compose with translation too. ret.a= p.a*m.a11 + p.b*m.a21 + p.c*m.a31 + p.d*m.a41; ret.b= p.a*m.a12 + p.b*m.a22 + p.c*m.a32 + p.d*m.a42; ret.c= p.a*m.a13 + p.b*m.a23 + p.c*m.a33 + p.d*m.a43; ret.d= p.a*m.a14 + p.b*m.a24 + p.c*m.a34 + p.d*m.a44; return ret; } else if( m.StateBit & MAT_TRANS ) { // Compose just with a translation. ret.a= p.a; ret.b= p.b; ret.c= p.c; ret.d= p.a*m.a14 + p.b*m.a24 + p.c*m.a34 + p.d*m.a44; return ret; } else // Identity!! return p; } // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== void CMatrix::setRot(const CQuat &quat) { // A quaternion do not have scale. StateBit&= ~(MAT_ROT | MAT_SCALEANY|MAT_SCALEUNI); Scale33= 1.0f; if(quat.isIdentity()) { a11= 1; a12= 0; a13= 0; a21= 0; a22= 1; a23= 0; a31= 0; a32= 0; a33= 1; } else { StateBit|= MAT_ROT; float wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; // calculate coefficients x2 = quat.x + quat.x; y2 = quat.y + quat.y; z2 = quat.z + quat.z; xx = quat.x * x2; xy = quat.x * y2; xz = quat.x * z2; yy = quat.y * y2; yz = quat.y * z2; zz = quat.z * z2; wx = quat.w * x2; wy = quat.w * y2; wz = quat.w * z2; a11 = 1.0f - (yy + zz); a12 = xy - wz; a13 = xz + wy; a21 = xy + wz; a22 = 1.0f - (xx + zz); a23 = yz - wx; a31 = xz - wy; a32 = yz + wx; a33 = 1.0f - (xx + yy); } } // ====================================================================================================== void CMatrix::getRot(CQuat &quat) const { const CMatrix *pmat= this; CMatrix MatNormed; // Rot Indentity? if(! (StateBit & MAT_ROT)) { quat= CQuat::Identity; return; } // Must normalize the matrix?? if(StateBit & (MAT_SCALEUNI | MAT_SCALEANY) ) { MatNormed= *this; MatNormed.normalize(ZYX); pmat= &MatNormed; } // Compute quaternion. float tr, s, q[4]; tr = pmat->a11 + pmat->a22 + pmat->a33; // check the diagonal if (tr > 0.0) { s = (float)sqrt (tr + 1.0f); quat.w = s / 2.0f; s = 0.5f / s; quat.x = (pmat->a32 - pmat->a23) * s; quat.y = (pmat->a13 - pmat->a31) * s; quat.z = (pmat->a21 - pmat->a12) * s; } else { sint i, j, k; sint nxt[3] = {1, 2, 0}; // diagonal is negative i = 0; if (pmat->a22 > pmat->a11) i = 1; if (pmat->a33 > pmat->mat(i,i) ) i = 2; j = nxt[i]; k = nxt[j]; s = (float) sqrt ( (pmat->mat(i,i) - (pmat->mat(j,j) + pmat->mat(k,k)) ) + 1.0); q[i] = s * 0.5f; if (s != 0.0f) s = 0.5f / s; q[j] = (pmat->mat(j,i) + pmat->mat(i,j)) * s; q[k] = (pmat->mat(k,i) + pmat->mat(i,k)) * s; q[3] = (pmat->mat(k,j) - pmat->mat(j,k)) * s; quat.x = q[0]; quat.y = q[1]; quat.z = q[2]; quat.w = q[3]; } } // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== inline void CMatrix::setScaleUni(float scale) { // A Scale matrix do not have rotation. StateBit&= ~(MAT_ROT | MAT_SCALEANY | MAT_SCALEUNI); StateBit|= MAT_SCALEUNI | MAT_VALIDROT; Scale33= scale; a11= scale; a12= 0; a13= 0; a21= 0; a22= scale; a23= 0; a31= 0; a32= 0; a33= scale; } // ====================================================================================================== void CMatrix::setScale(float scale) { setScaleUni(scale); } // ====================================================================================================== void CMatrix::setScale(const CVector &v) { // actually a scale uniform? if(v.x==v.y && v.x==v.z) setScaleUni(v.x); // A Scale matrix do not have rotation. StateBit&= ~(MAT_ROT | MAT_SCALEANY | MAT_SCALEUNI); StateBit|= MAT_SCALEANY | MAT_VALIDROT; a11= v.x; a12= 0; a13= 0; a21= 0; a22= v.y; a23= 0; a31= 0; a32= 0; a33= v.z; } // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== // ====================================================================================================== void CMatrix::serial(IStream &f) { // Use versionning, maybe for futur improvement. (void)f.serialVersion(0); if(f.isReading()) identity(); f.serial(StateBit); // avoid serial of random data if(!f.isReading() && !hasScaleUniform()) { float fs= 1.f; f.serial(fs); } else f.serial(Scale33); if( hasRot() ) { f.serial(a11, a12, a13); f.serial(a21, a22, a23); f.serial(a31, a32, a33); } if( hasTrans() ) { f.serial(a14, a24, a34); } else if(f.isReading()) { // must reset because Pos must always be valid a14= a24= a34= 0; } if( hasProj() ) { f.serial(a41, a42, a43, a44); } } // ====================================================================================================== void CMatrix::setArbitraryRotI(const CVector &idir) { // avoid gimbal lock. if idir == nearly K, use another second lead vector if( fabs(idir.z)<0.9f ) setRot(idir, CVector::J, CVector::K); else setRot(idir, CVector::J, CVector::I); normalize(CMatrix::XZY); } void CMatrix::setArbitraryRotJ(const CVector &jdir) { // avoid gimbal lock. if jdir == nearly K, use another second lead vector if(fabs(jdir.z)<0.9f) setRot(CVector::I, jdir, CVector::K); else setRot(CVector::I, jdir, CVector::J); normalize(CMatrix::YZX); } void CMatrix::setArbitraryRotK(const CVector &kdir) { // avoid gimbal lock. if kdir == nearly I, use another second lead vector if( fabs(kdir.y)<0.9f ) setRot(CVector::I, CVector::J, kdir); else setRot(CVector::I, CVector::K, kdir); normalize(CMatrix::ZYX); } }