![]() |
Shadowrun: Awakened 29 September 2011 - Build 871
|
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.