/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2012-2013 DreamWorks Animation LLC // // All rights reserved. This software is distributed under the // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) // // Redistributions of source code must retain the above copyright // and license notice and the following restrictions and disclaimer. // // * Neither the name of DreamWorks Animation nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. // /////////////////////////////////////////////////////////////////////////// #ifndef OPENVDB_MATH_MAT4_H_HAS_BEEN_INCLUDED #define OPENVDB_MATH_MAT4_H_HAS_BEEN_INCLUDED #include #include #include #include #include #include #include "Math.h" #include "Mat3.h" #include "Vec3.h" #include "Vec4.h" namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace math { template class Vec4; /// @class Mat4 Mat4.h /// @brief 4x4 -matrix class. template class Mat4: public Mat<4, T> { public: /// Data type held by the matrix. typedef T value_type; typedef T ValueType; typedef Mat<4, T> MyBase; /// Trivial constructor, the matrix is NOT initialized Mat4() {} /// Constructor given array of elements, the ordering is in row major form: /** @verbatim a[ 0] a[1] a[ 2] a[ 3] a[ 4] a[5] a[ 6] a[ 7] a[ 8] a[9] a[10] a[11] a[12] a[13] a[14] a[15] @endverbatim */ template Mat4(Source *a) { register int i; for (i = 0; i < 16; i++) { MyBase::mm[i] = a[i]; } } /// Constructor given array of elements, the ordering is in row major form: /** @verbatim a b c d e f g h i j k l m n o p @endverbatim */ template Mat4(Source a, Source b, Source c, Source d, Source e, Source f, Source g, Source h, Source i, Source j, Source k, Source l, Source m, Source n, Source o, Source p) { MyBase::mm[ 0] = a; MyBase::mm[ 1] = b; MyBase::mm[ 2] = c; MyBase::mm[ 3] = d; MyBase::mm[ 4] = e; MyBase::mm[ 5] = f; MyBase::mm[ 6] = g; MyBase::mm[ 7] = h; MyBase::mm[ 8] = i; MyBase::mm[ 9] = j; MyBase::mm[10] = k; MyBase::mm[11] = l; MyBase::mm[12] = m; MyBase::mm[13] = n; MyBase::mm[14] = o; MyBase::mm[15] = p; } /// Construct matrix given basis vectors (columns) template Mat4(const Vec4 &v1, const Vec4 &v2, const Vec4 &v3, const Vec4 &v4) { setBasis(v1, v2, v3, v4); } /// Copy constructor Mat4(const Mat<4, T> &m) { for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { MyBase::mm[i*4 + j] = m[i][j]; } } } /// Conversion constructor template explicit Mat4(const Mat4 &m) { const Source *src = m.asPointer(); for (int i=0; i<16; ++i) { MyBase::mm[i] = static_cast(src[i]); } } /// Predefined constant for identity matrix static const Mat4& identity() { return sIdentity; } /// Predefined constant for zero matrix static const Mat4& zero() { return sZero; } /// Set ith row to vector v void setRow(int i, const Vec4 &v) { // assert(i>=0 && i<4); int i4 = i * 4; MyBase::mm[i4+0] = v[0]; MyBase::mm[i4+1] = v[1]; MyBase::mm[i4+2] = v[2]; MyBase::mm[i4+3] = v[3]; } /// Get ith row, e.g. Vec4f v = m.row(1); Vec4 row(int i) const { // assert(i>=0 && i<3); return Vec4((*this)(i,0), (*this)(i,1), (*this)(i,2), (*this)(i,3)); } /// Set jth column to vector v void setCol(int j, const Vec4& v) { // assert(j>=0 && j<4); MyBase::mm[ 0+j] = v[0]; MyBase::mm[ 4+j] = v[1]; MyBase::mm[ 8+j] = v[2]; MyBase::mm[12+j] = v[3]; } /// Get jth column, e.g. Vec4f v = m.col(0); Vec4 col(int j) const { // assert(j>=0 && j<4); return Vec4((*this)(0,j), (*this)(1,j), (*this)(2,j), (*this)(3,j)); } //@{ /// Array style reference to ith row /// e.g. m[1][3] = 4; T* operator[](int i) { return &(MyBase::mm[i<<2]); } const T* operator[](int i) const { return &(MyBase::mm[i<<2]); } //@} /// Direct access to the internal data T* asPointer() {return MyBase::mm;} const T* asPointer() const {return MyBase::mm;} /// Alternative indexed reference to the elements /// Note that the indices are row first and column second. /// e.g. m(0,0) = 1; T& operator()(int i, int j) { // assert(i>=0 && i<4); // assert(j>=0 && j<4); return MyBase::mm[4*i+j]; } /// Alternative indexed constant reference to the elements, /// Note that the indices are row first and column second. /// e.g. float f = m(1,0); T operator()(int i, int j) const { // assert(i>=0 && i<4); // assert(j>=0 && j<4); return MyBase::mm[4*i+j]; } /// Set the columns of "this" matrix to the vectors v1, v2, v3, v4 void setBasis(const Vec4 &v1, const Vec4 &v2, const Vec4 &v3, const Vec4 &v4) { MyBase::mm[ 0] = v1[0]; MyBase::mm[ 1] = v1[1]; MyBase::mm[ 2] = v1[2]; MyBase::mm[ 3] = v1[3]; MyBase::mm[ 4] = v2[0]; MyBase::mm[ 5] = v2[1]; MyBase::mm[ 6] = v2[2]; MyBase::mm[ 7] = v2[3]; MyBase::mm[ 8] = v3[0]; MyBase::mm[ 9] = v3[1]; MyBase::mm[10] = v3[2]; MyBase::mm[11] = v3[3]; MyBase::mm[12] = v4[0]; MyBase::mm[13] = v4[1]; MyBase::mm[14] = v4[2]; MyBase::mm[15] = v4[3]; } // Set "this" matrix to zero void setZero() { MyBase::mm[ 0] = 0; MyBase::mm[ 1] = 0; MyBase::mm[ 2] = 0; MyBase::mm[ 3] = 0; MyBase::mm[ 4] = 0; MyBase::mm[ 5] = 0; MyBase::mm[ 6] = 0; MyBase::mm[ 7] = 0; MyBase::mm[ 8] = 0; MyBase::mm[ 9] = 0; MyBase::mm[10] = 0; MyBase::mm[11] = 0; MyBase::mm[12] = 0; MyBase::mm[13] = 0; MyBase::mm[14] = 0; MyBase::mm[15] = 0; } /// Set "this" matrix to identity void setIdentity() { MyBase::mm[ 0] = 1; MyBase::mm[ 1] = 0; MyBase::mm[ 2] = 0; MyBase::mm[ 3] = 0; MyBase::mm[ 4] = 0; MyBase::mm[ 5] = 1; MyBase::mm[ 6] = 0; MyBase::mm[ 7] = 0; MyBase::mm[ 8] = 0; MyBase::mm[ 9] = 0; MyBase::mm[10] = 1; MyBase::mm[11] = 0; MyBase::mm[12] = 0; MyBase::mm[13] = 0; MyBase::mm[14] = 0; MyBase::mm[15] = 1; } /// Set upper left to a Mat3 void setMat3(const Mat3 &m) { for (int i = 0; i < 3; i++) for (int j=0; j < 3; j++) MyBase::mm[i*4+j] = m[i][j]; } Mat3 getMat3() const { Mat3 m; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) m[i][j] = MyBase::mm[i*4+j]; return m; } /// Return the translation component Vec3 getTranslation() const { return Vec3(MyBase::mm[12], MyBase::mm[13], MyBase::mm[14]); } void setTranslation(const Vec3 &t) { MyBase::mm[12] = t[0]; MyBase::mm[13] = t[1]; MyBase::mm[14] = t[2]; } /// Assignment operator template const Mat4& operator=(const Mat4 &m) { const Source *src = m.asPointer(); // don't suppress warnings when assigning from different numerical types std::copy(src, (src + this->numElements()), MyBase::mm); return *this; } /// Test if "this" is equivalent to m with tolerance of eps value bool eq(const Mat4 &m, T eps=1.0e-8) const { for (int i = 0; i < 16; i++) { if (!isApproxEqual(MyBase::mm[i], m.mm[i], eps)) return false; } return true; } /// Negation operator, for e.g. m1 = -m2; Mat4 operator-() const { return Mat4( -MyBase::mm[ 0], -MyBase::mm[ 1], -MyBase::mm[ 2], -MyBase::mm[ 3], -MyBase::mm[ 4], -MyBase::mm[ 5], -MyBase::mm[ 6], -MyBase::mm[ 7], -MyBase::mm[ 8], -MyBase::mm[ 9], -MyBase::mm[10], -MyBase::mm[11], -MyBase::mm[12], -MyBase::mm[13], -MyBase::mm[14], -MyBase::mm[15] ); } // trivial /// Return m, where \f$m_{i,j} *= scalar\f$ for \f$i, j \in [0, 3]\f$ template const Mat4& operator*=(S scalar) { MyBase::mm[ 0] *= scalar; MyBase::mm[ 1] *= scalar; MyBase::mm[ 2] *= scalar; MyBase::mm[ 3] *= scalar; MyBase::mm[ 4] *= scalar; MyBase::mm[ 5] *= scalar; MyBase::mm[ 6] *= scalar; MyBase::mm[ 7] *= scalar; MyBase::mm[ 8] *= scalar; MyBase::mm[ 9] *= scalar; MyBase::mm[10] *= scalar; MyBase::mm[11] *= scalar; MyBase::mm[12] *= scalar; MyBase::mm[13] *= scalar; MyBase::mm[14] *= scalar; MyBase::mm[15] *= scalar; return *this; } /// @brief Returns m0, where \f$m0_{i,j} += m1_{i,j}\f$ for \f$i, j \in [0, 3]\f$ template const Mat4 &operator+=(const Mat4 &m1) { const S* s = m1.asPointer(); MyBase::mm[ 0] += s[ 0]; MyBase::mm[ 1] += s[ 1]; MyBase::mm[ 2] += s[ 2]; MyBase::mm[ 3] += s[ 3]; MyBase::mm[ 4] += s[ 4]; MyBase::mm[ 5] += s[ 5]; MyBase::mm[ 6] += s[ 6]; MyBase::mm[ 7] += s[ 7]; MyBase::mm[ 8] += s[ 8]; MyBase::mm[ 9] += s[ 9]; MyBase::mm[10] += s[10]; MyBase::mm[11] += s[11]; MyBase::mm[12] += s[12]; MyBase::mm[13] += s[13]; MyBase::mm[14] += s[14]; MyBase::mm[15] += s[15]; return *this; } /// @brief Returns m0, where \f$m0_{i,j} -= m1_{i,j}\f$ for \f$i, j \in [0, 3]\f$ template const Mat4 &operator-=(const Mat4 &m1) { const S* s = m1.asPointer(); MyBase::mm[ 0] -= s[ 0]; MyBase::mm[ 1] -= s[ 1]; MyBase::mm[ 2] -= s[ 2]; MyBase::mm[ 3] -= s[ 3]; MyBase::mm[ 4] -= s[ 4]; MyBase::mm[ 5] -= s[ 5]; MyBase::mm[ 6] -= s[ 6]; MyBase::mm[ 7] -= s[ 7]; MyBase::mm[ 8] -= s[ 8]; MyBase::mm[ 9] -= s[ 9]; MyBase::mm[10] -= s[10]; MyBase::mm[11] -= s[11]; MyBase::mm[12] -= s[12]; MyBase::mm[13] -= s[13]; MyBase::mm[14] -= s[14]; MyBase::mm[15] -= s[15]; return *this; } /// Return m, where \f$m_{i,j} = \sum_{k} m0_{i,k}*m1_{k,j}\f$ for \f$i, j \in [0, 3]\f$ template const Mat4 &operator*=(const Mat4 &m1) { Mat4 m0(*this); const T* s0 = m0.asPointer(); const S* s1 = m1.asPointer(); for (int i = 0; i < 4; i++) { int i4 = 4 * i; MyBase::mm[i4+0] = static_cast(s0[i4+0] * s1[ 0] + s0[i4+1] * s1[ 4] + s0[i4+2] * s1[ 8] + s0[i4+3] * s1[12]); MyBase::mm[i4+1] = static_cast(s0[i4+0] * s1[ 1] + s0[i4+1] * s1[ 5] + s0[i4+2] * s1[ 9] + s0[i4+3] * s1[13]); MyBase::mm[i4+2] = static_cast(s0[i4+0] * s1[ 2] + s0[i4+1] * s1[ 6] + s0[i4+2] * s1[10] + s0[i4+3] * s1[14]); MyBase::mm[i4+3] = static_cast(s0[i4+0] * s1[ 3] + s0[i4+1] * s1[ 7] + s0[i4+2] * s1[11] + s0[i4+3] * s1[15]); } return *this; } /// @return transpose of this Mat4 transpose() const { return Mat4( MyBase::mm[ 0], MyBase::mm[ 4], MyBase::mm[ 8], MyBase::mm[12], MyBase::mm[ 1], MyBase::mm[ 5], MyBase::mm[ 9], MyBase::mm[13], MyBase::mm[ 2], MyBase::mm[ 6], MyBase::mm[10], MyBase::mm[14], MyBase::mm[ 3], MyBase::mm[ 7], MyBase::mm[11], MyBase::mm[15] ); } /// @return inverse of this /// @throw ArithmeticError if singular Mat4 inverse(T tolerance = 0) const { // // inv [ A | b ] = [ E | f ] A: 3x3, b: 3x1, c': 1x3 d: 1x1 // [ c' | d ] [ g' | h ] // // If A is invertible use // // E = A^-1 + p*h*r // p = A^-1 * b // f = -p * h // g' = -h * c' // h = 1 / (d - c'*p) // r' = c'*A^-1 // // Otherwise use gauss-jordan elimination // // // We create this alias to ourself so we can easily use own subscript // operator. const Mat4& m(*this); T m0011 = m[0][0] * m[1][1]; T m0012 = m[0][0] * m[1][2]; T m0110 = m[0][1] * m[1][0]; T m0210 = m[0][2] * m[1][0]; T m0120 = m[0][1] * m[2][0]; T m0220 = m[0][2] * m[2][0]; T detA = m0011 * m[2][2] - m0012 * m[2][1] - m0110 * m[2][2] + m0210 * m[2][1] + m0120 * m[1][2] - m0220 * m[1][1]; bool hasPerspective = (!isExactlyEqual(m[0][3], T(0.0)) || !isExactlyEqual(m[1][3], T(0.0)) || !isExactlyEqual(m[2][3], T(0.0)) || !isExactlyEqual(m[3][3], T(1.0))); T det; if (hasPerspective) { det = m[0][3] * det3(m, 1,2,3, 0,2,1) + m[1][3] * det3(m, 2,0,3, 0,2,1) + m[2][3] * det3(m, 3,0,1, 0,2,1) + m[3][3] * detA; } else { det = detA * m[3][3]; } Mat4 inv; bool invertible; if (isApproxEqual(det,T(0.0),tolerance)) { invertible = false; } else if (isApproxEqual(detA,T(0.0),T(1e-8))) { // det is too small to rely on inversion by subblocks invertible = m.invert(inv, tolerance); } else { invertible = true; detA = 1.0 / detA; // // Calculate A^-1 // inv[0][0] = detA * ( m[1][1] * m[2][2] - m[1][2] * m[2][1]); inv[0][1] = detA * (-m[0][1] * m[2][2] + m[0][2] * m[2][1]); inv[0][2] = detA * ( m[0][1] * m[1][2] - m[0][2] * m[1][1]); inv[1][0] = detA * (-m[1][0] * m[2][2] + m[1][2] * m[2][0]); inv[1][1] = detA * ( m[0][0] * m[2][2] - m0220); inv[1][2] = detA * ( m0210 - m0012); inv[2][0] = detA * ( m[1][0] * m[2][1] - m[1][1] * m[2][0]); inv[2][1] = detA * ( m0120 - m[0][0] * m[2][1]); inv[2][2] = detA * ( m0011 - m0110); if (hasPerspective) { // // Calculate r, p, and h // Vec3 r; r[0] = m[3][0] * inv[0][0] + m[3][1] * inv[1][0] + m[3][2] * inv[2][0]; r[1] = m[3][0] * inv[0][1] + m[3][1] * inv[1][1] + m[3][2] * inv[2][1]; r[2] = m[3][0] * inv[0][2] + m[3][1] * inv[1][2] + m[3][2] * inv[2][2]; Vec3 p; p[0] = inv[0][0] * m[0][3] + inv[0][1] * m[1][3] + inv[0][2] * m[2][3]; p[1] = inv[1][0] * m[0][3] + inv[1][1] * m[1][3] + inv[1][2] * m[2][3]; p[2] = inv[2][0] * m[0][3] + inv[2][1] * m[1][3] + inv[2][2] * m[2][3]; T h = m[3][3] - p.dot(Vec3(m[3][0],m[3][1],m[3][2])); if (isApproxEqual(h,T(0.0),tolerance)) { invertible = false; } else { h = 1.0 / h; // // Calculate h, g, and f // inv[3][3] = h; inv[3][0] = -h * r[0]; inv[3][1] = -h * r[1]; inv[3][2] = -h * r[2]; inv[0][3] = -h * p[0]; inv[1][3] = -h * p[1]; inv[2][3] = -h * p[2]; // // Calculate E // p *= h; inv[0][0] += p[0] * r[0]; inv[0][1] += p[0] * r[1]; inv[0][2] += p[0] * r[2]; inv[1][0] += p[1] * r[0]; inv[1][1] += p[1] * r[1]; inv[1][2] += p[1] * r[2]; inv[2][0] += p[2] * r[0]; inv[2][1] += p[2] * r[1]; inv[2][2] += p[2] * r[2]; } } else { // Equations are much simpler in the non-perspective case inv[3][0] = - (m[3][0] * inv[0][0] + m[3][1] * inv[1][0] + m[3][2] * inv[2][0]); inv[3][1] = - (m[3][0] * inv[0][1] + m[3][1] * inv[1][1] + m[3][2] * inv[2][1]); inv[3][2] = - (m[3][0] * inv[0][2] + m[3][1] * inv[1][2] + m[3][2] * inv[2][2]); inv[0][3] = 0.0; inv[1][3] = 0.0; inv[2][3] = 0.0; inv[3][3] = 1.0; } } if (!invertible) OPENVDB_THROW(ArithmeticError, "Inversion of singular 4x4 matrix"); return inv; } /// Determinant of matrix T det() const { const T *ap; Mat3 submat; T det; T *sp; int i, j, k, sign; det = 0; sign = 1; for (i = 0; i < 4; i++) { ap = &MyBase::mm[ 0]; sp = submat.asPointer(); for (j = 0; j < 4; j++) { for (k = 0; k < 4; k++) { if ((k != i) && (j != 0)) { *sp++ = *ap; } ap++; } } det += sign * MyBase::mm[i] * submat.det(); sign = -sign; } return det; } /// This function snaps a specific axis to a specific direction, /// preserving scaling. It does this using minimum energy, thus /// posing a unique solution if basis & direction arent parralel. /// Direction need not be unit. Mat4 snapBasis(Axis axis, const Vec3 &direction) {return snapBasis(*this, axis, direction);} /// Sets the matrix to a matrix that translates by v static Mat4 translation(const Vec3d& v) { return Mat4( T(1), T(0), T(0), T(0), T(0), T(1), T(0), T(0), T(0), T(0), T(1), T(0), T(v.x()), T(v.y()),T(v.z()), T(1)); } /// Sets the matrix to a matrix that translates by v template void setToTranslation(const Vec3& v) { MyBase::mm[ 0] = 1; MyBase::mm[ 1] = 0; MyBase::mm[ 2] = 0; MyBase::mm[ 3] = 0; MyBase::mm[ 4] = 0; MyBase::mm[ 5] = 1; MyBase::mm[ 6] = 0; MyBase::mm[ 7] = 0; MyBase::mm[ 8] = 0; MyBase::mm[ 9] = 0; MyBase::mm[10] = 1; MyBase::mm[11] = 0; MyBase::mm[12] = v.x(); MyBase::mm[13] = v.y(); MyBase::mm[14] = v.z(); MyBase::mm[15] = 1; } /// Left multiples by the specified translation, i.e. Trans * (*this) template void preTranslate(const Vec3& tr) { Vec3 tmp(tr.x(), tr.y(), tr.z()); Mat4 Tr = Mat4::translation(tmp); *this = Tr * (*this); } /// Right multiplies by the specified translation matrix, i.e. (*this) * Trans template void postTranslate(const Vec3& tr) { Vec3 tmp(tr.x(), tr.y(), tr.z()); Mat4 Tr = Mat4::translation(tmp); *this = (*this) * Tr; } /// Sets the matrix to a matrix that scales by v template void setToScale(const Vec3& v) { this->setIdentity(); MyBase::mm[ 0] = v.x(); MyBase::mm[ 5] = v.y(); MyBase::mm[10] = v.z(); } // Left multiples by the specified scale matrix, i.e. Sc * (*this) template void preScale(const Vec3& v) { MyBase::mm[ 0] *= v.x(); MyBase::mm[ 1] *= v.x(); MyBase::mm[ 2] *= v.x(); MyBase::mm[ 3] *= v.x(); MyBase::mm[ 4] *= v.y(); MyBase::mm[ 5] *= v.y(); MyBase::mm[ 6] *= v.y(); MyBase::mm[ 7] *= v.y(); MyBase::mm[ 8] *= v.z(); MyBase::mm[ 9] *= v.z(); MyBase::mm[10] *= v.z(); MyBase::mm[11] *= v.z(); } // Right multiples by the specified scale matrix, i.e. (*this) * Sc template void postScale(const Vec3& v) { MyBase::mm[ 0] *= v.x(); MyBase::mm[ 1] *= v.y(); MyBase::mm[ 2] *= v.z(); MyBase::mm[ 4] *= v.x(); MyBase::mm[ 5] *= v.y(); MyBase::mm[ 6] *= v.z(); MyBase::mm[ 8] *= v.x(); MyBase::mm[ 9] *= v.y(); MyBase::mm[10] *= v.z(); MyBase::mm[12] *= v.x(); MyBase::mm[13] *= v.y(); MyBase::mm[14] *= v.z(); } /// @brief Sets the matrix to a rotation about the given axis. /// @param axis The axis (one of X, Y, Z) to rotate about. /// @param angle The rotation angle, in radians. void setToRotation(Axis axis, T angle) {*this = rotation >(axis, angle);} /// @brief Sets the matrix to a rotation about an arbitrary axis /// @param axis The axis of rotation (cannot be zero-length) /// @param angle The rotation angle, in radians. void setToRotation(const Vec3& axis, T angle) {*this = rotation >(axis, angle);} /// @brief Sets the matrix to a rotation that maps v1 onto v2 about the cross /// product of v1 and v2. void setToRotation(const Vec3& v1, const Vec3& v2) {*this = rotation >(v1, v2);} /// @brief Left multiplies by a rotation clock-wiseabout the given axis into this matrix. /// @param axis The axis (one of X, Y, Z) of rotation. /// @param angle The clock-wise rotation angle, in radians. void preRotate(Axis axis, T angle) { T c = static_cast(cos(angle)); T s = -static_cast(sin(angle)); // the "-" makes it clockwise switch (axis) { case X_AXIS: { T a4, a5, a6, a7; a4 = c * MyBase::mm[ 4] - s * MyBase::mm[ 8]; a5 = c * MyBase::mm[ 5] - s * MyBase::mm[ 9]; a6 = c * MyBase::mm[ 6] - s * MyBase::mm[10]; a7 = c * MyBase::mm[ 7] - s * MyBase::mm[11]; MyBase::mm[ 8] = s * MyBase::mm[ 4] + c * MyBase::mm[ 8]; MyBase::mm[ 9] = s * MyBase::mm[ 5] + c * MyBase::mm[ 9]; MyBase::mm[10] = s * MyBase::mm[ 6] + c * MyBase::mm[10]; MyBase::mm[11] = s * MyBase::mm[ 7] + c * MyBase::mm[11]; MyBase::mm[ 4] = a4; MyBase::mm[ 5] = a5; MyBase::mm[ 6] = a6; MyBase::mm[ 7] = a7; } break; case Y_AXIS: { T a0, a1, a2, a3; a0 = c * MyBase::mm[ 0] + s * MyBase::mm[ 8]; a1 = c * MyBase::mm[ 1] + s * MyBase::mm[ 9]; a2 = c * MyBase::mm[ 2] + s * MyBase::mm[10]; a3 = c * MyBase::mm[ 3] + s * MyBase::mm[11]; MyBase::mm[ 8] = -s * MyBase::mm[ 0] + c * MyBase::mm[ 8]; MyBase::mm[ 9] = -s * MyBase::mm[ 1] + c * MyBase::mm[ 9]; MyBase::mm[10] = -s * MyBase::mm[ 2] + c * MyBase::mm[10]; MyBase::mm[11] = -s * MyBase::mm[ 3] + c * MyBase::mm[11]; MyBase::mm[ 0] = a0; MyBase::mm[ 1] = a1; MyBase::mm[ 2] = a2; MyBase::mm[ 3] = a3; } break; case Z_AXIS: { T a0, a1, a2, a3; a0 = c * MyBase::mm[ 0] - s * MyBase::mm[ 4]; a1 = c * MyBase::mm[ 1] - s * MyBase::mm[ 5]; a2 = c * MyBase::mm[ 2] - s * MyBase::mm[ 6]; a3 = c * MyBase::mm[ 3] - s * MyBase::mm[ 7]; MyBase::mm[ 4] = s * MyBase::mm[ 0] + c * MyBase::mm[ 4]; MyBase::mm[ 5] = s * MyBase::mm[ 1] + c * MyBase::mm[ 5]; MyBase::mm[ 6] = s * MyBase::mm[ 2] + c * MyBase::mm[ 6]; MyBase::mm[ 7] = s * MyBase::mm[ 3] + c * MyBase::mm[ 7]; MyBase::mm[ 0] = a0; MyBase::mm[ 1] = a1; MyBase::mm[ 2] = a2; MyBase::mm[ 3] = a3; } break; default: assert(axis==X_AXIS || axis==Y_AXIS || axis==Z_AXIS); } } /// @brief Right multiplies by a rotation clock-wiseabout the given axis into this matrix. /// @param axis The axis (one of X, Y, Z) of rotation. /// @param angle The clock-wise rotation angle, in radians. void postRotate(Axis axis, T angle) { T c = static_cast(cos(angle)); T s = -static_cast(sin(angle)); // the "-" makes it clockwise switch (axis) { case X_AXIS: { T a2, a6, a10, a14; a2 = c * MyBase::mm[ 2] - s * MyBase::mm[ 1]; a6 = c * MyBase::mm[ 6] - s * MyBase::mm[ 5]; a10 = c * MyBase::mm[10] - s * MyBase::mm[ 9]; a14 = c * MyBase::mm[14] - s * MyBase::mm[13]; MyBase::mm[ 1] = c * MyBase::mm[ 1] + s * MyBase::mm[ 2]; MyBase::mm[ 5] = c * MyBase::mm[ 5] + s * MyBase::mm[ 6]; MyBase::mm[ 9] = c * MyBase::mm[ 9] + s * MyBase::mm[10]; MyBase::mm[13] = c * MyBase::mm[13] + s * MyBase::mm[14]; MyBase::mm[ 2] = a2; MyBase::mm[ 6] = a6; MyBase::mm[10] = a10; MyBase::mm[14] = a14; } break; case Y_AXIS: { T a2, a6, a10, a14; a2 = c * MyBase::mm[ 2] + s * MyBase::mm[ 0]; a6 = c * MyBase::mm[ 6] + s * MyBase::mm[ 4]; a10 = c * MyBase::mm[10] + s * MyBase::mm[ 8]; a14 = c * MyBase::mm[14] + s * MyBase::mm[12]; MyBase::mm[ 0] = c * MyBase::mm[ 0] - s * MyBase::mm[ 2]; MyBase::mm[ 4] = c * MyBase::mm[ 4] - s * MyBase::mm[ 6]; MyBase::mm[ 8] = c * MyBase::mm[ 8] - s * MyBase::mm[10]; MyBase::mm[12] = c * MyBase::mm[12] - s * MyBase::mm[14]; MyBase::mm[ 2] = a2; MyBase::mm[ 6] = a6; MyBase::mm[10] = a10; MyBase::mm[14] = a14; } break; case Z_AXIS: { T a1, a5, a9, a13; a1 = c * MyBase::mm[ 1] - s * MyBase::mm[ 0]; a5 = c * MyBase::mm[ 5] - s * MyBase::mm[ 4]; a9 = c * MyBase::mm[ 9] - s * MyBase::mm[ 8]; a13 = c * MyBase::mm[13] - s * MyBase::mm[12]; MyBase::mm[ 0] = c * MyBase::mm[ 0] + s * MyBase::mm[ 1]; MyBase::mm[ 4] = c * MyBase::mm[ 4] + s * MyBase::mm[ 5]; MyBase::mm[ 8] = c * MyBase::mm[ 8] + s * MyBase::mm[ 9]; MyBase::mm[12] = c * MyBase::mm[12] + s * MyBase::mm[13]; MyBase::mm[ 1] = a1; MyBase::mm[ 5] = a5; MyBase::mm[ 9] = a9; MyBase::mm[13] = a13; } break; default: assert(axis==X_AXIS || axis==Y_AXIS || axis==Z_AXIS); } } /// @brief Sets the matrix to a shear along axis0 by a fraction of axis1. /// @param axis0 The fixed axis of the shear. /// @param axis1 The shear axis. /// @param shearby The shear factor. void setToShear(Axis axis0, Axis axis1, T shearby) { *this = shear >(axis0, axis1, shearby); } /// @brief Left multiplies a shearing transformation into the matrix. /// @see setToShear void preShear(Axis axis0, Axis axis1, T shear) { int index0 = static_cast(axis0); int index1 = static_cast(axis1); // to row "index1" add a multiple of the index0 row MyBase::mm[index1 * 4 + 0] += shear * MyBase::mm[index0 * 4 + 0]; MyBase::mm[index1 * 4 + 1] += shear * MyBase::mm[index0 * 4 + 1]; MyBase::mm[index1 * 4 + 2] += shear * MyBase::mm[index0 * 4 + 2]; MyBase::mm[index1 * 4 + 3] += shear * MyBase::mm[index0 * 4 + 3]; } /// @brief Right multiplies a shearing transformation into the matrix. /// @see setToShear void postShear(Axis axis0, Axis axis1, T shear) { int index0 = static_cast(axis0); int index1 = static_cast(axis1); // to collumn "index0" add a multiple of the index1 row MyBase::mm[index0 + 0] += shear * MyBase::mm[index1 + 0]; MyBase::mm[index0 + 4] += shear * MyBase::mm[index1 + 4]; MyBase::mm[index0 + 8] += shear * MyBase::mm[index1 + 8]; MyBase::mm[index0 + 12] += shear * MyBase::mm[index1 + 12]; } /// Transform a Vec4 by post-multiplication. template Vec4 transform(const Vec4 &v) const { return static_cast< Vec4 >(v * *this); } /// Transform a Vec3 by post-multiplication, without homogenous division. template Vec3 transform(const Vec3 &v) const { return static_cast< Vec3 >(v * *this); } /// Transform a Vec4 by pre-multiplication. template Vec4 pretransform(const Vec4 &v) const { return static_cast< Vec4 >(*this * v); } /// Transform a Vec3 by pre-multiplication, without homogenous division. template Vec3 pretransform(const Vec3 &v) const { return static_cast< Vec3 >(*this * v); } /// Transform a Vec3 by post-multiplication, doing homogenous divison. template Vec3 transformH(const Vec3 &p) const { T0 w; // w = p * (*this).col(3); w = p[0] * MyBase::mm[ 3] + p[1] * MyBase::mm[ 7] + p[2] * MyBase::mm[11] + MyBase::mm[15]; if ( !isExactlyEqual(w , 0.0) ) { return Vec3(static_cast((p[0] * MyBase::mm[ 0] + p[1] * MyBase::mm[ 4] + p[2] * MyBase::mm[ 8] + MyBase::mm[12]) / w), static_cast((p[0] * MyBase::mm[ 1] + p[1] * MyBase::mm[ 5] + p[2] * MyBase::mm[ 9] + MyBase::mm[13]) / w), static_cast((p[0] * MyBase::mm[ 2] + p[1] * MyBase::mm[ 6] + p[2] * MyBase::mm[10] + MyBase::mm[14]) / w)); } return Vec3(0, 0, 0); } /// Transform a Vec3 by pre-multiplication, doing homogenous division. template Vec3 pretransformH(const Vec3 &p) const { T0 w; // w = p * (*this).col(3); w = p[0] * MyBase::mm[12] + p[1] * MyBase::mm[13] + p[2] * MyBase::mm[14] + MyBase::mm[15]; if ( !isExactlyEqual(w , 0.0) ) { return Vec3(static_cast((p[0] * MyBase::mm[ 0] + p[1] * MyBase::mm[ 1] + p[2] * MyBase::mm[ 2] + MyBase::mm[ 3]) / w), static_cast((p[0] * MyBase::mm[ 4] + p[1] * MyBase::mm[ 5] + p[2] * MyBase::mm[ 6] + MyBase::mm[ 7]) / w), static_cast((p[0] * MyBase::mm[ 8] + p[1] * MyBase::mm[ 9] + p[2] * MyBase::mm[10] + MyBase::mm[11]) / w)); } return Vec3(0, 0, 0); } /// Transform a Vec3 by post-multiplication, without translation. template Vec3 transform3x3(const Vec3 &v) const { return Vec3( static_cast(v[0] * MyBase::mm[ 0] + v[1] * MyBase::mm[ 4] + v[2] * MyBase::mm[ 8]), static_cast(v[0] * MyBase::mm[ 1] + v[1] * MyBase::mm[ 5] + v[2] * MyBase::mm[ 9]), static_cast(v[0] * MyBase::mm[ 2] + v[1] * MyBase::mm[ 6] + v[2] * MyBase::mm[10])); } private: bool invert(Mat4 &inverse, T tolerance) const; T det2(const Mat4 &a, int i0, int i1, int j0, int j1) const { int i0row = i0 * 4; int i1row = i1 * 4; return a.mm[i0row+j0]*a.mm[i1row+j1] - a.mm[i0row+j1]*a.mm[i1row+j0]; } T det3(const Mat4 &a, int i0, int i1, int i2, int j0, int j1, int j2) const { int i0row = i0 * 4; return a.mm[i0row+j0]*det2(a, i1,i2, j1,j2) + a.mm[i0row+j1]*det2(a, i1,i2, j2,j0) + a.mm[i0row+j2]*det2(a, i1,i2, j0,j1); } static const Mat4 sIdentity; static const Mat4 sZero; }; // class Mat4 template const Mat4 Mat4::sIdentity = Mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); template const Mat4 Mat4::sZero = Mat4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); /// @relates Mat4 /// @brief Equality operator, does exact floating point comparisons template bool operator==(const Mat4 &m0, const Mat4 &m1) { const T0 *t0 = m0.asPointer(); const T1 *t1 = m1.asPointer(); for (int i=0; i<16; ++i) if (!isExactlyEqual(t0[i], t1[i])) return false; return true; } /// @relates Mat4 /// @brief Inequality operator, does exact floating point comparisons template bool operator!=(const Mat4 &m0, const Mat4 &m1) { return !(m0 == m1); } /// @relates Mat4 /// @brief Returns M, where \f$M_{i,j} = m_{i,j} * scalar\f$ for \f$i, j \in [0, 3]\f$ template Mat4::type> operator*(S scalar, const Mat4 &m) { return m*scalar; } /// @relates Mat4 /// @brief Returns M, where \f$M_{i,j} = m_{i,j} * scalar\f$ for \f$i, j \in [0, 3]\f$ template Mat4::type> operator*(const Mat4 &m, S scalar) { Mat4::type> result(m); result *= scalar; return result; } /// @relates Mat4 /// @brief Returns v, where \f$v_{i} = \sum_{n=0}^3 m_{i,n} * v_n \f$ for \f$i \in [0, 3]\f$ template inline Vec4::type> operator*(const Mat4 &_m, const Vec4 &_v) { MT const *m = _m.asPointer(); return Vec4::type>( _v[0]*m[0] + _v[1]*m[1] + _v[2]*m[2] + _v[3]*m[3], _v[0]*m[4] + _v[1]*m[5] + _v[2]*m[6] + _v[3]*m[7], _v[0]*m[8] + _v[1]*m[9] + _v[2]*m[10] + _v[3]*m[11], _v[0]*m[12] + _v[1]*m[13] + _v[2]*m[14] + _v[3]*m[15]); } /// @relates Mat4 /// @brief Returns v, where \f$v_{i} = \sum_{n=0}^3 m_{n,i} * v_n \f$ for \f$i \in [0, 3]\f$ template inline Vec4::type> operator*(const Vec4 &_v, const Mat4 &_m) { MT const *m = _m.asPointer(); return Vec4::type>( _v[0]*m[0] + _v[1]*m[4] + _v[2]*m[8] + _v[3]*m[12], _v[0]*m[1] + _v[1]*m[5] + _v[2]*m[9] + _v[3]*m[13], _v[0]*m[2] + _v[1]*m[6] + _v[2]*m[10] + _v[3]*m[14], _v[0]*m[3] + _v[1]*m[7] + _v[2]*m[11] + _v[3]*m[15]); } /// @relates Mat4 /// @brief Returns v, where /// \f$v_{i} = \sum_{n=0}^3\left(m_{i,n} * v_n + m_{i,3}\right)\f$ for \f$i \in [0, 2]\f$ template inline Vec3::type> operator*(const Mat4 &_m, const Vec3 &_v) { MT const *m = _m.asPointer(); return Vec3::type>( _v[0]*m[0] + _v[1]*m[1] + _v[2]*m[2] + m[3], _v[0]*m[4] + _v[1]*m[5] + _v[2]*m[6] + m[7], _v[0]*m[8] + _v[1]*m[9] + _v[2]*m[10] + m[11]); } /// @relates Mat4 /// @brief Returns v, where /// \f$v_{i} = \sum_{n=0}^3\left(m_{n,i} * v_n + m_{3,i}\right)\f$ for \f$i \in [0, 2]\f$ template inline Vec3::type> operator*(const Vec3 &_v, const Mat4 &_m) { MT const *m = _m.asPointer(); return Vec3::type>( _v[0]*m[0] + _v[1]*m[4] + _v[2]*m[8] + m[12], _v[0]*m[1] + _v[1]*m[5] + _v[2]*m[9] + m[13], _v[0]*m[2] + _v[1]*m[6] + _v[2]*m[10] + m[14]); } /// @relates Mat4 /// @brief Returns M, where \f$M_{i,j} = m0_{i,j} + m1_{i,j}\f$ for \f$i, j \in [0, 3]\f$ template Mat4::type> operator+(const Mat4 &m0, const Mat4 &m1) { Mat4::type> result(m0); result += m1; return result; } /// @relates Mat4 /// @brief Returns M, where \f$M_{i,j} = m0_{i,j} - m1_{i,j}\f$ for \f$i, j \in [0, 3]\f$ template Mat4::type> operator-(const Mat4 &m0, const Mat4 &m1) { Mat4::type> result(m0); result -= m1; return result; } /// @relates Mat4 /// @brief Returns M, where /// \f$M_{ij} = \sum_{n=0}^3\left(m0_{nj} + m1_{in}\right)\f$ for \f$i, j \in [0, 3]\f$ template Mat4::type> operator*(const Mat4 &m0, const Mat4 &m1) { Mat4::type> result(m0); result *= m1; return result; } /// Transform a Vec3 by pre-multiplication, without translation. /// Presumes this matrix is inverse of coordinate transform /// Synonymous to "pretransform3x3" template Vec3 transformNormal(const Mat4 &m, const Vec3 &n) { return Vec3( static_cast(m[0][0]*n[0] + m[0][1]*n[1] + m[0][2]*n[2]), static_cast(m[1][0]*n[0] + m[1][1]*n[1] + m[1][2]*n[2]), static_cast(m[2][0]*n[0] + m[2][1]*n[1] + m[2][2]*n[2])); } /// Invert via gauss-jordan elimination. Modified from dreamworks internal mx library template bool Mat4::invert(Mat4 &inverse, T tolerance) const { Mat4 temp(*this); inverse.setIdentity(); // Forward elimination step double det = 1.0; for (int i = 0; i < 4; ++i) { int row = i; double max = fabs(temp[i][i]); for (int k = i+1; k < 4; ++k) { if (fabs(temp[k][i]) > max) { row = k; max = fabs(temp[k][i]); } } if (isExactlyEqual(max, 0.0)) return false; // must move pivot to row i if (row != i) { det = -det; for (int k = 0; k < 4; ++k) { std::swap(temp[row][k], temp[i][k]); std::swap(inverse[row][k], inverse[i][k]); } } double pivot = temp[i][i]; det *= pivot; // scale row i for (int k = 0; k < 4; ++k) { temp[i][k] /= pivot; inverse[i][k] /= pivot; } // eliminate in rows below i for (int j = i+1; j < 4; ++j) { double t = temp[j][i]; if (!isExactlyEqual(t, 0.0)) { // subtract scaled row i from row j for (int k = 0; k < 4; ++k) { temp[j][k] -= temp[i][k] * t; inverse[j][k] -= inverse[i][k] * t; } } } } // Backward elimination step for (int i = 3; i > 0; --i) { for (int j = 0; j < i; ++j) { double t = temp[j][i]; if (!isExactlyEqual(t, 0.0)) { for (int k = 0; k < 4; ++k) { inverse[j][k] -= inverse[i][k]*t; } } } } return det*det >= tolerance*tolerance; } template inline bool isAffine(const Mat4& m) { return (m.col(3) == Vec4(0, 0, 0, 1)); } template inline bool hasTranslation(const Mat4& m) { return (m.row(3) != Vec4(0, 0, 0, 1)); } typedef Mat4 Mat4s; typedef Mat4 Mat4d; #if DWREAL_IS_DOUBLE == 1 typedef Mat4d Mat4f; #else typedef Mat4s Mat4f; #endif // DWREAL_IS_DOUBLE } // namespace math template<> inline math::Mat4s zeroVal() { return math::Mat4s::identity(); } template<> inline math::Mat4d zeroVal() { return math::Mat4d::identity(); } } // namespace OPENVDB_VERSION_NAME } // namespace openvdb #endif // OPENVDB_UTIL_MAT4_H_HAS_BEEN_INCLUDED // Copyright (c) 2012-2013 DreamWorks Animation LLC // All rights reserved. This software is distributed under the // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )