#pragma once //########################################################################## //# # //# CLOUDCOMPARE # //# # //# This program 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; version 2 or later of the License. # //# # //# 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 General Public License for more details. # //# # //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) # //# # //########################################################################## //Local #include "ccSerializableObject.h" //CCCoreLib #include #include //Qt #include //! Model view matrix size (OpenGL) static const unsigned OPENGL_MATRIX_SIZE = 16; //Matrix element shortcuts in (line,column) order #define CC_MAT_R11 m_mat[0] #define CC_MAT_R21 m_mat[1] #define CC_MAT_R31 m_mat[2] #define CC_MAT_R41 m_mat[3] #define CC_MAT_R12 m_mat[4] #define CC_MAT_R22 m_mat[5] #define CC_MAT_R32 m_mat[6] #define CC_MAT_R42 m_mat[7] #define CC_MAT_R13 m_mat[8] #define CC_MAT_R23 m_mat[9] #define CC_MAT_R33 m_mat[10] #define CC_MAT_R43 m_mat[11] #define CC_MAT_R14 m_mat[12] #define CC_MAT_R24 m_mat[13] #define CC_MAT_R34 m_mat[14] #define CC_MAT_R44 m_mat[15] //! A 4x4 'transformation' matrix (column major order) /** Transformation (M) is composed by a rotation (R) and a translation (T): M*X = R*X + T **/ template class ccGLMatrixTpl : public ccSerializableObject { public: //! Default constructor /** Matrix is set to identity (see toIdentity) by default. **/ ccGLMatrixTpl() { toIdentity(); } //! Copy constructor ccGLMatrixTpl(const ccGLMatrixTpl& mat) { for (unsigned i = 0; i < OPENGL_MATRIX_SIZE; ++i) m_mat[i] = mat.m_mat[i]; } //! Constructor from a float GL matrix array /** \param mat16f a 16 elements array (column major order) **/ explicit ccGLMatrixTpl(const float* mat16f) { for (unsigned i = 0; i < OPENGL_MATRIX_SIZE; ++i) m_mat[i] = static_cast(mat16f[i]); internalRescale(); } //! Constructor from a double GL matrix array /** \warning Will implicitly cast the elements to float! \param mat16d a 16 elements array (column major order) **/ explicit ccGLMatrixTpl(const double* mat16d) { for (unsigned i = 0; i < OPENGL_MATRIX_SIZE; ++i) m_mat[i] = static_cast(mat16d[i]); internalRescale(); } //! Constructor from 4 columns (X,Y,Z,T) /** \param X 3 first elements of the 1st column (last one is 0) \param Y 3 first elements of the 2nd column (last one is 0) \param Z 3 first elements of the 3rd column (last one is 0) \param Tr 3 first elements of the last column (last one is 1) **/ ccGLMatrixTpl(const Vector3Tpl& X, const Vector3Tpl& Y, const Vector3Tpl& Z, const Vector3Tpl& Tr) { CC_MAT_R11 = X.x; CC_MAT_R21 = X.y; CC_MAT_R31 = X.z; CC_MAT_R41 = 0; CC_MAT_R12 = Y.x; CC_MAT_R22 = Y.y; CC_MAT_R32 = Y.z; CC_MAT_R42 = 0; CC_MAT_R13 = Z.x; CC_MAT_R23 = Z.y; CC_MAT_R33 = Z.z; CC_MAT_R43 = 0; CC_MAT_R14 = Tr.x; CC_MAT_R24 = Tr.y; CC_MAT_R34 = Tr.z; CC_MAT_R44 = static_cast(1); } //! Interpolates two matrices at relative position 'coef' /** \param coef interpolation position (should be between 0 and 1). \param glMat1 'left' matrix \param glMat2 'right' matrix **/ static ccGLMatrixTpl Interpolate(T coef, const ccGLMatrixTpl& glMat1, const ccGLMatrixTpl& glMat2) { //we compute the transformation matrix between glMat1 and glMat2 ccGLMatrixTpl invTrans1 = glMat1.inverse(); ccGLMatrixTpl m12 = invTrans1 * glMat2; Vector3Tpl axis,Tr; T alpha; m12.getParameters(alpha, axis, Tr); //we only have to interpolate the angle value alpha *= coef; //and the translation Tr *= coef; //we build-up the resulting matrix m12.initFromParameters(alpha, axis, Tr); //eventually we build-up resulting transformation return glMat1 * m12; } //! Creates a transformation matrix that rotates a vector to another /** Adapted from "Efficiently Building a Matrix to Rotate One Vector to Another" By Tomas Möller, John Hughes, Journal of Graphics Tools, 4(4):1-4, 1999 \param from normalized non-zero source vector \param to normalized non-zero destination vector **/ static ccGLMatrixTpl FromToRotation(const Vector3Tpl& from, const Vector3Tpl& to) { T c = from.dot(to); T f = (c < 0 ? -c : c); ccGLMatrixTpl result; if ( CCCoreLib::LessThanEpsilon( 1.0 - f ) ) //"from" and "to"-vector almost parallel { // "to" vector most nearly orthogonal to "from" Vector3Tpl x(0, 0, 0); if (std::abs(from.x) < std::abs(from.y)) { if (std::abs(from.x) < std::abs(from.z)) x.x = static_cast(1); else x.z = static_cast(1); } else { if (std::abs(from.y) < std::abs(from.z)) x.y = static_cast(1); else x.z = static_cast(1); } Vector3Tpl u = x - from; Vector3Tpl v = x - to; T c1 = 2 / u.dot(u); T c2 = 2 / v.dot(v); T c3 = c1 * c2 * u.dot(v); T* mat = result.data(); for (unsigned i = 0; i < 3; i++) { for (unsigned j = 0; j < 3; j++) { mat[i * 4 + j] = c3 * v.u[i] * u.u[j] - c2 * v.u[i] * v.u[j] - c1 * u.u[i] * u.u[j]; } mat[i * 4 + i] += static_cast(1); } } else // the most common case, unless "from"="to", or "from"=-"to" { //see Efficiently Building a Matrix to Rotate One Vector to Another //T. Moller and J.F. Hugues (1999) Vector3Tpl v = from.cross(to); T h = 1 / (1 + c); T hvx = h * v.x; T hvz = h * v.z; T hvxy = hvx * v.y; T hvxz = hvx * v.z; T hvyz = hvz * v.y; T* mat = result.data(); mat[0] = c + hvx * v.x; mat[1] = hvxy + v.z; mat[2] = hvxz - v.y; mat[4] = hvxy - v.z; mat[5] = c + h * v.y * v.y; mat[6] = hvyz + v.x; mat[8] = hvxz + v.y; mat[9] = hvyz - v.x; mat[10] = c + hvz * v.z; } return result; } //! Converts a quaternion to a rotation matrix /** \param q quaternion (4 values: w,x,y,z) \return corresponding rotation matrix **/ template static ccGLMatrixTpl FromQuaternion(const Tq q[]) { assert(q); ccGLMatrixTpl rotMat; T* mat = rotMat.data(); //diagonal { Tq q00 = q[0] * q[0]; Tq q11 = q[1] * q[1]; Tq q22 = q[2] * q[2]; Tq q33 = q[3] * q[3]; mat[0] = static_cast(q00 + q11 - q22 - q33); mat[5] = static_cast(q00 - q11 + q22 - q33); mat[10] = static_cast(q00 - q11 - q22 + q33); mat[15] = static_cast(1); } //non-diagonal elements { Tq q03 = q[0] * q[3]; Tq q13 = q[1] * q[3]; Tq q23 = q[2] * q[3]; Tq q02 = q[0] * q[2]; Tq q12 = q[1] * q[2]; Tq q01 = q[0] * q[1]; mat[1] = static_cast((q12 + q03) * 2); mat[2] = static_cast((q13 - q02) * 2); mat[4] = static_cast((q12 - q03) * 2); mat[6] = static_cast((q23 + q01) * 2); mat[8] = static_cast((q13 + q02) * 2); mat[9] = static_cast((q23 - q01) * 2); } return rotMat; } //! Generates a 'viewing' matrix from a looking vector and a 'up' direction /** \warning No translation is applied (pure rotation matrix) \param forward forward 'view' vector \param up up vector \return corresponding rotation matrix **/ static ccGLMatrixTpl FromViewDirAndUpDir(const Vector3Tpl& forward, const Vector3Tpl& up) { //normalize forward Vector3Tpl uForward = forward; uForward.normalize(); //side = forward x up Vector3Tpl uSide = uForward.cross(up); uSide.normalize(); //recompute 'up' as: up = side x forward Vector3Tpl uUp = uSide.cross(uForward); uUp.normalize(); ccGLMatrixTpl matrix; { T* mat = matrix.data(); mat[ 0] = uSide.x; mat[ 4] = uSide.y; mat[ 8] = uSide.z; mat[12] = 0; mat[ 1] = uUp.x; mat[ 5] = uUp.y; mat[ 9] = uUp.z; mat[13] = 0 ; mat[ 2] = -uForward.x; mat[ 6] = -uForward.y; mat[10] = -uForward.z; mat[14] = 0 ; mat[ 3] = 0; mat[ 7] = 0; mat[11] = 0; mat[15] = static_cast(1) ; } return matrix; } //! Converts a 'text' matrix to a ccGLMatrix /** \param[in] matText matrix in text format \param[out] success whether input matrix text is valid or not **/ static ccGLMatrixTpl FromString(const QString& matText, bool& success) { QStringList valuesStr = matText.simplified().split(QChar(' '), QString::SkipEmptyParts); if (valuesStr.size() != OPENGL_MATRIX_SIZE) { success = false; return ccGLMatrixTpl(); } ccGLMatrixTpl matrix; T* matValues = matrix.data(); for (unsigned i = 0; i < OPENGL_MATRIX_SIZE; ++i) { matValues[i] = static_cast(valuesStr[(i % 4) * 4 + (i >> 2)].toDouble(&success)); if (!success) return ccGLMatrixTpl(); } matrix.internalRescale(); success = true; return matrix; } //! Returns matrix as a string /** \param precision numerical precision \param separator separator \return string **/ QString toString(int precision = 12, QChar separator = ' ') const { QString str; for (unsigned l = 0; l < 4; ++l) //lines { for (unsigned c = 0; c < 4; ++c) //columns { str.append(QString::number(m_mat[c * 4 + l], 'f', precision)); if (c != 3) str.append(separator); } if (l != 3) str.append("\n"); } return str; } //! Saves matrix to an ASCII file /** \param filename output file name \param precision output digits precision ***/ virtual bool toAsciiFile(QString filename, int precision = 12) const { QFile fp(filename); if (!fp.open(QFile::WriteOnly | QFile::Text)) return false; QTextStream stream(&fp); stream.setRealNumberPrecision(precision); stream.setRealNumberNotation(QTextStream::FixedNotation); for (unsigned i = 0; i < 4; ++i) { stream << m_mat[i] << " " << m_mat[i + 4] << " " << m_mat[i + 8] << " " << m_mat[i + 12] << endl; } return (fp.error() == QFile::NoError); } //! Loads matrix from an ASCII file /** \param filename input file name ***/ virtual bool fromAsciiFile(QString filename) { QFile fp(filename); if (!fp.open(QFile::ReadOnly | QFile::Text)) return false; QTextStream stream(&fp); for (unsigned i = 0; i < 4; ++i) { stream >> m_mat[i ]; stream >> m_mat[i + 4]; stream >> m_mat[i + 8]; stream >> m_mat[i + 12]; } internalRescale(); return (fp.error() == QFile::NoError); } //! Returns the rotation component around X only ccGLMatrixTpl xRotation() const { ccGLMatrixTpl newRotMat; newRotMat.toIdentity(); //we use a specific Euler angles convention here if (CC_MAT_R13 >= 1) { //simpler/faster to ignore this (very) specific case! return newRotMat; } T phi = -asin(CC_MAT_R13); T cos_phi = cos(phi); T theta = atan2(CC_MAT_R23 / cos_phi, CC_MAT_R33 / cos_phi); newRotMat.CC_MAT_R22 = newRotMat.CC_MAT_R33 = cos(theta); newRotMat.CC_MAT_R32 = newRotMat.CC_MAT_R23 = sin(theta); newRotMat.CC_MAT_R23 *= static_cast(-1); newRotMat.setTranslation(getTranslation()); return newRotMat; } //! Returns the rotation component around Y only ccGLMatrixTpl yRotation() const { ccGLMatrixTpl newRotMat; newRotMat.toIdentity(); //we use a specific Euler angles convention here if (CC_MAT_R32 >= 1) { //simpler/faster to ignore this (very) specific case! return newRotMat; } T theta = asin(CC_MAT_R32); T cos_theta = cos(theta); T phi = atan2(-CC_MAT_R31 / cos_theta, CC_MAT_R33 / cos_theta); newRotMat.CC_MAT_R11 = newRotMat.CC_MAT_R33 = cos(phi); newRotMat.CC_MAT_R31 = newRotMat.CC_MAT_R13 = sin(phi); newRotMat.CC_MAT_R31 *= static_cast(-1); newRotMat.setTranslation(getTranslation()); return newRotMat; } //! Returns the rotation component around Z only ccGLMatrixTpl zRotation() const { //we can use the standard Euler angles convention here T phi, theta, psi; Vector3Tpl Tr; getParameters(phi, theta, psi, Tr); assert(Tr.norm2() == 0); ccGLMatrixTpl newRotMat; newRotMat.initFromParameters(phi, 0, 0, Tr); return newRotMat; } //! Clears matrix /** Matrix is set to 0. **/ inline virtual void toZero() { memset(m_mat, 0, OPENGL_MATRIX_SIZE * sizeof(T)); } //! Sets matrix to identity inline virtual void toIdentity() { toZero(); CC_MAT_R11 = CC_MAT_R22 = CC_MAT_R33 = CC_MAT_R44 = static_cast(1); } //! Returns whether this matrix is equal to identity inline virtual bool isIdentity() const { for (unsigned l = 0; l < 4; ++l) //lines { for (unsigned c = 0; c < 4; ++c) //columns { if (m_mat[c * 4 + l] != static_cast(l == c ? 1 : 0)) return false; } } return true; } //! Clears translation /** Translation is set to (0,0,0). **/ inline void clearTranslation() { CC_MAT_R14 = CC_MAT_R24 = CC_MAT_R34 = 0; } //! Inits transformation from a rotation axis, an angle and a translation /** \param[in] alpha_rad rotation angle (in radians) \param[in] axis3D rotation axis \param[in] t3D translation **/ void initFromParameters(T alpha_rad, const Vector3Tpl& axis3D, const Vector3Tpl& t3D) { T cos_t = cos(alpha_rad); T sin_t = sin(alpha_rad); T inv_cos_t = static_cast(1) - cos_t; //normalize rotation axis Vector3Tpl uAxis3D = axis3D; uAxis3D.normalize(); const T& l1 = uAxis3D.x; const T& l2 = uAxis3D.y; const T& l3 = uAxis3D.z; T l1_inv_cos_t = l1 * inv_cos_t; T l3_inv_cos_t = l3 * inv_cos_t; //1st column CC_MAT_R11 = cos_t + l1 * l1_inv_cos_t; CC_MAT_R21 = l2 * l1_inv_cos_t + l3 * sin_t; CC_MAT_R31 = l3 * l1_inv_cos_t - l2 * sin_t; CC_MAT_R41 = 0; //2nd column CC_MAT_R12 = l2 * l1_inv_cos_t - l3 * sin_t; CC_MAT_R22 = cos_t + l2 * l2*inv_cos_t; CC_MAT_R32 = l2 * l3_inv_cos_t + l1 * sin_t; CC_MAT_R42 = 0; //3rd column CC_MAT_R13 = l3 * l1_inv_cos_t + l2 * sin_t; CC_MAT_R23 = l2 * l3_inv_cos_t - l1 * sin_t; CC_MAT_R33 = cos_t + l3 * l3_inv_cos_t; CC_MAT_R43 = 0; //4th column CC_MAT_R14 = t3D.x; CC_MAT_R24 = t3D.y; CC_MAT_R34 = t3D.z; CC_MAT_R44 = static_cast(1); } //! Inits transformation from 3 rotation angles and a translation /** See http://en.wikipedia.org/wiki/Euler_angles (Tait-Bryan Z1Y2X3) \param[in] phi_rad Phi angle (in radians) \param[in] theta_rad Theta angle (in radians) \param[in] psi_rad Psi angle (in radians) \param[in] t3D translation \param[in] scale rotation scale **/ void initFromParameters( T phi_rad, T theta_rad, T psi_rad, const Vector3Tpl& t3D) { T c1 = cos(phi_rad); T c2 = cos(theta_rad); T c3 = cos(psi_rad); T s1 = sin(phi_rad); T s2 = sin(theta_rad); T s3 = sin(psi_rad); //1st column CC_MAT_R11 = c2 * c1; CC_MAT_R21 = c2 * s1; CC_MAT_R31 = -s2; CC_MAT_R41 = 0; //2nd column CC_MAT_R12 = s3 * s2*c1 - c3 * s1; CC_MAT_R22 = s3 * s2*s1 + c3 * c1; CC_MAT_R32 = s3 * c2; CC_MAT_R42 = 0; //3rd column CC_MAT_R13 = c3 * s2*c1 + s3 * s1; CC_MAT_R23 = c3 * s2*s1 - s3 * c1; CC_MAT_R33 = c3 * c2; CC_MAT_R43 = 0; //4th column CC_MAT_R14 = t3D.x; CC_MAT_R24 = t3D.y; CC_MAT_R34 = t3D.z; CC_MAT_R44 = static_cast(1); } //! Returns equivalent parameters: a rotation axis, an angle and a translation /** \param[out] alpha_rad rotation angle in radians (in [0;pi]) \param[out] axis3D unit rotation axis \param[out] t3D translation \param[out] scale [optional] scale **/ void getParameters( T& alpha_rad, Vector3Tpl& axis3D, Vector3Tpl& t3D, T* scale = nullptr) const { T trace = CC_MAT_R11 + CC_MAT_R22 + CC_MAT_R33; // we arbitrarily consider the first column to read the scale Vector3Tpl rotX = getColumnAsVec3D(0); T _scale = rotX.norm(); if (scale) { *scale = _scale; } if (_scale != static_cast(1) && CCCoreLib::GreaterThanEpsilon(_scale)) { trace /= _scale; } T cos_t = (trace - 1) / 2; if (std::abs(cos_t) <= 1) { alpha_rad = acos(cos_t); //result in [0;pi] } else { alpha_rad = 0; } axis3D.x = CC_MAT_R32 - CC_MAT_R23; axis3D.y = CC_MAT_R13 - CC_MAT_R31; axis3D.z = CC_MAT_R21 - CC_MAT_R12; //normalize axis T n2 = axis3D.norm2(); if (CCCoreLib::GreaterThanEpsilon(n2)) { axis3D /= sqrt(n2); } else { //axis is too small! axis3D = Vector3Tpl(0, 0, 1); } t3D.x = CC_MAT_R14; t3D.y = CC_MAT_R24; t3D.z = CC_MAT_R34; } //! Returns equivalent parameters: 3 rotation angles and a translation /** See http://en.wikipedia.org/wiki/Euler_angles (Tait-Bryan Z1Y2X3) \param[out] phi_rad Phi angle (in radians) \param[out] theta_rad Theta angle (in radians) \param[out] psi_rad Psi angle (in radians) \param[out] t3D translation \param[out] scale [optional] scale **/ void getParameters( T &phi_rad, T &theta_rad, T &psi_rad, Vector3Tpl& t3D, T* scale = nullptr) const { Vector3Tpl rotX = getColumnAsVec3D(0); Vector3Tpl rotY = getColumnAsVec3D(1); Vector3Tpl rotZ = getColumnAsVec3D(2); // we arbitrarily consider the first column to read the scale T _scale = rotX.norm(); if (scale) { *scale = _scale; } if (_scale != static_cast(1) && CCCoreLib::GreaterThanEpsilon(_scale)) { rotX /= _scale; rotY /= _scale; rotZ /= _scale; } if (std::abs(rotX.z) != 1) { theta_rad = -asin(rotX.z); T cos_theta = cos(theta_rad); psi_rad = atan2(rotY.z / cos_theta, rotZ.z / cos_theta); phi_rad = atan2(rotX.y / cos_theta, rotX.x / cos_theta); //Other solution //theta = M_PI + asin(rotX.z); //T cos_theta = cos(theta); //psi = atan2(rotY.z / cos_theta, rotZ.z / cos_theta); //phi = atan2(rotX.y / cos_theta, rotX.x / cos_theta); } else { phi_rad = 0; if (rotX.z == -1) { theta_rad = static_cast(M_PI_2); psi_rad = atan2(rotY.x, rotZ.x); } else { theta_rad = -static_cast(M_PI_2); psi_rad = -atan2(rotY.x, rotZ.x); } } t3D.x = CC_MAT_R14; t3D.y = CC_MAT_R24; t3D.z = CC_MAT_R34; } //! Returns a pointer to internal data inline T* data() { return m_mat; } //! Returns a const pointer to internal data inline const T* data() const { return m_mat; } //! Returns a pointer to internal translation /** Translation corresponds to the beginning of the third column of the matrix. **/ inline T* getTranslation() { return m_mat+12; } //! Returns a const pointer to internal translation /** Translation corresponds to the beginning of the third column of the matrix. **/ inline const T* getTranslation() const { return m_mat+12; } //! Returns a copy of the translation as a CCVector3 /** \return translation vector **/ inline Vector3Tpl getTranslationAsVec3D() const { return getColumnAsVec3D(3); } //! Sets translation (float version) /** \param Tr 3D vector **/ inline void setTranslation(const Vector3Tpl& Tr) { CC_MAT_R14 = static_cast(Tr.x); CC_MAT_R24 = static_cast(Tr.y); CC_MAT_R34 = static_cast(Tr.z); } //! Sets translation (double version) /** \param Tr 3D vector **/ inline void setTranslation(const Vector3Tpl& Tr) { CC_MAT_R14 = static_cast(Tr.x); CC_MAT_R24 = static_cast(Tr.y); CC_MAT_R34 = static_cast(Tr.z); } //! Sets translation from a float array /** \param Tr 3D vector as a float array **/ void setTranslation(const float Tr[3]) { CC_MAT_R14 = static_cast(Tr[0]); CC_MAT_R24 = static_cast(Tr[1]); CC_MAT_R34 = static_cast(Tr[2]); } //! Sets translation from a double array /** \param Tr 3D vector as a double array **/ void setTranslation(const double Tr[3]) { CC_MAT_R14 = static_cast(Tr[0]); CC_MAT_R24 = static_cast(Tr[1]); CC_MAT_R34 = static_cast(Tr[2]); } //! Returns a pointer to a given column /** \param index column index (between 0 and 3) \return pointer to the first element of the corresponding column **/ inline T* getColumn(unsigned index) { return m_mat + (index << 2); } //! Returns a const pointer to a given column /** \param index column index (between 0 and 3) \return pointer to the first element of the corresponding column **/ inline const T* getColumn(unsigned index) const { return m_mat + (index << 2); } //! Returns a copy of a given column as a CCVector3 /** 4th value is ignored. \param index column index (between 0 and 3) \return copy of the three first elements of the corresponding column **/ inline Vector3Tpl getColumnAsVec3D(unsigned index) const { return Vector3Tpl::fromArray(getColumn(index)); } //! Sets the content of a given column /** \param index column index (between 0 and 3) \param v new column values **/ inline void setColumn(unsigned index, const Vector3Tpl& v) { T* col = m_mat + (index << 2); col[0] = v.x; col[1] = v.y; col[2] = v.z; } //! Sets the content of a given column /** \param index column index (between 0 and 3) \param v new column values **/ inline void setColumn(unsigned index, const Tuple4Tpl& v) { T* col = m_mat + (index << 2); col[0] = v.x; col[1] = v.y; col[2] = v.z; col[3] = v.w; } //! Multiplication by a matrix operator ccGLMatrixTpl operator * (const ccGLMatrixTpl& mat) const { ccGLMatrixTpl result; const T* A = m_mat; const T* B = mat.m_mat; T* C = result.m_mat; for (unsigned j = 0; j < 4; ++j, B += 4) for (unsigned i = 0; i < 4; ++i) *C++ = A[i] * B[0] + A[i + 4] * B[1] + A[i + 8] * B[2] + A[i + 12] * B[3]; return result; } //! Multiplication by a vector operator (float version) inline Vector3Tpl operator * (const Vector3Tpl& vec) const { return Vector3Tpl(applyX(vec), applyY(vec), applyZ(vec)); } //! Multiplication by a vector operator (double version) inline Vector3Tpl operator * (const Vector3Tpl& vec) const { return Vector3Tpl(applyX(vec), applyY(vec), applyZ(vec)); } //! Multiplication by a 4D vector operator (float version) inline Tuple4Tpl operator * (const Tuple4Tpl& vec) const { return Tuple4Tpl(applyX(vec), applyY(vec), applyZ(vec), applyW(vec)); } //! Multiplication by a 4D vector operator (double version) inline Tuple4Tpl operator * (const Tuple4Tpl& vec) const { return Tuple4Tpl(applyX(vec), applyY(vec), applyZ(vec), applyW(vec)); } //! (in place) Addition operator ccGLMatrixTpl& operator += (const ccGLMatrixTpl& mat) { for (unsigned i = 0; i < OPENGL_MATRIX_SIZE; ++i) m_mat[i] += mat.m_mat[i]; return (*this); } //! (in place) Difference operator ccGLMatrixTpl& operator -= (const ccGLMatrixTpl& mat) { for (unsigned i = 0; i < OPENGL_MATRIX_SIZE; ++i) m_mat[i] -= mat.m_mat[i]; return (*this); } //! (in place) Multiplication operator ccGLMatrixTpl& operator *= (const ccGLMatrixTpl& mat) { (*this) = (*this) * mat; return (*this); } //! (in place) Forward translation operator (float version) ccGLMatrixTpl& operator += (const Vector3Tpl& Tr) { CC_MAT_R14 += static_cast(Tr.x); CC_MAT_R24 += static_cast(Tr.y); CC_MAT_R34 += static_cast(Tr.z); return (*this); } //! (in place) Forward translation operator (double version) ccGLMatrixTpl& operator += (const Vector3Tpl& Tr) { CC_MAT_R14 += static_cast(Tr.x); CC_MAT_R24 += static_cast(Tr.y); CC_MAT_R34 += static_cast(Tr.z); return (*this); } //! (in place) Backward translation operator (float version) ccGLMatrixTpl& operator -= (const Vector3Tpl& Tr) { CC_MAT_R14 -= static_cast(Tr.x); CC_MAT_R24 -= static_cast(Tr.y); CC_MAT_R34 -= static_cast(Tr.z); return (*this); } //! (in place) Backward translation operator (double version) ccGLMatrixTpl& operator -= (const Vector3Tpl& Tr) { CC_MAT_R14 -= static_cast(Tr.x); CC_MAT_R24 -= static_cast(Tr.y); CC_MAT_R34 -= static_cast(Tr.z); return (*this); } //! Returns the value at a given position T operator () (unsigned row, unsigned col) const { return m_mat[(col << 2) + row]; } //! Applies transformation to a 3D vector (in place) - float version /** Input vector is directly modified after calling this method **/ inline void apply(Vector3Tpl& vec) const { vec = (*this)*vec; } //! Applies transformation to a 3D vector (in place) - double version /** Input vector is directly modified after calling this method **/ inline void apply(Vector3Tpl& vec) const { vec = (*this)*vec; } //! Applies transformation to a 4D vector (in place) - float version /** Input vector is directly modified after calling this method **/ inline void apply(Tuple4Tpl& vec) const { vec = (*this)*vec; } //! Applies transformation to a 3D vector (in place) - double version /** Input vector is directly modified after calling this method **/ inline void apply(Tuple4Tpl& vec) const { vec = (*this)*vec; } //! Get the resulting transformation along X dimension (float version) inline float applyX(const Vector3Tpl& vec) const { return static_cast(CC_MAT_R11) * vec.x + static_cast(CC_MAT_R12) * vec.y + static_cast(CC_MAT_R13) * vec.z + static_cast(CC_MAT_R14); } //! Get the resulting transformation along X dimension (double version) inline double applyX(const Vector3Tpl& vec) const { return static_cast(CC_MAT_R11) * vec.x + static_cast(CC_MAT_R12) * vec.y + static_cast(CC_MAT_R13) * vec.z + static_cast(CC_MAT_R14); } //! Get the resulting transformation along Y dimension (float version) inline float applyY(const Vector3Tpl& vec) const { return static_cast(CC_MAT_R21) * vec.x + static_cast(CC_MAT_R22) * vec.y + static_cast(CC_MAT_R23) * vec.z + static_cast(CC_MAT_R24); } //! Get the resulting transformation along Y dimension (double version) inline double applyY(const Vector3Tpl& vec) const { return static_cast(CC_MAT_R21) * vec.x + static_cast(CC_MAT_R22) * vec.y + static_cast(CC_MAT_R23) * vec.z + static_cast(CC_MAT_R24); } //! Get the resulting transformation along Z dimension (float version) inline float applyZ(const Vector3Tpl& vec) const { return static_cast(CC_MAT_R31) * vec.x + static_cast(CC_MAT_R32) * vec.y + static_cast(CC_MAT_R33) * vec.z + static_cast(CC_MAT_R34); } //! Get the resulting transformation along Z dimension (double version) inline double applyZ(const Vector3Tpl& vec) const { return static_cast(CC_MAT_R31) * vec.x + static_cast(CC_MAT_R32) * vec.y + static_cast(CC_MAT_R33) * vec.z + static_cast(CC_MAT_R34); } //! Get the resulting transformation along X dimension (float version) inline float applyX(const Tuple4Tpl& vec) const { return static_cast(CC_MAT_R11) * vec.x + static_cast(CC_MAT_R12) * vec.y + static_cast(CC_MAT_R13) * vec.z + static_cast(CC_MAT_R14) * vec.w; } //! Get the resulting transformation along X dimension (double version) inline double applyX(const Tuple4Tpl& vec) const { return static_cast(CC_MAT_R11) * vec.x + static_cast(CC_MAT_R12) * vec.y + static_cast(CC_MAT_R13) * vec.z + static_cast(CC_MAT_R14) * vec.w; } //! Get the resulting transformation along Y dimension (float version) inline float applyY(const Tuple4Tpl& vec) const { return static_cast(CC_MAT_R21) * vec.x + static_cast(CC_MAT_R22) * vec.y + static_cast(CC_MAT_R23) * vec.z + static_cast(CC_MAT_R24) * vec.w; } //! Get the resulting transformation along Y dimension (double version) inline double applyY(const Tuple4Tpl& vec) const { return static_cast(CC_MAT_R21) * vec.x + static_cast(CC_MAT_R22) * vec.y + static_cast(CC_MAT_R23) * vec.z + static_cast(CC_MAT_R24) * vec.w; } //! Get the resulting transformation along Z dimension (float version) inline float applyZ(const Tuple4Tpl& vec) const { return static_cast(CC_MAT_R31) * vec.x + static_cast(CC_MAT_R32) * vec.y + static_cast(CC_MAT_R33) * vec.z + static_cast(CC_MAT_R34) * vec.w; } //! Get the resulting transformation along Z dimension (double version) inline double applyZ(const Tuple4Tpl& vec) const { return static_cast(CC_MAT_R31) * vec.x + static_cast(CC_MAT_R32) * vec.y + static_cast(CC_MAT_R33) * vec.z + static_cast(CC_MAT_R34) * vec.w; } //! Get the resulting transformation along the 4th dimension (float version) inline float applyW(const Tuple4Tpl& vec) const { return static_cast(CC_MAT_R41) * vec.x + static_cast(CC_MAT_R42) * vec.y + static_cast(CC_MAT_R43) * vec.z + static_cast(CC_MAT_R44) * vec.w; } //! Get the resulting transformation along the 4th dimension (double version) inline double applyW(const Tuple4Tpl& vec) const { return static_cast(CC_MAT_R41) * vec.x + static_cast(CC_MAT_R42) * vec.y + static_cast(CC_MAT_R43) * vec.z + static_cast(CC_MAT_R44) * vec.w; } //! Applies rotation only to a 3D vector (in place) - float version /** Input vector is directly modified after calling this method **/ inline void applyRotation(Vector3Tpl& vec) const { float vx = vec.x; float vy = vec.y; float vz = vec.z; vec.x = static_cast(CC_MAT_R11) * vx + static_cast(CC_MAT_R12) * vy + static_cast(CC_MAT_R13) * vz; vec.y = static_cast(CC_MAT_R21) * vx + static_cast(CC_MAT_R22) * vy + static_cast(CC_MAT_R23) * vz; vec.z = static_cast(CC_MAT_R31) * vx + static_cast(CC_MAT_R32) * vy + static_cast(CC_MAT_R33) * vz; } //! Applies rotation only to a 3D vector (in place) - double version /** Input vector is directly modified after calling this method **/ inline void applyRotation(Vector3Tpl& vec) const { double vx = vec.x; double vy = vec.y; double vz = vec.z; vec.x = static_cast(CC_MAT_R11) * vx + static_cast(CC_MAT_R12) * vy + static_cast(CC_MAT_R13) * vz; vec.y = static_cast(CC_MAT_R21) * vx + static_cast(CC_MAT_R22) * vy + static_cast(CC_MAT_R23) * vz; vec.z = static_cast(CC_MAT_R31) * vx + static_cast(CC_MAT_R32) * vy + static_cast(CC_MAT_R33) * vz; } //! Applies rotation only to a 3D vector (in place) - float version /** Input array is directly modified after calling this method **/ inline void applyRotation(float vec[3]) const { float vx = vec[0]; float vy = vec[1]; float vz = vec[2]; vec[0] = static_cast(CC_MAT_R11) * vx + static_cast(CC_MAT_R12) * vy + static_cast(CC_MAT_R13) * vz; vec[1] = static_cast(CC_MAT_R21) * vx + static_cast(CC_MAT_R22) * vy + static_cast(CC_MAT_R23) * vz; vec[2] = static_cast(CC_MAT_R31) * vx + static_cast(CC_MAT_R32) * vy + static_cast(CC_MAT_R33) * vz; } //! Applies rotation only to a 3D vector (in place) - float version /** Input array is directly modified after calling this method **/ inline void applyRotation(double vec[3]) const { double vx = vec[0]; double vy = vec[1]; double vz = vec[2]; vec[0] = static_cast(CC_MAT_R11) * vx + static_cast(CC_MAT_R12) * vy + static_cast(CC_MAT_R13) * vz; vec[1] = static_cast(CC_MAT_R21) * vx + static_cast(CC_MAT_R22) * vy + static_cast(CC_MAT_R23) * vz; vec[2] = static_cast(CC_MAT_R31) * vx + static_cast(CC_MAT_R32) * vy + static_cast(CC_MAT_R33) * vz; } //! Shifts rotation center /** Warning, this method only applies a shift (i.e. relatively to the current rotation center). This is not a way to set an absolute rotation center 'directly'. **/ void shiftRotationCenter(const Vector3Tpl& vec) { //R(X-vec)+T+vec = R(X)+T + vec-R(vec) Vector3Tpl Rvec = vec; applyRotation(Rvec); *this += (vec - Rvec); } //! Transposes matrix (in place) void transpose() { std::swap(CC_MAT_R21, CC_MAT_R12); std::swap(CC_MAT_R31, CC_MAT_R13); std::swap(CC_MAT_R41, CC_MAT_R14); std::swap(CC_MAT_R32, CC_MAT_R23); std::swap(CC_MAT_R42, CC_MAT_R24); std::swap(CC_MAT_R43, CC_MAT_R34); } //! Returns transposed matrix ccGLMatrixTpl transposed() const { ccGLMatrixTpl t(*this); t.transpose(); return t; } //! Inverts transformation void invert() { //inverse scale as well! //we use the columns norm (considering is should be 1 for an 'unscaled' matrix) T sx2 = getColumnAsVec3D(0).norm2(); T sy2 = getColumnAsVec3D(1).norm2(); T sz2 = getColumnAsVec3D(2).norm2(); //we invert rotation std::swap(CC_MAT_R21, CC_MAT_R12); std::swap(CC_MAT_R31, CC_MAT_R13); std::swap(CC_MAT_R32, CC_MAT_R23); if (std::abs(sx2) > std::numeric_limits::epsilon() && sx2 != 1) { CC_MAT_R11 /= sx2; CC_MAT_R21 /= sx2; CC_MAT_R31 /= sx2; } if (std::abs(sy2) > std::numeric_limits::epsilon() && sy2 != 1) { CC_MAT_R12 /= sy2; CC_MAT_R22 /= sy2; CC_MAT_R32 /= sy2; } if (std::abs(sz2) > std::numeric_limits::epsilon() && sz2 != 1) { CC_MAT_R13 /= sz2; CC_MAT_R23 /= sz2; CC_MAT_R33 /= sz2; } //eventually we invert translation applyRotation(m_mat + 12); double w = CC_MAT_R44; if (std::abs(w) > std::numeric_limits::epsilon()) { CC_MAT_R14 = -CC_MAT_R14 / w; CC_MAT_R24 = -CC_MAT_R24 / w; CC_MAT_R34 = -CC_MAT_R34 / w; CC_MAT_R44 = 1.0 / w; } else { assert(false); CC_MAT_R14 = -CC_MAT_R14; CC_MAT_R24 = -CC_MAT_R24; CC_MAT_R34 = -CC_MAT_R34; } } //! Returns inverse transformation ccGLMatrixTpl inverse() const { ccGLMatrixTpl t(*this); t.invert(); return t; } //! Scales the rotation and the translation (but not the the last row) /** \param coef scaling coef. **/ inline void scale(T coef) { CC_MAT_R11 *= coef; CC_MAT_R12 *= coef; CC_MAT_R13 *= coef; CC_MAT_R14 *= coef; CC_MAT_R21 *= coef; CC_MAT_R22 *= coef; CC_MAT_R23 *= coef; CC_MAT_R24 *= coef; CC_MAT_R31 *= coef; CC_MAT_R32 *= coef; CC_MAT_R33 *= coef; CC_MAT_R34 *= coef; } //! Scales the rotation part of the matrix /** \param coef scaling coef. **/ inline void scaleRotation(T coef) { CC_MAT_R11 *= coef; CC_MAT_R12 *= coef; CC_MAT_R13 *= coef; CC_MAT_R21 *= coef; CC_MAT_R22 *= coef; CC_MAT_R23 *= coef; CC_MAT_R31 *= coef; CC_MAT_R32 *= coef; CC_MAT_R33 *= coef; } //! Scales one row of the matrix /** \param rowIndex tow index (0-3) \param coef scaling coef. **/ void scaleRow(unsigned rowIndex, T coef) { assert(rowIndex < 4); m_mat[ rowIndex] *= coef; m_mat[ 4+rowIndex] *= coef; m_mat[ 8+rowIndex] *= coef; m_mat[12+rowIndex] *= coef; } //! Scales one column of the matrix /** \warning The last row is never scaled \param colIndex column index (0-3) \param coef scaling coef. **/ void scaleColumn(unsigned colIndex, T coef) { assert(colIndex < 4); T* col = getColumn(colIndex); col[0] *= coef; col[1] *= coef; col[2] *= coef; //col[3] *= coef; } //inherited from ccSerializableObject bool isSerializable() const override { return true; } //inherited from ccSerializableObject bool toFile(QFile& out, short dataVersion) const override { assert(out.isOpen() && (out.openMode() & QIODevice::WriteOnly)); if (dataVersion < 20) { assert(false); return false; } //data (dataVersion>=20) if (out.write(reinterpret_cast(m_mat), sizeof(T)*OPENGL_MATRIX_SIZE) < 0) { return WriteError(); } return true; } //inherited from ccSerializableObject bool fromFile(QFile& in, short dataVersion, int flags, LoadedIDMap& oldToNewIDMap) override { assert(in.isOpen() && (in.openMode() & QIODevice::ReadOnly)); if (dataVersion < 20) return CorruptError(); //data (dataVersion>=20) if (in.read(reinterpret_cast(m_mat), sizeof(T)*OPENGL_MATRIX_SIZE) < 0) return ReadError(); return true; } //inherited from ccSerializableObject short minimumFileVersion() const override { return 20; } protected: //! Transfers the homogenous coordinate (scale) to the whole matrix void internalRescale() { if (CC_MAT_R44 != static_cast(1) && CCCoreLib::GreaterThanEpsilon(CC_MAT_R44)) { scaleRotation(1.0 / CC_MAT_R44); CC_MAT_R44 = static_cast(1); } } //! Internal 4x4 GL-style matrix data T m_mat[OPENGL_MATRIX_SIZE]; };