1. Summary
  2. Files
  3. Support
  4. Report Spam
  5. Create account
  6. Log in

root/branches/v0.8.0.0/Projects/Axiom/Engine/Math/Quaternion.cs @ 2353

Revision 2353, 25.5 KB (checked in by borrillis, 2 years ago)

Updated Copyright + License text

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#region LGPL License
2/*
3Axiom Graphics Engine Library
4Copyright © 2003-2011 Axiom Project Team
5
6The overall design, and a majority of the core engine and rendering code
7contained within this library is a derivative of the open source Object Oriented
8Graphics Engine OGRE, which can be found at http://ogre.sourceforge.net. 
9Many thanks to the OGRE team for maintaining such a high quality project.
10
11The math library included in this project, in addition to being a derivative of
12the works of Ogre, also include derivative work of the free portion of the
13Wild Magic mathematics source code that is distributed with the excellent
14book Game Engine Design.
15http://www.wild-magic.com/
16
17This library is free software; you can redistribute it and/or
18modify it under the terms of the GNU Lesser General Public
19License as published by the Free Software Foundation; either
20version 2.1 of the License, or (at your option) any later version.
21
22This library is distributed in the hope that it will be useful,
23but WITHOUT ANY WARRANTY; without even the implied warranty of
24MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25Lesser General Public License for more details.
26
27You should have received a copy of the GNU Lesser General Public
28License along with this library; if not, write to the Free Software
29Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30*/
31#endregion
32
33#region SVN Version Information
34// <file>
35//     <license see="http://axiom3d.net/wiki/index.php/license.txt"/>
36//     <id value="$Id$"/>
37// </file>
38#endregion SVN Version Information
39
40#region Namespace Declarations
41
42using System;
43using System.Diagnostics;
44using System.Globalization;
45
46#endregion Namespace Declarations
47
48namespace Axiom.Math
49{
50        /// <summary>
51        ///             Summary description for Quaternion.
52        /// </summary>
53        public struct Quaternion
54        {
55                #region Private member variables and constants
56
57                const float EPSILON = 1e-03f;
58
59                public Real w, x, y, z;
60
61                private static readonly Quaternion identityQuat = new Quaternion( 1.0f, 0.0f, 0.0f, 0.0f );
62                private static readonly Quaternion zeroQuat = new Quaternion( 0.0f, 0.0f, 0.0f, 0.0f );
63                private static readonly int[] next = new int[ 3 ] { 1, 2, 0 };
64
65                #endregion
66
67                #region Constructors
68
69                //              public Quaternion()
70                //              {
71                //                      this.w = 1.0f;
72                //              }
73
74                /// <summary>
75                ///             Creates a new Quaternion.
76                /// </summary>
77                public Quaternion( Real w, Real x, Real y, Real z )
78                {
79                        this.w = w;
80                        this.x = x;
81                        this.y = y;
82                        this.z = z;
83                }
84
85                #endregion
86
87                #region Operator Overloads + CLS compliant method equivalents
88
89                /// <summary>
90                /// Used to multiply 2 Quaternions together.
91                /// </summary>
92                /// <remarks>
93                ///             Quaternion multiplication is not communative in most cases.
94                ///             i.e. p*q != q*p
95                /// </remarks>
96                /// <param name="left"></param>
97                /// <param name="right"></param>
98                /// <returns></returns>
99                public static Quaternion Multiply( Quaternion left, Quaternion right )
100                {
101                        return left * right;
102                }
103
104                /// <summary>
105                /// Used to multiply 2 Quaternions together.
106                /// </summary>
107                /// <remarks>
108                ///             Quaternion multiplication is not communative in most cases.
109                ///             i.e. p*q != q*p
110                /// </remarks>
111                /// <param name="left"></param>
112                /// <param name="right"></param>
113                /// <returns></returns>
114                public static Quaternion operator *( Quaternion left, Quaternion right )
115                {
116                        Quaternion q = new Quaternion();
117
118                        q.w = left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z;
119                        q.x = left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y;
120                        q.y = left.w * right.y + left.y * right.w + left.z * right.x - left.x * right.z;
121                        q.z = left.w * right.z + left.z * right.w + left.x * right.y - left.y * right.x;
122
123                        /*
124                        return new Quaternion
125                                (
126                                left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z,
127                                left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y,
128                                left.w * right.y + left.y * right.w + left.z * right.x - left.x * right.z,
129                                left.w * right.z + left.z * right.w + left.x * right.y - left.y * right.x
130                                ); */
131
132                        return q;
133                }
134
135
136                /// <summary>
137                ///
138                /// </summary>
139                /// <param name="quat"></param>
140                /// <param name="vector"></param>
141                /// <returns></returns>
142                public static Vector3 Multiply( Quaternion quat, Vector3 vector )
143                {
144                        return quat * vector;
145                }
146
147                /// <summary>
148                ///
149                /// </summary>
150                /// <param name="quat"></param>
151                /// <param name="vector"></param>
152                /// <returns></returns>
153                public static Vector3 operator *( Quaternion quat, Vector3 vector )
154                {
155                        // nVidia SDK implementation
156                        Vector3 uv, uuv;
157                        Vector3 qvec = new Vector3( quat.x, quat.y, quat.z );
158
159                        uv = qvec.Cross( vector );
160                        uuv = qvec.Cross( uv );
161                        uv *= ( 2.0f * quat.w );
162                        uuv *= 2.0f;
163
164                        return vector + uv + uuv;
165
166                        // get the rotation matrix of the Quaternion and multiply it times the vector
167                        //return quat.ToRotationMatrix() * vector;
168                }
169
170                /// <summary>
171                /// Used when a Real value is multiplied by a Quaternion.
172                /// </summary>
173                /// <param name="scalar"></param>
174                /// <param name="right"></param>
175                /// <returns></returns>
176                public static Quaternion Multiply( Real scalar, Quaternion right )
177                {
178                        return scalar * right;
179                }
180
181                /// <summary>
182                /// Used when a Real value is multiplied by a Quaternion.
183                /// </summary>
184                /// <param name="scalar"></param>
185                /// <param name="right"></param>
186                /// <returns></returns>
187                public static Quaternion operator *( Real scalar, Quaternion right )
188                {
189                        return new Quaternion( scalar * right.w, scalar * right.x, scalar * right.y, scalar * right.z );
190                }
191
192                /// <summary>
193                /// Used when a Quaternion is multiplied by a Real value.
194                /// </summary>
195                /// <param name="left"></param>
196                /// <param name="scalar"></param>
197                /// <returns></returns>
198                public static Quaternion Multiply( Quaternion left, Real scalar )
199                {
200                        return left * scalar;
201                }
202
203                /// <summary>
204                /// Used when a Quaternion is multiplied by a Real value.
205                /// </summary>
206                /// <param name="left"></param>
207                /// <param name="scalar"></param>
208                /// <returns></returns>
209                public static Quaternion operator *( Quaternion left, Real scalar )
210                {
211                        return new Quaternion( scalar * left.w, scalar * left.x, scalar * left.y, scalar * left.z );
212                }
213
214                /// <summary>
215                /// Used when a Quaternion is added to another Quaternion.
216                /// </summary>
217                /// <param name="left"></param>
218                /// <param name="right"></param>
219                /// <returns></returns>
220                public static Quaternion Add( Quaternion left, Quaternion right )
221                {
222                        return left + right;
223                }
224
225                public static Quaternion Subtract( Quaternion left, Quaternion right )
226                {
227                        return left - right;
228                }
229
230                /// <summary>
231                /// Used when a Quaternion is added to another Quaternion.
232                /// </summary>
233                /// <param name="left"></param>
234                /// <param name="right"></param>
235                /// <returns></returns>
236                public static Quaternion operator +( Quaternion left, Quaternion right )
237                {
238                        return new Quaternion( left.w + right.w, left.x + right.x, left.y + right.y, left.z + right.z );
239                }
240
241                public static Quaternion operator -( Quaternion left, Quaternion right )
242                {
243                        return new Quaternion( left.w - right.w, left.x - right.x, left.y - right.y, left.z - right.z );
244                }
245
246                /// <summary>
247                ///     Negates a Quaternion, which simply returns a new Quaternion
248                ///     with all components negated.
249                /// </summary>
250                /// <param name="right"></param>
251                /// <returns></returns>
252                public static Quaternion operator -( Quaternion right )
253                {
254                        return new Quaternion( -right.w, -right.x, -right.y, -right.z );
255                }
256
257                public static bool operator ==( Quaternion left, Quaternion right )
258                {
259                        return ( left.w == right.w && left.x == right.x && left.y == right.y && left.z == right.z );
260                }
261
262                public static bool operator !=( Quaternion left, Quaternion right )
263                {
264                        return !( left == right );
265                }
266
267                #endregion
268
269                #region Properties
270
271                /// <summary>
272                ///    An Identity Quaternion.
273                /// </summary>
274                public static Quaternion Identity
275                {
276                        get
277                        {
278                                return identityQuat;
279                        }
280                }
281
282                /// <summary>
283                ///    A Quaternion with all elements set to 0.0f;
284                /// </summary>
285                public static Quaternion Zero
286                {
287                        get
288                        {
289                                return zeroQuat;
290                        }
291                }
292
293                /// <summary>
294                ///             Squared 'length' of this quaternion.
295                /// </summary>
296                public Real Norm
297                {
298                        get
299                        {
300                                return x * x + y * y + z * z + w * w;
301                        }
302                }
303
304                /// <summary>
305                ///    Local X-axis portion of this rotation.
306                /// </summary>
307                public Vector3 XAxis
308                {
309                        get
310                        {
311                                Real fTx = 2.0f * x;
312                                Real fTy = 2.0f * y;
313                                Real fTz = 2.0f * z;
314                                Real fTwy = fTy * w;
315                                Real fTwz = fTz * w;
316                                Real fTxy = fTy * x;
317                                Real fTxz = fTz * x;
318                                Real fTyy = fTy * y;
319                                Real fTzz = fTz * z;
320
321                                return new Vector3( 1.0f - ( fTyy + fTzz ), fTxy + fTwz, fTxz - fTwy );
322                        }
323                }
324
325                /// <summary>
326                ///    Local Y-axis portion of this rotation.
327                /// </summary>
328                public Vector3 YAxis
329                {
330                        get
331                        {
332                                Real fTx = 2.0f * x;
333                                Real fTy = 2.0f * y;
334                                Real fTz = 2.0f * z;
335                                Real fTwx = fTx * w;
336                                Real fTwz = fTz * w;
337                                Real fTxx = fTx * x;
338                                Real fTxy = fTy * x;
339                                Real fTyz = fTz * y;
340                                Real fTzz = fTz * z;
341
342                                return new Vector3( fTxy - fTwz, 1.0f - ( fTxx + fTzz ), fTyz + fTwx );
343                        }
344                }
345
346                /// <summary>
347                ///    Local Z-axis portion of this rotation.
348                /// </summary>
349                public Vector3 ZAxis
350                {
351                        get
352                        {
353                                Real fTx = 2.0f * x;
354                                Real fTy = 2.0f * y;
355                                Real fTz = 2.0f * z;
356                                Real fTwx = fTx * w;
357                                Real fTwy = fTy * w;
358                                Real fTxx = fTx * x;
359                                Real fTxz = fTz * x;
360                                Real fTyy = fTy * y;
361                                Real fTyz = fTz * y;
362
363                                return new Vector3( fTxz + fTwy, fTyz - fTwx, 1.0f - ( fTxx + fTyy ) );
364                        }
365                }
366                public Real PitchInDegrees
367                {
368                        get
369                        {
370                                return Utility.RadiansToDegrees( Pitch );
371                        }
372                        set
373                        {
374                                Pitch = Utility.DegreesToRadians( value );
375                        }
376                }
377                public Real YawInDegrees
378                {
379                        get
380                        {
381                                return Utility.RadiansToDegrees( Yaw );
382                        }
383                        set
384                        {
385                                Yaw = Utility.DegreesToRadians( value );
386                        }
387                }
388                public Real RollInDegrees
389                {
390                        get
391                        {
392                                return Utility.RadiansToDegrees( Roll );
393                        }
394                        set
395                        {
396                                Roll = Utility.DegreesToRadians( value );
397                        }
398                }
399
400                public Real Pitch
401                {
402                        set
403                        {
404                                Real pitch, yaw, roll;
405                                ToEulerAngles( out pitch, out yaw, out roll );
406                                this = FromEulerAngles( value, yaw, roll );
407                        }
408                        get
409                        {
410                                Real test = x * y + z * w;
411                                if ( Utility.Abs( test ) > 0.499f ) // singularity at north and south pole
412                                        return 0f;
413                                return (Real)Utility.ATan2( 2 * x * w - 2 * y * z, 1 - 2 * x * x - 2 * z * z );
414                        }
415                }
416
417
418                public Real Yaw
419                {
420                        set
421                        {
422                                Real pitch, yaw, roll;
423                                ToEulerAngles( out pitch, out yaw, out roll );
424                                this = FromEulerAngles( pitch, value, roll );
425                        }
426                        get
427                        {
428                                Real test = x * y + z * w;
429                                if ( Utility.Abs( test ) > 0.499f ) // singularity at north and south pole
430                                        return Utility.Sign( test ) * 2 * Utility.ATan2( x, w );
431                                return Utility.ATan2( 2 * y * w - 2 * x * z, 1 - 2 * y * y - 2 * z * z );
432                        }
433                }
434                public Real Roll
435                {
436                        set
437                        {
438
439                                Real pitch, yaw, roll;
440                                ToEulerAngles( out pitch, out yaw, out roll );
441                                this = FromEulerAngles( pitch, yaw, value );
442                        }
443                        get
444                        {
445                                Real test = x * y + z * w;
446                                if ( Utility.Abs( test ) > 0.499f ) // singularity at north and south pole
447                                        return Utility.Sign( test ) * Utility.PI / 2;
448                                return (Real)Utility.ASin( 2 * test );
449                        }
450                }
451
452
453                #endregion
454
455                #region Static methods
456
457                public static Quaternion Slerp( Real time, Quaternion quatA, Quaternion quatB )
458                {
459                        return Slerp( time, quatA, quatB, false );
460                }
461
462                /// <summary>
463                ///
464                /// </summary>
465                /// <param name="time"></param>
466                /// <param name="quatA"></param>
467                /// <param name="quatB"></param>
468                /// <param name="useShortestPath"></param>
469                /// <returns></returns>
470                public static Quaternion Slerp( Real time, Quaternion quatA, Quaternion quatB, bool useShortestPath )
471                {
472                        Real cos = quatA.Dot( quatB );
473
474                        Real angle = (Real)Utility.ACos( cos );
475
476                        if ( Utility.Abs( angle ) < EPSILON )
477                        {
478                                return quatA;
479                        }
480
481                        Real sin = Utility.Sin( angle );
482                        Real inverseSin = 1.0f / sin;
483                        Real coeff0 = Utility.Sin( ( 1.0f - time ) * angle ) * inverseSin;
484                        Real coeff1 = Utility.Sin( time * angle ) * inverseSin;
485
486                        Quaternion result;
487
488                        if ( cos < 0.0f && useShortestPath )
489                        {
490                                coeff0 = -coeff0;
491                                // taking the complement requires renormalisation
492                                Quaternion t = coeff0 * quatA + coeff1 * quatB;
493                                t.Normalize();
494                                result = t;
495                        }
496                        else
497                        {
498                                result = ( coeff0 * quatA + coeff1 * quatB );
499                        }
500
501                        return result;
502                }
503
504                /// <overloads><summary>
505                /// normalised linear interpolation - faster but less accurate (non-constant rotation velocity)
506                /// </summary>
507                /// <param name="fT"></param>
508                /// <param name="rkP"></param>
509                /// <param name="rkQ"></param>
510                /// <returns></returns>
511                /// </overloads>
512                public static Quaternion Nlerp( Real fT, Quaternion rkP, Quaternion rkQ )
513                {
514                        return Nlerp( fT, rkP, rkQ, false );
515                }
516
517
518                /// <param name="shortestPath"></param>
519                public static Quaternion Nlerp( Real fT, Quaternion rkP, Quaternion rkQ, bool shortestPath )
520                {
521                        Quaternion result;
522                        Real fCos = rkP.Dot( rkQ );
523                        if ( fCos < 0.0f && shortestPath )
524                        {
525                                result = rkP + fT * ( ( -rkQ ) - rkP );
526                        }
527                        else
528                        {
529                                result = rkP + fT * ( rkQ - rkP );
530
531                        }
532                        result.Normalize();
533                        return result;
534                }
535
536                /// <summary>
537                /// Creates a Quaternion from a supplied angle and axis.
538                /// </summary>
539                /// <param name="angle">Value of an angle in radians.</param>
540                /// <param name="axis">Arbitrary axis vector.</param>
541                /// <returns></returns>
542                public static Quaternion FromAngleAxis( Real angle, Vector3 axis )
543                {
544                        Quaternion quat = new Quaternion();
545
546                        Real halfAngle = 0.5f * angle;
547                        Real sin = Utility.Sin( halfAngle );
548
549                        quat.w = Utility.Cos( halfAngle );
550                        quat.x = sin * axis.x;
551                        quat.y = sin * axis.y;
552                        quat.z = sin * axis.z;
553
554                        return quat;
555                }
556
557                public static Quaternion Squad( Real t, Quaternion p, Quaternion a, Quaternion b, Quaternion q )
558                {
559                        return Squad( t, p, a, b, q, false );
560                }
561
562                /// <summary>
563                ///             Performs spherical quadratic interpolation.
564                /// </summary>
565                /// <param name="t"></param>
566                /// <param name="p"></param>
567                /// <param name="a"></param>
568                /// <param name="b"></param>
569                /// <param name="q"></param>
570                /// <returns></returns>
571                public static Quaternion Squad( Real t, Quaternion p, Quaternion a, Quaternion b, Quaternion q, bool useShortestPath )
572                {
573                        Real slerpT = 2.0f * t * ( 1.0f - t );
574
575                        // use spherical linear interpolation
576                        Quaternion slerpP = Slerp( t, p, q, useShortestPath );
577                        Quaternion slerpQ = Slerp( t, a, b );
578
579                        // run another Slerp on the results of the first 2, and return the results
580                        return Slerp( slerpT, slerpP, slerpQ );
581                }
582
583                #endregion
584
585                #region Public methods
586
587                #region Euler Angles
588
589                public Vector3 ToEulerAnglesInDegrees()
590                {
591                        Real pitch, yaw, roll;
592                        ToEulerAngles( out pitch, out yaw, out roll );
593                        return new Vector3( Utility.RadiansToDegrees( pitch ), Utility.RadiansToDegrees( yaw ), Utility.RadiansToDegrees( roll ) );
594                }
595
596                public Vector3 ToEulerAngles()
597                {
598                        Real pitch, yaw, roll;
599                        ToEulerAngles( out pitch, out yaw, out roll );
600                        return new Vector3( pitch, yaw, roll );
601                }
602
603                public void ToEulerAnglesInDegrees( out Real pitch, out Real yaw, out Real roll )
604                {
605                        ToEulerAngles( out pitch, out yaw, out roll );
606                        pitch = Utility.RadiansToDegrees( pitch );
607                        yaw = Utility.RadiansToDegrees( yaw );
608                        roll = Utility.RadiansToDegrees( roll );
609                }
610
611                public void ToEulerAngles( out Real pitch, out Real yaw, out Real roll )
612                {
613
614                        Real halfPi = Utility.PI / 2;
615                        Real test = x * y + z * w;
616                        if ( test > 0.499f )
617                        { // singularity at north pole
618                                yaw = 2 * Utility.ATan2( x, w );
619                                roll = halfPi;
620                                pitch = 0;
621                        }
622                        else if ( test < -0.499f )
623                        { // singularity at south pole
624                                yaw = -2 * Utility.ATan2( x, w );
625                                roll = -halfPi;
626                                pitch = 0;
627                        }
628                        else
629                        {
630                                Real sqx = x * x;
631                                Real sqy = y * y;
632                                Real sqz = z * z;
633                                yaw = Utility.ATan2( 2 * y * w - 2 * x * z, 1 - 2 * sqy - 2 * sqz );
634                                roll = (Real)Utility.ASin( 2 * test );
635                                pitch = Utility.ATan2( 2 * x * w - 2 * y * z, 1 - 2 * sqx - 2 * sqz );
636                        }
637
638                        if ( pitch <= Real.Epsilon )
639                                pitch = 0f;
640                        if ( yaw <= Real.Epsilon )
641                                yaw = 0f;
642                        if ( roll <= Real.Epsilon )
643                                roll = 0f;
644                }
645
646                public static Quaternion FromEulerAnglesInDegrees( Real pitch, Real yaw, Real roll )
647                {
648                        return FromEulerAngles( Utility.DegreesToRadians( pitch ), Utility.DegreesToRadians( yaw ), Utility.DegreesToRadians( roll ) );
649                }
650
651                /// <summary>
652                /// Combines the euler angles in the order yaw, pitch, roll to create a rotation quaternion
653                /// </summary>
654                /// <param name="pitch"></param>
655                /// <param name="yaw"></param>
656                /// <param name="roll"></param>
657                /// <returns></returns>
658                public static Quaternion FromEulerAngles( Real pitch, Real yaw, Real roll )
659                {
660                        return Quaternion.FromAngleAxis( yaw, Vector3.UnitY )
661                                * Quaternion.FromAngleAxis( pitch, Vector3.UnitX )
662                                * Quaternion.FromAngleAxis( roll, Vector3.UnitZ );
663
664                        /*TODO: Debug
665                        //Equation from http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm
666                        //heading
667                       
668                        Real c1 = (Real)Math.Cos(yaw/2);
669                        Real s1 = (Real)Math.Sin(yaw/2);
670                        //attitude
671                        Real c2 = (Real)Math.Cos(roll/2);
672                        Real s2 = (Real)Math.Sin(roll/2);
673                        //bank
674                        Real c3 = (Real)Math.Cos(pitch/2);
675                        Real s3 = (Real)Math.Sin(pitch/2);
676                        Real c1c2 = c1*c2;
677                        Real s1s2 = s1*s2;
678
679                        Real w =c1c2*c3 - s1s2*s3;
680                        Real x =c1c2*s3 + s1s2*c3;
681                        Real y =s1*c2*c3 + c1*s2*s3;
682                        Real z =c1*s2*c3 - s1*c2*s3;
683                        return new Quaternion(w,x,y,z);*/
684                }
685
686                #endregion
687
688                /// <summary>
689                /// Performs a Dot Product operation on 2 Quaternions.
690                /// </summary>
691                /// <param name="quat"></param>
692                /// <returns></returns>
693                public Real Dot( Quaternion quat )
694                {
695                        return this.w * quat.w + this.x * quat.x + this.y * quat.y + this.z * quat.z;
696                }
697
698                /// <summary>
699                ///             Normalizes elements of this quaterion to the range [0,1].
700                /// </summary>
701                public void Normalize()
702                {
703                        Real factor = 1.0f / Utility.Sqrt( this.Norm );
704
705                        w = w * factor;
706                        x = x * factor;
707                        y = y * factor;
708                        z = z * factor;
709                }
710
711                /// <summary>
712                ///   
713                /// </summary>
714                /// <param name="angle"></param>
715                /// <param name="axis"></param>
716                /// <returns></returns>
717                public void ToAngleAxis( ref Real angle, ref Vector3 axis )
718                {
719                        // The quaternion representing the rotation is
720                        //   q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k)
721
722                        Real sqrLength = x * x + y * y + z * z;
723
724                        if ( sqrLength > 0.0f )
725                        {
726                                angle = 2.0f * (Real)Utility.ACos( w );
727                                Real invLength = Utility.InvSqrt( sqrLength );
728                                axis.x = x * invLength;
729                                axis.y = y * invLength;
730                                axis.z = z * invLength;
731                        }
732                        else
733                        {
734                                angle = 0.0f;
735                                axis.x = 1.0f;
736                                axis.y = 0.0f;
737                                axis.z = 0.0f;
738                        }
739                }
740
741                /// <summary>
742                /// Gets a 3x3 rotation matrix from this Quaternion.
743                /// </summary>
744                /// <returns></returns>
745                public Matrix3 ToRotationMatrix()
746                {
747                        Matrix3 rotation = new Matrix3();
748
749                        Real tx = 2.0f * this.x;
750                        Real ty = 2.0f * this.y;
751                        Real tz = 2.0f * this.z;
752                        Real twx = tx * this.w;
753                        Real twy = ty * this.w;
754                        Real twz = tz * this.w;
755                        Real txx = tx * this.x;
756                        Real txy = ty * this.x;
757                        Real txz = tz * this.x;
758                        Real tyy = ty * this.y;
759                        Real tyz = tz * this.y;
760                        Real tzz = tz * this.z;
761
762                        rotation.m00 = 1.0f - ( tyy + tzz );
763                        rotation.m01 = txy - twz;
764                        rotation.m02 = txz + twy;
765                        rotation.m10 = txy + twz;
766                        rotation.m11 = 1.0f - ( txx + tzz );
767                        rotation.m12 = tyz - twx;
768                        rotation.m20 = txz - twy;
769                        rotation.m21 = tyz + twx;
770                        rotation.m22 = 1.0f - ( txx + tyy );
771
772                        return rotation;
773                }
774
775                /// <summary>
776                /// Computes the inverse of a Quaternion.
777                /// </summary>
778                /// <returns></returns>
779                public Quaternion Inverse()
780                {
781                        Real norm = this.w * this.w + this.x * this.x + this.y * this.y + this.z * this.z;
782                        if ( norm > 0.0f )
783                        {
784                                Real inverseNorm = 1.0f / norm;
785                                return new Quaternion( this.w * inverseNorm, -this.x * inverseNorm, -this.y * inverseNorm, -this.z * inverseNorm );
786                        }
787                        else
788                        {
789                                // return an invalid result to flag the error
790                                return Quaternion.Zero;
791                        }
792                }
793
794                /// <summary>
795                ///   Variant of Inverse() that is only valid for unit quaternions.
796                /// </summary>
797                /// <returns></returns>
798                public Quaternion UnitInverse
799                {
800                        get
801                        {
802                                return new Quaternion( w, -x, -y, -z );
803                        }
804                }
805
806                /// <summary>
807                ///
808                /// </summary>
809                /// <param name="xAxis"></param>
810                /// <param name="yAxis"></param>
811                /// <param name="zAxis"></param>
812                public void ToAxes( out Vector3 xAxis, out Vector3 yAxis, out Vector3 zAxis )
813                {
814                        xAxis = new Vector3();
815                        yAxis = new Vector3();
816                        zAxis = new Vector3();
817
818                        Matrix3 rotation = this.ToRotationMatrix();
819
820                        xAxis.x = rotation.m00;
821                        xAxis.y = rotation.m10;
822                        xAxis.z = rotation.m20;
823
824                        yAxis.x = rotation.m01;
825                        yAxis.y = rotation.m11;
826                        yAxis.z = rotation.m21;
827
828                        zAxis.x = rotation.m02;
829                        zAxis.y = rotation.m12;
830                        zAxis.z = rotation.m22;
831                }
832
833                /// <summary>
834                ///
835                /// </summary>
836                /// <param name="xAxis"></param>
837                /// <param name="yAxis"></param>
838                /// <param name="zAxis"></param>
839                public static Quaternion FromAxes( Vector3 xAxis, Vector3 yAxis, Vector3 zAxis )
840                {
841                        Matrix3 rotation = new Matrix3();
842
843                        rotation.m00 = xAxis.x;
844                        rotation.m10 = xAxis.y;
845                        rotation.m20 = xAxis.z;
846
847                        rotation.m01 = yAxis.x;
848                        rotation.m11 = yAxis.y;
849                        rotation.m21 = yAxis.z;
850
851                        rotation.m02 = zAxis.x;
852                        rotation.m12 = zAxis.y;
853                        rotation.m22 = zAxis.z;
854
855                        // set this quaternions values from the rotation matrix built
856                        return FromRotationMatrix( rotation );
857                }
858
859                /// <summary>
860                ///
861                /// </summary>
862                /// <param name="matrix"></param>
863                public static Quaternion FromRotationMatrix( Matrix3 matrix )
864                {
865                        // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
866                        // article "Quaternion Calculus and Fast Animation".
867
868                        Quaternion result = Quaternion.Zero;
869
870                        Real trace = matrix.m00 + matrix.m11 + matrix.m22;
871
872                        Real root = 0.0f;
873
874                        if ( trace > 0.0f )
875                        {
876                                // |this.w| > 1/2, may as well choose this.w > 1/2
877                                root = Utility.Sqrt( trace + 1.0f );  // 2w
878                                result.w = 0.5f * root;
879
880                                root = 0.5f / root;  // 1/(4w)
881
882                                result.x = ( matrix.m21 - matrix.m12 ) * root;
883                                result.y = ( matrix.m02 - matrix.m20 ) * root;
884                                result.z = ( matrix.m10 - matrix.m01 ) * root;
885                        }
886                        else
887                        {
888                                // |result.w| <= 1/2
889
890                                int i = 0;
891                                if ( matrix.m11 > matrix.m00 )
892                                        i = 1;
893                                if ( matrix.m22 > matrix[ i, i ] )
894                                        i = 2;
895
896                                int j = next[ i ];
897                                int k = next[ j ];
898
899                                root = Utility.Sqrt( matrix[ i, i ] - matrix[ j, j ] - matrix[ k, k ] + 1.0f );
900
901                                unsafe
902                                {
903                                        Real* apkQuat = &result.x;
904
905                                        apkQuat[ i ] = 0.5f * root;
906                                        root = 0.5f / root;
907
908                                        result.w = ( matrix[ k, j ] - matrix[ j, k ] ) * root;
909
910                                        apkQuat[ j ] = ( matrix[ j, i ] + matrix[ i, j ] ) * root;
911                                        apkQuat[ k ] = ( matrix[ k, i ] + matrix[ i, k ] ) * root;
912                                }
913                        }
914
915                        return result;
916                }
917
918                /// <summary>
919                ///             Calculates the logarithm of a Quaternion.
920                /// </summary>
921                /// <returns></returns>
922                public Quaternion Log()
923                {
924                        // BLACKBOX: Learn this
925                        // If q = cos(A)+sin(A)*(x*i+y*j+z*k) where (x,y,z) is unit length, then
926                        // log(q) = A*(x*i+y*j+z*k).  If sin(A) is near zero, use log(q) =
927                        // sin(A)*(x*i+y*j+z*k) since sin(A)/A has limit 1.
928
929                        // start off with a zero quat
930                        Quaternion result = Quaternion.Zero;
931
932                        if ( Utility.Abs( w ) < 1.0f )
933                        {
934                                Real angle = (Real)Utility.ACos( w );
935                                Real sin = Utility.Sin( angle );
936
937                                if ( Utility.Abs( sin ) >= EPSILON )
938                                {
939                                        Real coeff = angle / sin;
940                                        result.x = coeff * x;
941                                        result.y = coeff * y;
942                                        result.z = coeff * z;
943                                }
944                                else
945                                {
946                                        result.x = x;
947                                        result.y = y;
948                                        result.z = z;
949                                }
950                        }
951
952                        return result;
953                }
954
955                /// <summary>
956                ///             Calculates the Exponent of a Quaternion.
957                /// </summary>
958                /// <returns></returns>
959                public Quaternion Exp()
960                {
961                        // If q = A*(x*i+y*j+z*k) where (x,y,z) is unit length, then
962                        // exp(q) = cos(A)+sin(A)*(x*i+y*j+z*k).  If sin(A) is near zero,
963                        // use exp(q) = cos(A)+A*(x*i+y*j+z*k) since A/sin(A) has limit 1.
964
965                        Real angle = Utility.Sqrt( x * x + y * y + z * z );
966                        Real sin = Utility.Sin( angle );
967
968                        // start off with a zero quat
969                        Quaternion result = Quaternion.Zero;
970
971                        result.w = Utility.Cos( angle );
972
973                        if ( Utility.Abs( sin ) >= EPSILON )
974                        {
975                                Real coeff = sin / angle;
976
977                                result.x = coeff * x;
978                                result.y = coeff * y;
979                                result.z = coeff * z;
980                        }
981                        else
982                        {
983                                result.x = x;
984                                result.y = y;
985                                result.z = z;
986                        }
987
988                        return result;
989                }
990
991                #endregion
992
993                #region Object overloads
994
995                /// <summary>
996                ///             Overrides the Object.ToString() method to provide a text representation of
997                ///             a Quaternion.
998                /// </summary>
999                /// <returns>A string representation of a Quaternion.</returns>
1000                public override string ToString()
1001                {
1002                        return string.Format( CultureInfo.InvariantCulture, "Quaternion({0}, {1}, {2}, {3})", this.w, this.x, this.y, this.z );
1003                }
1004
1005                public override int GetHashCode()
1006                {
1007                        return (int)x ^ (int)y ^ (int)z ^ (int)w;
1008                }
1009
1010                public override bool Equals( object obj )
1011                {
1012                        Quaternion quat = (Quaternion)obj;
1013
1014                        return quat == this;
1015                }
1016
1017                public bool Equals( Quaternion rhs, Real tolerance )
1018                {
1019                        Real fCos = Dot( rhs );
1020                        Real angle = (Real)Utility.ACos( fCos );
1021
1022                        return Utility.Abs( angle ) <= tolerance;
1023                }
1024
1025                #endregion
1026
1027                #region Parse from string
1028
1029                public Quaternion Parse( string quat )
1030                {
1031                        // the format is "Quaternion(w, x, y, z)"
1032                        if ( !quat.StartsWith( "Quaternion(" ) )
1033                                throw new FormatException();
1034
1035                        string[] values = quat.Substring( 11 ).TrimEnd( ')' ).Split( ',' );
1036
1037                        return new Quaternion( Real.Parse( values[ 0 ], CultureInfo.InvariantCulture ),
1038                                                                  Real.Parse( values[ 1 ], CultureInfo.InvariantCulture ),
1039                                                                  Real.Parse( values[ 2 ], CultureInfo.InvariantCulture ),
1040                                                                  Real.Parse( values[ 3 ], CultureInfo.InvariantCulture ) );
1041
1042                }
1043
1044                #endregion
1045        }
1046}
Note: See TracBrowser for help on using the browser.