418 lines
14 KiB
C++
418 lines
14 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
#ifndef NL_MATRIX_H
|
|
#define NL_MATRIX_H
|
|
|
|
#include "vector.h"
|
|
#include "vector_h.h"
|
|
#include "quat.h"
|
|
|
|
|
|
namespace NLMISC
|
|
{
|
|
|
|
class CPlane;
|
|
|
|
|
|
// ======================================================================================================
|
|
/**
|
|
* A 4*4 Homogeneous Matrix.
|
|
* This is a column matrix, so operations like: \c v1=A*B*C*v0; applies C first , then B, then A to vector v0. \n
|
|
* Since it is a column matrix, the first column is the I vector of the base, 2nd is J, 3th is K. \n
|
|
* 4th column vector is T, the translation vector.
|
|
*
|
|
* Angle orientation are: Xaxis: YtoZ. Yaxis: ZtoX. Zaxis: XtoY.
|
|
*
|
|
* This matrix keep a matrix state to improve Matrix, vector and plane computing (matrix inversion, vector multiplication...).
|
|
* The internal matrix know if:
|
|
* - matrix is identity
|
|
* - matrix has a translation component
|
|
* - matrix has a rotation component
|
|
* - matrix has a uniform scale component (scale which is the same along the 3 axis)
|
|
* - matrix has a non-uniform scale component
|
|
* - matrix has a projection component (4th line of the matrix is not 0 0 0 1).
|
|
*
|
|
* An example of improvement is that CMatrix::operator*(const CVector &v) return v if the matrix is identity.
|
|
*
|
|
* By default, a matrix is identity. But for a performance view, this is just a StateBit=0...
|
|
* \author Lionel Berenguier
|
|
* \author Nevrax France
|
|
* \date 2000
|
|
*/
|
|
class CMatrix
|
|
{
|
|
public:
|
|
/// Rotation Order.
|
|
enum TRotOrder
|
|
{
|
|
XYZ,
|
|
XZY,
|
|
YXZ,
|
|
YZX,
|
|
ZXY,
|
|
ZYX
|
|
};
|
|
|
|
/// The identity matrix. Same as CMatrix().
|
|
static const CMatrix Identity;
|
|
|
|
public:
|
|
|
|
/// \name Object
|
|
//@{
|
|
/// Constructor which init to identity().
|
|
CMatrix()
|
|
{
|
|
StateBit= 0;
|
|
// Init just Pos because must always be valid for faster getPos()
|
|
M[12]= M[13]= M[14]= 0;
|
|
}
|
|
/// Copy Constructor.
|
|
CMatrix(const CMatrix &);
|
|
/// operator=.
|
|
CMatrix &operator=(const CMatrix &);
|
|
//@}
|
|
|
|
|
|
|
|
/// \name Sets
|
|
//@{
|
|
/// Reset the matrix to identity.
|
|
void identity();
|
|
/** Explicit setup the Rotation/Scale matrix (base).
|
|
* Avoid it. It implies low compute since no check is done on base to see what type of matrix it is
|
|
* (identity, rotation, scale, uniform scale...)
|
|
* \param i The I vector of the Cartesian base.
|
|
* \param j The J vector of the Cartesian base.
|
|
* \param k The K vector of the Cartesian base.
|
|
* \param hintNoScale set it to true if you are sure that your rot matrix is a pure rot matrix with no scale.
|
|
* If set to true and your rotation is not an orthonormal basis, unpredictable result are excepted.
|
|
*/
|
|
void setRot(const CVector &i, const CVector &j, const CVector &k, bool hintNoScale=false);
|
|
/** Explicit setup the Rotation/Scale matrix (base).
|
|
* Avoid it. It implies low compute since no check is done on m33 to see what type of matrix it is
|
|
* (identity, rotation, scale, uniform scale)
|
|
* \param m33 the 3*3 column rotation matrix. (3x3 matrix stored in column-major order as 9 consecutive values)
|
|
* \param hintNoScale set it to true if you are sure that your rot matrix is a pure rot matrix with no scale.
|
|
* If set to true and your rotation is not an orthonormal basis, unpredictable result are excepted.
|
|
*/
|
|
void setRot(const float m33[9], bool hintNoScale=false);
|
|
/** Explicit setup the Rotation matrix (base) as a Euler rotation matrix.
|
|
* \param v a vector of 3 angle (in radian), giving rotation around each axis (x,y,z)
|
|
* \param ro the order of transformation applied. if ro==XYZ, then the transform is M=M*Rx*Ry*Rz
|
|
*/
|
|
void setRot(const CVector &v, TRotOrder ro);
|
|
/** Explicit setup the Rotation matrix (base) as a Quaternion rotation matrix.
|
|
* \param quat a UNIT quaternion
|
|
*/
|
|
void setRot(const CQuat &quat);
|
|
/** Explicit setup the Rotation/Scale matrix (base) with the rotation part of another matrix.
|
|
* \param matrix the matrix to copy rot part.
|
|
*/
|
|
void setRot(const CMatrix &matrix);
|
|
/** Explicit setup the Rotation/Scale matrix (base) with a scale (=> matrix has no Rotation).
|
|
* 1 is tested to update bits accordingly
|
|
* \param scale the scale to set
|
|
*/
|
|
void setScale(float scale);
|
|
/** Explicit setup the Rotation/Scale matrix (base) with a scale (=> matrix has no Rotation).
|
|
* case where v.x==v.y==v.z is tested to set a uniform scale
|
|
* \param scale the scale to set
|
|
*/
|
|
void setScale(const CVector &v);
|
|
/** Explicit setup the Translation component.
|
|
* v==Null is tested to see if the matrix now have a translation component.
|
|
* \param v the translation vector.
|
|
*/
|
|
void setPos(const CVector &v);
|
|
/** Explicit move the Translation component.
|
|
* \param v a vector to move the translation vector.
|
|
*/
|
|
void movePos(const CVector &v);
|
|
/** Explicit setup the Projection component.
|
|
* Proj is tested to see if the matrix now have a projection component.
|
|
* \param proj the 4th line of the matrix. Set it to 0 0 0 1 to reset it to default.
|
|
*/
|
|
void setProj(const float proj[4]);
|
|
/** Reset the Projection component to 0 0 0 1.
|
|
*/
|
|
void resetProj();
|
|
/** Explicit setup the 4*4 matrix.
|
|
* Avoid it. It implies low compute since no check is done on rotation matrix to see what type of matrix it is
|
|
* (identity, rotation, scale, uniform scale).
|
|
* BUT check are made to see if it has translation or projection components.
|
|
* \param m44 the 4*4 column matrix (4x4 matrix stored in column-major order as 16 consecutive values)
|
|
*/
|
|
void set(const float m44[16]);
|
|
/** Setup the (i, j) matrix coefficient
|
|
* \param coeff: coefficient value.
|
|
* \param i : column index.
|
|
* \param j : line index.
|
|
*/
|
|
void setCoefficient(float coeff, sint i, sint j)
|
|
{
|
|
M[ (j<<2) + i] = coeff;
|
|
}
|
|
//@}
|
|
/** Choose an arbitrary rotation matrix for the given direction. The matrix will have I==idir
|
|
* \param idir: vector direction. MUST be normalized.
|
|
*/
|
|
|
|
void setArbitraryRotI(const CVector &idir);
|
|
/** Choose an arbitrary rotation matrix for the given direction. The matrix will have J==jdir
|
|
* \param jdir: vector direction. MUST be normalized.
|
|
*/
|
|
void setArbitraryRotJ(const CVector &jdir);
|
|
/** Choose an arbitrary rotation matrix for the given direction. The matrix will have K==kdir
|
|
* \param kdir: vector direction. MUST be normalized.
|
|
*/
|
|
void setArbitraryRotK(const CVector &kdir);
|
|
//@}
|
|
|
|
|
|
|
|
|
|
/// \name Gets.
|
|
//@{
|
|
/** Get the Rotation/Scale matrix (base).
|
|
* \param i The matrix's I vector of the Cartesian base.
|
|
* \param j The matrix's J vector of the Cartesian base.
|
|
* \param k The matrix's K vector of the Cartesian base.
|
|
*/
|
|
void getRot(CVector &i, CVector &j, CVector &k) const;
|
|
/** Get the Rotation/Scale matrix (base).
|
|
* \param m33 the matrix's 3*3 column rotation matrix. (3x3 matrix stored in column-major order as 9 consecutive values)
|
|
*/
|
|
void getRot(float m33[9]) const;
|
|
/** Get the Rotation matrix (base).
|
|
* \param quat the return quaternion.
|
|
*/
|
|
void getRot(CQuat &quat) const;
|
|
/** Get the Rotation matrix (base).
|
|
* \param quat the return quaternion.
|
|
*/
|
|
CQuat getRot() const {CQuat ret; getRot(ret); return ret;}
|
|
/** Get the Translation component.
|
|
* \param v the matrix's translation vector.
|
|
*/
|
|
void getPos(CVector &v) const {v.x= M[12]; v.y= M[13]; v.z= M[14];}
|
|
/** Get the Translation component.
|
|
* NB: a const & works because it is a column vector
|
|
* \return the matrix's translation vector.
|
|
*/
|
|
const CVector &getPos() const {return *(CVector*)(M+12);}
|
|
/** Get the Projection component.
|
|
* \param proj the matrix's projection vector.
|
|
*/
|
|
void getProj(float proj[4]) const;
|
|
/// Get the I vector of the Rotation/Scale matrix (base).
|
|
CVector getI() const;
|
|
/// Get the J vector of the Rotation/Scale matrix (base).
|
|
CVector getJ() const;
|
|
/// Get the K vector of the Rotation/Scale matrix (base).
|
|
CVector getK() const;
|
|
/** Get 4*4 matrix.
|
|
* \param m44 the matrix's 4*4 column matrix (4x4 matrix stored in column-major order as 16 consecutive values)
|
|
*/
|
|
void get(float m44[16]) const;
|
|
/** Get 4*4 matrix.
|
|
* \return the matrix's 4*4 column matrix (4x4 matrix stored in column-major order as 16 consecutive values)
|
|
*/
|
|
const float *get() const;
|
|
//@}
|
|
|
|
|
|
|
|
/// \name 3D Operations.
|
|
//@{
|
|
/// Apply a translation to the matrix. same as M=M*T
|
|
void translate(const CVector &v);
|
|
/** Apply a rotation on axis X to the matrix. same as M=M*Rx
|
|
* \param a angle (in radian).
|
|
*/
|
|
void rotateX(float a);
|
|
/** Apply a rotation on axis Y to the matrix. same as M=M*Ry
|
|
* \param a angle (in radian).
|
|
*/
|
|
void rotateY(float a);
|
|
/** Apply a rotation on axis Z to the matrix. same as M=M*Rz
|
|
* \param a angle (in radian).
|
|
*/
|
|
void rotateZ(float a);
|
|
/** Apply a Euler rotation.
|
|
* \param v a vector of 3 angle (in radian), giving rotation around each axis (x,y,z)
|
|
* \param ro the order of transformation applied. if ro==XYZ, then the transform is M=M*Rx*Ry*Rz
|
|
*/
|
|
void rotate(const CVector &v, TRotOrder ro);
|
|
/** Apply a quaternion rotation.
|
|
*/
|
|
void rotate(const CQuat &quat);
|
|
/// Apply a uniform scale to the matrix.
|
|
void scale(float f);
|
|
/// Apply a non-uniform scale to the matrix.
|
|
void scale(const CVector &scale);
|
|
//@}
|
|
|
|
|
|
|
|
/// \name Matrix Operations.
|
|
//@{
|
|
/** Matrix multiplication. Because of copy avoidance, this is the fastest method
|
|
* Equivalent to *this= m1 * m2
|
|
* \warning *this MUST NOT be the same as m1 or m2, else it doesn't work (not checked/nlasserted)
|
|
*/
|
|
void setMulMatrix(const CMatrix &m1, const CMatrix &m2);
|
|
/// Matrix multiplication
|
|
CMatrix operator*(const CMatrix &in) const
|
|
{
|
|
CMatrix ret;
|
|
ret.setMulMatrix(*this, in);
|
|
return ret;
|
|
}
|
|
/// equivalent to M=M*in
|
|
CMatrix &operator*=(const CMatrix &in)
|
|
{
|
|
CMatrix ret;
|
|
ret.setMulMatrix(*this, in);
|
|
*this= ret;
|
|
return *this;
|
|
}
|
|
/** Matrix multiplication assuming no projection at all in m1/m2 and Hence this. Even Faster than setMulMatrix()
|
|
* Equivalent to *this= m1 * m2
|
|
* NB: Also always suppose m1 has a translation, for optimization consideration
|
|
* \warning *this MUST NOT be the same as m1 or m2, else it doesn't work (not checked/nlasserted)
|
|
*/
|
|
void setMulMatrixNoProj(const CMatrix &m1, const CMatrix &m2);
|
|
/** transpose the rotation part only of the matrix (swap columns/lines).
|
|
*/
|
|
void transpose3x3();
|
|
/** transpose the matrix (swap columns/lines).
|
|
* NB: this transpose the 4*4 matrix entirely (even proj/translate part).
|
|
*/
|
|
void transpose();
|
|
/** Invert the matrix.
|
|
* if the matrix can't be inverted, it is set to identity.
|
|
*/
|
|
void invert();
|
|
/** Return the matrix inverted.
|
|
* if the matrix can't be inverted, identity is returned.
|
|
*/
|
|
CMatrix inverted() const;
|
|
/** Normalize the matrix so that the rotation part is now an orthonormal basis, ie a rotation with no scale.
|
|
* NB: projection part and translation part are not modified.
|
|
* \param pref the preference axis order to normalize. ZYX means that K direction will be kept, and the plane JK
|
|
* will be used to lead the I vector.
|
|
* \return false if One of the vector basis is null. true otherwise.
|
|
*/
|
|
bool normalize(TRotOrder pref);
|
|
//@}
|
|
|
|
|
|
|
|
/// \ Vector Operations.
|
|
//@{
|
|
/// Multiply a normal. ie v.w=0 so the Translation component doesn't affect result. Projection doesn't affect result.
|
|
CVector mulVector(const CVector &v) const;
|
|
/// Multiply a point. ie v.w=1 so the Translation component do affect result. Projection doesn't affect result.
|
|
CVector mulPoint(const CVector &v) const;
|
|
/** Multiply a point. \sa mulPoint
|
|
*/
|
|
CVector operator*(const CVector &v) const
|
|
{
|
|
return mulPoint(v);
|
|
}
|
|
|
|
/// Multiply with an homogeneous vector
|
|
CVectorH operator*(const CVectorH& v) const;
|
|
//@}
|
|
|
|
/// \name Misc
|
|
//@{
|
|
void serial(IStream &f);
|
|
/// return true if the matrix has a scale part (by scale(), by multiplication etc...)
|
|
bool hasScalePart() const;
|
|
/// return true if hasScalePart() and if if this scale is uniform.
|
|
bool hasScaleUniform() const;
|
|
/// return true the uniform scale. valid only if hasScaleUniform() is true, else 1 is returned.
|
|
float getScaleUniform() const;
|
|
/// return true if the matrix has a projection part (by setProj(), by multiplication etc...)
|
|
bool hasProjectionPart() const;
|
|
//@}
|
|
|
|
// Friend.
|
|
/// Plane (line vector) multiplication.
|
|
friend CPlane operator*(const CPlane &p, const CMatrix &m);
|
|
|
|
|
|
private:
|
|
float M[16];
|
|
float Scale33;
|
|
uint32 StateBit; // BitVector. 0<=>identity.
|
|
|
|
// Methods For inversion.
|
|
bool fastInvert33(CMatrix &ret) const;
|
|
bool slowInvert33(CMatrix &ret) const;
|
|
bool slowInvert44(CMatrix &ret) const;
|
|
// access to M, in math conventions (mat(1,1) ... mat(4,4)). Indices from 0 to 3.
|
|
float &mat(sint i, sint j)
|
|
{
|
|
return M[ (j<<2) + i];
|
|
}
|
|
// access to M, in math conventions (mat(1,1) ... mat(4,4)). Indices from 0 to 3.
|
|
const float &mat(sint i, sint j) const
|
|
{
|
|
return M[ (j<<2) + i];
|
|
}
|
|
// return the good 3x3 Id to compute the minor of (i,j);
|
|
void getCofactIndex(sint i, sint &l1, sint &l2, sint &l3) const
|
|
{
|
|
switch(i)
|
|
{
|
|
case 0: l1=1; l2=2; l3=3; break;
|
|
case 1: l1=0; l2=2; l3=3; break;
|
|
case 2: l1=0; l2=1; l3=3; break;
|
|
case 3: l1=0; l2=1; l3=2; break;
|
|
default: l1=0; l2=0; l3=0; break;
|
|
}
|
|
}
|
|
|
|
// true if MAT_TRANS.
|
|
// trans part is true means the right 3x1 translation part matrix is relevant.
|
|
// Else it IS initialized to (0,0,0) (exception!!!)
|
|
bool hasTrans() const;
|
|
// true if MAT_ROT | MAT_SCALEUNI | MAT_SCALEANY.
|
|
// rot part is true means the 3x3 rot matrix AND Scale33 are relevant.
|
|
// Else they are not initialized but are supposed to represent identity and Scale33==1.
|
|
bool hasRot() const;
|
|
// true if MAT_PROJ.
|
|
// proj part is true means the bottom 1x4 projection part matrix is relevant.
|
|
// Else it is not initialized but is supposed to represent the line vector (0,0,0,1).
|
|
bool hasProj() const;
|
|
bool hasAll() const;
|
|
|
|
void testExpandRot() const;
|
|
void testExpandProj() const;
|
|
|
|
// inline
|
|
void setScaleUni(float scale);
|
|
};
|
|
|
|
}
|
|
|
|
#endif // NL_MATRIX_H
|
|
|
|
/* End of matrix.h */
|