1 // Copyright (C) 2002-2007 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
5 #ifndef __IRR_QUATERNION_H_INCLUDED__
6 #define __IRR_QUATERNION_H_INCLUDED__
23 //! Default Constructor
24 quaternion() : X(0.0f
), Y(0.0f
), Z(0.0f
), W(1.0f
) {}
27 quaternion(f32 x
, f32 y
, f32 z
, f32 w
) : X(x
), Y(y
), Z(z
), W(w
) { }
29 //! Constructor which converts euler angles (radians) to a quaternion
30 quaternion(f32 x
, f32 y
, f32 z
);
32 //! Constructor which converts euler angles (radians) to a quaternion
33 quaternion(const vector3df
& vec
);
35 //! Constructor which converts a matrix to a quaternion
36 quaternion(const matrix4
& mat
);
39 bool operator==(const quaternion
& other
) const;
41 //! assignment operator
42 inline quaternion
& operator=(const quaternion
& other
);
44 //! matrix assignment operator
45 inline quaternion
& operator=(const matrix4
& other
);
48 quaternion
operator+(const quaternion
& other
) const;
50 //! multiplication operator
51 quaternion
operator*(const quaternion
& other
) const;
53 //! multiplication operator
54 quaternion
operator*(f32 s
) const;
56 //! multiplication operator
57 quaternion
& operator*=(f32 s
);
59 //! multiplication operator
60 vector3df
operator* (const vector3df
& v
) const;
62 //! multiplication operator
63 quaternion
& operator*=(const quaternion
& other
);
65 //! calculates the dot product
66 inline f32
getDotProduct(const quaternion
& other
) const;
68 //! sets new quaternion
69 inline void set(f32 x
, f32 y
, f32 z
, f32 w
);
71 //! sets new quaternion based on euler angles (radians)
72 inline void set(f32 x
, f32 y
, f32 z
);
74 //! sets new quaternion based on euler angles (radians)
75 inline void set(const core::vector3df
& vec
);
77 //! normalizes the quaternion
78 inline quaternion
& normalize();
80 //! Creates a matrix from this quaternion
81 matrix4
getMatrix() const;
83 //! Creates a matrix from this quaternion
84 void getMatrix( matrix4
&dest
) const;
86 //! Creates a matrix from this quaternion
87 void getMatrix_transposed( matrix4
&dest
) const;
89 //! Inverts this quaternion
92 //! set this quaternion to the result of the interpolation between two quaternions
93 void slerp( quaternion q1
, quaternion q2
, f32 interpolate
);
95 //! axis must be unit length
96 //! The quaternion representing the rotation is
97 //! q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k)
98 void fromAngleAxis (f32 angle
, const vector3df
& axis
);
100 //! Fills an angle (radians) around an axis (unit vector)
101 void toAngleAxis (f32
&angle
, vector3df
& axis
) const;
103 //! Output this quaternion to an euler angle (radians)
104 void toEuler(vector3df
& euler
) const;
106 //! set quaternion to identity
109 //! sets quaternion to represent a rotation from one angle to another
110 void rotationFromTo(const vector3df
& from
, const vector3df
& to
);
116 //! Constructor which converts euler angles to a quaternion
117 inline quaternion::quaternion(f32 x
, f32 y
, f32 z
)
123 //! Constructor which converts euler angles to a quaternion
124 inline quaternion::quaternion(const vector3df
& vec
)
126 set(vec
.X
,vec
.Y
,vec
.Z
);
130 //! Constructor which converts a matrix to a quaternion
131 inline quaternion::quaternion(const matrix4
& mat
)
138 inline bool quaternion::operator==(const quaternion
& other
) const
153 //! assignment operator
154 inline quaternion
& quaternion::operator=(const quaternion
& other
)
164 //! matrix assignment operator
165 inline quaternion
& quaternion::operator=(const matrix4
& m
)
167 f32 diag
= m(0,0) + m(1,1) + m(2,2) + 1;
172 scale
= sqrtf(diag
) * 2.0f
; // get scale from diagonal
174 // TODO: speed this up
175 X
= ( m(2,1) - m(1,2)) / scale
;
176 Y
= ( m(0,2) - m(2,0)) / scale
;
177 Z
= ( m(1,0) - m(0,1)) / scale
;
182 if ( m(0,0) > m(1,1) && m(0,0) > m(2,2))
184 // 1st element of diag is greatest value
185 // find scale according to 1st element, and double it
186 scale
= sqrtf( 1.0f
+ m(0,0) - m(1,1) - m(2,2)) * 2.0f
;
188 // TODO: speed this up
190 Y
= (m(0,1) + m(1,0)) / scale
;
191 Z
= (m(2,0) + m(0,2)) / scale
;
192 W
= (m(2,1) - m(1,2)) / scale
;
194 else if ( m(1,1) > m(2,2))
196 // 2nd element of diag is greatest value
197 // find scale according to 2nd element, and double it
198 scale
= sqrtf( 1.0f
+ m(1,1) - m(0,0) - m(2,2)) * 2.0f
;
200 // TODO: speed this up
201 X
= (m(0,1) + m(1,0) ) / scale
;
203 Z
= (m(1,2) + m(2,1) ) / scale
;
204 W
= (m(0,2) - m(2,0) ) / scale
;
208 // 3rd element of diag is greatest value
209 // find scale according to 3rd element, and double it
210 scale
= sqrtf( 1.0f
+ m(2,2) - m(0,0) - m(1,1)) * 2.0f
;
212 // TODO: speed this up
213 X
= (m(0,2) + m(2,0)) / scale
;
214 Y
= (m(1,2) + m(2,1)) / scale
;
216 W
= (m(1,0) - m(0,1)) / scale
;
225 //! multiplication operator
226 inline quaternion
quaternion::operator*(const quaternion
& other
) const
230 tmp
.W
= (other
.W
* W
) - (other
.X
* X
) - (other
.Y
* Y
) - (other
.Z
* Z
);
231 tmp
.X
= (other
.W
* X
) + (other
.X
* W
) + (other
.Y
* Z
) - (other
.Z
* Y
);
232 tmp
.Y
= (other
.W
* Y
) + (other
.Y
* W
) + (other
.Z
* X
) - (other
.X
* Z
);
233 tmp
.Z
= (other
.W
* Z
) + (other
.Z
* W
) + (other
.X
* Y
) - (other
.Y
* X
);
239 //! multiplication operator
240 inline quaternion
quaternion::operator*(f32 s
) const
242 return quaternion(s
*X
, s
*Y
, s
*Z
, s
*W
);
245 //! multiplication operator
246 inline quaternion
& quaternion::operator*=(f32 s
)
248 X
*= s
; Y
*=s
; Z
*=s
; W
*=s
;
252 //! multiplication operator
253 inline quaternion
& quaternion::operator*=(const quaternion
& other
)
255 *this = other
* (*this);
260 inline quaternion
quaternion::operator+(const quaternion
& b
) const
262 return quaternion(X
+b
.X
, Y
+b
.Y
, Z
+b
.Z
, W
+b
.W
);
266 //! Creates a matrix from this quaternion
267 inline matrix4
quaternion::getMatrix() const
271 m(0,0) = 1.0f
- 2.0f
*Y
*Y
- 2.0f
*Z
*Z
;
272 m(1,0) = 2.0f
*X
*Y
+ 2.0f
*Z
*W
;
273 m(2,0) = 2.0f
*X
*Z
- 2.0f
*Y
*W
;
276 m(0,1) = 2.0f
*X
*Y
- 2.0f
*Z
*W
;
277 m(1,1) = 1.0f
- 2.0f
*X
*X
- 2.0f
*Z
*Z
;
278 m(2,1) = 2.0f
*Z
*Y
+ 2.0f
*X
*W
;
281 m(0,2) = 2.0f
*X
*Z
+ 2.0f
*Y
*W
;
282 m(1,2) = 2.0f
*Z
*Y
- 2.0f
*X
*W
;
283 m(2,2) = 1.0f
- 2.0f
*X
*X
- 2.0f
*Y
*Y
;
295 //! Creates a matrix from this quaternion
296 inline void quaternion::getMatrix( matrix4
&dest
) const
298 dest
[0] = 1.0f
- 2.0f
*Y
*Y
- 2.0f
*Z
*Z
;
299 dest
[1] = 2.0f
*X
*Y
+ 2.0f
*Z
*W
;
300 dest
[2] = 2.0f
*X
*Z
- 2.0f
*Y
*W
;
303 dest
[4] = 2.0f
*X
*Y
- 2.0f
*Z
*W
;
304 dest
[5] = 1.0f
- 2.0f
*X
*X
- 2.0f
*Z
*Z
;
305 dest
[6] = 2.0f
*Z
*Y
+ 2.0f
*X
*W
;
308 dest
[8] = 2.0f
*X
*Z
+ 2.0f
*Y
*W
;
309 dest
[9] = 2.0f
*Z
*Y
- 2.0f
*X
*W
;
310 dest
[10] = 1.0f
- 2.0f
*X
*X
- 2.0f
*Y
*Y
;
319 //! Creates a matrix from this quaternion
320 inline void quaternion::getMatrix_transposed( matrix4
&dest
) const
322 dest
[0] = 1.0f
- 2.0f
*Y
*Y
- 2.0f
*Z
*Z
;
323 dest
[4] = 2.0f
*X
*Y
+ 2.0f
*Z
*W
;
324 dest
[8] = 2.0f
*X
*Z
- 2.0f
*Y
*W
;
327 dest
[1] = 2.0f
*X
*Y
- 2.0f
*Z
*W
;
328 dest
[5] = 1.0f
- 2.0f
*X
*X
- 2.0f
*Z
*Z
;
329 dest
[9] = 2.0f
*Z
*Y
+ 2.0f
*X
*W
;
332 dest
[2] = 2.0f
*X
*Z
+ 2.0f
*Y
*W
;
333 dest
[6] = 2.0f
*Z
*Y
- 2.0f
*X
*W
;
334 dest
[10] = 1.0f
- 2.0f
*X
*X
- 2.0f
*Y
*Y
;
345 //! Inverts this quaternion
346 inline void quaternion::makeInverse()
348 X
= -X
; Y
= -Y
; Z
= -Z
;
351 //! sets new quaternion
352 inline void quaternion::set(f32 x
, f32 y
, f32 z
, f32 w
)
361 //! sets new quaternion based on euler angles
362 inline void quaternion::set(f32 x
, f32 y
, f32 z
)
367 f64 sr
= (f32
)sin(angle
);
368 f64 cr
= (f32
)cos(angle
);
371 f64 sp
= (f32
)sin(angle
);
372 f64 cp
= (f32
)cos(angle
);
375 f64 sy
= (f32
)sin(angle
);
376 f64 cy
= (f32
)cos(angle
);
383 X
= (f32
)(sr
* cpcy
- cr
* spsy
);
384 Y
= (f32
)(cr
* spcy
+ sr
* cpsy
);
385 Z
= (f32
)(cr
* cpsy
- sr
* spcy
);
386 W
= (f32
)(cr
* cpcy
+ sr
* spsy
);
391 //! sets new quaternion based on euler angles
392 inline void quaternion::set(const core::vector3df
& vec
)
394 set(vec
.X
, vec
.Y
, vec
.Z
);
397 //! normalizes the quaternion
398 inline quaternion
& quaternion::normalize()
400 f32 n
= X
*X
+ Y
*Y
+ Z
*Z
+ W
*W
;
405 //n = 1.0f / sqrtf(n);
406 n
= reciprocal_squareroot ( n
);
416 // set this quaternion to the result of the interpolation between two quaternions
417 inline void quaternion::slerp( quaternion q1
, quaternion q2
, f32 time
)
419 f32 angle
= q1
.getDotProduct(q2
);
430 if ((angle
+ 1.0f
) > 0.05f
)
432 if ((1.0f
- angle
) >= 0.05f
) // spherical interpolation
434 f32 theta
= (f32
)acos(angle
);
435 f32 invsintheta
= 1.0f
/ (f32
)sin(theta
);
436 scale
= (f32
)sin(theta
* (1.0f
-time
)) * invsintheta
;
437 invscale
= (f32
)sin(theta
* time
) * invsintheta
;
439 else // linear interploation
447 q2
.set(-q1
.Y
, q1
.X
, -q1
.W
, q1
.Z
);
448 scale
= (f32
)sin(PI
* (0.5f
- time
));
449 invscale
= (f32
)sin(PI
* time
);
452 *this = (q1
*scale
) + (q2
*invscale
);
456 //! calculates the dot product
457 inline f32
quaternion::getDotProduct(const quaternion
& q2
) const
459 return (X
* q2
.X
) + (Y
* q2
.Y
) + (Z
* q2
.Z
) + (W
* q2
.W
);
463 inline void quaternion::fromAngleAxis(f32 angle
, const vector3df
& axis
)
465 f32 fHalfAngle
= 0.5f
*angle
;
466 f32 fSin
= (f32
)sin(fHalfAngle
);
467 W
= (f32
)cos(fHalfAngle
);
473 inline void quaternion::toAngleAxis(f32
&angle
, core::vector3df
&axis
) const
475 f32 scale
= sqrtf(X
*X
+ Y
*Y
+ Z
*Z
);
477 if (core::iszero(scale
) || W
> 1.0f
|| W
< -1.0f
)
486 angle
= 2.0f
* acos(W
);
493 inline void quaternion::toEuler(vector3df
& euler
) const
500 // heading = rotation about z-axis
501 euler
.Z
= (f32
) (atan2(2.0 * (X
*Y
+Z
*W
),(sqx
- sqy
- sqz
+ sqw
)));
503 // bank = rotation about x-axis
504 euler
.X
= (f32
) (atan2(2.0 * (Y
*Z
+X
*W
),(-sqx
- sqy
+ sqz
+ sqw
)));
506 // attitude = rotation about y-axis
507 euler
.Y
= (f32
) (asin( clamp(-2.0 * (X
*Z
- Y
*W
), -1.0, 1.0) ));
510 inline vector3df
quaternion::operator* (const vector3df
& v
) const
512 // nVidia SDK implementation
515 vector3df
qvec(X
, Y
, Z
);
516 uv
= qvec
.crossProduct(v
);
517 uuv
= qvec
.crossProduct(uv
);
524 //! set quaterion to identity
525 inline void quaternion::makeIdentity()
533 inline void quaternion::rotationFromTo(const vector3df
& from
, const vector3df
& to
)
535 // Based on Stan Melax's article in Game Programming Gems
536 // Copy, since cannot modify local
542 vector3df c
= v0
.crossProduct(v1
);
544 f32 d
= v0
.dotProduct(v1
);
545 if (d
>= 1.0f
) // If dot == 1, vectors are the same
547 *this=quaternion(0,0,0,1); //IDENTITY;
549 f32 s
= sqrtf( (1+d
)*2 ); // optimize inv_sqrt
559 } // end namespace core
560 } // end namespace irr