diff options
author | cutealien <cutealien@dfc29bdd-3216-0410-991c-e03cc46cb475> | 2020-01-03 19:05:16 +0000 |
---|---|---|
committer | cutealien <cutealien@dfc29bdd-3216-0410-991c-e03cc46cb475> | 2020-01-03 19:05:16 +0000 |
commit | 2ae2a551a6290f46734307bbfdafea4b1a2cf2ba (patch) | |
tree | ba2f0b468640e44899fed3df2d4cc58795f4a003 /include/quaternion.h | |
download | irrlicht-2ae2a551a6290f46734307bbfdafea4b1a2cf2ba.tar.xz |
Merging r5975 through r6036 from trunk to ogl-es branch.
GLES drivers adapted, but only did make compile-tests.
git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@6038 dfc29bdd-3216-0410-991c-e03cc46cb475
Diffstat (limited to 'include/quaternion.h')
-rw-r--r-- | include/quaternion.h | 771 |
1 files changed, 771 insertions, 0 deletions
diff --git a/include/quaternion.h b/include/quaternion.h new file mode 100644 index 0000000..5fdff79 --- /dev/null +++ b/include/quaternion.h @@ -0,0 +1,771 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#ifndef __IRR_QUATERNION_H_INCLUDED__
+#define __IRR_QUATERNION_H_INCLUDED__
+
+#include "irrTypes.h"
+#include "irrMath.h"
+#include "matrix4.h"
+#include "vector3d.h"
+
+// NOTE: You *only* need this when updating an application from Irrlicht before 1.8 to Irrlicht 1.8 or later.
+// Between Irrlicht 1.7 and Irrlicht 1.8 the quaternion-matrix conversions changed.
+// Before the fix they had mixed left- and right-handed rotations.
+// To test if your code was affected by the change enable IRR_TEST_BROKEN_QUATERNION_USE and try to compile your application.
+// This defines removes those functions so you get compile errors anywhere you use them in your code.
+// For every line with a compile-errors you have to change the corresponding lines like that:
+// - When you pass the matrix to the quaternion constructor then replace the matrix by the transposed matrix.
+// - For uses of getMatrix() you have to use quaternion::getMatrix_transposed instead.
+// #define IRR_TEST_BROKEN_QUATERNION_USE
+
+namespace irr
+{
+namespace core
+{
+
+//! Quaternion class for representing rotations.
+/** It provides cheap combinations and avoids gimbal locks.
+Also useful for interpolations. */
+class quaternion
+{
+ public:
+
+ //! Default Constructor
+ quaternion() : X(0.0f), Y(0.0f), Z(0.0f), W(1.0f) {}
+
+ //! Constructor
+ quaternion(f32 x, f32 y, f32 z, f32 w) : X(x), Y(y), Z(z), W(w) { }
+
+ //! Constructor which converts Euler angles (radians) to a quaternion
+ quaternion(f32 x, f32 y, f32 z);
+
+ //! Constructor which converts Euler angles (radians) to a quaternion
+ quaternion(const vector3df& vec);
+
+#ifndef IRR_TEST_BROKEN_QUATERNION_USE
+ //! Constructor which converts a matrix to a quaternion
+ quaternion(const matrix4& mat);
+#endif
+
+ //! Equality operator
+ bool operator==(const quaternion& other) const;
+
+ //! inequality operator
+ bool operator!=(const quaternion& other) const;
+
+ //! Assignment operator
+ inline quaternion& operator=(const quaternion& other);
+
+#ifndef IRR_TEST_BROKEN_QUATERNION_USE
+ //! Matrix assignment operator
+ inline quaternion& operator=(const matrix4& other);
+#endif
+
+ //! Add operator
+ quaternion operator+(const quaternion& other) const;
+
+ //! Multiplication operator
+ //! Be careful, unfortunately the operator order here is opposite of that in CMatrix4::operator*
+ quaternion operator*(const quaternion& other) const;
+
+ //! Multiplication operator with scalar
+ quaternion operator*(f32 s) const;
+
+ //! Multiplication operator with scalar
+ quaternion& operator*=(f32 s);
+
+ //! Multiplication operator
+ vector3df operator*(const vector3df& v) const;
+
+ //! Multiplication operator
+ quaternion& operator*=(const quaternion& other);
+
+ //! Calculates the dot product
+ inline f32 dotProduct(const quaternion& other) const;
+
+ //! Sets new quaternion
+ inline quaternion& set(f32 x, f32 y, f32 z, f32 w);
+
+ //! Sets new quaternion based on Euler angles (radians)
+ inline quaternion& set(f32 x, f32 y, f32 z);
+
+ //! Sets new quaternion based on Euler angles (radians)
+ inline quaternion& set(const core::vector3df& vec);
+
+ //! Sets new quaternion from other quaternion
+ inline quaternion& set(const core::quaternion& quat);
+
+ //! returns if this quaternion equals the other one, taking floating point rounding errors into account
+ inline bool equals(const quaternion& other,
+ const f32 tolerance = ROUNDING_ERROR_f32 ) const;
+
+ //! Normalizes the quaternion
+ inline quaternion& normalize();
+
+#ifndef IRR_TEST_BROKEN_QUATERNION_USE
+ //! Creates a matrix from this quaternion
+ matrix4 getMatrix() const;
+#endif
+ //! Faster method to create a rotation matrix, you should normalize the quaternion before!
+ void getMatrixFast(matrix4 &dest) const;
+
+ //! Creates a matrix from this quaternion
+ void getMatrix( matrix4 &dest, const core::vector3df &translation=core::vector3df() ) const;
+
+ /*!
+ Creates a matrix from this quaternion
+ Rotate about a center point
+ shortcut for
+ core::quaternion q;
+ q.rotationFromTo ( vin[i].Normal, forward );
+ q.getMatrixCenter ( lookat, center, newPos );
+
+ core::matrix4 m2;
+ m2.setInverseTranslation ( center );
+ lookat *= m2;
+
+ core::matrix4 m3;
+ m2.setTranslation ( newPos );
+ lookat *= m3;
+
+ */
+ void getMatrixCenter( matrix4 &dest, const core::vector3df ¢er, const core::vector3df &translation ) const;
+
+ //! Creates a matrix from this quaternion
+ inline void getMatrix_transposed( matrix4 &dest ) const;
+
+ //! Inverts this quaternion
+ quaternion& makeInverse();
+
+ //! Set this quaternion to the linear interpolation between two quaternions
+ /** NOTE: lerp result is *not* a normalized quaternion. In most cases
+ you will want to use lerpN instead as most other quaternion functions expect
+ to work with a normalized quaternion.
+ \param q1 First quaternion to be interpolated.
+ \param q2 Second quaternion to be interpolated.
+ \param time Progress of interpolation. For time=0 the result is
+ q1, for time=1 the result is q2. Otherwise interpolation
+ between q1 and q2. Result is not normalized.
+ */
+ quaternion& lerp(quaternion q1, quaternion q2, f32 time);
+
+ //! Set this quaternion to the linear interpolation between two quaternions and normalize the result
+ /**
+ \param q1 First quaternion to be interpolated.
+ \param q2 Second quaternion to be interpolated.
+ \param time Progress of interpolation. For time=0 the result is
+ q1, for time=1 the result is q2. Otherwise interpolation
+ between q1 and q2. Result is normalized.
+ */
+ quaternion& lerpN(quaternion q1, quaternion q2, f32 time);
+
+ //! Set this quaternion to the result of the spherical interpolation between two quaternions
+ /** \param q1 First quaternion to be interpolated.
+ \param q2 Second quaternion to be interpolated.
+ \param time Progress of interpolation. For time=0 the result is
+ q1, for time=1 the result is q2. Otherwise interpolation
+ between q1 and q2.
+ \param threshold To avoid inaccuracies at the end (time=1) the
+ interpolation switches to linear interpolation at some point.
+ This value defines how much of the remaining interpolation will
+ be calculated with lerp. Everything from 1-threshold up will be
+ linear interpolation.
+ */
+ quaternion& slerp(quaternion q1, quaternion q2,
+ f32 time, f32 threshold=.05f);
+
+ //! Set this quaternion to represent a rotation from angle and axis.
+ /** Axis must be unit length.
+ The quaternion representing the rotation is
+ q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k).
+ \param angle Rotation Angle in radians.
+ \param axis Rotation axis. */
+ quaternion& fromAngleAxis (f32 angle, const vector3df& axis);
+
+ //! Fills an angle (radians) around an axis (unit vector)
+ void toAngleAxis (f32 &angle, core::vector3df& axis) const;
+
+ //! Output this quaternion to an Euler angle (radians)
+ void toEuler(vector3df& euler) const;
+
+ //! Set quaternion to identity
+ quaternion& makeIdentity();
+
+ //! Set quaternion to represent a rotation from one vector to another.
+ quaternion& rotationFromTo(const vector3df& from, const vector3df& to);
+
+ //! Quaternion elements.
+ f32 X; // vectorial (imaginary) part
+ f32 Y;
+ f32 Z;
+ f32 W; // real part
+};
+
+
+// Constructor which converts Euler angles to a quaternion
+inline quaternion::quaternion(f32 x, f32 y, f32 z)
+{
+ set(x,y,z);
+}
+
+
+// Constructor which converts Euler angles to a quaternion
+inline quaternion::quaternion(const vector3df& vec)
+{
+ set(vec.X,vec.Y,vec.Z);
+}
+
+#ifndef IRR_TEST_BROKEN_QUATERNION_USE
+// Constructor which converts a matrix to a quaternion
+inline quaternion::quaternion(const matrix4& mat)
+{
+ (*this) = mat;
+}
+#endif
+
+// equal operator
+inline bool quaternion::operator==(const quaternion& other) const
+{
+ return ((X == other.X) &&
+ (Y == other.Y) &&
+ (Z == other.Z) &&
+ (W == other.W));
+}
+
+// inequality operator
+inline bool quaternion::operator!=(const quaternion& other) const
+{
+ return !(*this == other);
+}
+
+// assignment operator
+inline quaternion& quaternion::operator=(const quaternion& other)
+{
+ X = other.X;
+ Y = other.Y;
+ Z = other.Z;
+ W = other.W;
+ return *this;
+}
+
+#ifndef IRR_TEST_BROKEN_QUATERNION_USE
+// matrix assignment operator
+inline quaternion& quaternion::operator=(const matrix4& m)
+{
+ const f32 diag = m[0] + m[5] + m[10] + 1;
+
+ if( diag > 0.0f )
+ {
+ const f32 scale = sqrtf(diag) * 2.0f; // get scale from diagonal
+
+ // TODO: speed this up
+ X = (m[6] - m[9]) / scale;
+ Y = (m[8] - m[2]) / scale;
+ Z = (m[1] - m[4]) / scale;
+ W = 0.25f * scale;
+ }
+ else
+ {
+ if (m[0]>m[5] && m[0]>m[10])
+ {
+ // 1st element of diag is greatest value
+ // find scale according to 1st element, and double it
+ const f32 scale = sqrtf(1.0f + m[0] - m[5] - m[10]) * 2.0f;
+
+ // TODO: speed this up
+ X = 0.25f * scale;
+ Y = (m[4] + m[1]) / scale;
+ Z = (m[2] + m[8]) / scale;
+ W = (m[6] - m[9]) / scale;
+ }
+ else if (m[5]>m[10])
+ {
+ // 2nd element of diag is greatest value
+ // find scale according to 2nd element, and double it
+ const f32 scale = sqrtf(1.0f + m[5] - m[0] - m[10]) * 2.0f;
+
+ // TODO: speed this up
+ X = (m[4] + m[1]) / scale;
+ Y = 0.25f * scale;
+ Z = (m[9] + m[6]) / scale;
+ W = (m[8] - m[2]) / scale;
+ }
+ else
+ {
+ // 3rd element of diag is greatest value
+ // find scale according to 3rd element, and double it
+ const f32 scale = sqrtf(1.0f + m[10] - m[0] - m[5]) * 2.0f;
+
+ // TODO: speed this up
+ X = (m[8] + m[2]) / scale;
+ Y = (m[9] + m[6]) / scale;
+ Z = 0.25f * scale;
+ W = (m[1] - m[4]) / scale;
+ }
+ }
+
+ return normalize();
+}
+#endif
+
+
+// multiplication operator
+inline quaternion quaternion::operator*(const quaternion& other) const
+{
+ quaternion tmp;
+
+ tmp.W = (other.W * W) - (other.X * X) - (other.Y * Y) - (other.Z * Z);
+ tmp.X = (other.W * X) + (other.X * W) + (other.Y * Z) - (other.Z * Y);
+ tmp.Y = (other.W * Y) + (other.Y * W) + (other.Z * X) - (other.X * Z);
+ tmp.Z = (other.W * Z) + (other.Z * W) + (other.X * Y) - (other.Y * X);
+
+ return tmp;
+}
+
+
+// multiplication operator
+inline quaternion quaternion::operator*(f32 s) const
+{
+ return quaternion(s*X, s*Y, s*Z, s*W);
+}
+
+
+// multiplication operator
+inline quaternion& quaternion::operator*=(f32 s)
+{
+ X*=s;
+ Y*=s;
+ Z*=s;
+ W*=s;
+ return *this;
+}
+
+// multiplication operator
+inline quaternion& quaternion::operator*=(const quaternion& other)
+{
+ return (*this = other * (*this));
+}
+
+// add operator
+inline quaternion quaternion::operator+(const quaternion& b) const
+{
+ return quaternion(X+b.X, Y+b.Y, Z+b.Z, W+b.W);
+}
+
+#ifndef IRR_TEST_BROKEN_QUATERNION_USE
+// Creates a matrix from this quaternion
+inline matrix4 quaternion::getMatrix() const
+{
+ core::matrix4 m;
+ getMatrix(m);
+ return m;
+}
+#endif
+
+//! Faster method to create a rotation matrix, you should normalize the quaternion before!
+inline void quaternion::getMatrixFast( matrix4 &dest) const
+{
+ // TODO:
+ // gpu quaternion skinning => fast Bones transform chain O_O YEAH!
+ // http://www.mrelusive.com/publications/papers/SIMD-From-Quaternion-to-Matrix-and-Back.pdf
+ dest[0] = 1.0f - 2.0f*Y*Y - 2.0f*Z*Z;
+ dest[1] = 2.0f*X*Y + 2.0f*Z*W;
+ dest[2] = 2.0f*X*Z - 2.0f*Y*W;
+ dest[3] = 0.0f;
+
+ dest[4] = 2.0f*X*Y - 2.0f*Z*W;
+ dest[5] = 1.0f - 2.0f*X*X - 2.0f*Z*Z;
+ dest[6] = 2.0f*Z*Y + 2.0f*X*W;
+ dest[7] = 0.0f;
+
+ dest[8] = 2.0f*X*Z + 2.0f*Y*W;
+ dest[9] = 2.0f*Z*Y - 2.0f*X*W;
+ dest[10] = 1.0f - 2.0f*X*X - 2.0f*Y*Y;
+ dest[11] = 0.0f;
+
+ dest[12] = 0.f;
+ dest[13] = 0.f;
+ dest[14] = 0.f;
+ dest[15] = 1.f;
+
+ dest.setDefinitelyIdentityMatrix(false);
+}
+
+/*!
+ Creates a matrix from this quaternion
+*/
+inline void quaternion::getMatrix(matrix4 &dest,
+ const core::vector3df ¢er) const
+{
+ // ok creating a copy may be slower, but at least avoid internal
+ // state chance (also because otherwise we cannot keep this method "const").
+
+ quaternion q( *this);
+ q.normalize();
+ f32 X = q.X;
+ f32 Y = q.Y;
+ f32 Z = q.Z;
+ f32 W = q.W;
+
+ dest[0] = 1.0f - 2.0f*Y*Y - 2.0f*Z*Z;
+ dest[1] = 2.0f*X*Y + 2.0f*Z*W;
+ dest[2] = 2.0f*X*Z - 2.0f*Y*W;
+ dest[3] = 0.0f;
+
+ dest[4] = 2.0f*X*Y - 2.0f*Z*W;
+ dest[5] = 1.0f - 2.0f*X*X - 2.0f*Z*Z;
+ dest[6] = 2.0f*Z*Y + 2.0f*X*W;
+ dest[7] = 0.0f;
+
+ dest[8] = 2.0f*X*Z + 2.0f*Y*W;
+ dest[9] = 2.0f*Z*Y - 2.0f*X*W;
+ dest[10] = 1.0f - 2.0f*X*X - 2.0f*Y*Y;
+ dest[11] = 0.0f;
+
+ dest[12] = center.X;
+ dest[13] = center.Y;
+ dest[14] = center.Z;
+ dest[15] = 1.f;
+
+ dest.setDefinitelyIdentityMatrix ( false );
+}
+
+
+/*!
+ Creates a matrix from this quaternion
+ Rotate about a center point
+ shortcut for
+ core::quaternion q;
+ q.rotationFromTo(vin[i].Normal, forward);
+ q.getMatrix(lookat, center);
+
+ core::matrix4 m2;
+ m2.setInverseTranslation(center);
+ lookat *= m2;
+*/
+inline void quaternion::getMatrixCenter(matrix4 &dest,
+ const core::vector3df ¢er,
+ const core::vector3df &translation) const
+{
+ quaternion q(*this);
+ q.normalize();
+ f32 X = q.X;
+ f32 Y = q.Y;
+ f32 Z = q.Z;
+ f32 W = q.W;
+
+ dest[0] = 1.0f - 2.0f*Y*Y - 2.0f*Z*Z;
+ dest[1] = 2.0f*X*Y + 2.0f*Z*W;
+ dest[2] = 2.0f*X*Z - 2.0f*Y*W;
+ dest[3] = 0.0f;
+
+ dest[4] = 2.0f*X*Y - 2.0f*Z*W;
+ dest[5] = 1.0f - 2.0f*X*X - 2.0f*Z*Z;
+ dest[6] = 2.0f*Z*Y + 2.0f*X*W;
+ dest[7] = 0.0f;
+
+ dest[8] = 2.0f*X*Z + 2.0f*Y*W;
+ dest[9] = 2.0f*Z*Y - 2.0f*X*W;
+ dest[10] = 1.0f - 2.0f*X*X - 2.0f*Y*Y;
+ dest[11] = 0.0f;
+
+ dest.setRotationCenter ( center, translation );
+}
+
+// Creates a matrix from this quaternion
+inline void quaternion::getMatrix_transposed(matrix4 &dest) const
+{
+ quaternion q(*this);
+ q.normalize();
+ f32 X = q.X;
+ f32 Y = q.Y;
+ f32 Z = q.Z;
+ f32 W = q.W;
+
+ dest[0] = 1.0f - 2.0f*Y*Y - 2.0f*Z*Z;
+ dest[4] = 2.0f*X*Y + 2.0f*Z*W;
+ dest[8] = 2.0f*X*Z - 2.0f*Y*W;
+ dest[12] = 0.0f;
+
+ dest[1] = 2.0f*X*Y - 2.0f*Z*W;
+ dest[5] = 1.0f - 2.0f*X*X - 2.0f*Z*Z;
+ dest[9] = 2.0f*Z*Y + 2.0f*X*W;
+ dest[13] = 0.0f;
+
+ dest[2] = 2.0f*X*Z + 2.0f*Y*W;
+ dest[6] = 2.0f*Z*Y - 2.0f*X*W;
+ dest[10] = 1.0f - 2.0f*X*X - 2.0f*Y*Y;
+ dest[14] = 0.0f;
+
+ dest[3] = 0.f;
+ dest[7] = 0.f;
+ dest[11] = 0.f;
+ dest[15] = 1.f;
+
+ dest.setDefinitelyIdentityMatrix(false);
+}
+
+
+// Inverts this quaternion
+inline quaternion& quaternion::makeInverse()
+{
+ X = -X; Y = -Y; Z = -Z;
+ return *this;
+}
+
+
+// sets new quaternion
+inline quaternion& quaternion::set(f32 x, f32 y, f32 z, f32 w)
+{
+ X = x;
+ Y = y;
+ Z = z;
+ W = w;
+ return *this;
+}
+
+
+// sets new quaternion based on Euler angles
+inline quaternion& quaternion::set(f32 x, f32 y, f32 z)
+{
+ f64 angle;
+
+ angle = x * 0.5;
+ const f64 sr = sin(angle);
+ const f64 cr = cos(angle);
+
+ angle = y * 0.5;
+ const f64 sp = sin(angle);
+ const f64 cp = cos(angle);
+
+ angle = z * 0.5;
+ const f64 sy = sin(angle);
+ const f64 cy = cos(angle);
+
+ const f64 cpcy = cp * cy;
+ const f64 spcy = sp * cy;
+ const f64 cpsy = cp * sy;
+ const f64 spsy = sp * sy;
+
+ X = (f32)(sr * cpcy - cr * spsy);
+ Y = (f32)(cr * spcy + sr * cpsy);
+ Z = (f32)(cr * cpsy - sr * spcy);
+ W = (f32)(cr * cpcy + sr * spsy);
+
+ return normalize();
+}
+
+// sets new quaternion based on Euler angles
+inline quaternion& quaternion::set(const core::vector3df& vec)
+{
+ return set( vec.X, vec.Y, vec.Z);
+}
+
+// sets new quaternion based on other quaternion
+inline quaternion& quaternion::set(const core::quaternion& quat)
+{
+ return (*this=quat);
+}
+
+
+//! returns if this quaternion equals the other one, taking floating point rounding errors into account
+inline bool quaternion::equals(const quaternion& other, const f32 tolerance) const
+{
+ return core::equals( X, other.X, tolerance) &&
+ core::equals( Y, other.Y, tolerance) &&
+ core::equals( Z, other.Z, tolerance) &&
+ core::equals( W, other.W, tolerance);
+}
+
+
+// normalizes the quaternion
+inline quaternion& quaternion::normalize()
+{
+ // removed conditional branch since it may slow down and anyway the condition was
+ // false even after normalization in some cases.
+ return (*this *= (f32)reciprocal_squareroot ( (f64)(X*X + Y*Y + Z*Z + W*W) ));
+}
+
+// Set this quaternion to the result of the linear interpolation between two quaternions
+inline quaternion& quaternion::lerp( quaternion q1, quaternion q2, f32 time)
+{
+ const f32 scale = 1.0f - time;
+ return (*this = (q1*scale) + (q2*time));
+}
+
+// Set this quaternion to the result of the linear interpolation between two quaternions and normalize the result
+inline quaternion& quaternion::lerpN( quaternion q1, quaternion q2, f32 time)
+{
+ const f32 scale = 1.0f - time;
+ return (*this = ((q1*scale) + (q2*time)).normalize() );
+}
+
+// set this quaternion to the result of the interpolation between two quaternions
+inline quaternion& quaternion::slerp( quaternion q1, quaternion q2, f32 time, f32 threshold)
+{
+ f32 angle = q1.dotProduct(q2);
+
+ // make sure we use the short rotation
+ if (angle < 0.0f)
+ {
+ q1 *= -1.0f;
+ angle *= -1.0f;
+ }
+
+ if (angle <= (1-threshold)) // spherical interpolation
+ {
+ const f32 theta = acosf(angle);
+ const f32 invsintheta = reciprocal(sinf(theta));
+ const f32 scale = sinf(theta * (1.0f-time)) * invsintheta;
+ const f32 invscale = sinf(theta * time) * invsintheta;
+ return (*this = (q1*scale) + (q2*invscale));
+ }
+ else // linear interpolation
+ return lerpN(q1,q2,time);
+}
+
+
+// calculates the dot product
+inline f32 quaternion::dotProduct(const quaternion& q2) const
+{
+ return (X * q2.X) + (Y * q2.Y) + (Z * q2.Z) + (W * q2.W);
+}
+
+
+//! axis must be unit length, angle in radians
+inline quaternion& quaternion::fromAngleAxis(f32 angle, const vector3df& axis)
+{
+ const f32 fHalfAngle = 0.5f*angle;
+ const f32 fSin = sinf(fHalfAngle);
+ W = cosf(fHalfAngle);
+ X = fSin*axis.X;
+ Y = fSin*axis.Y;
+ Z = fSin*axis.Z;
+ return *this;
+}
+
+
+inline void quaternion::toAngleAxis(f32 &angle, core::vector3df &axis) const
+{
+ const f32 scale = sqrtf(X*X + Y*Y + Z*Z);
+
+ if (core::iszero(scale) || W > 1.0f || W < -1.0f)
+ {
+ angle = 0.0f;
+ axis.X = 0.0f;
+ axis.Y = 1.0f;
+ axis.Z = 0.0f;
+ }
+ else
+ {
+ const f32 invscale = reciprocal(scale);
+ angle = 2.0f * acosf(W);
+ axis.X = X * invscale;
+ axis.Y = Y * invscale;
+ axis.Z = Z * invscale;
+ }
+}
+
+inline void quaternion::toEuler(vector3df& euler) const
+{
+ const f64 sqw = W*W;
+ const f64 sqx = X*X;
+ const f64 sqy = Y*Y;
+ const f64 sqz = Z*Z;
+ const f64 test = 2.0 * (Y*W - X*Z);
+
+ if (core::equals(test, 1.0, 0.000001))
+ {
+ // heading = rotation about z-axis
+ euler.Z = (f32) (-2.0*atan2(X, W));
+ // bank = rotation about x-axis
+ euler.X = 0;
+ // attitude = rotation about y-axis
+ euler.Y = (f32) (core::PI64/2.0);
+ }
+ else if (core::equals(test, -1.0, 0.000001))
+ {
+ // heading = rotation about z-axis
+ euler.Z = (f32) (2.0*atan2(X, W));
+ // bank = rotation about x-axis
+ euler.X = 0;
+ // attitude = rotation about y-axis
+ euler.Y = (f32) (core::PI64/-2.0);
+ }
+ else
+ {
+ // heading = rotation about z-axis
+ euler.Z = (f32) atan2(2.0 * (X*Y +Z*W),(sqx - sqy - sqz + sqw));
+ // bank = rotation about x-axis
+ euler.X = (f32) atan2(2.0 * (Y*Z +X*W),(-sqx - sqy + sqz + sqw));
+ // attitude = rotation about y-axis
+ euler.Y = (f32) asin( clamp(test, -1.0, 1.0) );
+ }
+}
+
+
+inline vector3df quaternion::operator* (const vector3df& v) const
+{
+ // nVidia SDK implementation
+
+ vector3df uv, uuv;
+ const vector3df qvec(X, Y, Z);
+ uv = qvec.crossProduct(v);
+ uuv = qvec.crossProduct(uv);
+ uv *= (2.0f * W);
+ uuv *= 2.0f;
+
+ return v + uv + uuv;
+}
+
+// set quaternion to identity
+inline core::quaternion& quaternion::makeIdentity()
+{
+ W = 1.f;
+ X = 0.f;
+ Y = 0.f;
+ Z = 0.f;
+ return *this;
+}
+
+inline core::quaternion& quaternion::rotationFromTo(const vector3df& from, const vector3df& to)
+{
+ // Based on Stan Melax's article in Game Programming Gems
+ // Copy, since cannot modify local
+ vector3df v0 = from;
+ vector3df v1 = to;
+ v0.normalize();
+ v1.normalize();
+
+ const f32 d = v0.dotProduct(v1);
+ if (d >= 1.0f) // If dot == 1, vectors are the same
+ {
+ return makeIdentity();
+ }
+ else if (d <= -1.0f) // exactly opposite
+ {
+ core::vector3df axis(1.0f, 0.f, 0.f);
+ axis = axis.crossProduct(v0);
+ if (axis.getLength()==0)
+ {
+ axis.set(0.f,1.f,0.f);
+ axis = axis.crossProduct(v0);
+ }
+ // same as fromAngleAxis(core::PI, axis).normalize();
+ return set(axis.X, axis.Y, axis.Z, 0).normalize();
+ }
+
+ const f32 s = sqrtf( (1+d)*2 ); // optimize inv_sqrt
+ const f32 invs = 1.f / s;
+ const vector3df c = v0.crossProduct(v1)*invs;
+ return set(c.X, c.Y, c.Z, s * 0.5f).normalize();
+}
+
+
+} // end namespace core
+} // end namespace irr
+
+#endif
+
|