// ============================================================== // This file is part of MegaGlest (www.glest.org) // // Mathlib.inl -- Copyright (c) 2005-2006 David Henry // changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda// // ------------------------------------------------------------------- // Portions Copyright (c) Dante Treglia II and Mark A. DeLoura, 2000. // Portions Copyright (c) Fletcher Dunn and Ian Parberry, 2002. // ------------------------------------------------------------------- // // This code is licensed under the MIT license: // http://www.opensource.org/licenses/mit-license.php // // Open Source Initiative OSI - The MIT License (MIT):Licensing // // The MIT License (MIT) // Copyright (c) // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE.// // // Implementation of a math library to use with OpenGL. // // Provide vector, matrix and quaternion operations. // ///////////////////////////////////////////////////////////////////////////// #include ///////////////////////////////////////////////////////////////////////////// // // Global functions // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // wrapPi // // "Wrap" an angle in range -pi...pi by adding the correct multiple // of 2 pi // -------------------------------------------------------------------------- template inline Real wrapPi (Real theta) { theta += kPi; theta -= std::floor (theta * k1Over2Pi) * k2Pi; theta -= kPi; return theta; } // -------------------------------------------------------------------------- // safeAcos // // Same as acos(x), but if x is out of range, it is "clamped" to the nearest // valid value. The value returned is in range 0...pi, the same as the // standard C acos() function // -------------------------------------------------------------------------- template inline Real safeAcos (Real x) { // Check limit conditions if (x <= -1.0) return kPi; if (x >= 1.0) return 0.0; // value is in the domain - use standard C function return std::acos (x); } // -------------------------------------------------------------------------- // canonizeEulerAngles // // Set the Euler angle triple to its "canonical" value. This does not change // the meaning of the Euler angles as a representation of Orientation in 3D, // but if the angles are for other purposes such as angular velocities, etc, // then the operation might not be valid. // -------------------------------------------------------------------------- template inline void canonizeEulerAngles (Real &roll, Real &pitch, Real &yaw) { // First, wrap pitch in range -pi ... pi pitch = wrapPi (pitch); // Now, check for "the back side" of the matrix, pitch outside // the canonical range of -pi/2 ... pi/2 if (pitch < -kPiOver2) { roll += kPi; pitch = -kPi - pitch; yaw += kPi; } else if (pitch > kPiOver2) { roll += kPi; pitch = kPi - pitch; yaw += kPi; } // OK, now check for the Gimbal lock case (within a slight // tolerance) if (std::fabs (pitch) > kPiOver2 - 1e-4) { // We are in gimbal lock. Assign all rotation // about the vertical axis to heading yaw += roll; roll = 0.0; } else { // Not in gimbal lock. Wrap the bank angle in // canonical range roll = wrapPi (roll); } // Wrap heading in canonical range yaw = wrapPi (yaw); } ///////////////////////////////////////////////////////////////////////////// // // class Vector3 implementation. // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // Vector3::isZero // // Return true if is zero vector. // -------------------------------------------------------------------------- template inline bool Vector3::isZero () { return (_x == 0.0) && (_y == 0.0) && (_z == 0.0); } // -------------------------------------------------------------------------- // Vector3::normalize // // Set vector length to 1. // -------------------------------------------------------------------------- template inline void Vector3::normalize() { Real magSq = (_x * _x) + (_y * _y) + (_z * _z); if (magSq > 0.0) { // check for divide-by-zero Real oneOverMag = 1.0 / std::sqrt (magSq); _x *= oneOverMag; _y *= oneOverMag; _z *= oneOverMag; } } // -------------------------------------------------------------------------- // Vector3 operators // // Operator overloading for basic vector operations. // -------------------------------------------------------------------------- template inline bool Vector3::operator== (const Vector3 &v) const { return ((_x == v._x) && (_y == v._y) && (_z == v._z)); } template inline bool Vector3::operator!= (const Vector3 &v) const { return ((_x != v._x) || (_y != v._y) || (_z != v._z)); } template inline Vector3 Vector3::operator- () const { return Vector3 (-_x, -_y, -_z); } template inline Vector3 Vector3::operator+ (const Vector3 &v) const { return Vector3 (_x + v._x, _y + v._y, _z + v._z); } template inline Vector3 Vector3::operator- (const Vector3 &v) const { return Vector3 (_x - v._x, _y - v._y, _z - v._z); } template inline Vector3 Vector3::operator* (Real s) const { return Vector3 (_x * s, _y * s, _z * s); } template inline Vector3 Vector3::operator/ (Real s) const { Real oneOverS = 1.0 / s; // Note: no check for divide by zero return Vector3 (_x * oneOverS, _y * oneOverS, _z * oneOverS); } template inline Vector3 & Vector3::operator+= (const Vector3 &v) { _x += v._x; _y += v._y; _z += v._z; return *this; } template inline Vector3 & Vector3::operator-= (const Vector3 &v) { _x -= v._x; _y -= v._y; _z -= v._z; return *this; } template inline Vector3 & Vector3::operator*= (Real s) { _x *= s; _y *= s; _z *= s; return *this; } template inline Vector3 & Vector3::operator/= (Real s) { Real oneOverS = 1.0 / s; // Note: no check for divide by zero! _x *= oneOverS; _y *= oneOverS ; _z *= oneOverS; return *this; } // -------------------------------------------------------------------------- // // Nonmember Vector3 functions // // -------------------------------------------------------------------------- // Scalar on the left multiplication, for symmetry template inline Vector3 operator* (Real k, Vector3 v) { return Vector3 (k * v._x, k * v._y, k * v._z); } // Compute vector lenght template inline Real VectorMag (const Vector3 &v) { return std::sqrt ((v._x * v._x) + (v._y * v._y) + (v._z * v._z)); } // Vector3 dot product template inline Real DotProduct (const Vector3 &a, const Vector3 &b) { return ((a._x * b._x) + (a._y * b._y) + (a._z * b._z)); } // Vector3 cross product template inline Vector3 CrossProduct (const Vector3 &a, const Vector3 &b) { return Vector3 ( (a._y * b._z) - (a._z * b._y), (a._z * b._x) - (a._x * b._z), (a._x * b._y) - (a._y * b._x) ); } // Compute normal plane given three points template inline Vector3 ComputeNormal (const Vector3 &p1, const Vector3 &p2, const Vector3 &p3) { Vector3 vec1 (p1 - p2); Vector3 vec2 (p1 - p3); Vector3 result (CrossProduct (vec1, vec2)); result.normalize (); return result; } // Compute distance between two points template inline Real Distance (const Vector3 &a, const Vector3 &b) { Real dx = a._x - b._x; Real dy = a._y - b._y; Real dz = a._z - b._z; return std::sqrt ((dx * dx) + (dy * dy) + (dz * dz)); } // Compute squared distance between two points. // Useful when comparing distances, since we don't need // to square the result. template inline Real DistanceSquared (const Vector3 &a, const Vector3 &b) { Real dx = a._x - b._x; Real dy = a._y - b._y; Real dz = a._z - b._z; return ((dx * dx) + (dy * dy) + (dz * dz)); } ///////////////////////////////////////////////////////////////////////////// // // class Matrix4x4 implementation. // // -------------------------------------------------------------------------- // // MATRIX ORGANIZATION // // The purpose of this class is so that a user might perform transformations // without fiddling with plus or minus signs or transposing the matrix // until the output "looks right". But of course, the specifics of the // internal representation is important. Not only for the implementation // in this file to be correct, but occasionally direct access to the // matrix variables is necessary, or beneficial for optimization. Thus, // we document our matrix conventions here. // // Strict adherance to linear algebra rules dictates that the // multiplication of a 4x4 matrix by a 3D vector is actually undefined. // To circumvent this, we can consider the input and output vectors as // having an assumed fourth coordinate of 1. Also, since the rightmost // column is [ 0 0 0 1 ], we can simplificate calculations ignoring // this last column. This is shown below: // // | m11 m12 m13 0 | | x | | x'| // | m21 m22 m23 0 | | y | = | y'| // | m31 m32 m33 0 | | z | | z'| // | tx ty tz 1 | | 1 | | 1 | // // We use row vectors to represent the right, up and forward vectors // in the 4x4 matrix. OpenGL uses column vectors, but the elements of // an OpenGL matrix are ordered in columns so that m[i][j] is in row j // and column i. This is the reverse of the standard C convention in // which m[i][j] is in row i and column j. The matrix should be // transposed before being sent to OpenGL. // // | m11 m21 m31 tx | | m0 m4 m8 m12 | | m0 m1 m2 m3 | // | m12 m22 m32 ty | | m1 m5 m9 m13 | | m4 m5 m6 m7 | // | m13 m23 m33 tz | | m2 m6 m10 m14 | | m8 m9 m10 m11 | // | 0 0 0 tw | | m3 m7 m11 m15 | | m12 m13 m14 m15 | // // OpenGL style OpenGL matrix standard C // arrangement convention // // Fortunately, accessing to the raw matrix data via the _m[] array gives // us the transpose matrix; i.e. in OpenGL form, so that we can directly use // it with glLoadMatrix{fd}() or glMultMatrix{fd}(). // // Also, since the rightmost column (in standard C form) should always // be [ 0 0 0 1 ], and sice these values (_h14, _h24, _h34 and _tw) are // initialized in constructors, we don't need to modify them in our // matrix operations, so we don't perform useless calculations... // // The right-hand rule is used to define "positive" rotation. // // +y +x' // | | // | | // |______ +x +y' ______| // / / // / / // +z +z' // // initial position Positive rotation of // pi/2 around z-axis // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // Matrix4x4::identity // // Set matrix to identity. // -------------------------------------------------------------------------- template inline void Matrix4x4::identity () { _m11 = 1.0; _m21 = 0.0; _m31 = 0.0; _tx = 0.0; _m12 = 0.0; _m22 = 1.0; _m32 = 0.0; _ty = 0.0; _m13 = 0.0; _m23 = 0.0; _m33 = 1.0; _tz = 0.0; _h14 = 0.0; _h24 = 0.0; _h34 = 0.0; _tw = 1.0; } // -------------------------------------------------------------------------- // Matrix4x4::transpose // // Transpose the current matrix. // -------------------------------------------------------------------------- template inline void Matrix4x4::transpose () { *this = Transpose (*this); } // -------------------------------------------------------------------------- // Matrix4x4::invert // // Invert the current matrix. // -------------------------------------------------------------------------- template inline void Matrix4x4::invert () { *this = Invert (*this); } // -------------------------------------------------------------------------- // Matrix4x4::setTranslation // // Set the translation portion of the matrix. // -------------------------------------------------------------------------- template inline void Matrix4x4::setTranslation (const Vector3 &v) { _tx = v._x; _ty = v._y; _tz = v._z; } // -------------------------------------------------------------------------- // Matrix4x4::transform // // Transform a point by the matrix. // -------------------------------------------------------------------------- template inline void Matrix4x4::transform (Vector3 &v) const { // Grind through the linear algebra. v = Vector3 ( (v._x * _m11) + (v._y * _m21) + (v._z * _m31) + _tx, (v._x * _m12) + (v._y * _m22) + (v._z * _m32) + _ty, (v._x * _m13) + (v._y * _m23) + (v._z * _m33) + _tz ); } // -------------------------------------------------------------------------- // Matrix4x4::rotate // // Rotate a point by the 3x3 upper left portion of the matrix. // -------------------------------------------------------------------------- template inline void Matrix4x4::rotate (Vector3 &v) const { v = Vector3 ( (v._x * _m11) + (v._y * _m21) + (v._z * _m31), (v._x * _m12) + (v._y * _m22) + (v._z * _m32), (v._x * _m13) + (v._y * _m23) + (v._z * _m33) ); } // -------------------------------------------------------------------------- // Matrix4x4::inverseRotate // // Rotate a point by the inverse 3x3 upper left portion of the matrix. // -------------------------------------------------------------------------- template inline void Matrix4x4::inverseRotate (Vector3 &v) const { v = Vector3 ( (v._x * _m11) + (v._y * _m12) + (v._z * _m13), (v._x * _m21) + (v._y * _m22) + (v._z * _m23), (v._x * _m31) + (v._y * _m32) + (v._z * _m33) ); } // -------------------------------------------------------------------------- // Matrix4x4::inverseRotate // // Translate a point by the inverse matrix. // -------------------------------------------------------------------------- template inline void Matrix4x4::inverseTranslate (Vector3 &v) const { v._x -= _tx; v._y -= _ty; v._z -= _tz; } // -------------------------------------------------------------------------- // Matrix4x4::fromQuaternion // // Convert a quaternion to a matrix. // -------------------------------------------------------------------------- template inline void Matrix4x4::fromQuaternion (const Quaternion &q) { // Compute a few values to optimize common subexpressions Real ww = 2.0 * q._w; Real xx = 2.0 * q._x; Real yy = 2.0 * q._y; Real zz = 2.0 * q._z; // Set the matrix elements. There is still a little more // opportunity for optimization due to the many common // subexpressions. We'll let the compiler handle that... _m11 = 1.0 - (yy * q._y) - (zz * q._z); _m12 = (xx * q._y) + (ww * q._z); _m13 = (xx * q._z) - (ww * q._y); _m21 = (xx * q._y) - (ww * q._z); _m22 = 1.0 - (xx * q._x) - (zz * q._z); _m23 = (yy * q._z) + (ww * q._x); _m31 = (xx * q._z) + (ww * q._y); _m32 = (yy * q._z) - (ww * q._x); _m33 = 1.0 - (xx * q._x) - (yy * q._y); // Reset the translation portion _tx = _ty = _tz = 0.0; } // -------------------------------------------------------------------------- // Matrix4x4::fromEulerAngles // // Setup a rotation matrix, given three X-Y-Z rotation angles. The // rotations are performed first on x-axis, then y-axis and finaly z-axis. // -------------------------------------------------------------------------- template inline void Matrix4x4::fromEulerAngles (Real x, Real y, Real z) { // Fetch sine and cosine of angles Real cx = std::cos (x); Real sx = std::sin (x); Real cy = std::cos (y); Real sy = std::sin (y); Real cz = std::cos (z); Real sz = std::sin (z); Real sxsy = sx * sy; Real cxsy = cx * sy; // Fill in the matrix elements _m11 = (cy * cz); _m12 = (sxsy * cz) + (cx * sz); _m13 = -(cxsy * cz) + (sx * sz); _m21 = -(cy * sz); _m22 = -(sxsy * sz) + (cx * cz); _m23 = (cxsy * sz) + (sx * cz); _m31 = (sy); _m32 = -(sx * cy); _m33 = (cx * cy); // Reset the translation portion _tx = _ty = _tz = 0.0; } // -------------------------------------------------------------------------- // Matrix4x4::toEulerAngles // // Setup the euler angles in radians, given a rotation matrix. The rotation // matrix could have been obtained from euler angles given the expression: // M = X.Y.Z // where X, Y and Z are rotation matrices about X, Y and Z axes. // This is the "opposite" of the fromEulerAngles function. // -------------------------------------------------------------------------- template inline void Matrix4x4::toEulerAngles (Real &x, Real &y, Real &z) const { // Compute Y-axis angle y = std::asin (_m31); // Compute cos and one over cos for optimization Real cy = std::cos (y); Real oneOverCosY = 1.0 / cy; if (std::fabs (cy) > 0.001) { // No gimball lock x = std::atan2 (-_m32 * oneOverCosY, _m33 * oneOverCosY); z = std::atan2 (-_m21 * oneOverCosY, _m11 * oneOverCosY); } else { // Gimbal lock case x = 0.0; z = std::atan2 (_m12, _m22); } } // -------------------------------------------------------------------------- // Matrix4x4::rightVector // Matrix4x4::upVector // Matrix4x4::forwardVector // Matrix4x4::translationVector // // Return a base vector from the matrix. // -------------------------------------------------------------------------- template inline Vector3 Matrix4x4::rightVector () const { return Vector3 (_m11, _m12, _m13); } template inline Vector3 Matrix4x4::upVector () const { return Vector3 (_m21, _m22, _m23); } template inline Vector3 Matrix4x4::forwardVector () const { return Vector3 (_m31, _m32, _m33); } template inline Vector3 Matrix4x4::translationVector () const { return Vector3 (_tx, _ty, _tz); } // -------------------------------------------------------------------------- // // Nonmember Matrix4x4 functions // // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // Matrix4x4 * Matrix4x4 // // Matrix concatenation. // // We also provide a *= operator, as per C convention. // -------------------------------------------------------------------------- template inline Matrix4x4 operator* (const Matrix4x4 &a, const Matrix4x4 &b) { Matrix4x4 res; // Compute the left 4x3 (linear transformation) portion res._m11 = (a._m11 * b._m11) + (a._m21 * b._m12) + (a._m31 * b._m13); res._m12 = (a._m12 * b._m11) + (a._m22 * b._m12) + (a._m32 * b._m13); res._m13 = (a._m13 * b._m11) + (a._m23 * b._m12) + (a._m33 * b._m13); res._m21 = (a._m11 * b._m21) + (a._m21 * b._m22) + (a._m31 * b._m23); res._m22 = (a._m12 * b._m21) + (a._m22 * b._m22) + (a._m32 * b._m23); res._m23 = (a._m13 * b._m21) + (a._m23 * b._m22) + (a._m33 * b._m23); res._m31 = (a._m11 * b._m31) + (a._m21 * b._m32) + (a._m31 * b._m33); res._m32 = (a._m12 * b._m31) + (a._m22 * b._m32) + (a._m32 * b._m33); res._m33 = (a._m13 * b._m31) + (a._m23 * b._m32) + (a._m33 * b._m33); // Compute the translation portion res._tx = (a._m11 * b._tx) + (a._m21 * b._ty) + (a._m31 * b._tz) + a._tx; res._ty = (a._m12 * b._tx) + (a._m22 * b._ty) + (a._m32 * b._tz) + a._ty; res._tz = (a._m13 * b._tx) + (a._m23 * b._ty) + (a._m33 * b._tz) + a._tz; return res; } template inline Matrix4x4 & operator*= (Matrix4x4 &a, const Matrix4x4 &b) { a = a * b; return a; } // -------------------------------------------------------------------------- // Matrix4x4 * Vector3 // // Transform a point by a matrix. This makes using the vector class look // like it does with linear algebra notation on paper. // -------------------------------------------------------------------------- template inline Vector3 operator* (const Matrix4x4 &m, const Vector3 &p) { Vector3 res (p); m.transform (res); return res; } // -------------------------------------------------------------------------- // Transpose // // Return the transpose matrix. // -------------------------------------------------------------------------- template inline Matrix4x4 Transpose (const Matrix4x4 &m) { Matrix4x4 res; res._m11 = m._m11; res._m21 = m._m12; res._m31 = m._m13; res._tx = m._h14; res._m12 = m._m21; res._m22 = m._m22; res._m32 = m._m23; res._ty = m._h24; res._m13 = m._m31; res._m23 = m._m32; res._m33 = m._m33; res._tz = m._h34; res._h14 = m._tx; res._h24 = m._ty; res._h34 = m._tz; res._tw = m._tw; return res; } // -------------------------------------------------------------------------- // Determinant // // Compute the determinant of the 3x3 portion of the matrix. // -------------------------------------------------------------------------- template inline static Real Determinant3x3 (const Matrix4x4 &m) { return m._m11 * ((m._m22 * m._m33) - (m._m23 * m._m32)) + m._m12 * ((m._m23 * m._m31) - (m._m21 * m._m33)) + m._m13 * ((m._m21 * m._m32) - (m._m22 * m._m31)); } // -------------------------------------------------------------------------- // Invert // // Compute the inverse of a matrix. We use the classical adjoint divided // by the determinant method. // -------------------------------------------------------------------------- template inline Matrix4x4 Invert (const Matrix4x4 &m) { // Compute the determinant of the 3x3 portion Real det = Determinant3x3 (m); // If we're singular, then the determinant is zero and there's // no inverse assert (std::fabs (det) > 0.000001); // Compute one over the determinant, so we divide once and // can *multiply* per element Real oneOverDet = 1.0 / det; // Compute the 3x3 portion of the inverse, by // dividing the adjoint by the determinant Matrix4x4 res; res._m11 = ((m._m22 * m._m33) - (m._m23 * m._m32)) * oneOverDet; res._m12 = ((m._m13 * m._m32) - (m._m12 * m._m33)) * oneOverDet; res._m13 = ((m._m12 * m._m23) - (m._m13 * m._m22)) * oneOverDet; res._m21 = ((m._m23 * m._m31) - (m._m21 * m._m33)) * oneOverDet; res._m22 = ((m._m11 * m._m33) - (m._m13 * m._m31)) * oneOverDet; res._m23 = ((m._m13 * m._m21) - (m._m11 * m._m23)) * oneOverDet; res._m31 = ((m._m21 * m._m32) - (m._m22 * m._m31)) * oneOverDet; res._m32 = ((m._m12 * m._m31) - (m._m11 * m._m32)) * oneOverDet; res._m33 = ((m._m11 * m._m22) - (m._m12 * m._m21)) * oneOverDet; // Compute the translation portion of the inverse res._tx = -((m._tx * res._m11) + (m._ty * res._m21) + (m._tz * res._m31)); res._ty = -((m._tx * res._m12) + (m._ty * res._m22) + (m._tz * res._m32)); res._tz = -((m._tx * res._m13) + (m._ty * res._m23) + (m._tz * res._m33)); // Return it. return res; } // -------------------------------------------------------------------------- // RotationMatrix // // Setup the matrix to perform a rotation about one of the three cardinal // X-Y-Z axes. // // The axis of rotation is specified by the 1-based "axis" index. // // theta is the amount of rotation, in radians. The right-hand rule is // used to define "positive" rotation. // // The translation portion is reset. // -------------------------------------------------------------------------- template inline Matrix4x4 RotationMatrix (Axis axis, Real theta) { Matrix4x4 res; // Get sin and cosine of rotation angle Real s = std::sin (theta); Real c = std::cos (theta); // Check which axis they are rotating about switch (axis) { case kXaxis: // Rotate about the x-axis res._m11 = 1.0; res._m21 = 0.0; res._m31 = 0.0; res._m12 = 0.0; res._m22 = c; res._m32 = -s; res._m13 = 0.0; res._m23 = s; res._m33 = c; break; case kYaxis: // Rotate about the y-axis res._m11 = c; res._m21 = 0.0; res._m31 = s; res._m12 = 0.0; res._m22 = 1.0; res._m32 = 0.0; res._m13 = -s; res._m23 = 0.0; res._m33 = c; break; case kZaxis: // Rotate about the z-axis res._m11 = c; res._m21 = -s; res._m31 = 0.0; res._m12 = s; res._m22 = c; res._m32 = 0.0; res._m13 = 0.0; res._m23 = 0.0; res._m33 = 1.0; break; default: // bogus axis index assert (false); } // Reset the translation portion res._tx = res._ty = res._tz = 0.0; return res; } //--------------------------------------------------------------------------- // AxisRotationMatrix // // Setup the matrix to perform a rotation about an arbitrary axis. // The axis of rotation must pass through the origin. // // axis defines the axis of rotation, and must be a unit vector. // // theta is the amount of rotation, in radians. The right-hand rule is // used to define "positive" rotation. // // The translation portion is reset. // -------------------------------------------------------------------------- template inline Matrix4x4 RotationMatrix (const Vector3 &axis, Real theta) { Matrix4x4 res; // Quick sanity check to make sure they passed in a unit vector // to specify the axis assert (std::fabs (DotProduct (axis, axis) - 1.0) < 0.001); // Get sin and cosine of rotation angle Real s = std::sin (theta); Real c = std::cos (theta); // Compute 1 - cos(theta) and some common subexpressions Real a = 1.0 - c; Real ax = a * axis._x; Real ay = a * axis._y; Real az = a * axis._z; // Set the matrix elements. There is still a little more // opportunity for optimization due to the many common // subexpressions. We'll let the compiler handle that... res._m11 = (ax * axis._x) + c; res._m12 = (ax * axis._y) + (axis._z * s); res._m13 = (ax * axis._z) - (axis._y * s); res._m21 = (ay * axis._x) - (axis._z * s); res._m22 = (ay * axis._y) + c; res._m23 = (ay * axis._z) + (axis._x * s); res._m31 = (az * axis._x) + (axis._y * s); res._m32 = (az * axis._y) - (axis._x * s); res._m33 = (az * axis._z) + c; // Reset the translation portion res._tx = res._ty = res._tz = 0.0; return res; } // -------------------------------------------------------------------------- // TranslationMatrix // // Build a translation matrix given a translation vector. // -------------------------------------------------------------------------- template inline Matrix4x4 TranslationMatrix (Real x, Real y, Real z) { return TranslationMatrix (Vector3 (x, y, z)); } template inline Matrix4x4 TranslationMatrix (const Vector3 &v) { Matrix4x4 res; res._m11 = 1.0; res._m21 = 0.0; res._m31 = 0.0; res._tx = v._x; res._m12 = 0.0; res._m22 = 1.0; res._m32 = 0.0; res._ty = v._y; res._m13 = 0.0; res._m23 = 0.0; res._m33 = 1.0; res._tz = v._z; return res; } // -------------------------------------------------------------------------- // ScaleMatrix // // Setup the matrix to perform scale on each axis. For uniform scale by k, // use a vector of the form Vector3( k, k, k ). // // The translation portion is reset. // -------------------------------------------------------------------------- template inline Matrix4x4 ScaleMatrix (const Vector3 &s) { Matrix4x4 res; // Set the matrix elements. Pretty straightforward res._m11 = s._x; res._m21 = 0.0; res._m31 = 0.0; res._m12 = 0.0; res._m22 = s._y; res._m32 = 0.0; res._m13 = 0.0; res._m23 = 0.0; res._m33 = s._z; // Reset the translation portion res._tx = res._ty = res._tz = 0.0; return res; } // -------------------------------------------------------------------------- // ScaleAlongAxisMatrix // // Setup the matrix to perform scale along an arbitrary axis. // // The axis is specified using a unit vector. // // The translation portion is reset. // -------------------------------------------------------------------------- template inline Matrix4x4 ScaleAlongAxisMatrix (const Vector3 &axis, Real k) { Matrix4x4 res; // Quick sanity check to make sure they passed in a unit vector // to specify the axis assert (std::fabs (DotProduct (axis, axis) - 1.0) < 0.001); // Compute k-1 and some common subexpressions Real a = k - 1.0; Real ax = a * axis._x; Real ay = a * axis._y; Real az = a * axis._z; // Fill in the matrix elements. We'll do the common // subexpression optimization ourselves here, since diagonally // opposite matrix elements are equal res._m11 = (ax * axis._x) + 1.0; res._m22 = (ay * axis._y) + 1.0; res._m32 = (az * axis._z) + 1.0; res._m12 = res._m21 = (ax * axis._y); res._m13 = res._m31 = (ax * axis._z); res._m23 = res._m32 = (ay * axis._z); // Reset the translation portion res._tx = res._ty = res._tz = 0.0; return res; } // -------------------------------------------------------------------------- // ShearMatrix // // Setup the matrix to perform a shear // // The type of shear is specified by the 1-based "axis" index. The effect // of transforming a point by the matrix is described by the pseudocode // below: // // xAxis => y += s * x, z += t * x // yAxis => x += s * y, z += t * y // zAxis => x += s * z, y += t * z // // The translation portion is reset. // -------------------------------------------------------------------------- template inline Matrix4x4 ShearMatrix (Axis axis, Real s, Real t) { Matrix4x4 res; // Check which type of shear they want switch (axis) { case kXaxis: // Shear y and z using x res._m11 = 1.0; res._m21 = 0.0; res._m31 = 0.0; res._m12 = s; res._m22 = 1.0; res._m32 = 0.0; res._m13 = t; res._m23 = 0.0; res._m33 = 1.0; break; case kYaxis: // Shear x and z using y res._m11 = 1.0; res._m21 = s; res._m31 = 0.0; res._m12 = 0.0; res._m22 = 1.0; res._m32 = 0.0; res._m13 = 0.0; res._m23 = t; res._m33 = 1.0; break; case kZaxis: // Shear x and y using z res._m11 = 1.0; res._m21 = 0.0; res._m31 = s; res._m12 = 0.0; res._m22 = 1.0; res._m32 = t; res._m13 = 0.0; res._m23 = 0.0; res._m33 = 1.0; break; default: // bogus axis index assert (false); } // Reset the translation portion res._tx = res._ty = res._tz = 0.0; return res; } // -------------------------------------------------------------------------- // ProjectionMatrix // // Setup the matrix to perform a projection onto a plane passing // through the origin. The plane is perpendicular to the // unit vector n. // -------------------------------------------------------------------------- template inline Matrix4x4 ProjectionMatrix (const Vector3 &n) { Matrix4x4 res; // Quick sanity check to make sure they passed in a unit vector // to specify the axis assert (std::fabs (DotProduct (n, n) - 1.0) < 0.001); // Fill in the matrix elements. We'll do the common // subexpression optimization ourselves here, since diagonally // opposite matrix elements are equal res._m11 = 1.0 - (n._x * n._x); res._m22 = 1.0 - (n._y * n._y); res._m33 = 1.0 - (n._z * n._z); res._m12 = res._m21 = -(n._x * n._y); res._m13 = res._m31 = -(n._x * n._z); res._m23 = res._m32 = -(n._y * n._z); // Reset the translation portion res._tx = res._ty = res._tz = 0.0; return res; } // -------------------------------------------------------------------------- // ReflectionMatrix // // Setup the matrix to perform a reflection about a plane parallel // to a cardinal plane. // // axis is a 1-based index which specifies the plane to project about: // // xAxis => reflect about the plane x=k // yAxis => reflect about the plane y=k // zAxis => reflect about the plane z=k // // The translation is set appropriately, since translation must occur if // k != 0 // -------------------------------------------------------------------------- template inline Matrix4x4 ReflectionMatrix (Axis axis, Real k) { Matrix4x4 res; // Check which plane they want to reflect about switch (axis) { case kXaxis: // Reflect about the plane x=k res._m11 = -1.0; res._m21 = 0.0; res._m31 = 0.0; res._tx = 2.0 * k; res._m12 = 0.0; res._m22 = 1.0; res._m32 = 0.0; res._ty = 0.0; res._m13 = 0.0; res._m23 = 0.0; res._m33 = 1.0; res._tz = 0.0; break; case kYaxis: // Reflect about the plane y=k res._m11 = 1.0; res._m21 = 0.0; res._m31 = 0.0; res._tx = 0.0; res._m12 = 0.0; res._m22 = -1.0; res._m32 = 0.0; res._ty = 2.0 * k; res._m13 = 0.0; res._m23 = 0.0; res._m33 = 1.0; res._tz = 0.0; break; case kZaxis: // Reflect about the plane z=k res._m11 = 1.0; res._m21 = 0.0; res._m31 = 0.0; res._tx = 0.0; res._m12 = 0.0; res._m22 = 1.0; res._m32 = 0.0; res._ty = 0.0; res._m13 = 0.0; res._m23 = 0.0; res._m33 = -1.0; res._tz = 2.0 * k; break; default: // bogus axis index assert (false); } return res; } // -------------------------------------------------------------------------- // AxisReflectionMatrix // // Setup the matrix to perform a reflection about an arbitrary plane // through the origin. The unit vector n is perpendicular to the plane. // // The translation portion is reset. // -------------------------------------------------------------------------- template inline Matrix4x4 AxisReflectionMatrix (const Vector3 &n) { Matrix4x4 res; // Quick sanity check to make sure they passed in a unit vector // to specify the axis assert (std::fabs (DotProduct (n, n) - 1.0) < 0.001); // Compute common subexpressions Real ax = -2.0 * n._x; Real ay = -2.0 * n._y; Real az = -2.0 * n._z; // Fill in the matrix elements. We'll do the common // subexpression optimization ourselves here, since diagonally // opposite matrix elements are equal res._m11 = 1.0 + (ax * n._x); res._m22 = 1.0 + (ay * n._y); res._m32 = 1.0 + (az * n._z); res._m12 = res._m21 = (ax * n._y); res._m13 = res._m31 = (ax * n._z); res._m23 = res._m32 = (ay * n._z); // Reset the translation portion res._tx = res._ty = res._tz = 0.00; return res; } // -------------------------------------------------------------------------- // LookAtMatrix // // Setup the matrix to perform a "Look At" transformation like a first // person camera. // -------------------------------------------------------------------------- template inline Matrix4x4 LookAtMatrix (const Vector3 &camPos, const Vector3 &target, const Vector3 &camUp) { Matrix4x4 rot, trans; Vector3 forward (camPos - target); forward.normalize (); Vector3 right (CrossProduct (camUp, forward)); Vector3 up (CrossProduct (forward, right)); right.normalize (); up.normalize (); rot._m11 = right._x; rot._m21 = right._y; rot._m31 = right._z; rot._m12 = up._x; rot._m22 = up._y; rot._m32 = up._z; rot._m13 = forward._x; rot._m23 = forward._y; rot._m33 = forward._z; rot._tx = 0.0; rot._ty = 0.0; rot._tz = 0.0; trans = TranslationMatrix (-camPos); return (rot * trans); } // -------------------------------------------------------------------------- // FrustumMatrix // // Setup a frustum matrix given the left, right, bottom, top, near, and far // values for the frustum boundaries. // -------------------------------------------------------------------------- template inline Matrix4x4 FrustumMatrix (Real l, Real r, Real b, Real t, Real n, Real f) { assert (n >= 0.0); assert (f >= 0.0); Matrix4x4 res; Real width = r - l; Real height = t - b; Real depth = f - n; res._m[0] = (2 * n) / width; res._m[1] = 0.0; res._m[2] = 0.0; res._m[3] = 0.0; res._m[4] = 0.0; res._m[5] = (2 * n) / height; res._m[6] = 0.0; res._m[7] = 0.0; res._m[8] = (r + l) / width; res._m[9] = (t + b) / height; res._m[10]= -(f + n) / depth; res._m[11]= -1.0; res._m[12]= 0.0; res._m[13]= 0.0; res._m[14]= -(2 * f * n) / depth; res._m[15]= 0.0; return res; } // -------------------------------------------------------------------------- // PerspectiveMatrix // // Setup a perspective matrix given the field-of-view in the Y direction // in degrees, the aspect ratio of Y/X, and near and far plane distances. // -------------------------------------------------------------------------- template inline Matrix4x4 PerspectiveMatrix (Real fovY, Real aspect, Real n, Real f) { Matrix4x4 res; Real angle; Real cot; angle = fovY / 2.0; angle = degToRad (angle); cot = std::cos (angle) / std::sin (angle); res._m[0] = cot / aspect; res._m[1] = 0.0; res._m[2] = 0.0; res._m[3] = 0.0; res._m[4] = 0.0; res._m[5] = cot; res._m[6] = 0.0; res._m[7] = 0.0; res._m[8] = 0.0; res._m[9] = 0.0; res._m[10]= -(f + n) / (f - n); res._m[11]= -1.0; res._m[12]= 0.0; res._m[13]= 0.0; res._m[14]= -(2 * f * n) / (f - n); res._m[15]= 0.0; return res; } // -------------------------------------------------------------------------- // OrthoMatrix // // Setup an orthographic Matrix4x4 given the left, right, bottom, top, near, // and far values for the frustum boundaries. // -------------------------------------------------------------------------- template inline Matrix4x4 OrthoMatrix (Real l, Real r, Real b, Real t, Real n, Real f) { Matrix4x4 res; Real width = r - l; Real height = t - b; Real depth = f - n; res._m[0] = 2.0 / width; res._m[1] = 0.0; res._m[2] = 0.0; res._m[3] = 0.0; res._m[4] = 0.0; res._m[5] = 2.0 / height; res._m[6] = 0.0; res._m[7] = 0.0; res._m[8] = 0.0; res._m[9] = 0.0; res._m[10]= -2.0 / depth; res._m[11]= 0.0; res._m[12]= -(r + l) / width; res._m[13]= -(t + b) / height; res._m[14]= -(f + n) / depth; res._m[15]= 1.0; return res; } // -------------------------------------------------------------------------- // OrthoNormalMatrix // // Setup an orientation matrix using 3 basis normalized vectors. // -------------------------------------------------------------------------- template inline Matrix4x4 OrthoNormalMatrix (const Vector3 &xdir, const Vector3 &ydir, const Vector3 &zdir) { Matrix4x4 res; res._m[0] = xdir._x; res._m[4] = ydir._x; res._m[8] = zdir._x; res._m[12] = 0.0; res._m[1] = xdir._y; res._m[5] = ydir._y; res._m[9] = zdir._y; res._m[13] = 0.0; res._m[2] = xdir._z; res._m[6] = ydir._z; res._m[10]= zdir._z; res._m[14] = 0.0; res._m[3] = 0.0; res._m[7] = 0.0; res._m[11]= 0.0; res._m[15] = 1.0; return res; } ///////////////////////////////////////////////////////////////////////////// // // class Quaternion implementation. // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // Quaternion::identity // // Set to identity // -------------------------------------------------------------------------- template inline void Quaternion::identity () { _w = 1.0; _x = _y = _z = 0.0; } // -------------------------------------------------------------------------- // Quaternion::normalize // // "Normalize" a quaternion. Note that normally, quaternions // are always normalized (within limits of numerical precision). // // This function is provided primarily to combat floating point "error // creep", which can occur when many successive quaternion operations // are applied. // -------------------------------------------------------------------------- template inline void Quaternion::normalize () { // Compute magnitude of the quaternion Real mag = std::sqrt ((_w * _w) + (_x * _x) + (_y * _y) + (_z * _z)); // Check for bogus length, to protect against divide by zero if (mag > 0.0) { // Normalize it Real oneOverMag = 1.0 / mag; _w *= oneOverMag; _x *= oneOverMag; _y *= oneOverMag; _z *= oneOverMag; } } // -------------------------------------------------------------------------- // Quaternion::computeW // // Compute the W component of a unit quaternion given its x,y,z components. // -------------------------------------------------------------------------- template inline void Quaternion::computeW () { Real t = 1.0 - (_x * _x) - (_y * _y) - (_z * _z); if (t < 0.0) _w = 0.0; else _w = -std::sqrt (t); } // -------------------------------------------------------------------------- // Quaternion::rotate // // Rotate a point by quaternion. v' = q.p.q*, where p = <0, v>. // -------------------------------------------------------------------------- template inline void Quaternion::rotate (Vector3 &v) const { Quaternion qf = *this * v * ~(*this); v._x = qf._x; v._y = qf._y; v._z = qf._z; } // -------------------------------------------------------------------------- // Quaternion::fromMatrix // // Setup the quaternion to perform a rotation, given the angular displacement // in matrix form. // -------------------------------------------------------------------------- template inline void Quaternion::fromMatrix (const Matrix4x4 &m) { Real trace = m._m11 + m._m22 + m._m33 + 1.0; if (trace > 0.0001) { Real s = 0.5 / std::sqrt (trace); _w = 0.25 / s; _x = (m._m23 - m._m32) * s; _y = (m._m31 - m._m13) * s; _z = (m._m12 - m._m21) * s; } else { if ((m._m11 > m._m22) && (m._m11 > m._m33)) { Real s = 0.5 / std::sqrt (1.0 + m._m11 - m._m22 - m._m33); _x = 0.25 / s; _y = (m._m21 + m._m12) * s; _z = (m._m31 + m._m13) * s; _w = (m._m32 - m._m23) * s; } else if (m._m22 > m._m33) { Real s = 0.5 / std::sqrt (1.0 + m._m22 - m._m11 - m._m33); _x = (m._m21 + m._m12) * s; _y = 0.25 / s; _z = (m._m32 + m._m23) * s; _w = (m._m31 - m._m13) * s; } else { Real s = 0.5 / std::sqrt (1.0 + m._m33 - m._m11 - m._m22); _x = (m._m31 + m._m13) * s; _y = (m._m32 + m._m23) * s; _z = 0.25 / s; _w = (m._m21 - m._m12) * s; } } } // -------------------------------------------------------------------------- // Quaternion::fromEulerAngles // // Setup the quaternion to perform an object->inertial rotation, given the // orientation in XYZ-Euler angles format. x,y,z parameters must be in // radians. // -------------------------------------------------------------------------- template inline void Quaternion::fromEulerAngles (Real x, Real y, Real z) { // Compute sine and cosine of the half angles Real sr = std::sin (x * 0.5); Real cr = std::cos (x * 0.5); Real sp = std::sin (y * 0.5); Real cp = std::cos (y * 0.5); Real sy = std::sin (z * 0.5); Real cy = std::cos (z * 0.5); // Compute values _w = (cy * cp * cr) + (sy * sp * sr); _x = -(sy * sp * cr) + (cy * cp * sr); _y = (cy * sp * cr) + (sy * cp * sr); _z = -(cy * sp * sr) + (sy * cp * cr); } // -------------------------------------------------------------------------- // Quaternion::toEulerAngles // // Setup the Euler angles, given an object->inertial rotation quaternion. // Returned x,y,z are in radians. // -------------------------------------------------------------------------- template inline void Quaternion::toEulerAngles (Real &x, Real &y, Real &z) const { // Compute Y-axis angle y = std::asin (2.0 * ((_x * _z) + (_w * _y))); // Compute cos and one over cos for optimization Real cy = std::cos (y); Real oneOverCosY = 1.0 / cy; if (std::fabs (cy) > 0.001) { // No gimball lock x = std::atan2 (2.0 * ((_w * _x) - (_y * _z)) * oneOverCosY, (1.0 - 2.0 * (_x*_x + _y*_y)) * oneOverCosY); z = std::atan2 (2.0 * ((_w * _z) - (_x * _y)) * oneOverCosY, (1.0 - 2.0 * (_y*_y + _z*_z)) * oneOverCosY); } else { // Gimbal lock case x = 0.0; z = std::atan2 (2.0 * ((_x * _y) + (_w * _z)), 1.0 - 2.0 * (_x*_x + _z*_z)); } } // -------------------------------------------------------------------------- // Quaternion::getRotationAngle // // Return the rotation angle theta (in radians). // -------------------------------------------------------------------------- template inline Real Quaternion::rotationAngle () const { // Compute the half angle. Remember that w = cos(theta / 2) Real thetaOver2 = safeAcos (_w); // Return the rotation angle return thetaOver2 * 2.0; } // -------------------------------------------------------------------------- // Quaternion::getRotationAxis // // Return the rotation axis. // -------------------------------------------------------------------------- template inline Vector3 Quaternion::rotationAxis () const { // Compute sin^2(theta/2). Remember that w = cos(theta/2), // and sin^2(x) + cos^2(x) = 1 Real sinThetaOver2Sq = 1.0 - (_w * _w); // Protect against numerical imprecision if (sinThetaOver2Sq <= 0.0) { // Identity quaternion, or numerical imprecision. Just // return any valid vector, since it doesn't matter return Vector3 (1.0, 0.0, 0.0); } // Compute 1 / sin(theta/2) Real oneOverSinThetaOver2 = 1.0 / std::sqrt (sinThetaOver2Sq); // Return axis of rotation return Vector3 ( _x * oneOverSinThetaOver2, _y * oneOverSinThetaOver2, _z * oneOverSinThetaOver2 ); } // -------------------------------------------------------------------------- // Quaternion operators // // Operator overloading for basic quaternion operations. // -------------------------------------------------------------------------- template inline Quaternion Quaternion::operator+ (const Quaternion &q) const { return Quaternion (_w + q._w, _x + q._x, _y + q._y, _z + q._z); } template inline Quaternion & Quaternion::operator+= (const Quaternion &q) { _w += q._w; _x += q._x; _y += q._y; _z += q._z; return *this; } template inline Quaternion Quaternion::operator- (const Quaternion &q) const { return Quaternion (_w - q._w, _x - q._x, _y - q._y, _z - q._z); } template inline Quaternion & Quaternion::operator-= (const Quaternion &q) { _w -= q._w; _x -= q._x; _y -= q._y; _z -= q._z; return *this; } // Quaternion multiplication. The order of multiplication, from left // to right, corresponds to the order of concatenated rotations. // NOTE: Quaternion multiplication is NOT commutative, p * q != q * p template inline Quaternion Quaternion::operator* (const Quaternion &q) const { // We use the Grassman product formula: // pq = return Quaternion ( (_w * q._w) - (_x * q._x) - (_y * q._y) - (_z * q._z), (_x * q._w) + (_w * q._x) + (_y * q._z) - (_z * q._y), (_y * q._w) + (_w * q._y) + (_z * q._x) - (_x * q._z), (_z * q._w) + (_w * q._z) + (_x * q._y) - (_y * q._x) ); } template inline Quaternion & Quaternion::operator*= (const Quaternion &q) { *this = *this * q; return *this; } template inline Quaternion Quaternion::operator* (const Vector3 &v) const { // q * v = q * p where p = <0,v> // Thus, we can simplify the operations. return Quaternion ( - (_x * v._x) - (_y * v._y) - (_z * v._z), (_w * v._x) + (_y * v._z) - (_z * v._y), (_w * v._y) + (_z * v._x) - (_x * v._z), (_w * v._z) + (_x * v._y) - (_y * v._x) ); } template inline Quaternion & Quaternion::operator*= (const Vector3 &v) { *this = *this * v; return *this; } template inline Quaternion Quaternion::operator* (Real k) const { return Quaternion (_w * k, _x * k, _y * k, _z * k); } template inline Quaternion & Quaternion::operator*= (Real k) { _w *= k; _x *= k; _y *= k; _z *= k; return *this; } template inline Quaternion Quaternion::operator/ (Real k) const { Real oneOverK = 1.0 / k; return Quaternion (_w * oneOverK, _x * oneOverK, _y * oneOverK, _z * oneOverK); } template inline Quaternion & Quaternion::operator/= (Real k) { Real oneOverK = 1.0 / k; _w *= oneOverK; _x *= oneOverK; _y *= oneOverK; _z *= oneOverK; return *this; } // Quaternion conjugate template inline Quaternion Quaternion::operator~ () const { return Quaternion (_w, -_x, -_y, -_z); } // Quaternion negation template inline Quaternion Quaternion::operator- () const { return Quaternion (-_w, -_x, -_y, -_z); } // -------------------------------------------------------------------------- // // Nonmember Quaternion functions // // -------------------------------------------------------------------------- // Scalar on left multiplication template inline Quaternion operator* (Real k, const Quaternion &q) { return Quaternion (q._w * k, q._x * k, q._y * k, q._z * k); } // Quaternion dot product template inline Real DotProduct (const Quaternion &a, const Quaternion &b) { return ((a._w * b._w) + (a._x * b._x) + (a._y * b._y) + (a._z * b._z)); } // Compute the quaternion conjugate. This is the quaternian // with the opposite rotation as the original quaternion. template inline Quaternion Conjugate (const Quaternion &q) { return Quaternion (q._w, -q._x, -q._y, -q._z); } // Compute the inverse quaternion (for unit quaternion only). template inline Quaternion Inverse (const Quaternion &q) { // Assume this is a unit quaternion! No check for this! Quaternion res (q._w, -q._x, -q._y, -q._z); res.normalize (); return res; } // -------------------------------------------------------------------------- // RotationQuaternion // // Setup the quaternion to rotate about the specified axis. theta must // be in radians. // -------------------------------------------------------------------------- template inline Quaternion RotationQuaternion (Axis axis, Real theta) { Quaternion res; // Compute the half angle Real thetaOver2 = theta * 0.5; // Set the values switch (axis) { case kXaxis: res._w = std::cos (thetaOver2); res._x = std::sin (thetaOver2); res._y = 0.0; res._z = 0.0; break; case kYaxis: res._w = std::cos (thetaOver2); res._x = 0.0; res._y = std::sin (thetaOver2); res._z = 0.0; break; case kZaxis: res._w = std::cos (thetaOver2); res._x = 0.0; res._y = 0.0; res._z = std::sin (thetaOver2); break; default: // Bad axis assert (false); } return res; } template inline Quaternion RotationQuaternion (const Vector3 &axis, Real theta) { Quaternion res; // The axis of rotation must be normalized assert (std::fabs (DotProduct (axis, axis) - 1.0) < 0.001); // Compute the half angle and its sin Real thetaOver2 = theta * 0.5; Real sinThetaOver2 = std::sin (thetaOver2); // Set the values res._w = std::cos (thetaOver2); res._x = axis._x * sinThetaOver2; res._y = axis._y * sinThetaOver2; res._z = axis._z * sinThetaOver2; return res; } // -------------------------------------------------------------------------- // Log // // Unit quaternion logarithm. log(q) = log(cos(theta) + n*sin(theta)) // -------------------------------------------------------------------------- template inline Quaternion Log (const Quaternion &q) { Quaternion res; res._w = 0.0; if (std::fabs (q._w) < 1.0) { Real theta = std::acos (q._w); Real sin_theta = std::sin (theta); if (std::fabs (sin_theta) > 0.00001) { Real thetaOverSinTheta = theta / sin_theta; res._x = q._x * thetaOverSinTheta; res._y = q._y * thetaOverSinTheta; res._z = q._z * thetaOverSinTheta; return res; } } res._x = q._x; res._y = q._y; res._z = q._z; return res; } // -------------------------------------------------------------------------- // Exp // // Quaternion exponential. // -------------------------------------------------------------------------- template inline Quaternion Exp (const Quaternion &q) { Real theta = std::sqrt (DotProduct (q, q)); Real sin_theta = std::sin (theta); Quaternion res; res._w = std::cos (theta); if (std::fabs (sin_theta) > 0.00001) { Real sinThetaOverTheta = sin_theta / theta; res._x = q._x * sinThetaOverTheta; res._y = q._y * sinThetaOverTheta; res._z = q._z * sinThetaOverTheta; } else { res._x = q._x; res._y = q._y; res._z = q._z; } return res; } // -------------------------------------------------------------------------- // Pow // // Quaternion exponentiation. // -------------------------------------------------------------------------- template inline Quaternion Pow (const Quaternion &q, Real exponent) { // Check for the case of an identity quaternion. // This will protect against divide by zero if (std::fabs (q._w) > 0.9999) return q; // Extract the half angle alpha (alpha = theta/2) Real alpha = std::acos (q._w); // Compute new alpha value Real newAlpha = alpha * exponent; // Compute new quaternion Vector3 n (q._x, q._y, q._z); n *= std::sin (newAlpha) / std::sin (alpha); return Quaternion (std::cos (newAlpha), n); } // -------------------------------------------------------------------------- // Slerp // // Spherical linear interpolation. // -------------------------------------------------------------------------- template inline Quaternion Slerp (const Quaternion &q0, const Quaternion &q1, Real t) { // Check for out-of range parameter and return edge points if so if (t <= 0.0) return q0; if (t >= 1.0) return q1; // Compute "cosine of angle between quaternions" using dot product Real cosOmega = DotProduct (q0, q1); // If negative dot, use -q1. Two quaternions q and -q // represent the same rotation, but may produce // different slerp. We chose q or -q to rotate using // the acute angle. Real q1w = q1._w; Real q1x = q1._x; Real q1y = q1._y; Real q1z = q1._z; if (cosOmega < 0.0) { q1w = -q1w; q1x = -q1x; q1y = -q1y; q1z = -q1z; cosOmega = -cosOmega; } // We should have two unit quaternions, so dot should be <= 1.0 assert (cosOmega < 1.1); // Compute interpolation fraction, checking for quaternions // almost exactly the same Real k0, k1; if (cosOmega > 0.9999) { // Very close - just use linear interpolation, // which will protect againt a divide by zero k0 = 1.0 - t; k1 = t; } else { // Compute the sin of the angle using the // trig identity sin^2(omega) + cos^2(omega) = 1 Real sinOmega = std::sqrt (1.0 - (cosOmega * cosOmega)); // Compute the angle from its sin and cosine Real omega = std::atan2 (sinOmega, cosOmega); // Compute inverse of denominator, so we only have // to divide once Real oneOverSinOmega = 1.0 / sinOmega; // Compute interpolation parameters k0 = std::sin ((1.0 - t) * omega) * oneOverSinOmega; k1 = std::sin (t * omega) * oneOverSinOmega; } // Interpolate and return new quaternion return Quaternion ( (k0 * q0._w) + (k1 * q1w), (k0 * q0._x) + (k1 * q1x), (k0 * q0._y) + (k1 * q1y), (k0 * q0._z) + (k1 * q1z) ); } // -------------------------------------------------------------------------- // Squad // // Spherical cubic interpolation. // squad = slerp (slerp (q0, q1, t), slerp (qa, qb, t), 2t(1 - t)). // -------------------------------------------------------------------------- template inline Quaternion Squad (const Quaternion &q0, const Quaternion &qa, const Quaternion &qb, const Quaternion &q1, Real t) { Real slerp_t = 2.0 * t * (1.0 - t); Quaternion slerp_q0 = Slerp (q0, q1, t); Quaternion slerp_q1 = Slerp (qa, qb, t); return Slerp (slerp_q0, slerp_q1, slerp_t); } // -------------------------------------------------------------------------- // Intermediate // // Compute intermediate quaternions for building spline segments. // -------------------------------------------------------------------------- template inline void Intermediate (const Quaternion &qprev, const Quaternion &qcurr, const Quaternion &qnext, Quaternion &qa, Quaternion &qb) { // We should have unit quaternions assert (DotProduct (qprev, qprev) <= 1.0001); assert (DotProduct (qcurr, qcurr) <= 1.0001); Quaternion inv_prev = Conjugate (qprev); Quaternion inv_curr = Conjugate (qcurr); Quaternion p0 = inv_prev * qcurr; Quaternion p1 = inv_curr * qnext; Quaternion arg = (Log (p0) - Log (p1)) * 0.25; qa = qcurr * Exp ( arg); qb = qcurr * Exp (-arg); }