123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714 |
- // 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
- #pragma once
- #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
- constexpr quaternion() :
- X(0.0f), Y(0.0f), Z(0.0f), W(1.0f) {}
- //! Constructor
- constexpr 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
- constexpr bool operator==(const quaternion &other) const
- {
- return ((X == other.X) &&
- (Y == other.Y) &&
- (Z == other.Z) &&
- (W == other.W));
- }
- //! inequality operator
- constexpr bool operator!=(const quaternion &other) const
- {
- return !(*this == 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
- #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;
- }
- }
- normalize();
- return *this;
- }
- #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
- // Optimized by Robert Eisele: https://raw.org/proof/quaternion-from-two-vectors
- // 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 vector3df c = v0.crossProduct(v1);
- return set(c.X, c.Y, c.Z, 1 + d).normalize();
- }
- } // end namespace core
- } // end namespace irr
|