1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
12 // NOTE: You *only* need this when updating an application from Irrlicht before 1.8 to Irrlicht 1.8 or later.
13 // Between Irrlicht 1.7 and Irrlicht 1.8 the quaternion-matrix conversions changed.
14 // Before the fix they had mixed left- and right-handed rotations.
15 // To test if your code was affected by the change enable IRR_TEST_BROKEN_QUATERNION_USE and try to compile your application.
16 // This defines removes those functions so you get compile errors anywhere you use them in your code.
17 // For every line with a compile-errors you have to change the corresponding lines like that:
18 // - When you pass the matrix to the quaternion constructor then replace the matrix by the transposed matrix.
19 // - For uses of getMatrix() you have to use quaternion::getMatrix_transposed instead.
20 // #define IRR_TEST_BROKEN_QUATERNION_USE
27 //! Quaternion class for representing rotations.
28 /** It provides cheap combinations and avoids gimbal locks.
29 Also useful for interpolations. */
33 //! Default Constructor
34 constexpr quaternion() :
35 X(0.0f
), Y(0.0f
), Z(0.0f
), W(1.0f
) {}
38 constexpr quaternion(f32 x
, f32 y
, f32 z
, f32 w
) :
39 X(x
), Y(y
), Z(z
), W(w
) {}
41 //! Constructor which converts Euler angles (radians) to a quaternion
42 quaternion(f32 x
, f32 y
, f32 z
);
44 //! Constructor which converts Euler angles (radians) to a quaternion
45 quaternion(const vector3df
&vec
);
47 #ifndef IRR_TEST_BROKEN_QUATERNION_USE
48 //! Constructor which converts a matrix to a quaternion
49 quaternion(const matrix4
&mat
);
53 constexpr bool operator==(const quaternion
&other
) const
55 return ((X
== other
.X
) &&
61 //! inequality operator
62 constexpr bool operator!=(const quaternion
&other
) const
64 return !(*this == other
);
67 #ifndef IRR_TEST_BROKEN_QUATERNION_USE
68 //! Matrix assignment operator
69 inline quaternion
&operator=(const matrix4
&other
);
73 quaternion
operator+(const quaternion
&other
) const;
75 //! Multiplication operator
76 //! Be careful, unfortunately the operator order here is opposite of that in CMatrix4::operator*
77 quaternion
operator*(const quaternion
&other
) const;
79 //! Multiplication operator with scalar
80 quaternion
operator*(f32 s
) const;
82 //! Multiplication operator with scalar
83 quaternion
&operator*=(f32 s
);
85 //! Multiplication operator
86 vector3df
operator*(const vector3df
&v
) const;
88 //! Multiplication operator
89 quaternion
&operator*=(const quaternion
&other
);
91 //! Calculates the dot product
92 inline f32
dotProduct(const quaternion
&other
) const;
94 //! Sets new quaternion
95 inline quaternion
&set(f32 x
, f32 y
, f32 z
, f32 w
);
97 //! Sets new quaternion based on Euler angles (radians)
98 inline quaternion
&set(f32 x
, f32 y
, f32 z
);
100 //! Sets new quaternion based on Euler angles (radians)
101 inline quaternion
&set(const core::vector3df
&vec
);
103 //! Sets new quaternion from other quaternion
104 inline quaternion
&set(const core::quaternion
&quat
);
106 //! returns if this quaternion equals the other one, taking floating point rounding errors into account
107 inline bool equals(const quaternion
&other
,
108 const f32 tolerance
= ROUNDING_ERROR_f32
) const;
110 //! Normalizes the quaternion
111 inline quaternion
&normalize();
113 #ifndef IRR_TEST_BROKEN_QUATERNION_USE
114 //! Creates a matrix from this quaternion
115 matrix4
getMatrix() const;
117 //! Faster method to create a rotation matrix, you should normalize the quaternion before!
118 void getMatrixFast(matrix4
&dest
) const;
120 //! Creates a matrix from this quaternion
121 void getMatrix(matrix4
&dest
, const core::vector3df
&translation
= core::vector3df()) const;
124 Creates a matrix from this quaternion
125 Rotate about a center point
128 q.rotationFromTo ( vin[i].Normal, forward );
129 q.getMatrixCenter ( lookat, center, newPos );
132 m2.setInverseTranslation ( center );
136 m2.setTranslation ( newPos );
140 void getMatrixCenter(matrix4
&dest
, const core::vector3df
¢er
, const core::vector3df
&translation
) const;
142 //! Creates a matrix from this quaternion
143 inline void getMatrix_transposed(matrix4
&dest
) const;
145 //! Inverts this quaternion
146 quaternion
&makeInverse();
148 //! Set this quaternion to the linear interpolation between two quaternions
149 /** NOTE: lerp result is *not* a normalized quaternion. In most cases
150 you will want to use lerpN instead as most other quaternion functions expect
151 to work with a normalized quaternion.
152 \param q1 First quaternion to be interpolated.
153 \param q2 Second quaternion to be interpolated.
154 \param time Progress of interpolation. For time=0 the result is
155 q1, for time=1 the result is q2. Otherwise interpolation
156 between q1 and q2. Result is not normalized.
158 quaternion
&lerp(quaternion q1
, quaternion q2
, f32 time
);
160 //! Set this quaternion to the linear interpolation between two quaternions and normalize the result
162 \param q1 First quaternion to be interpolated.
163 \param q2 Second quaternion to be interpolated.
164 \param time Progress of interpolation. For time=0 the result is
165 q1, for time=1 the result is q2. Otherwise interpolation
166 between q1 and q2. Result is normalized.
168 quaternion
&lerpN(quaternion q1
, quaternion q2
, f32 time
);
170 //! Set this quaternion to the result of the spherical interpolation between two quaternions
171 /** \param q1 First quaternion to be interpolated.
172 \param q2 Second quaternion to be interpolated.
173 \param time Progress of interpolation. For time=0 the result is
174 q1, for time=1 the result is q2. Otherwise interpolation
176 \param threshold To avoid inaccuracies at the end (time=1) the
177 interpolation switches to linear interpolation at some point.
178 This value defines how much of the remaining interpolation will
179 be calculated with lerp. Everything from 1-threshold up will be
180 linear interpolation.
182 quaternion
&slerp(quaternion q1
, quaternion q2
,
183 f32 time
, f32 threshold
= .05f
);
185 //! Set this quaternion to represent a rotation from angle and axis.
186 /** Axis must be unit length.
187 The quaternion representing the rotation is
188 q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k).
189 \param angle Rotation Angle in radians.
190 \param axis Rotation axis. */
191 quaternion
&fromAngleAxis(f32 angle
, const vector3df
&axis
);
193 //! Fills an angle (radians) around an axis (unit vector)
194 void toAngleAxis(f32
&angle
, core::vector3df
&axis
) const;
196 //! Output this quaternion to an Euler angle (radians)
197 void toEuler(vector3df
&euler
) const;
199 //! Set quaternion to identity
200 quaternion
&makeIdentity();
202 //! Set quaternion to represent a rotation from one vector to another.
203 quaternion
&rotationFromTo(const vector3df
&from
, const vector3df
&to
);
205 //! Quaternion elements.
206 f32 X
; // vectorial (imaginary) part
212 // Constructor which converts Euler angles to a quaternion
213 inline quaternion::quaternion(f32 x
, f32 y
, f32 z
)
218 // Constructor which converts Euler angles to a quaternion
219 inline quaternion::quaternion(const vector3df
&vec
)
221 set(vec
.X
, vec
.Y
, vec
.Z
);
224 #ifndef IRR_TEST_BROKEN_QUATERNION_USE
225 // Constructor which converts a matrix to a quaternion
226 inline quaternion::quaternion(const matrix4
&mat
)
232 #ifndef IRR_TEST_BROKEN_QUATERNION_USE
233 // matrix assignment operator
234 inline quaternion
&quaternion::operator=(const matrix4
&m
)
236 const f32 diag
= m
[0] + m
[5] + m
[10] + 1;
239 const f32 scale
= sqrtf(diag
) * 2.0f
; // get scale from diagonal
241 // TODO: speed this up
242 X
= (m
[6] - m
[9]) / scale
;
243 Y
= (m
[8] - m
[2]) / scale
;
244 Z
= (m
[1] - m
[4]) / scale
;
247 if (m
[0] > m
[5] && m
[0] > m
[10]) {
248 // 1st element of diag is greatest value
249 // find scale according to 1st element, and double it
250 const f32 scale
= sqrtf(1.0f
+ m
[0] - m
[5] - m
[10]) * 2.0f
;
252 // TODO: speed this up
254 Y
= (m
[4] + m
[1]) / scale
;
255 Z
= (m
[2] + m
[8]) / scale
;
256 W
= (m
[6] - m
[9]) / scale
;
257 } else if (m
[5] > m
[10]) {
258 // 2nd element of diag is greatest value
259 // find scale according to 2nd element, and double it
260 const f32 scale
= sqrtf(1.0f
+ m
[5] - m
[0] - m
[10]) * 2.0f
;
262 // TODO: speed this up
263 X
= (m
[4] + m
[1]) / scale
;
265 Z
= (m
[9] + m
[6]) / scale
;
266 W
= (m
[8] - m
[2]) / scale
;
268 // 3rd element of diag is greatest value
269 // find scale according to 3rd element, and double it
270 const f32 scale
= sqrtf(1.0f
+ m
[10] - m
[0] - m
[5]) * 2.0f
;
272 // TODO: speed this up
273 X
= (m
[8] + m
[2]) / scale
;
274 Y
= (m
[9] + m
[6]) / scale
;
276 W
= (m
[1] - m
[4]) / scale
;
285 // multiplication operator
286 inline quaternion
quaternion::operator*(const quaternion
&other
) const
290 tmp
.W
= (other
.W
* W
) - (other
.X
* X
) - (other
.Y
* Y
) - (other
.Z
* Z
);
291 tmp
.X
= (other
.W
* X
) + (other
.X
* W
) + (other
.Y
* Z
) - (other
.Z
* Y
);
292 tmp
.Y
= (other
.W
* Y
) + (other
.Y
* W
) + (other
.Z
* X
) - (other
.X
* Z
);
293 tmp
.Z
= (other
.W
* Z
) + (other
.Z
* W
) + (other
.X
* Y
) - (other
.Y
* X
);
298 // multiplication operator
299 inline quaternion
quaternion::operator*(f32 s
) const
301 return quaternion(s
* X
, s
* Y
, s
* Z
, s
* W
);
304 // multiplication operator
305 inline quaternion
&quaternion::operator*=(f32 s
)
314 // multiplication operator
315 inline quaternion
&quaternion::operator*=(const quaternion
&other
)
317 return (*this = other
* (*this));
321 inline quaternion
quaternion::operator+(const quaternion
&b
) const
323 return quaternion(X
+ b
.X
, Y
+ b
.Y
, Z
+ b
.Z
, W
+ b
.W
);
326 #ifndef IRR_TEST_BROKEN_QUATERNION_USE
327 // Creates a matrix from this quaternion
328 inline matrix4
quaternion::getMatrix() const
336 //! Faster method to create a rotation matrix, you should normalize the quaternion before!
337 inline void quaternion::getMatrixFast(matrix4
&dest
) const
340 // gpu quaternion skinning => fast Bones transform chain O_O YEAH!
341 // http://www.mrelusive.com/publications/papers/SIMD-From-Quaternion-to-Matrix-and-Back.pdf
342 dest
[0] = 1.0f
- 2.0f
* Y
* Y
- 2.0f
* Z
* Z
;
343 dest
[1] = 2.0f
* X
* Y
+ 2.0f
* Z
* W
;
344 dest
[2] = 2.0f
* X
* Z
- 2.0f
* Y
* W
;
347 dest
[4] = 2.0f
* X
* Y
- 2.0f
* Z
* W
;
348 dest
[5] = 1.0f
- 2.0f
* X
* X
- 2.0f
* Z
* Z
;
349 dest
[6] = 2.0f
* Z
* Y
+ 2.0f
* X
* W
;
352 dest
[8] = 2.0f
* X
* Z
+ 2.0f
* Y
* W
;
353 dest
[9] = 2.0f
* Z
* Y
- 2.0f
* X
* W
;
354 dest
[10] = 1.0f
- 2.0f
* X
* X
- 2.0f
* Y
* Y
;
364 Creates a matrix from this quaternion
366 inline void quaternion::getMatrix(matrix4
&dest
,
367 const core::vector3df
¢er
) const
369 // ok creating a copy may be slower, but at least avoid internal
370 // state chance (also because otherwise we cannot keep this method "const").
379 dest
[0] = 1.0f
- 2.0f
* Y
* Y
- 2.0f
* Z
* Z
;
380 dest
[1] = 2.0f
* X
* Y
+ 2.0f
* Z
* W
;
381 dest
[2] = 2.0f
* X
* Z
- 2.0f
* Y
* W
;
384 dest
[4] = 2.0f
* X
* Y
- 2.0f
* Z
* W
;
385 dest
[5] = 1.0f
- 2.0f
* X
* X
- 2.0f
* Z
* Z
;
386 dest
[6] = 2.0f
* Z
* Y
+ 2.0f
* X
* W
;
389 dest
[8] = 2.0f
* X
* Z
+ 2.0f
* Y
* W
;
390 dest
[9] = 2.0f
* Z
* Y
- 2.0f
* X
* W
;
391 dest
[10] = 1.0f
- 2.0f
* X
* X
- 2.0f
* Y
* Y
;
401 Creates a matrix from this quaternion
402 Rotate about a center point
405 q.rotationFromTo(vin[i].Normal, forward);
406 q.getMatrix(lookat, center);
409 m2.setInverseTranslation(center);
412 inline void quaternion::getMatrixCenter(matrix4
&dest
,
413 const core::vector3df
¢er
,
414 const core::vector3df
&translation
) const
423 dest
[0] = 1.0f
- 2.0f
* Y
* Y
- 2.0f
* Z
* Z
;
424 dest
[1] = 2.0f
* X
* Y
+ 2.0f
* Z
* W
;
425 dest
[2] = 2.0f
* X
* Z
- 2.0f
* Y
* W
;
428 dest
[4] = 2.0f
* X
* Y
- 2.0f
* Z
* W
;
429 dest
[5] = 1.0f
- 2.0f
* X
* X
- 2.0f
* Z
* Z
;
430 dest
[6] = 2.0f
* Z
* Y
+ 2.0f
* X
* W
;
433 dest
[8] = 2.0f
* X
* Z
+ 2.0f
* Y
* W
;
434 dest
[9] = 2.0f
* Z
* Y
- 2.0f
* X
* W
;
435 dest
[10] = 1.0f
- 2.0f
* X
* X
- 2.0f
* Y
* Y
;
438 dest
.setRotationCenter(center
, translation
);
441 // Creates a matrix from this quaternion
442 inline void quaternion::getMatrix_transposed(matrix4
&dest
) const
451 dest
[0] = 1.0f
- 2.0f
* Y
* Y
- 2.0f
* Z
* Z
;
452 dest
[4] = 2.0f
* X
* Y
+ 2.0f
* Z
* W
;
453 dest
[8] = 2.0f
* X
* Z
- 2.0f
* Y
* W
;
456 dest
[1] = 2.0f
* X
* Y
- 2.0f
* Z
* W
;
457 dest
[5] = 1.0f
- 2.0f
* X
* X
- 2.0f
* Z
* Z
;
458 dest
[9] = 2.0f
* Z
* Y
+ 2.0f
* X
* W
;
461 dest
[2] = 2.0f
* X
* Z
+ 2.0f
* Y
* W
;
462 dest
[6] = 2.0f
* Z
* Y
- 2.0f
* X
* W
;
463 dest
[10] = 1.0f
- 2.0f
* X
* X
- 2.0f
* Y
* Y
;
472 // Inverts this quaternion
473 inline quaternion
&quaternion::makeInverse()
481 // sets new quaternion
482 inline quaternion
&quaternion::set(f32 x
, f32 y
, f32 z
, f32 w
)
491 // sets new quaternion based on Euler angles
492 inline quaternion
&quaternion::set(f32 x
, f32 y
, f32 z
)
497 const f64 sr
= sin(angle
);
498 const f64 cr
= cos(angle
);
501 const f64 sp
= sin(angle
);
502 const f64 cp
= cos(angle
);
505 const f64 sy
= sin(angle
);
506 const f64 cy
= cos(angle
);
508 const f64 cpcy
= cp
* cy
;
509 const f64 spcy
= sp
* cy
;
510 const f64 cpsy
= cp
* sy
;
511 const f64 spsy
= sp
* sy
;
513 X
= (f32
)(sr
* cpcy
- cr
* spsy
);
514 Y
= (f32
)(cr
* spcy
+ sr
* cpsy
);
515 Z
= (f32
)(cr
* cpsy
- sr
* spcy
);
516 W
= (f32
)(cr
* cpcy
+ sr
* spsy
);
521 // sets new quaternion based on Euler angles
522 inline quaternion
&quaternion::set(const core::vector3df
&vec
)
524 return set(vec
.X
, vec
.Y
, vec
.Z
);
527 // sets new quaternion based on other quaternion
528 inline quaternion
&quaternion::set(const core::quaternion
&quat
)
530 return (*this = quat
);
533 //! returns if this quaternion equals the other one, taking floating point rounding errors into account
534 inline bool quaternion::equals(const quaternion
&other
, const f32 tolerance
) const
536 return core::equals(X
, other
.X
, tolerance
) &&
537 core::equals(Y
, other
.Y
, tolerance
) &&
538 core::equals(Z
, other
.Z
, tolerance
) &&
539 core::equals(W
, other
.W
, tolerance
);
542 // normalizes the quaternion
543 inline quaternion
&quaternion::normalize()
545 // removed conditional branch since it may slow down and anyway the condition was
546 // false even after normalization in some cases.
547 return (*this *= (f32
)reciprocal_squareroot((f64
)(X
* X
+ Y
* Y
+ Z
* Z
+ W
* W
)));
550 // Set this quaternion to the result of the linear interpolation between two quaternions
551 inline quaternion
&quaternion::lerp(quaternion q1
, quaternion q2
, f32 time
)
553 const f32 scale
= 1.0f
- time
;
554 return (*this = (q1
* scale
) + (q2
* time
));
557 // Set this quaternion to the result of the linear interpolation between two quaternions and normalize the result
558 inline quaternion
&quaternion::lerpN(quaternion q1
, quaternion q2
, f32 time
)
560 const f32 scale
= 1.0f
- time
;
561 return (*this = ((q1
* scale
) + (q2
* time
)).normalize());
564 // set this quaternion to the result of the interpolation between two quaternions
565 inline quaternion
&quaternion::slerp(quaternion q1
, quaternion q2
, f32 time
, f32 threshold
)
567 f32 angle
= q1
.dotProduct(q2
);
569 // make sure we use the short rotation
575 if (angle
<= (1 - threshold
)) { // spherical interpolation
576 const f32 theta
= acosf(angle
);
577 const f32 invsintheta
= reciprocal(sinf(theta
));
578 const f32 scale
= sinf(theta
* (1.0f
- time
)) * invsintheta
;
579 const f32 invscale
= sinf(theta
* time
) * invsintheta
;
580 return (*this = (q1
* scale
) + (q2
* invscale
));
581 } else // linear interpolation
582 return lerpN(q1
, q2
, time
);
585 // calculates the dot product
586 inline f32
quaternion::dotProduct(const quaternion
&q2
) const
588 return (X
* q2
.X
) + (Y
* q2
.Y
) + (Z
* q2
.Z
) + (W
* q2
.W
);
591 //! axis must be unit length, angle in radians
592 inline quaternion
&quaternion::fromAngleAxis(f32 angle
, const vector3df
&axis
)
594 const f32 fHalfAngle
= 0.5f
* angle
;
595 const f32 fSin
= sinf(fHalfAngle
);
596 W
= cosf(fHalfAngle
);
603 inline void quaternion::toAngleAxis(f32
&angle
, core::vector3df
&axis
) const
605 const f32 scale
= sqrtf(X
* X
+ Y
* Y
+ Z
* Z
);
607 if (core::iszero(scale
) || W
> 1.0f
|| W
< -1.0f
) {
613 const f32 invscale
= reciprocal(scale
);
614 angle
= 2.0f
* acosf(W
);
615 axis
.X
= X
* invscale
;
616 axis
.Y
= Y
* invscale
;
617 axis
.Z
= Z
* invscale
;
621 inline void quaternion::toEuler(vector3df
&euler
) const
623 const f64 sqw
= W
* W
;
624 const f64 sqx
= X
* X
;
625 const f64 sqy
= Y
* Y
;
626 const f64 sqz
= Z
* Z
;
627 const f64 test
= 2.0 * (Y
* W
- X
* Z
);
629 if (core::equals(test
, 1.0, 0.000001)) {
630 // heading = rotation about z-axis
631 euler
.Z
= (f32
)(-2.0 * atan2(X
, W
));
632 // bank = rotation about x-axis
634 // attitude = rotation about y-axis
635 euler
.Y
= (f32
)(core::PI64
/ 2.0);
636 } else if (core::equals(test
, -1.0, 0.000001)) {
637 // heading = rotation about z-axis
638 euler
.Z
= (f32
)(2.0 * atan2(X
, W
));
639 // bank = rotation about x-axis
641 // attitude = rotation about y-axis
642 euler
.Y
= (f32
)(core::PI64
/ -2.0);
644 // heading = rotation about z-axis
645 euler
.Z
= (f32
)atan2(2.0 * (X
* Y
+ Z
* W
), (sqx
- sqy
- sqz
+ sqw
));
646 // bank = rotation about x-axis
647 euler
.X
= (f32
)atan2(2.0 * (Y
* Z
+ X
* W
), (-sqx
- sqy
+ sqz
+ sqw
));
648 // attitude = rotation about y-axis
649 euler
.Y
= (f32
)asin(clamp(test
, -1.0, 1.0));
653 inline vector3df
quaternion::operator*(const vector3df
&v
) const
655 // nVidia SDK implementation
658 const vector3df
qvec(X
, Y
, Z
);
659 uv
= qvec
.crossProduct(v
);
660 uuv
= qvec
.crossProduct(uv
);
667 // set quaternion to identity
668 inline core::quaternion
&quaternion::makeIdentity()
677 inline core::quaternion
&quaternion::rotationFromTo(const vector3df
&from
, const vector3df
&to
)
679 // Based on Stan Melax's article in Game Programming Gems
680 // Optimized by Robert Eisele: https://raw.org/proof/quaternion-from-two-vectors
682 // Copy, since cannot modify local
688 const f32 d
= v0
.dotProduct(v1
);
689 if (d
>= 1.0f
) { // If dot == 1, vectors are the same
690 return makeIdentity();
691 } else if (d
<= -1.0f
) { // exactly opposite
692 core::vector3df
axis(1.0f
, 0.f
, 0.f
);
693 axis
= axis
.crossProduct(v0
);
694 if (axis
.getLength() == 0) {
695 axis
.set(0.f
, 1.f
, 0.f
);
696 axis
= axis
.crossProduct(v0
);
698 // same as fromAngleAxis(core::PI, axis).normalize();
699 return set(axis
.X
, axis
.Y
, axis
.Z
, 0).normalize();
702 const vector3df c
= v0
.crossProduct(v1
);
703 return set(c
.X
, c
.Y
, c
.Z
, 1 + d
).normalize();
706 } // end namespace core
707 } // end namespace irr