IrrlichtEngine
quaternion.h
Go to the documentation of this file.
00001 // Copyright (C) 2002-2011 Nikolaus Gebhardt
00002 // This file is part of the "Irrlicht Engine".
00003 // For conditions of distribution and use, see copyright notice in irrlicht.h
00004 
00005 #ifndef __IRR_QUATERNION_H_INCLUDED__
00006 #define __IRR_QUATERNION_H_INCLUDED__
00007 
00008 #include "irrTypes.h"
00009 #include "irrMath.h"
00010 #include "matrix4.h"
00011 #include "vector3d.h"
00012 
00013 namespace irr
00014 {
00015 namespace core
00016 {
00017 
00019 
00021 class quaternion
00022 {
00023         public:
00024 
00026                 quaternion() : X(0.0f), Y(0.0f), Z(0.0f), W(1.0f) {}
00027 
00029                 quaternion(f32 x, f32 y, f32 z, f32 w) : X(x), Y(y), Z(z), W(w) { }
00030 
00032                 quaternion(f32 x, f32 y, f32 z);
00033 
00035                 quaternion(const vector3df& vec);
00036 
00038                 quaternion(const matrix4& mat);
00039 
00041                 bool operator==(const quaternion& other) const;
00042 
00044                 bool operator!=(const quaternion& other) const;
00045 
00047                 inline quaternion& operator=(const quaternion& other);
00048 
00050                 inline quaternion& operator=(const matrix4& other);
00051 
00053                 quaternion operator+(const quaternion& other) const;
00054 
00056                 quaternion operator*(const quaternion& other) const;
00057 
00059                 quaternion operator*(f32 s) const;
00060 
00062                 quaternion& operator*=(f32 s);
00063 
00065                 vector3df operator*(const vector3df& v) const;
00066 
00068                 quaternion& operator*=(const quaternion& other);
00069 
00071                 inline f32 dotProduct(const quaternion& other) const;
00072 
00074                 inline quaternion& set(f32 x, f32 y, f32 z, f32 w);
00075 
00077                 inline quaternion& set(f32 x, f32 y, f32 z);
00078 
00080                 inline quaternion& set(const core::vector3df& vec);
00081 
00083                 inline quaternion& set(const core::quaternion& quat);
00084 
00086                 inline bool equals(const quaternion& other,
00087                                 const f32 tolerance = ROUNDING_ERROR_f32 ) const;
00088 
00090                 inline quaternion& normalize();
00091 
00093                 matrix4 getMatrix() const;
00094 
00096                 void getMatrix( matrix4 &dest, const core::vector3df &translation ) const;
00097 
00115                 void getMatrixCenter( matrix4 &dest, const core::vector3df &center, const core::vector3df &translation ) const;
00116 
00118                 inline void getMatrix_transposed( matrix4 &dest ) const;
00119 
00121                 quaternion& makeInverse();
00122 
00124                 quaternion& lerp(quaternion q1, quaternion q2, f32 time);
00125 
00127                 quaternion& slerp( quaternion q1, quaternion q2, f32 interpolate );
00128 
00130 
00135                 quaternion& fromAngleAxis (f32 angle, const vector3df& axis);
00136 
00138                 void toAngleAxis (f32 &angle, core::vector3df& axis) const;
00139 
00141                 void toEuler(vector3df& euler) const;
00142 
00144                 quaternion& makeIdentity();
00145 
00147                 quaternion& rotationFromTo(const vector3df& from, const vector3df& to);
00148 
00150                 f32 X; // vectorial (imaginary) part
00151                 f32 Y;
00152                 f32 Z;
00153                 f32 W; // real part
00154 };
00155 
00156 
00157 // Constructor which converts euler angles to a quaternion
00158 inline quaternion::quaternion(f32 x, f32 y, f32 z)
00159 {
00160         set(x,y,z);
00161 }
00162 
00163 
00164 // Constructor which converts euler angles to a quaternion
00165 inline quaternion::quaternion(const vector3df& vec)
00166 {
00167         set(vec.X,vec.Y,vec.Z);
00168 }
00169 
00170 
00171 // Constructor which converts a matrix to a quaternion
00172 inline quaternion::quaternion(const matrix4& mat)
00173 {
00174         (*this) = mat;
00175 }
00176 
00177 
00178 // equal operator
00179 inline bool quaternion::operator==(const quaternion& other) const
00180 {
00181         return ((X == other.X) &&
00182                 (Y == other.Y) &&
00183                 (Z == other.Z) &&
00184                 (W == other.W));
00185 }
00186 
00187 // inequality operator
00188 inline bool quaternion::operator!=(const quaternion& other) const
00189 {
00190         return !(*this == other);
00191 }
00192 
00193 // assignment operator
00194 inline quaternion& quaternion::operator=(const quaternion& other)
00195 {
00196         X = other.X;
00197         Y = other.Y;
00198         Z = other.Z;
00199         W = other.W;
00200         return *this;
00201 }
00202 
00203 
00204 // matrix assignment operator
00205 inline quaternion& quaternion::operator=(const matrix4& m)
00206 {
00207         const f32 diag = m(0,0) + m(1,1) + m(2,2) + 1;
00208 
00209         if( diag > 0.0f )
00210         {
00211                 const f32 scale = sqrtf(diag) * 2.0f; // get scale from diagonal
00212 
00213                 // TODO: speed this up
00214                 X = ( m(2,1) - m(1,2)) / scale;
00215                 Y = ( m(0,2) - m(2,0)) / scale;
00216                 Z = ( m(1,0) - m(0,1)) / scale;
00217                 W = 0.25f * scale;
00218         }
00219         else
00220         {
00221                 if ( m(0,0) > m(1,1) && m(0,0) > m(2,2))
00222                 {
00223                         // 1st element of diag is greatest value
00224                         // find scale according to 1st element, and double it
00225                         const f32 scale = sqrtf( 1.0f + m(0,0) - m(1,1) - m(2,2)) * 2.0f;
00226 
00227                         // TODO: speed this up
00228                         X = 0.25f * scale;
00229                         Y = (m(0,1) + m(1,0)) / scale;
00230                         Z = (m(2,0) + m(0,2)) / scale;
00231                         W = (m(2,1) - m(1,2)) / scale;
00232                 }
00233                 else if ( m(1,1) > m(2,2))
00234                 {
00235                         // 2nd element of diag is greatest value
00236                         // find scale according to 2nd element, and double it
00237                         const f32 scale = sqrtf( 1.0f + m(1,1) - m(0,0) - m(2,2)) * 2.0f;
00238 
00239                         // TODO: speed this up
00240                         X = (m(0,1) + m(1,0) ) / scale;
00241                         Y = 0.25f * scale;
00242                         Z = (m(1,2) + m(2,1) ) / scale;
00243                         W = (m(0,2) - m(2,0) ) / scale;
00244                 }
00245                 else
00246                 {
00247                         // 3rd element of diag is greatest value
00248                         // find scale according to 3rd element, and double it
00249                         const f32 scale = sqrtf( 1.0f + m(2,2) - m(0,0) - m(1,1)) * 2.0f;
00250 
00251                         // TODO: speed this up
00252                         X = (m(0,2) + m(2,0)) / scale;
00253                         Y = (m(1,2) + m(2,1)) / scale;
00254                         Z = 0.25f * scale;
00255                         W = (m(1,0) - m(0,1)) / scale;
00256                 }
00257         }
00258 
00259         return normalize();
00260 }
00261 
00262 
00263 // multiplication operator
00264 inline quaternion quaternion::operator*(const quaternion& other) const
00265 {
00266         quaternion tmp;
00267 
00268         tmp.W = (other.W * W) - (other.X * X) - (other.Y * Y) - (other.Z * Z);
00269         tmp.X = (other.W * X) + (other.X * W) + (other.Y * Z) - (other.Z * Y);
00270         tmp.Y = (other.W * Y) + (other.Y * W) + (other.Z * X) - (other.X * Z);
00271         tmp.Z = (other.W * Z) + (other.Z * W) + (other.X * Y) - (other.Y * X);
00272 
00273         return tmp;
00274 }
00275 
00276 
00277 // multiplication operator
00278 inline quaternion quaternion::operator*(f32 s) const
00279 {
00280         return quaternion(s*X, s*Y, s*Z, s*W);
00281 }
00282 
00283 // multiplication operator
00284 inline quaternion& quaternion::operator*=(f32 s)
00285 {
00286         X*=s;
00287         Y*=s;
00288         Z*=s;
00289         W*=s;
00290         return *this;
00291 }
00292 
00293 // multiplication operator
00294 inline quaternion& quaternion::operator*=(const quaternion& other)
00295 {
00296         return (*this = other * (*this));
00297 }
00298 
00299 // add operator
00300 inline quaternion quaternion::operator+(const quaternion& b) const
00301 {
00302         return quaternion(X+b.X, Y+b.Y, Z+b.Z, W+b.W);
00303 }
00304 
00305 
00306 // Creates a matrix from this quaternion
00307 inline matrix4 quaternion::getMatrix() const
00308 {
00309         core::matrix4 m;
00310         getMatrix_transposed(m);
00311         return m;
00312 }
00313 
00314 
00318 inline void quaternion::getMatrix( matrix4 &dest, const core::vector3df &center ) const
00319 {
00320         f32 * m = dest.pointer();
00321 
00322         m[0] = 1.0f - 2.0f*Y*Y - 2.0f*Z*Z;
00323         m[1] = 2.0f*X*Y + 2.0f*Z*W;
00324         m[2] = 2.0f*X*Z - 2.0f*Y*W;
00325         m[3] = 0.0f;
00326 
00327         m[4] = 2.0f*X*Y - 2.0f*Z*W;
00328         m[5] = 1.0f - 2.0f*X*X - 2.0f*Z*Z;
00329         m[6] = 2.0f*Z*Y + 2.0f*X*W;
00330         m[7] = 0.0f;
00331 
00332         m[8] = 2.0f*X*Z + 2.0f*Y*W;
00333         m[9] = 2.0f*Z*Y - 2.0f*X*W;
00334         m[10] = 1.0f - 2.0f*X*X - 2.0f*Y*Y;
00335         m[11] = 0.0f;
00336 
00337         m[12] = center.X;
00338         m[13] = center.Y;
00339         m[14] = center.Z;
00340         m[15] = 1.f;
00341 
00342         //dest.setDefinitelyIdentityMatrix ( matrix4::BIT_IS_NOT_IDENTITY );
00343         dest.setDefinitelyIdentityMatrix ( false );
00344 }
00345 
00346 
00347 
00360 inline void quaternion::getMatrixCenter(matrix4 &dest,
00361                                         const core::vector3df &center,
00362                                         const core::vector3df &translation) const
00363 {
00364         f32 * m = dest.pointer();
00365 
00366         m[0] = 1.0f - 2.0f*Y*Y - 2.0f*Z*Z;
00367         m[1] = 2.0f*X*Y + 2.0f*Z*W;
00368         m[2] = 2.0f*X*Z - 2.0f*Y*W;
00369         m[3] = 0.0f;
00370 
00371         m[4] = 2.0f*X*Y - 2.0f*Z*W;
00372         m[5] = 1.0f - 2.0f*X*X - 2.0f*Z*Z;
00373         m[6] = 2.0f*Z*Y + 2.0f*X*W;
00374         m[7] = 0.0f;
00375 
00376         m[8] = 2.0f*X*Z + 2.0f*Y*W;
00377         m[9] = 2.0f*Z*Y - 2.0f*X*W;
00378         m[10] = 1.0f - 2.0f*X*X - 2.0f*Y*Y;
00379         m[11] = 0.0f;
00380 
00381         dest.setRotationCenter ( center, translation );
00382 }
00383 
00384 // Creates a matrix from this quaternion
00385 inline void quaternion::getMatrix_transposed( matrix4 &dest ) const
00386 {
00387         dest[0] = 1.0f - 2.0f*Y*Y - 2.0f*Z*Z;
00388         dest[4] = 2.0f*X*Y + 2.0f*Z*W;
00389         dest[8] = 2.0f*X*Z - 2.0f*Y*W;
00390         dest[12] = 0.0f;
00391 
00392         dest[1] = 2.0f*X*Y - 2.0f*Z*W;
00393         dest[5] = 1.0f - 2.0f*X*X - 2.0f*Z*Z;
00394         dest[9] = 2.0f*Z*Y + 2.0f*X*W;
00395         dest[13] = 0.0f;
00396 
00397         dest[2] = 2.0f*X*Z + 2.0f*Y*W;
00398         dest[6] = 2.0f*Z*Y - 2.0f*X*W;
00399         dest[10] = 1.0f - 2.0f*X*X - 2.0f*Y*Y;
00400         dest[14] = 0.0f;
00401 
00402         dest[3] = 0.f;
00403         dest[7] = 0.f;
00404         dest[11] = 0.f;
00405         dest[15] = 1.f;
00406         //dest.setDefinitelyIdentityMatrix ( matrix4::BIT_IS_NOT_IDENTITY );
00407         dest.setDefinitelyIdentityMatrix ( false );
00408 }
00409 
00410 
00411 
00412 // Inverts this quaternion
00413 inline quaternion& quaternion::makeInverse()
00414 {
00415         X = -X; Y = -Y; Z = -Z;
00416         return *this;
00417 }
00418 
00419 // sets new quaternion
00420 inline quaternion& quaternion::set(f32 x, f32 y, f32 z, f32 w)
00421 {
00422         X = x;
00423         Y = y;
00424         Z = z;
00425         W = w;
00426         return *this;
00427 }
00428 
00429 
00430 // sets new quaternion based on euler angles
00431 inline quaternion& quaternion::set(f32 x, f32 y, f32 z)
00432 {
00433         f64 angle;
00434 
00435         angle = x * 0.5;
00436         const f64 sr = sin(angle);
00437         const f64 cr = cos(angle);
00438 
00439         angle = y * 0.5;
00440         const f64 sp = sin(angle);
00441         const f64 cp = cos(angle);
00442 
00443         angle = z * 0.5;
00444         const f64 sy = sin(angle);
00445         const f64 cy = cos(angle);
00446 
00447         const f64 cpcy = cp * cy;
00448         const f64 spcy = sp * cy;
00449         const f64 cpsy = cp * sy;
00450         const f64 spsy = sp * sy;
00451 
00452         X = (f32)(sr * cpcy - cr * spsy);
00453         Y = (f32)(cr * spcy + sr * cpsy);
00454         Z = (f32)(cr * cpsy - sr * spcy);
00455         W = (f32)(cr * cpcy + sr * spsy);
00456 
00457         return normalize();
00458 }
00459 
00460 // sets new quaternion based on euler angles
00461 inline quaternion& quaternion::set(const core::vector3df& vec)
00462 {
00463         return set(vec.X, vec.Y, vec.Z);
00464 }
00465 
00466 // sets new quaternion based on other quaternion
00467 inline quaternion& quaternion::set(const core::quaternion& quat)
00468 {
00469         return (*this=quat);
00470 }
00471 
00472 
00474 inline bool quaternion::equals(const quaternion& other, const f32 tolerance) const
00475 {
00476         return core::equals(X, other.X, tolerance) &&
00477                 core::equals(Y, other.Y, tolerance) &&
00478                 core::equals(Z, other.Z, tolerance) &&
00479                 core::equals(W, other.W, tolerance);
00480 }
00481 
00482 
00483 // normalizes the quaternion
00484 inline quaternion& quaternion::normalize()
00485 {
00486         const f32 n = X*X + Y*Y + Z*Z + W*W;
00487 
00488         if (n == 1)
00489                 return *this;
00490 
00491         //n = 1.0f / sqrtf(n);
00492         return (*this *= reciprocal_squareroot ( n ));
00493 }
00494 
00495 
00496 // set this quaternion to the result of the linear interpolation between two quaternions
00497 inline quaternion& quaternion::lerp(quaternion q1, quaternion q2, f32 time)
00498 {
00499         const f32 scale = 1.0f - time;
00500         const f32 invscale = time;
00501         return (*this = (q1*scale) + (q2*invscale));
00502 }
00503 
00504 
00505 // set this quaternion to the result of the interpolation between two quaternions
00506 inline quaternion& quaternion::slerp(quaternion q1, quaternion q2, f32 time)
00507 {
00508         f32 angle = q1.dotProduct(q2);
00509 
00510         // make sure we use the short rotation
00511         if (angle < 0.0f)
00512         {
00513                 q1 *= -1.0f;
00514                 angle *= -1.0f;
00515         }
00516 
00517         if (angle <= 0.95f) // spherical interpolation
00518         {
00519                 const f32 theta = acosf(angle);
00520                 const f32 invsintheta = reciprocal(sinf(theta));
00521                 const f32 scale = sinf(theta * (1.0f-time)) * invsintheta;
00522                 const f32 invscale = sinf(theta * time) * invsintheta;
00523                 return (*this = (q1*scale) + (q2*invscale));
00524         }
00525         else // linear interploation
00526                 return lerp(q1,q2,time);
00527 }
00528 
00529 
00530 // calculates the dot product
00531 inline f32 quaternion::dotProduct(const quaternion& q2) const
00532 {
00533         return (X * q2.X) + (Y * q2.Y) + (Z * q2.Z) + (W * q2.W);
00534 }
00535 
00536 
00539 inline quaternion& quaternion::fromAngleAxis(f32 angle, const vector3df& axis)
00540 {
00541         const f32 fHalfAngle = 0.5f*angle;
00542         const f32 fSin = sinf(fHalfAngle);
00543         W = cosf(fHalfAngle);
00544         X = fSin*axis.X;
00545         Y = fSin*axis.Y;
00546         Z = fSin*axis.Z;
00547         return *this;
00548 }
00549 
00550 
00551 inline void quaternion::toAngleAxis(f32 &angle, core::vector3df &axis) const
00552 {
00553         const f32 scale = sqrtf(X*X + Y*Y + Z*Z);
00554 
00555         if (core::iszero(scale) || W > 1.0f || W < -1.0f)
00556         {
00557                 angle = 0.0f;
00558                 axis.X = 0.0f;
00559                 axis.Y = 1.0f;
00560                 axis.Z = 0.0f;
00561         }
00562         else
00563         {
00564                 const f32 invscale = reciprocal(scale);
00565                 angle = 2.0f * acosf(W);
00566                 axis.X = X * invscale;
00567                 axis.Y = Y * invscale;
00568                 axis.Z = Z * invscale;
00569         }
00570 }
00571 
00572 inline void quaternion::toEuler(vector3df& euler) const
00573 {
00574         const f64 sqw = W*W;
00575         const f64 sqx = X*X;
00576         const f64 sqy = Y*Y;
00577         const f64 sqz = Z*Z;
00578         const f64 test = 2.0 * (Y*W - X*Z);
00579 
00580         if (core::equals(test, 1.0, 0.000001))
00581         {
00582                 // heading = rotation about z-axis
00583                 euler.Z = (f32) (-2.0*atan2(X, W));
00584                 // bank = rotation about x-axis
00585                 euler.X = 0;
00586                 // attitude = rotation about y-axis
00587                 euler.Y = (f32) (core::PI64/2.0);
00588         }
00589         else if (core::equals(test, -1.0, 0.000001))
00590         {
00591                 // heading = rotation about z-axis
00592                 euler.Z = (f32) (2.0*atan2(X, W));
00593                 // bank = rotation about x-axis
00594                 euler.X = 0;
00595                 // attitude = rotation about y-axis
00596                 euler.Y = (f32) (core::PI64/-2.0);
00597         }
00598         else
00599         {
00600                 // heading = rotation about z-axis
00601                 euler.Z = (f32) atan2(2.0 * (X*Y +Z*W),(sqx - sqy - sqz + sqw));
00602                 // bank = rotation about x-axis
00603                 euler.X = (f32) atan2(2.0 * (Y*Z +X*W),(-sqx - sqy + sqz + sqw));
00604                 // attitude = rotation about y-axis
00605                 euler.Y = (f32) asin( clamp(test, -1.0, 1.0) );
00606         }
00607 }
00608 
00609 
00610 inline vector3df quaternion::operator* (const vector3df& v) const
00611 {
00612         // nVidia SDK implementation
00613 
00614         vector3df uv, uuv;
00615         vector3df qvec(X, Y, Z);
00616         uv = qvec.crossProduct(v);
00617         uuv = qvec.crossProduct(uv);
00618         uv *= (2.0f * W);
00619         uuv *= 2.0f;
00620 
00621         return v + uv + uuv;
00622 }
00623 
00624 // set quaternion to identity
00625 inline core::quaternion& quaternion::makeIdentity()
00626 {
00627         W = 1.f;
00628         X = 0.f;
00629         Y = 0.f;
00630         Z = 0.f;
00631         return *this;
00632 }
00633 
00634 inline core::quaternion& quaternion::rotationFromTo(const vector3df& from, const vector3df& to)
00635 {
00636         // Based on Stan Melax's article in Game Programming Gems
00637         // Copy, since cannot modify local
00638         vector3df v0 = from;
00639         vector3df v1 = to;
00640         v0.normalize();
00641         v1.normalize();
00642 
00643         const f32 d = v0.dotProduct(v1);
00644         if (d >= 1.0f) // If dot == 1, vectors are the same
00645         {
00646                 return makeIdentity();
00647         }
00648         else if (d <= -1.0f) // exactly opposite
00649         {
00650                 core::vector3df axis(1.0f, 0.f, 0.f);
00651                 axis = axis.crossProduct(v0);
00652                 if (axis.getLength()==0)
00653                 {
00654                         axis.set(0.f,1.f,0.f);
00655                         axis.crossProduct(v0);
00656                 }
00657                 // same as fromAngleAxis(core::PI, axis).normalize();
00658                 return set(axis.X, axis.Y, axis.Z, 0).normalize();
00659         }
00660 
00661         const f32 s = sqrtf( (1+d)*2 ); // optimize inv_sqrt
00662         const f32 invs = 1.f / s;
00663         const vector3df c = v0.crossProduct(v1)*invs;
00664         return set(c.X, c.Y, c.Z, s * 0.5f).normalize();
00665 }
00666 
00667 
00668 } // end namespace core
00669 } // end namespace irr
00670 
00671 #endif
00672