| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- /*
- Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * Neither the name of LibCat 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 HOLDER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, 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.
- */
- /*
- Based on code from "Physics for Game Developers", David M. Bourg
- slerp() code adapted from this website:
- http://www.number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/index.html
- */
- #ifndef CAT_QUATERNION_HPP
- #define CAT_QUATERNION_HPP
- #include <cat/gfx/Vector.hpp>
- #include <cat/gfx/Matrix.hpp>
- namespace cat {
- // Generic quaternion class for linear algebra
- template <typename Scalar, typename Double>
- class Quaternion
- {
- protected:
- // Backed by a 4D vector with elements arranged as <x,y,z,w>
- Vector<4, Scalar, Double> _v;
- public:
- // Short-hand for the current quaternion type
- typedef Quaternion<Scalar, Double> mytype;
- // Short-hand for the vector type
- typedef Vector<3, Scalar, Double> vectype;
- // Uninitialized quaternion is not cleared
- Quaternion() { }
- // Copy constructor
- Quaternion(const mytype &u)
- : _v(u._v)
- {
- }
- // Initialization constructor
- Quaternion(const vectype &v, Scalar w)
- : _v(v)
- {
- }
- // Initialization constructor
- Quaternion(Scalar x, Scalar y, Scalar z, Scalar w)
- : _v(x, y, z, w)
- {
- }
- // Assignment operator
- mytype &operator=(const mytype &u)
- {
- _v.copy(u._v);
- return *this;
- }
- // Convert from Euler angle representation
- // Pre-condition: angles in radians (see Deg2Rad in Scalar.hpp)
- void setFromEulerAngles(f32 xroll, f32 ypitch, f32 zyaw)
- {
- f64 croll = cos(0.5f * xroll);
- f64 cpitch = cos(0.5f * ypitch);
- f64 cyaw = cos(0.5f * zyaw);
- f64 sroll = sin(0.5f * xroll);
- f64 spitch = sin(0.5f * ypitch);
- f64 syaw = sin(0.5f * zyaw);
- f64 cyawcpitch = cyaw * cpitch;
- f64 syawspitch = syaw * spitch;
- f64 cyawspitch = cyaw * spitch;
- f64 syawcpitch = syaw * cpitch;
- _v(0) = static_cast<Scalar>( cyawcpitch * sroll - syawspitch * croll );
- _v(1) = static_cast<Scalar>( cyawspitch * croll + syawcpitch * sroll );
- _v(2) = static_cast<Scalar>( syawcpitch * croll - cyawspitch * sroll );
- _v(3) = static_cast<Scalar>( cyawcpitch * croll + syawspitch * sroll );
- _v.normalize();
- }
- // Convert from axis-angle representation
- // Pre-condition: axis must be unit-length
- void setFromAxisAngle(const vectype &axis, f32 angle)
- {
- angle *= 0.5f;
- Scalar theta = static_cast<Scalar>( sin(angle) );
- _v(0) = theta * axis(0);
- _v(1) = theta * axis(1);
- _v(2) = theta * axis(2);
- _v(3) = static_cast<Scalar>( cos(angle) );
- }
- // Conjugate
- mytype operator~() const
- {
- return mytype(-_v(0), -_v(1), -_v(2), _v(3));
- }
- // Conjugate in-place
- mytype &conjugate() const
- {
- _v(0) = -_v(0);
- _v(1) = -_v(1);
- _v(2) = -_v(2);
- return *this;
- }
- // Multiply by quaternion
- mytype operator*(const mytype &u)
- {
- // Cache each of the elements since each is used 4 times
- Double x1 = _v(0), x2 = u._v(0);
- Double y1 = _v(1), y2 = u._v(1);
- Double z1 = _v(2), z2 = u._v(2);
- Double w1 = _v(3), w2 = u._v(3);
- // Quaternion multiplication formula:
- Scalar x3 = static_cast<Scalar>( w1*x2 + x1*w2 + y1*z2 - z1*y2 );
- Scalar y3 = static_cast<Scalar>( w1*y2 - x1*z2 + y1*w2 + z1*x2 );
- Scalar z3 = static_cast<Scalar>( w1*z2 + x1*y2 - y1*x2 + z1*w2 );
- Scalar w3 = static_cast<Scalar>( w1*w2 - x1*x2 - y1*y2 - z1*z2 );
- return mytype(x3, y3, z3, w3);
- }
- // Multiply by quaternion in-place: this = this * u
- mytype &operator*=(const mytype &u)
- {
- // Cache each of the elements since each is used 4 times
- Double x1 = _v(0), x2 = u._v(0);
- Double y1 = _v(1), y2 = u._v(1);
- Double z1 = _v(2), z2 = u._v(2);
- Double w1 = _v(3), w2 = u._v(3);
- // Quaternion multiplication formula:
- _v(0) = static_cast<Scalar>( w1*x2 + x1*w2 + y1*z2 - z1*y2 );
- _v(1) = static_cast<Scalar>( w1*y2 - x1*z2 + y1*w2 + z1*x2 );
- _v(2) = static_cast<Scalar>( w1*z2 + x1*y2 - y1*x2 + z1*w2 );
- _v(3) = static_cast<Scalar>( w1*w2 - x1*x2 - y1*y2 - z1*z2 );
- return *this;
- }
- // Multiply by quaternion in-place: this = u * this
- mytype &postMultiply(const mytype &u)
- {
- // Cache each of the elements since each is used 4 times
- Double x2 = _v(0), x1 = u._v(0);
- Double y2 = _v(1), y1 = u._v(1);
- Double z2 = _v(2), z1 = u._v(2);
- Double w2 = _v(3), w1 = u._v(3);
- // Quaternion multiplication formula:
- _v(0) = static_cast<Scalar>( w1*x2 + x1*w2 + y1*z2 - z1*y2 );
- _v(1) = static_cast<Scalar>( w1*y2 - x1*z2 + y1*w2 + z1*x2 );
- _v(2) = static_cast<Scalar>( w1*z2 + x1*y2 - y1*x2 + z1*w2 );
- _v(3) = static_cast<Scalar>( w1*w2 - x1*x2 - y1*y2 - z1*z2 );
- return *this;
- }
- // Rotate given vector in-place
- void rotate(vectype &u)
- {
- // Implements the simple formula: (q1 * u2 * ~q1).getVector()
- // Cache each of the elements since each is used 4 times
- Double x1 = _v(0), x2 = u(0);
- Double y1 = _v(1), y2 = u(1);
- Double z1 = _v(2), z2 = u(2);
- Double w1 = _v(3);
- // Quaternion-vector multiplication formula (q3 = q1 * u2):
- Double x3 = w1*x2 + y1*z2 - z1*y2;
- Double y3 = w1*y2 - x1*z2 + z1*x2;
- Double z3 = w1*z2 + x1*y2 - y1*x2;
- Double w3 = -(x1*x2 + y1*y2 + z1*z2);
- // Quaternion multiplication formula (q2' = q3 * ~q1):
- u(0) = static_cast<Scalar>( w1*x3 - x1*w3 + y1*z3 - z1*y3 );
- u(1) = static_cast<Scalar>( w1*y3 - x1*z3 - y1*w3 + z1*x3 );
- u(2) = static_cast<Scalar>( w1*z3 + x1*y3 - y1*x3 - z1*w3 );
- }
- // NLERP: Very fast, non-constant velocity and torque-minimal
- // Precondition: q1, q2 are unit length
- friend void nlerp(const mytype &q1, const mytype &q2, Scalar t, mytype &result)
- {
- // Linearly interpolate and normalize result
- // This formula is a little more work than "q1 + (q2 - q1) * t"
- // but less likely to lose precision when it matters
- // Cosine of phi, the angle between the two vectors
- Double cos_phi = q1._v.dotProduct(q2._v);
- // I have read this may try to rotate around the "long way" sometimes and to
- // fix that we check if cos(phi) is negative and invert one of the inputs.
- if (cos_phi < 0.0)
- {
- result._v = -q1._v;
- }
- else
- {
- result._v = q1._v;
- }
- // Simple linear interpolation
- Scalar scale0 = static_cast<Scalar>(1) - t;
- Scalar scale1 = t;
- result._v *= scale0;
- result._v += q2._v * scale1;
- result._v.normalize();
- }
- // SLERP: Slower, constant velocity and torque-minimal
- // Precondition: q1, q2 are unit length
- friend void slerp(const mytype &q1, const mytype &q2, Scalar t, mytype &result)
- {
- // Cosine of phi, the angle between the two vectors
- Double cos_phi = q1._v.dotProduct(q2._v);
- // I have read this may try to rotate around the "long way" sometimes and to
- // fix that we check if cos(phi) is negative and invert one of the inputs.
- if (cos_phi < 0.0)
- {
- cos_phi = -cos_phi;
- result._v = -q1._v;
- }
- else
- {
- result._v = q1._v;
- }
- // Default to simple linear interpolation
- Scalar scale0 = static_cast<Scalar>(1) - t;
- Scalar scale1 = t;
- // If the distance is not small we need to do full slerp:
- if (cos_phi < 0.9995)
- {
- // cos_phi is guaranteed to be within the domain of acos(), 0..1
- Double phi = static_cast<Double>( acos(cos_phi) );
- Double inv_sin_phi = static_cast<Double>(1) / sin(phi);
- scale0 = static_cast<Scalar>( sin(scale0 * phi) * inv_sin_phi );
- scale1 = static_cast<Scalar>( sin(scale1 * phi) * inv_sin_phi );
- }
- result._v *= scale0;
- result._v += q2._v * scale1;
- result._v.normalize();
- }
- // Get angle of rotation
- Scalar getAngle()
- {
- return static_cast<Scalar>( 2 ) * static_cast<Scalar>( acos(_v(3)) );
- }
- // Get axis of rotation
- vectype getAxis()
- {
- return vectype(_v(0), _v(1), _v(2)).normalize();
- }
- // Get matrix form of the rotation represented by this quaternion
- void getMatrix4x4(Matrix<4, 4, Scalar> &result)
- {
- Double dx = _v(0);
- Double dy = _v(1);
- Double dz = _v(2);
- Double dw = _v(3);
- Double x2 = dx * dx;
- Double y2 = dy * dy;
- Double z2 = dz * dz;
- Double xy = dx * dy;
- Double yz = dy * dz;
- Double zx = dz * dx;
- Double xw = dx * dw;
- Double yw = dy * dw;
- Double zw = dz * dw;
- const Double ONE = static_cast<Double>( 1 );
- const Double TWO = static_cast<Double>( 2 );
- // Result is written in OpenGL column-major matrix order:
- result( 0) = static_cast<Scalar>( ONE - TWO * (y2 + z2) );
- result( 1) = static_cast<Scalar>( TWO * (xy - zw) );
- result( 2) = static_cast<Scalar>( TWO * (zx + yw) );
- result( 3) = static_cast<Scalar>( 0 );
- result( 4) = static_cast<Scalar>( TWO * (xy + zw) );
- result( 5) = static_cast<Scalar>( ONE - TWO * (x2 + z2) );
- result( 6) = static_cast<Scalar>( TWO * (yz - xw) );
- result( 7) = static_cast<Scalar>( 0 );
- result( 8) = static_cast<Scalar>( TWO * (zx - yw) );
- result( 9) = static_cast<Scalar>( TWO * (yz + xw) );
- result(10) = static_cast<Scalar>( ONE - TWO * (x2 + y2) );
- result(11) = static_cast<Scalar>( 0 );
- result(12) = static_cast<Scalar>( 0 );
- result(13) = static_cast<Scalar>( 0 );
- result(14) = static_cast<Scalar>( 0 );
- result(15) = static_cast<Scalar>( ONE );
- }
- // Get Euler angles
- vectype Quaternion::getEulerAngles()
- {
- Double dx = _v(0);
- Double dy = _v(1);
- Double dz = _v(2);
- Double dw = _v(3);
- Double x2 = dx * dx;
- Double y2 = dy * dy;
- Double z2 = dz * dz;
- Double w2 = dw * dw;
- Double xy = dx * dy;
- Double yz = dy * dz;
- Double xw = dx * dw;
- Double yw = dy * dw;
- Double zw = dz * dw;
- const Double TWO = static_cast<Double>( 2 );
- Double r11 = w2 + x2 - y2 - z2;
- Double r21 = TWO * (xy + zw);
- Double r31 = TWO * (xy - yw);
- Double r32 = TWO * (yz + xw);
- Double r33 = w2 - x2 - y2 + z2;
- Double tmp = fabs(r31);
- const Double LIMIT = static_cast<Double>( 0.999999 );
- if (tmp > LIMIT)
- {
- Double xz = dx * dz;
- Double r12 = TWO * (yz - zw);
- Double r13 = TWO * (xz + yw);
- return vectype(static_cast<Scalar>( 0 ),
- static_cast<Scalar>( -CAT_HALF_PI_64 * r31 / tmp ),
- static_cast<Scalar>( atan2(-r12, -r31 * r13) ));
- }
- return vectype(static_cast<Scalar>( atan2(r32, r33) ),
- static_cast<Scalar>( asin(-r31) ),
- static_cast<Scalar>( atan2(r21, r11) ));
- }
- };
- // Short-hand for common usages:
- typedef Quaternion<f32, f64> Quaternion4f;
- typedef Quaternion<f64, f64> Quaternion4d;
- } // namespace cat
- #endif // CAT_QUATERNION_HPP
|