Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/OgreMain/src/OgreQuaternion.cpp @ 3

Last change on this file since 3 was 3, checked in by anonymous, 17 years ago

=update

File size: 19.4 KB
RevLine 
[3]1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4    (Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2006 Torus Knot Software Ltd
8Also see acknowledgements in Readme.html
9
10This program is free software; you can redistribute it and/or modify it under
11the terms of the GNU Lesser General Public License as published by the Free Software
12Foundation; either version 2 of the License, or (at your option) any later
13version.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public License along with
20this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22http://www.gnu.org/copyleft/lesser.txt.
23
24You may alternatively use this source under the terms of a specific version of
25the OGRE Unrestricted License provided you have obtained such a license from
26Torus Knot Software Ltd.
27-----------------------------------------------------------------------------
28*/
29#include "OgreStableHeaders.h"
30// NOTE THAT THIS FILE IS BASED ON MATERIAL FROM:
31
32// Magic Software, Inc.
33// http://www.geometrictools.com
34// Copyright (c) 2000, All Rights Reserved
35//
36// Source code from Magic Software is supplied under the terms of a license
37// agreement and may not be copied or disclosed except in accordance with the
38// terms of that agreement.  The various license agreements may be found at
39// the Magic Software web site.  This file is subject to the license
40//
41// FREE SOURCE CODE
42// http://www.geometrictools.com/License/WildMagic3License.pdf
43
44#include "OgreQuaternion.h"
45
46#include "OgreMath.h"
47#include "OgreMatrix3.h"
48#include "OgreVector3.h"
49
50namespace Ogre {
51
52    const Real Quaternion::ms_fEpsilon = 1e-03;
53    const Quaternion Quaternion::ZERO(0.0,0.0,0.0,0.0);
54    const Quaternion Quaternion::IDENTITY(1.0,0.0,0.0,0.0);
55
56    //-----------------------------------------------------------------------
57    void Quaternion::FromRotationMatrix (const Matrix3& kRot)
58    {
59        // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
60        // article "Quaternion Calculus and Fast Animation".
61
62        Real fTrace = kRot[0][0]+kRot[1][1]+kRot[2][2];
63        Real fRoot;
64
65        if ( fTrace > 0.0 )
66        {
67            // |w| > 1/2, may as well choose w > 1/2
68            fRoot = Math::Sqrt(fTrace + 1.0);  // 2w
69            w = 0.5*fRoot;
70            fRoot = 0.5/fRoot;  // 1/(4w)
71            x = (kRot[2][1]-kRot[1][2])*fRoot;
72            y = (kRot[0][2]-kRot[2][0])*fRoot;
73            z = (kRot[1][0]-kRot[0][1])*fRoot;
74        }
75        else
76        {
77            // |w| <= 1/2
78            static size_t s_iNext[3] = { 1, 2, 0 };
79            size_t i = 0;
80            if ( kRot[1][1] > kRot[0][0] )
81                i = 1;
82            if ( kRot[2][2] > kRot[i][i] )
83                i = 2;
84            size_t j = s_iNext[i];
85            size_t k = s_iNext[j];
86
87            fRoot = Math::Sqrt(kRot[i][i]-kRot[j][j]-kRot[k][k] + 1.0);
88            Real* apkQuat[3] = { &x, &y, &z };
89            *apkQuat[i] = 0.5*fRoot;
90            fRoot = 0.5/fRoot;
91            w = (kRot[k][j]-kRot[j][k])*fRoot;
92            *apkQuat[j] = (kRot[j][i]+kRot[i][j])*fRoot;
93            *apkQuat[k] = (kRot[k][i]+kRot[i][k])*fRoot;
94        }
95    }
96    //-----------------------------------------------------------------------
97    void Quaternion::ToRotationMatrix (Matrix3& kRot) const
98    {
99        Real fTx  = 2.0*x;
100        Real fTy  = 2.0*y;
101        Real fTz  = 2.0*z;
102        Real fTwx = fTx*w;
103        Real fTwy = fTy*w;
104        Real fTwz = fTz*w;
105        Real fTxx = fTx*x;
106        Real fTxy = fTy*x;
107        Real fTxz = fTz*x;
108        Real fTyy = fTy*y;
109        Real fTyz = fTz*y;
110        Real fTzz = fTz*z;
111
112        kRot[0][0] = 1.0-(fTyy+fTzz);
113        kRot[0][1] = fTxy-fTwz;
114        kRot[0][2] = fTxz+fTwy;
115        kRot[1][0] = fTxy+fTwz;
116        kRot[1][1] = 1.0-(fTxx+fTzz);
117        kRot[1][2] = fTyz-fTwx;
118        kRot[2][0] = fTxz-fTwy;
119        kRot[2][1] = fTyz+fTwx;
120        kRot[2][2] = 1.0-(fTxx+fTyy);
121    }
122    //-----------------------------------------------------------------------
123    void Quaternion::FromAngleAxis (const Radian& rfAngle,
124        const Vector3& rkAxis)
125    {
126        // assert:  axis[] is unit length
127        //
128        // The quaternion representing the rotation is
129        //   q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k)
130
131        Radian fHalfAngle ( 0.5*rfAngle );
132        Real fSin = Math::Sin(fHalfAngle);
133        w = Math::Cos(fHalfAngle);
134        x = fSin*rkAxis.x;
135        y = fSin*rkAxis.y;
136        z = fSin*rkAxis.z;
137    }
138    //-----------------------------------------------------------------------
139    void Quaternion::ToAngleAxis (Radian& rfAngle, Vector3& rkAxis) const
140    {
141        // The quaternion representing the rotation is
142        //   q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k)
143
144        Real fSqrLength = x*x+y*y+z*z;
145        if ( fSqrLength > 0.0 )
146        {
147            rfAngle = 2.0*Math::ACos(w);
148            Real fInvLength = Math::InvSqrt(fSqrLength);
149            rkAxis.x = x*fInvLength;
150            rkAxis.y = y*fInvLength;
151            rkAxis.z = z*fInvLength;
152        }
153        else
154        {
155            // angle is 0 (mod 2*pi), so any axis will do
156            rfAngle = Radian(0.0);
157            rkAxis.x = 1.0;
158            rkAxis.y = 0.0;
159            rkAxis.z = 0.0;
160        }
161    }
162    //-----------------------------------------------------------------------
163    void Quaternion::FromAxes (const Vector3* akAxis)
164    {
165        Matrix3 kRot;
166
167        for (size_t iCol = 0; iCol < 3; iCol++)
168        {
169            kRot[0][iCol] = akAxis[iCol].x;
170            kRot[1][iCol] = akAxis[iCol].y;
171            kRot[2][iCol] = akAxis[iCol].z;
172        }
173
174        FromRotationMatrix(kRot);
175    }
176    //-----------------------------------------------------------------------
177    void Quaternion::FromAxes (const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis)
178    {
179        Matrix3 kRot;
180
181        kRot[0][0] = xaxis.x;
182        kRot[1][0] = xaxis.y;
183        kRot[2][0] = xaxis.z;
184
185        kRot[0][1] = yaxis.x;
186        kRot[1][1] = yaxis.y;
187        kRot[2][1] = yaxis.z;
188
189        kRot[0][2] = zaxis.x;
190        kRot[1][2] = zaxis.y;
191        kRot[2][2] = zaxis.z;
192
193        FromRotationMatrix(kRot);
194
195    }
196    //-----------------------------------------------------------------------
197    void Quaternion::ToAxes (Vector3* akAxis) const
198    {
199        Matrix3 kRot;
200
201        ToRotationMatrix(kRot);
202
203        for (size_t iCol = 0; iCol < 3; iCol++)
204        {
205            akAxis[iCol].x = kRot[0][iCol];
206            akAxis[iCol].y = kRot[1][iCol];
207            akAxis[iCol].z = kRot[2][iCol];
208        }
209    }
210    //-----------------------------------------------------------------------
211    Vector3 Quaternion::xAxis(void) const
212    {
213        //Real fTx  = 2.0*x;
214        Real fTy  = 2.0*y;
215        Real fTz  = 2.0*z;
216        Real fTwy = fTy*w;
217        Real fTwz = fTz*w;
218        Real fTxy = fTy*x;
219        Real fTxz = fTz*x;
220        Real fTyy = fTy*y;
221        Real fTzz = fTz*z;
222
223        return Vector3(1.0-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy);
224    }
225    //-----------------------------------------------------------------------
226    Vector3 Quaternion::yAxis(void) const
227    {
228        Real fTx  = 2.0*x;
229        Real fTy  = 2.0*y;
230        Real fTz  = 2.0*z;
231        Real fTwx = fTx*w;
232        Real fTwz = fTz*w;
233        Real fTxx = fTx*x;
234        Real fTxy = fTy*x;
235        Real fTyz = fTz*y;
236        Real fTzz = fTz*z;
237
238        return Vector3(fTxy-fTwz, 1.0-(fTxx+fTzz), fTyz+fTwx);
239    }
240    //-----------------------------------------------------------------------
241    Vector3 Quaternion::zAxis(void) const
242    {
243        Real fTx  = 2.0*x;
244        Real fTy  = 2.0*y;
245        Real fTz  = 2.0*z;
246        Real fTwx = fTx*w;
247        Real fTwy = fTy*w;
248        Real fTxx = fTx*x;
249        Real fTxz = fTz*x;
250        Real fTyy = fTy*y;
251        Real fTyz = fTz*y;
252
253        return Vector3(fTxz+fTwy, fTyz-fTwx, 1.0-(fTxx+fTyy));
254    }
255    //-----------------------------------------------------------------------
256    void Quaternion::ToAxes (Vector3& xaxis, Vector3& yaxis, Vector3& zaxis) const
257    {
258        Matrix3 kRot;
259
260        ToRotationMatrix(kRot);
261
262        xaxis.x = kRot[0][0];
263        xaxis.y = kRot[1][0];
264        xaxis.z = kRot[2][0];
265
266        yaxis.x = kRot[0][1];
267        yaxis.y = kRot[1][1];
268        yaxis.z = kRot[2][1];
269
270        zaxis.x = kRot[0][2];
271        zaxis.y = kRot[1][2];
272        zaxis.z = kRot[2][2];
273    }
274
275    //-----------------------------------------------------------------------
276    Quaternion Quaternion::operator+ (const Quaternion& rkQ) const
277    {
278        return Quaternion(w+rkQ.w,x+rkQ.x,y+rkQ.y,z+rkQ.z);
279    }
280    //-----------------------------------------------------------------------
281    Quaternion Quaternion::operator- (const Quaternion& rkQ) const
282    {
283        return Quaternion(w-rkQ.w,x-rkQ.x,y-rkQ.y,z-rkQ.z);
284    }
285    //-----------------------------------------------------------------------
286    Quaternion Quaternion::operator* (const Quaternion& rkQ) const
287    {
288        // NOTE:  Multiplication is not generally commutative, so in most
289        // cases p*q != q*p.
290
291        return Quaternion
292        (
293            w * rkQ.w - x * rkQ.x - y * rkQ.y - z * rkQ.z,
294            w * rkQ.x + x * rkQ.w + y * rkQ.z - z * rkQ.y,
295            w * rkQ.y + y * rkQ.w + z * rkQ.x - x * rkQ.z,
296            w * rkQ.z + z * rkQ.w + x * rkQ.y - y * rkQ.x
297        );
298    }
299    //-----------------------------------------------------------------------
300    Quaternion Quaternion::operator* (Real fScalar) const
301    {
302        return Quaternion(fScalar*w,fScalar*x,fScalar*y,fScalar*z);
303    }
304    //-----------------------------------------------------------------------
305    Quaternion operator* (Real fScalar, const Quaternion& rkQ)
306    {
307        return Quaternion(fScalar*rkQ.w,fScalar*rkQ.x,fScalar*rkQ.y,
308            fScalar*rkQ.z);
309    }
310    //-----------------------------------------------------------------------
311    Quaternion Quaternion::operator- () const
312    {
313        return Quaternion(-w,-x,-y,-z);
314    }
315    //-----------------------------------------------------------------------
316    Real Quaternion::Dot (const Quaternion& rkQ) const
317    {
318        return w*rkQ.w+x*rkQ.x+y*rkQ.y+z*rkQ.z;
319    }
320    //-----------------------------------------------------------------------
321    Real Quaternion::Norm () const
322    {
323        return w*w+x*x+y*y+z*z;
324    }
325    //-----------------------------------------------------------------------
326    Quaternion Quaternion::Inverse () const
327    {
328        Real fNorm = w*w+x*x+y*y+z*z;
329        if ( fNorm > 0.0 )
330        {
331            Real fInvNorm = 1.0/fNorm;
332            return Quaternion(w*fInvNorm,-x*fInvNorm,-y*fInvNorm,-z*fInvNorm);
333        }
334        else
335        {
336            // return an invalid result to flag the error
337            return ZERO;
338        }
339    }
340    //-----------------------------------------------------------------------
341    Quaternion Quaternion::UnitInverse () const
342    {
343        // assert:  'this' is unit length
344        return Quaternion(w,-x,-y,-z);
345    }
346    //-----------------------------------------------------------------------
347    Quaternion Quaternion::Exp () const
348    {
349        // If q = A*(x*i+y*j+z*k) where (x,y,z) is unit length, then
350        // exp(q) = cos(A)+sin(A)*(x*i+y*j+z*k).  If sin(A) is near zero,
351        // use exp(q) = cos(A)+A*(x*i+y*j+z*k) since A/sin(A) has limit 1.
352
353        Radian fAngle ( Math::Sqrt(x*x+y*y+z*z) );
354        Real fSin = Math::Sin(fAngle);
355
356        Quaternion kResult;
357        kResult.w = Math::Cos(fAngle);
358
359        if ( Math::Abs(fSin) >= ms_fEpsilon )
360        {
361            Real fCoeff = fSin/(fAngle.valueRadians());
362            kResult.x = fCoeff*x;
363            kResult.y = fCoeff*y;
364            kResult.z = fCoeff*z;
365        }
366        else
367        {
368            kResult.x = x;
369            kResult.y = y;
370            kResult.z = z;
371        }
372
373        return kResult;
374    }
375    //-----------------------------------------------------------------------
376    Quaternion Quaternion::Log () const
377    {
378        // If q = cos(A)+sin(A)*(x*i+y*j+z*k) where (x,y,z) is unit length, then
379        // log(q) = A*(x*i+y*j+z*k).  If sin(A) is near zero, use log(q) =
380        // sin(A)*(x*i+y*j+z*k) since sin(A)/A has limit 1.
381
382        Quaternion kResult;
383        kResult.w = 0.0;
384
385        if ( Math::Abs(w) < 1.0 )
386        {
387            Radian fAngle ( Math::ACos(w) );
388            Real fSin = Math::Sin(fAngle);
389            if ( Math::Abs(fSin) >= ms_fEpsilon )
390            {
391                Real fCoeff = fAngle.valueRadians()/fSin;
392                kResult.x = fCoeff*x;
393                kResult.y = fCoeff*y;
394                kResult.z = fCoeff*z;
395                return kResult;
396            }
397        }
398
399        kResult.x = x;
400        kResult.y = y;
401        kResult.z = z;
402
403        return kResult;
404    }
405    //-----------------------------------------------------------------------
406    Vector3 Quaternion::operator* (const Vector3& v) const
407    {
408                // nVidia SDK implementation
409                Vector3 uv, uuv;
410                Vector3 qvec(x, y, z);
411                uv = qvec.crossProduct(v);
412                uuv = qvec.crossProduct(uv);
413                uv *= (2.0f * w);
414                uuv *= 2.0f;
415
416                return v + uv + uuv;
417
418    }
419    //-----------------------------------------------------------------------
420        bool Quaternion::equals(const Quaternion& rhs, const Radian& tolerance) const
421        {
422        Real fCos = Dot(rhs);
423        Radian angle = Math::ACos(fCos);
424
425                return (Math::Abs(angle.valueRadians()) <= tolerance.valueRadians())
426            || Math::RealEqual(angle.valueRadians(), Math::PI, tolerance.valueRadians());
427
428
429        }
430    //-----------------------------------------------------------------------
431    Quaternion Quaternion::Slerp (Real fT, const Quaternion& rkP,
432        const Quaternion& rkQ, bool shortestPath)
433    {
434        Real fCos = rkP.Dot(rkQ);
435        Quaternion rkT;
436
437        // Do we need to invert rotation?
438        if (fCos < 0.0f && shortestPath)
439        {
440            fCos = -fCos;
441            rkT = -rkQ;
442        }
443        else
444        {
445            rkT = rkQ;
446        }
447
448        if (Math::Abs(fCos) < 1 - ms_fEpsilon)
449        {
450            // Standard case (slerp)
451            Real fSin = Math::Sqrt(1 - Math::Sqr(fCos));
452            Radian fAngle = Math::ATan2(fSin, fCos);
453            Real fInvSin = 1.0f / fSin;
454            Real fCoeff0 = Math::Sin((1.0f - fT) * fAngle) * fInvSin;
455            Real fCoeff1 = Math::Sin(fT * fAngle) * fInvSin;
456            return fCoeff0 * rkP + fCoeff1 * rkT;
457        }
458        else
459        {
460            // There are two situations:
461            // 1. "rkP" and "rkQ" are very close (fCos ~= +1), so we can do a linear
462            //    interpolation safely.
463            // 2. "rkP" and "rkQ" are almost inverse of each other (fCos ~= -1), there
464            //    are an infinite number of possibilities interpolation. but we haven't
465            //    have method to fix this case, so just use linear interpolation here.
466            Quaternion t = (1.0f - fT) * rkP + fT * rkT;
467            // taking the complement requires renormalisation
468            t.normalise();
469            return t;
470        }
471    }
472    //-----------------------------------------------------------------------
473    Quaternion Quaternion::SlerpExtraSpins (Real fT,
474        const Quaternion& rkP, const Quaternion& rkQ, int iExtraSpins)
475    {
476        Real fCos = rkP.Dot(rkQ);
477        Radian fAngle ( Math::ACos(fCos) );
478
479        if ( Math::Abs(fAngle.valueRadians()) < ms_fEpsilon )
480            return rkP;
481
482        Real fSin = Math::Sin(fAngle);
483        Radian fPhase ( Math::PI*iExtraSpins*fT );
484        Real fInvSin = 1.0/fSin;
485        Real fCoeff0 = Math::Sin((1.0-fT)*fAngle - fPhase)*fInvSin;
486        Real fCoeff1 = Math::Sin(fT*fAngle + fPhase)*fInvSin;
487        return fCoeff0*rkP + fCoeff1*rkQ;
488    }
489    //-----------------------------------------------------------------------
490    void Quaternion::Intermediate (const Quaternion& rkQ0,
491        const Quaternion& rkQ1, const Quaternion& rkQ2,
492        Quaternion& rkA, Quaternion& rkB)
493    {
494        // assert:  q0, q1, q2 are unit quaternions
495
496        Quaternion kQ0inv = rkQ0.UnitInverse();
497        Quaternion kQ1inv = rkQ1.UnitInverse();
498        Quaternion rkP0 = kQ0inv*rkQ1;
499        Quaternion rkP1 = kQ1inv*rkQ2;
500        Quaternion kArg = 0.25*(rkP0.Log()-rkP1.Log());
501        Quaternion kMinusArg = -kArg;
502
503        rkA = rkQ1*kArg.Exp();
504        rkB = rkQ1*kMinusArg.Exp();
505    }
506    //-----------------------------------------------------------------------
507    Quaternion Quaternion::Squad (Real fT,
508        const Quaternion& rkP, const Quaternion& rkA,
509        const Quaternion& rkB, const Quaternion& rkQ, bool shortestPath)
510    {
511        Real fSlerpT = 2.0*fT*(1.0-fT);
512        Quaternion kSlerpP = Slerp(fT, rkP, rkQ, shortestPath);
513        Quaternion kSlerpQ = Slerp(fT, rkA, rkB);
514        return Slerp(fSlerpT, kSlerpP ,kSlerpQ);
515    }
516    //-----------------------------------------------------------------------
517    Real Quaternion::normalise(void)
518    {
519        Real len = Norm();
520        Real factor = 1.0f / Math::Sqrt(len);
521        *this = *this * factor;
522        return len;
523    }
524    //-----------------------------------------------------------------------
525        Radian Quaternion::getRoll(bool reprojectAxis) const
526        {
527                if (reprojectAxis)
528                {
529                        // roll = atan2(localx.y, localx.x)
530                        // pick parts of xAxis() implementation that we need
531                        Real fTx  = 2.0*x;
532                        Real fTy  = 2.0*y;
533                        Real fTz  = 2.0*z;
534                        Real fTwz = fTz*w;
535                        Real fTxy = fTy*x;
536                        Real fTyy = fTy*y;
537                        Real fTzz = fTz*z;
538
539                        // Vector3(1.0-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy);
540
541                        return Radian(Math::ATan2(fTxy+fTwz, 1.0-(fTyy+fTzz)));
542
543                }
544                else
545                {
546                        return Radian(Math::ATan2(2*(x*y + w*z), w*w + x*x - y*y - z*z));
547                }
548        }
549    //-----------------------------------------------------------------------
550        Radian Quaternion::getPitch(bool reprojectAxis) const
551        {
552                if (reprojectAxis)
553                {
554                        // pitch = atan2(localy.z, localy.y)
555                        // pick parts of yAxis() implementation that we need
556                        Real fTx  = 2.0*x;
557                        Real fTy  = 2.0*y;
558                        Real fTz  = 2.0*z;
559                        Real fTwx = fTx*w;
560                        Real fTxx = fTx*x;
561                        Real fTyz = fTz*y;
562                        Real fTzz = fTz*z;
563
564                        // Vector3(fTxy-fTwz, 1.0-(fTxx+fTzz), fTyz+fTwx);
565                        return Radian(Math::ATan2(fTyz+fTwx, 1.0-(fTxx+fTzz)));
566                }
567                else
568                {
569                        // internal version
570                        return Radian(Math::ATan2(2*(y*z + w*x), w*w - x*x - y*y + z*z));
571                }
572        }
573    //-----------------------------------------------------------------------
574        Radian Quaternion::getYaw(bool reprojectAxis) const
575        {
576                if (reprojectAxis)
577                {
578                        // yaw = atan2(localz.x, localz.z)
579                        // pick parts of zAxis() implementation that we need
580                        Real fTx  = 2.0*x;
581                        Real fTy  = 2.0*y;
582                        Real fTz  = 2.0*z;
583                        Real fTwy = fTy*w;
584                        Real fTxx = fTx*x;
585                        Real fTxz = fTz*x;
586                        Real fTyy = fTy*y;
587
588                        // Vector3(fTxz+fTwy, fTyz-fTwx, 1.0-(fTxx+fTyy));
589
590                        return Radian(Math::ATan2(fTxz+fTwy, 1.0-(fTxx+fTyy)));
591
592                }
593                else
594                {
595                        // internal version
596                        return Radian(Math::ASin(-2*(x*z - w*y)));
597                }
598        }
599    //-----------------------------------------------------------------------
600    Quaternion Quaternion::nlerp(Real fT, const Quaternion& rkP,
601        const Quaternion& rkQ, bool shortestPath)
602    {
603                Quaternion result;
604        Real fCos = rkP.Dot(rkQ);
605                if (fCos < 0.0f && shortestPath)
606                {
607                        result = rkP + fT * ((-rkQ) - rkP);
608                }
609                else
610                {
611                        result = rkP + fT * (rkQ - rkP);
612                }
613        result.normalise();
614        return result;
615    }
616}
Note: See TracBrowser for help on using the repository browser.