| Irrlicht 3D Engine |

quaternion.h
Go to the documentation of this file.

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