| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610 |
- /*
- -----------------------------------------------------------------------------
- This source file is part of OGRE
- (Object-oriented Graphics Rendering Engine)
- For the latest info, see http://www.ogre3d.org/
- Copyright (c) 2000-2011 Torus Knot Software Ltd
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- -----------------------------------------------------------------------------
- */
- // NOTE THAT THIS FILE IS BASED ON MATERIAL FROM:
- // Geometric Tools, LLC
- // Copyright (c) 1998-2010
- // Distributed under the Boost Software License, Version 1.0.
- // http://www.boost.org/LICENSE_1_0.txt
- // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
- #include "CmQuaternion.h"
- #include "CmMath.h"
- #include "CmMatrix3.h"
- #include "CmVector3.h"
- namespace CamelotEngine {
- const float Quaternion::ms_fEpsilon = 1e-03f;
- const Quaternion Quaternion::ZERO(0.0,0.0,0.0,0.0);
- const Quaternion Quaternion::IDENTITY(1.0,0.0,0.0,0.0);
- //-----------------------------------------------------------------------
- void Quaternion::FromRotationMatrix (const Matrix3& kRot)
- {
- // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
- // article "Quaternion Calculus and Fast Animation".
- float fTrace = kRot[0][0]+kRot[1][1]+kRot[2][2];
- float fRoot;
- if ( fTrace > 0.0 )
- {
- // |w| > 1/2, may as well choose w > 1/2
- fRoot = Math::Sqrt(fTrace + 1.0f); // 2w
- w = 0.5f*fRoot;
- fRoot = 0.5f/fRoot; // 1/(4w)
- x = (kRot[2][1]-kRot[1][2])*fRoot;
- y = (kRot[0][2]-kRot[2][0])*fRoot;
- z = (kRot[1][0]-kRot[0][1])*fRoot;
- }
- else
- {
- // |w| <= 1/2
- static size_t s_iNext[3] = { 1, 2, 0 };
- size_t i = 0;
- if ( kRot[1][1] > kRot[0][0] )
- i = 1;
- if ( kRot[2][2] > kRot[i][i] )
- i = 2;
- size_t j = s_iNext[i];
- size_t k = s_iNext[j];
- fRoot = Math::Sqrt(kRot[i][i]-kRot[j][j]-kRot[k][k] + 1.0f);
- float* apkQuat[3] = { &x, &y, &z };
- *apkQuat[i] = 0.5f*fRoot;
- fRoot = 0.5f/fRoot;
- w = (kRot[k][j]-kRot[j][k])*fRoot;
- *apkQuat[j] = (kRot[j][i]+kRot[i][j])*fRoot;
- *apkQuat[k] = (kRot[k][i]+kRot[i][k])*fRoot;
- }
- normalise();
- }
- //-----------------------------------------------------------------------
- void Quaternion::ToRotationMatrix (Matrix3& kRot) const
- {
- float fTx = x+x;
- float fTy = y+y;
- float fTz = z+z;
- float fTwx = fTx*w;
- float fTwy = fTy*w;
- float fTwz = fTz*w;
- float fTxx = fTx*x;
- float fTxy = fTy*x;
- float fTxz = fTz*x;
- float fTyy = fTy*y;
- float fTyz = fTz*y;
- float fTzz = fTz*z;
- kRot[0][0] = 1.0f-(fTyy+fTzz);
- kRot[0][1] = fTxy-fTwz;
- kRot[0][2] = fTxz+fTwy;
- kRot[1][0] = fTxy+fTwz;
- kRot[1][1] = 1.0f-(fTxx+fTzz);
- kRot[1][2] = fTyz-fTwx;
- kRot[2][0] = fTxz-fTwy;
- kRot[2][1] = fTyz+fTwx;
- kRot[2][2] = 1.0f-(fTxx+fTyy);
- }
- //-----------------------------------------------------------------------
- void Quaternion::FromAngleAxis (const Radian& rfAngle,
- const Vector3& rkAxis)
- {
- // assert: axis[] is unit length
- //
- // The quaternion representing the rotation is
- // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k)
- Radian fHalfAngle ( 0.5*rfAngle );
- float fSin = Math::Sin(fHalfAngle);
- w = Math::Cos(fHalfAngle);
- x = fSin*rkAxis.x;
- y = fSin*rkAxis.y;
- z = fSin*rkAxis.z;
- }
- //-----------------------------------------------------------------------
- void Quaternion::ToAngleAxis (Radian& rfAngle, Vector3& rkAxis) const
- {
- // The quaternion representing the rotation is
- // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k)
- float fSqrLength = x*x+y*y+z*z;
- if ( fSqrLength > 0.0 )
- {
- rfAngle = 2.0*Math::ACos(w);
- float fInvLength = Math::InvSqrt(fSqrLength);
- rkAxis.x = x*fInvLength;
- rkAxis.y = y*fInvLength;
- rkAxis.z = z*fInvLength;
- }
- else
- {
- // angle is 0 (mod 2*pi), so any axis will do
- rfAngle = Radian(0.0);
- rkAxis.x = 1.0;
- rkAxis.y = 0.0;
- rkAxis.z = 0.0;
- }
- }
- //-----------------------------------------------------------------------
- void Quaternion::FromAxes (const Vector3* akAxis)
- {
- Matrix3 kRot;
- for (size_t iCol = 0; iCol < 3; iCol++)
- {
- kRot[0][iCol] = akAxis[iCol].x;
- kRot[1][iCol] = akAxis[iCol].y;
- kRot[2][iCol] = akAxis[iCol].z;
- }
- FromRotationMatrix(kRot);
- }
- //-----------------------------------------------------------------------
- void Quaternion::FromAxes (const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis)
- {
- Matrix3 kRot;
- kRot[0][0] = xaxis.x;
- kRot[1][0] = xaxis.y;
- kRot[2][0] = xaxis.z;
- kRot[0][1] = yaxis.x;
- kRot[1][1] = yaxis.y;
- kRot[2][1] = yaxis.z;
- kRot[0][2] = zaxis.x;
- kRot[1][2] = zaxis.y;
- kRot[2][2] = zaxis.z;
- FromRotationMatrix(kRot);
- }
- //-----------------------------------------------------------------------
- void Quaternion::ToAxes (Vector3* akAxis) const
- {
- Matrix3 kRot;
- ToRotationMatrix(kRot);
- for (size_t iCol = 0; iCol < 3; iCol++)
- {
- akAxis[iCol].x = kRot[0][iCol];
- akAxis[iCol].y = kRot[1][iCol];
- akAxis[iCol].z = kRot[2][iCol];
- }
- }
- //-----------------------------------------------------------------------
- Vector3 Quaternion::xAxis(void) const
- {
- //float fTx = 2.0*x;
- float fTy = 2.0f*y;
- float fTz = 2.0f*z;
- float fTwy = fTy*w;
- float fTwz = fTz*w;
- float fTxy = fTy*x;
- float fTxz = fTz*x;
- float fTyy = fTy*y;
- float fTzz = fTz*z;
- return Vector3(1.0f-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy);
- }
- //-----------------------------------------------------------------------
- Vector3 Quaternion::yAxis(void) const
- {
- float fTx = 2.0f*x;
- float fTy = 2.0f*y;
- float fTz = 2.0f*z;
- float fTwx = fTx*w;
- float fTwz = fTz*w;
- float fTxx = fTx*x;
- float fTxy = fTy*x;
- float fTyz = fTz*y;
- float fTzz = fTz*z;
- return Vector3(fTxy-fTwz, 1.0f-(fTxx+fTzz), fTyz+fTwx);
- }
- //-----------------------------------------------------------------------
- Vector3 Quaternion::zAxis(void) const
- {
- float fTx = 2.0f*x;
- float fTy = 2.0f*y;
- float fTz = 2.0f*z;
- float fTwx = fTx*w;
- float fTwy = fTy*w;
- float fTxx = fTx*x;
- float fTxz = fTz*x;
- float fTyy = fTy*y;
- float fTyz = fTz*y;
- return Vector3(fTxz+fTwy, fTyz-fTwx, 1.0f-(fTxx+fTyy));
- }
- //-----------------------------------------------------------------------
- void Quaternion::ToAxes (Vector3& xaxis, Vector3& yaxis, Vector3& zaxis) const
- {
- Matrix3 kRot;
- ToRotationMatrix(kRot);
- xaxis.x = kRot[0][0];
- xaxis.y = kRot[1][0];
- xaxis.z = kRot[2][0];
- yaxis.x = kRot[0][1];
- yaxis.y = kRot[1][1];
- yaxis.z = kRot[2][1];
- zaxis.x = kRot[0][2];
- zaxis.y = kRot[1][2];
- zaxis.z = kRot[2][2];
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::operator+ (const Quaternion& rkQ) const
- {
- return Quaternion(w+rkQ.w,x+rkQ.x,y+rkQ.y,z+rkQ.z);
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::operator- (const Quaternion& rkQ) const
- {
- return Quaternion(w-rkQ.w,x-rkQ.x,y-rkQ.y,z-rkQ.z);
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::operator* (const Quaternion& rkQ) const
- {
- // NOTE: Multiplication is not generally commutative, so in most
- // cases p*q != q*p.
- return Quaternion
- (
- w * rkQ.w - x * rkQ.x - y * rkQ.y - z * rkQ.z,
- w * rkQ.x + x * rkQ.w + y * rkQ.z - z * rkQ.y,
- w * rkQ.y + y * rkQ.w + z * rkQ.x - x * rkQ.z,
- w * rkQ.z + z * rkQ.w + x * rkQ.y - y * rkQ.x
- );
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::operator* (float fScalar) const
- {
- return Quaternion(fScalar*w,fScalar*x,fScalar*y,fScalar*z);
- }
- //-----------------------------------------------------------------------
- Quaternion operator* (float fScalar, const Quaternion& rkQ)
- {
- return Quaternion(fScalar*rkQ.w,fScalar*rkQ.x,fScalar*rkQ.y,
- fScalar*rkQ.z);
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::operator- () const
- {
- return Quaternion(-w,-x,-y,-z);
- }
- //-----------------------------------------------------------------------
- float Quaternion::Dot (const Quaternion& rkQ) const
- {
- return w*rkQ.w+x*rkQ.x+y*rkQ.y+z*rkQ.z;
- }
- //-----------------------------------------------------------------------
- float Quaternion::Norm () const
- {
- return w*w+x*x+y*y+z*z;
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::Inverse () const
- {
- float fNorm = w*w+x*x+y*y+z*z;
- if ( fNorm > 0.0 )
- {
- float fInvNorm = 1.0f/fNorm;
- return Quaternion(w*fInvNorm,-x*fInvNorm,-y*fInvNorm,-z*fInvNorm);
- }
- else
- {
- // return an invalid result to flag the error
- return ZERO;
- }
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::UnitInverse () const
- {
- // assert: 'this' is unit length
- return Quaternion(w,-x,-y,-z);
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::Exp () const
- {
- // If q = A*(x*i+y*j+z*k) where (x,y,z) is unit length, then
- // exp(q) = cos(A)+sin(A)*(x*i+y*j+z*k). If sin(A) is near zero,
- // use exp(q) = cos(A)+A*(x*i+y*j+z*k) since A/sin(A) has limit 1.
- Radian fAngle ( Math::Sqrt(x*x+y*y+z*z) );
- float fSin = Math::Sin(fAngle);
- Quaternion kResult;
- kResult.w = Math::Cos(fAngle);
- if ( Math::Abs(fSin) >= ms_fEpsilon )
- {
- float fCoeff = fSin/(fAngle.valueRadians());
- kResult.x = fCoeff*x;
- kResult.y = fCoeff*y;
- kResult.z = fCoeff*z;
- }
- else
- {
- kResult.x = x;
- kResult.y = y;
- kResult.z = z;
- }
- return kResult;
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::Log () const
- {
- // If q = cos(A)+sin(A)*(x*i+y*j+z*k) where (x,y,z) is unit length, then
- // log(q) = A*(x*i+y*j+z*k). If sin(A) is near zero, use log(q) =
- // sin(A)*(x*i+y*j+z*k) since sin(A)/A has limit 1.
- Quaternion kResult;
- kResult.w = 0.0;
- if ( Math::Abs(w) < 1.0 )
- {
- Radian fAngle ( Math::ACos(w) );
- float fSin = Math::Sin(fAngle);
- if ( Math::Abs(fSin) >= ms_fEpsilon )
- {
- float fCoeff = fAngle.valueRadians()/fSin;
- kResult.x = fCoeff*x;
- kResult.y = fCoeff*y;
- kResult.z = fCoeff*z;
- return kResult;
- }
- }
- kResult.x = x;
- kResult.y = y;
- kResult.z = z;
- return kResult;
- }
- //-----------------------------------------------------------------------
- Vector3 Quaternion::operator* (const Vector3& v) const
- {
- // nVidia SDK implementation
- Vector3 uv, uuv;
- Vector3 qvec(x, y, z);
- uv = qvec.crossProduct(v);
- uuv = qvec.crossProduct(uv);
- uv *= (2.0f * w);
- uuv *= 2.0f;
- return v + uv + uuv;
- }
- //-----------------------------------------------------------------------
- bool Quaternion::equals(const Quaternion& rhs, const Radian& tolerance) const
- {
- float fCos = Dot(rhs);
- Radian angle = Math::ACos(fCos);
- return (Math::Abs(angle.valueRadians()) <= tolerance.valueRadians())
- || Math::RealEqual(angle.valueRadians(), Math::PI, tolerance.valueRadians());
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::Slerp (float fT, const Quaternion& rkP,
- const Quaternion& rkQ, bool shortestPath)
- {
- float fCos = rkP.Dot(rkQ);
- Quaternion rkT;
- // Do we need to invert rotation?
- if (fCos < 0.0f && shortestPath)
- {
- fCos = -fCos;
- rkT = -rkQ;
- }
- else
- {
- rkT = rkQ;
- }
- if (Math::Abs(fCos) < 1 - ms_fEpsilon)
- {
- // Standard case (slerp)
- float fSin = Math::Sqrt(1 - Math::Sqr(fCos));
- Radian fAngle = Math::ATan2(fSin, fCos);
- float fInvSin = 1.0f / fSin;
- float fCoeff0 = Math::Sin((1.0f - fT) * fAngle) * fInvSin;
- float fCoeff1 = Math::Sin(fT * fAngle) * fInvSin;
- return fCoeff0 * rkP + fCoeff1 * rkT;
- }
- else
- {
- // There are two situations:
- // 1. "rkP" and "rkQ" are very close (fCos ~= +1), so we can do a linear
- // interpolation safely.
- // 2. "rkP" and "rkQ" are almost inverse of each other (fCos ~= -1), there
- // are an infinite number of possibilities interpolation. but we haven't
- // have method to fix this case, so just use linear interpolation here.
- Quaternion t = (1.0f - fT) * rkP + fT * rkT;
- // taking the complement requires renormalisation
- t.normalise();
- return t;
- }
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::SlerpExtraSpins (float fT,
- const Quaternion& rkP, const Quaternion& rkQ, int iExtraSpins)
- {
- float fCos = rkP.Dot(rkQ);
- Radian fAngle ( Math::ACos(fCos) );
- if ( Math::Abs(fAngle.valueRadians()) < ms_fEpsilon )
- return rkP;
- float fSin = Math::Sin(fAngle);
- Radian fPhase ( Math::PI*iExtraSpins*fT );
- float fInvSin = 1.0f/fSin;
- float fCoeff0 = Math::Sin((1.0f-fT)*fAngle - fPhase)*fInvSin;
- float fCoeff1 = Math::Sin(fT*fAngle + fPhase)*fInvSin;
- return fCoeff0*rkP + fCoeff1*rkQ;
- }
- //-----------------------------------------------------------------------
- void Quaternion::Intermediate (const Quaternion& rkQ0,
- const Quaternion& rkQ1, const Quaternion& rkQ2,
- Quaternion& rkA, Quaternion& rkB)
- {
- // assert: q0, q1, q2 are unit quaternions
- Quaternion kQ0inv = rkQ0.UnitInverse();
- Quaternion kQ1inv = rkQ1.UnitInverse();
- Quaternion rkP0 = kQ0inv*rkQ1;
- Quaternion rkP1 = kQ1inv*rkQ2;
- Quaternion kArg = 0.25*(rkP0.Log()-rkP1.Log());
- Quaternion kMinusArg = -kArg;
- rkA = rkQ1*kArg.Exp();
- rkB = rkQ1*kMinusArg.Exp();
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::Squad (float fT,
- const Quaternion& rkP, const Quaternion& rkA,
- const Quaternion& rkB, const Quaternion& rkQ, bool shortestPath)
- {
- float fSlerpT = 2.0f*fT*(1.0f-fT);
- Quaternion kSlerpP = Slerp(fT, rkP, rkQ, shortestPath);
- Quaternion kSlerpQ = Slerp(fT, rkA, rkB);
- return Slerp(fSlerpT, kSlerpP ,kSlerpQ);
- }
- //-----------------------------------------------------------------------
- float Quaternion::normalise(void)
- {
- float len = Norm();
- float factor = 1.0f / Math::Sqrt(len);
- *this = *this * factor;
- return len;
- }
- //-----------------------------------------------------------------------
- Radian Quaternion::getRoll(bool reprojectAxis) const
- {
- if (reprojectAxis)
- {
- // roll = atan2(localx.y, localx.x)
- // pick parts of xAxis() implementation that we need
- // float fTx = 2.0*x;
- float fTy = 2.0f*y;
- float fTz = 2.0f*z;
- float fTwz = fTz*w;
- float fTxy = fTy*x;
- float fTyy = fTy*y;
- float fTzz = fTz*z;
- // Vector3(1.0-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy);
- return Radian(Math::ATan2(fTxy+fTwz, 1.0f-(fTyy+fTzz)));
- }
- else
- {
- return Radian(Math::ATan2(2*(x*y + w*z), w*w + x*x - y*y - z*z));
- }
- }
- //-----------------------------------------------------------------------
- Radian Quaternion::getPitch(bool reprojectAxis) const
- {
- if (reprojectAxis)
- {
- // pitch = atan2(localy.z, localy.y)
- // pick parts of yAxis() implementation that we need
- float fTx = 2.0f*x;
- // float fTy = 2.0f*y;
- float fTz = 2.0f*z;
- float fTwx = fTx*w;
- float fTxx = fTx*x;
- float fTyz = fTz*y;
- float fTzz = fTz*z;
- // Vector3(fTxy-fTwz, 1.0-(fTxx+fTzz), fTyz+fTwx);
- return Radian(Math::ATan2(fTyz+fTwx, 1.0f-(fTxx+fTzz)));
- }
- else
- {
- // internal version
- return Radian(Math::ATan2(2*(y*z + w*x), w*w - x*x - y*y + z*z));
- }
- }
- //-----------------------------------------------------------------------
- Radian Quaternion::getYaw(bool reprojectAxis) const
- {
- if (reprojectAxis)
- {
- // yaw = atan2(localz.x, localz.z)
- // pick parts of zAxis() implementation that we need
- float fTx = 2.0f*x;
- float fTy = 2.0f*y;
- float fTz = 2.0f*z;
- float fTwy = fTy*w;
- float fTxx = fTx*x;
- float fTxz = fTz*x;
- float fTyy = fTy*y;
- // Vector3(fTxz+fTwy, fTyz-fTwx, 1.0-(fTxx+fTyy));
- return Radian(Math::ATan2(fTxz+fTwy, 1.0f-(fTxx+fTyy)));
- }
- else
- {
- // internal version
- return Radian(Math::ASin(-2*(x*z - w*y)));
- }
- }
- //-----------------------------------------------------------------------
- Quaternion Quaternion::nlerp(float fT, const Quaternion& rkP,
- const Quaternion& rkQ, bool shortestPath)
- {
- Quaternion result;
- float fCos = rkP.Dot(rkQ);
- if (fCos < 0.0f && shortestPath)
- {
- result = rkP + fT * ((-rkQ) - rkP);
- }
- else
- {
- result = rkP + fT * (rkQ - rkP);
- }
- result.normalise();
- return result;
- }
- }
|