|
IrrlichtEngine
|
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 ¢er, 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 ¢er ) 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 ¢er, 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