Shadowrun: Awakened 29 September 2011 - Build 871
Quaternion.hpp
Go to the documentation of this file.
00001 /*
00002     Copyright (c) 2009 Christopher A. Taylor.  All rights reserved.
00003 
00004     Redistribution and use in source and binary forms, with or without
00005     modification, are permitted provided that the following conditions are met:
00006 
00007     * Redistributions of source code must retain the above copyright notice,
00008       this list of conditions and the following disclaimer.
00009     * Redistributions in binary form must reproduce the above copyright notice,
00010       this list of conditions and the following disclaimer in the documentation
00011       and/or other materials provided with the distribution.
00012     * Neither the name of LibCat nor the names of its contributors may be used
00013       to endorse or promote products derived from this software without
00014       specific prior written permission.
00015 
00016     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00017     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00018     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00019     ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00020     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00021     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00022     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00023     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00024     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00025     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00026     POSSIBILITY OF SUCH DAMAGE.
00027 */
00028 
00029 /*
00030     Based on code from "Physics for Game Developers", David M. Bourg
00031 
00032     slerp() code adapted from this website:
00033     http://www.number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/index.html
00034 */
00035 
00036 #ifndef CAT_QUATERNION_HPP
00037 #define CAT_QUATERNION_HPP
00038 
00039 #include <cat/gfx/Vector.hpp>
00040 #include <cat/gfx/Matrix.hpp>
00041 
00042 namespace cat {
00043 
00044 
00045 // Generic quaternion class for linear algebra
00046 template <typename Scalar, typename Double>
00047 class Quaternion
00048 {
00049 protected:
00050     // Backed by a 4D vector with elements arranged as <x,y,z,w>
00051     Vector<4, Scalar, Double> _v;
00052 
00053 public:
00054     // Short-hand for the current quaternion type
00055     typedef Quaternion<Scalar, Double> mytype;
00056 
00057     // Short-hand for the vector type
00058     typedef Vector<3, Scalar, Double> vectype;
00059 
00060     // Uninitialized quaternion is not cleared
00061     Quaternion() { }
00062 
00063     // Copy constructor
00064     Quaternion(const mytype &u)
00065         : _v(u._v)
00066     {
00067     }
00068 
00069     // Initialization constructor
00070     Quaternion(const vectype &v, Scalar w)
00071         : _v(v)
00072     {
00073     }
00074 
00075     // Initialization constructor
00076     Quaternion(Scalar x, Scalar y, Scalar z, Scalar w)
00077         : _v(x, y, z, w)
00078     {
00079     }
00080 
00081     // Assignment operator
00082     mytype &operator=(const mytype &u)
00083     {
00084         _v.copy(u._v);
00085 
00086         return *this;
00087     }
00088 
00089     // Convert from Euler angle representation
00090     // Pre-condition: angles in radians (see Deg2Rad in Scalar.hpp)
00091     void setFromEulerAngles(f32 xroll, f32 ypitch, f32 zyaw)
00092     {
00093         f64 croll = cos(0.5f * xroll);
00094         f64 cpitch = cos(0.5f * ypitch);
00095         f64 cyaw = cos(0.5f * zyaw);
00096         f64 sroll = sin(0.5f * xroll);
00097         f64 spitch = sin(0.5f * ypitch);
00098         f64 syaw = sin(0.5f * zyaw);
00099 
00100         f64 cyawcpitch = cyaw * cpitch;
00101         f64 syawspitch = syaw * spitch;
00102         f64 cyawspitch = cyaw * spitch;
00103         f64 syawcpitch = syaw * cpitch;
00104 
00105         _v(0) = static_cast<Scalar>( cyawcpitch * sroll - syawspitch * croll );
00106         _v(1) = static_cast<Scalar>( cyawspitch * croll + syawcpitch * sroll );
00107         _v(2) = static_cast<Scalar>( syawcpitch * croll - cyawspitch * sroll );
00108         _v(3) = static_cast<Scalar>( cyawcpitch * croll + syawspitch * sroll );
00109 
00110         _v.normalize();
00111     }
00112 
00113     // Convert from axis-angle representation
00114     // Pre-condition: axis must be unit-length
00115     void setFromAxisAngle(const vectype &axis, f32 angle)
00116     {
00117         angle *= 0.5f;
00118 
00119         Scalar theta = static_cast<Scalar>( sin(angle) );
00120 
00121         _v(0) = theta * axis(0);
00122         _v(1) = theta * axis(1);
00123         _v(2) = theta * axis(2);
00124         _v(3) = static_cast<Scalar>( cos(angle) );
00125     }
00126 
00127     // Conjugate
00128     mytype operator~() const
00129     {
00130         return mytype(-_v(0), -_v(1), -_v(2), _v(3));
00131     }
00132 
00133     // Conjugate in-place
00134     mytype &conjugate() const
00135     {
00136         _v(0) = -_v(0);
00137         _v(1) = -_v(1);
00138         _v(2) = -_v(2);
00139 
00140         return *this;
00141     }
00142 
00143     // Multiply by quaternion
00144     mytype operator*(const mytype &u)
00145     {
00146         // Cache each of the elements since each is used 4 times
00147         Double x1 = _v(0), x2 = u._v(0);
00148         Double y1 = _v(1), y2 = u._v(1);
00149         Double z1 = _v(2), z2 = u._v(2);
00150         Double w1 = _v(3), w2 = u._v(3);
00151 
00152         // Quaternion multiplication formula:
00153         Scalar x3 = static_cast<Scalar>( w1*x2 + x1*w2 + y1*z2 - z1*y2 );
00154         Scalar y3 = static_cast<Scalar>( w1*y2 - x1*z2 + y1*w2 + z1*x2 );
00155         Scalar z3 = static_cast<Scalar>( w1*z2 + x1*y2 - y1*x2 + z1*w2 );
00156         Scalar w3 = static_cast<Scalar>( w1*w2 - x1*x2 - y1*y2 - z1*z2 );
00157 
00158         return mytype(x3, y3, z3, w3);
00159     }
00160 
00161     // Multiply by quaternion in-place: this = this * u
00162     mytype &operator*=(const mytype &u)
00163     {
00164         // Cache each of the elements since each is used 4 times
00165         Double x1 = _v(0), x2 = u._v(0);
00166         Double y1 = _v(1), y2 = u._v(1);
00167         Double z1 = _v(2), z2 = u._v(2);
00168         Double w1 = _v(3), w2 = u._v(3);
00169 
00170         // Quaternion multiplication formula:
00171         _v(0) = static_cast<Scalar>( w1*x2 + x1*w2 + y1*z2 - z1*y2 );
00172         _v(1) = static_cast<Scalar>( w1*y2 - x1*z2 + y1*w2 + z1*x2 );
00173         _v(2) = static_cast<Scalar>( w1*z2 + x1*y2 - y1*x2 + z1*w2 );
00174         _v(3) = static_cast<Scalar>( w1*w2 - x1*x2 - y1*y2 - z1*z2 );
00175 
00176         return *this;
00177     }
00178 
00179     // Multiply by quaternion in-place: this = u * this
00180     mytype &postMultiply(const mytype &u)
00181     {
00182         // Cache each of the elements since each is used 4 times
00183         Double x2 = _v(0), x1 = u._v(0);
00184         Double y2 = _v(1), y1 = u._v(1);
00185         Double z2 = _v(2), z1 = u._v(2);
00186         Double w2 = _v(3), w1 = u._v(3);
00187 
00188         // Quaternion multiplication formula:
00189         _v(0) = static_cast<Scalar>( w1*x2 + x1*w2 + y1*z2 - z1*y2 );
00190         _v(1) = static_cast<Scalar>( w1*y2 - x1*z2 + y1*w2 + z1*x2 );
00191         _v(2) = static_cast<Scalar>( w1*z2 + x1*y2 - y1*x2 + z1*w2 );
00192         _v(3) = static_cast<Scalar>( w1*w2 - x1*x2 - y1*y2 - z1*z2 );
00193 
00194         return *this;
00195     }
00196 
00197     // Rotate given vector in-place
00198     void rotate(vectype &u)
00199     {
00200         // Implements the simple formula: (q1 * u2 * ~q1).getVector()
00201 
00202         // Cache each of the elements since each is used 4 times
00203         Double x1 = _v(0), x2 = u(0);
00204         Double y1 = _v(1), y2 = u(1);
00205         Double z1 = _v(2), z2 = u(2);
00206         Double w1 = _v(3);
00207 
00208         // Quaternion-vector multiplication formula (q3 = q1 * u2):
00209         Double x3 = w1*x2 + y1*z2 - z1*y2;
00210         Double y3 = w1*y2 - x1*z2 + z1*x2;
00211         Double z3 = w1*z2 + x1*y2 - y1*x2;
00212         Double w3 = -(x1*x2 + y1*y2 + z1*z2);
00213 
00214         // Quaternion multiplication formula (q2' = q3 * ~q1):
00215         u(0) = static_cast<Scalar>( w1*x3 - x1*w3 + y1*z3 - z1*y3 );
00216         u(1) = static_cast<Scalar>( w1*y3 - x1*z3 - y1*w3 + z1*x3 );
00217         u(2) = static_cast<Scalar>( w1*z3 + x1*y3 - y1*x3 - z1*w3 );
00218     }
00219 
00220     // NLERP: Very fast, non-constant velocity and torque-minimal
00221     // Precondition: q1, q2 are unit length
00222     friend void nlerp(const mytype &q1, const mytype &q2, Scalar t, mytype &result)
00223     {
00224         // Linearly interpolate and normalize result
00225         // This formula is a little more work than "q1 + (q2 - q1) * t"
00226         // but less likely to lose precision when it matters
00227 
00228         // Cosine of phi, the angle between the two vectors
00229         Double cos_phi = q1._v.dotProduct(q2._v);
00230 
00231         // I have read this may try to rotate around the "long way" sometimes and to
00232         // fix that we check if cos(phi) is negative and invert one of the inputs.
00233         if (cos_phi < 0.0)
00234         {
00235             result._v = -q1._v;
00236         }
00237         else
00238         {
00239             result._v = q1._v;
00240         }
00241 
00242         // Simple linear interpolation
00243         Scalar scale0 = static_cast<Scalar>(1) - t;
00244         Scalar scale1 = t;
00245 
00246         result._v *= scale0;
00247         result._v += q2._v * scale1;
00248         result._v.normalize();
00249     }
00250 
00251     // SLERP: Slower, constant velocity and torque-minimal
00252     // Precondition: q1, q2 are unit length
00253     friend void slerp(const mytype &q1, const mytype &q2, Scalar t, mytype &result)
00254     {
00255         // Cosine of phi, the angle between the two vectors
00256         Double cos_phi = q1._v.dotProduct(q2._v);
00257 
00258         // I have read this may try to rotate around the "long way" sometimes and to
00259         // fix that we check if cos(phi) is negative and invert one of the inputs.
00260         if (cos_phi < 0.0)
00261         {
00262             cos_phi = -cos_phi;
00263             result._v = -q1._v;
00264         }
00265         else
00266         {
00267             result._v = q1._v;
00268         }
00269 
00270         // Default to simple linear interpolation
00271         Scalar scale0 = static_cast<Scalar>(1) - t;
00272         Scalar scale1 = t;
00273 
00274         // If the distance is not small we need to do full slerp:
00275         if (cos_phi < 0.9995)
00276         {
00277             // cos_phi is guaranteed to be within the domain of acos(), 0..1
00278             Double phi = static_cast<Double>( acos(cos_phi) );
00279             Double inv_sin_phi = static_cast<Double>(1) / sin(phi);
00280 
00281             scale0 = static_cast<Scalar>( sin(scale0 * phi) * inv_sin_phi );
00282             scale1 = static_cast<Scalar>( sin(scale1 * phi) * inv_sin_phi );
00283         }
00284 
00285         result._v *= scale0;
00286         result._v += q2._v * scale1;
00287         result._v.normalize();
00288     }
00289 
00290     // Get angle of rotation
00291     Scalar getAngle()
00292     {
00293         return static_cast<Scalar>( 2 ) * static_cast<Scalar>( acos(_v(3)) );
00294     }
00295 
00296     // Get axis of rotation
00297     vectype getAxis()
00298     {
00299         return vectype(_v(0), _v(1), _v(2)).normalize();
00300     }
00301 
00302     // Get matrix form of the rotation represented by this quaternion
00303     void getMatrix4x4(Matrix<4, 4, Scalar> &result)
00304     {
00305         Double dx = _v(0);
00306         Double dy = _v(1);
00307         Double dz = _v(2);
00308         Double dw = _v(3);
00309 
00310         Double x2 = dx * dx;
00311         Double y2 = dy * dy;
00312         Double z2 = dz * dz;
00313         Double xy = dx * dy;
00314         Double yz = dy * dz;
00315         Double zx = dz * dx;
00316         Double xw = dx * dw;
00317         Double yw = dy * dw;
00318         Double zw = dz * dw;
00319 
00320         const Double ONE = static_cast<Double>( 1 );
00321         const Double TWO = static_cast<Double>( 2 );
00322 
00323         // Result is written in OpenGL column-major matrix order:
00324         result( 0) = static_cast<Scalar>( ONE - TWO * (y2 + z2) );
00325         result( 1) = static_cast<Scalar>( TWO * (xy - zw) );
00326         result( 2) = static_cast<Scalar>( TWO * (zx + yw) );
00327         result( 3) = static_cast<Scalar>( 0 );
00328 
00329         result( 4) = static_cast<Scalar>( TWO * (xy + zw) );
00330         result( 5) = static_cast<Scalar>( ONE - TWO * (x2 + z2) );
00331         result( 6) = static_cast<Scalar>( TWO * (yz - xw) );
00332         result( 7) = static_cast<Scalar>( 0 );
00333 
00334         result( 8) = static_cast<Scalar>( TWO * (zx - yw) );
00335         result( 9) = static_cast<Scalar>( TWO * (yz + xw) );
00336         result(10) = static_cast<Scalar>( ONE - TWO * (x2 + y2) );
00337         result(11) = static_cast<Scalar>( 0 );
00338 
00339         result(12) = static_cast<Scalar>( 0 );
00340         result(13) = static_cast<Scalar>( 0 );
00341         result(14) = static_cast<Scalar>( 0 );
00342         result(15) = static_cast<Scalar>( ONE );
00343     }
00344 
00345     // Get Euler angles
00346     vectype Quaternion::getEulerAngles()
00347     {
00348         Double dx = _v(0);
00349         Double dy = _v(1);
00350         Double dz = _v(2);
00351         Double dw = _v(3);
00352 
00353         Double x2 = dx * dx;
00354         Double y2 = dy * dy;
00355         Double z2 = dz * dz;
00356         Double w2 = dw * dw;
00357         Double xy = dx * dy;
00358         Double yz = dy * dz;
00359         Double xw = dx * dw;
00360         Double yw = dy * dw;
00361         Double zw = dz * dw;
00362 
00363         const Double TWO = static_cast<Double>( 2 );
00364 
00365         Double r11 = w2 + x2 - y2 - z2;
00366         Double r21 = TWO * (xy + zw);
00367         Double r31 = TWO * (xy - yw);
00368         Double r32 = TWO * (yz + xw);
00369         Double r33 = w2 - x2 - y2 + z2;
00370 
00371         Double tmp = fabs(r31);
00372         const Double LIMIT = static_cast<Double>( 0.999999 );
00373 
00374         if (tmp > LIMIT)
00375         {
00376             Double xz = dx * dz;
00377 
00378             Double r12 = TWO * (yz - zw);
00379             Double r13 = TWO * (xz + yw);
00380 
00381             return vectype(static_cast<Scalar>( 0 ),
00382                            static_cast<Scalar>( -CAT_HALF_PI_64 * r31 / tmp ),
00383                            static_cast<Scalar>( atan2(-r12, -r31 * r13) ));
00384         }
00385 
00386         return vectype(static_cast<Scalar>( atan2(r32, r33) ),
00387                        static_cast<Scalar>( asin(-r31) ),
00388                        static_cast<Scalar>( atan2(r21, r11) ));
00389     }
00390 };
00391 
00392 
00393 // Short-hand for common usages:
00394 
00395 typedef Quaternion<f32, f64> Quaternion4f;
00396 typedef Quaternion<f64, f64> Quaternion4d;
00397 
00398 
00399 } // namespace cat
00400 
00401 #endif // CAT_QUATERNION_HPP

Copyright © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.

GNU Lesser General Public License 3 Sourceforge.net