Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /************************************************ ************************//**
- * @filequat.h
- * @brief Description of the quaternion class.
- *
- * @date
- * @todo comments
- **************************************** ****************************/
- // What is a quaternion?
- // ---------------------
- // a quaternion is a point in 4-dimensional complex space.
- // Q = { Qx, Qy, Qz, Qw }
- //
- // Why are quaternions needed?
- //-----------------------
- // Set of all quaternions belonging to a sphere with radius 1
- // corresponds to the set of all possible rotation transformations in
- // three-dimensional Euclidean space. Rotation is easier to describe
- // in quaternion space than in rotation matrix space.
- // * Quaternion is identical to a 4-dimensional vector, easier to store in arrays,
- // as float or _m128
- // * There is no problem of choosing a storage method
- // * Being small
- // * Multiplying a vector by a quaternion contains fewer multiplications
- //
- // How to use quaternions?
- // -----------------------------
- // You should know how to get a quaternion from a matrix and vice versa
- // The rotation of vector v can be written as follows:
- //
- // v' = v * R
- //
- // 1) vector v' describes the same point in a different coordinate system
- // 2) vector v' describes a point rotated in accordance with
- // conversion parameters
- //
- //
- // Quaternions and rotation around an axis
- // ---------------------------------
- // Suppose we want to record a rotation by an angle (theta)
- // around the axis described by the unit vector ({Ax, Ay, Az})...
- //
- // s = sin(0.5 * theta)
- // c = cos(0.5 * theta)
- // Q = { s * Ax, s * Ay, s * Az, c }
- //
- // If vector A is unit, then the quaternion metric is too.
- //
- // 3x3 matrix corresponding to the quaternion
- // -----------------------------------------
- // Q = { x, y, z ,w }
- //
- // | |
- // | 1 - 2 * (y^2 + z^2) 2 * (x * y + z * w) 2 * (y * w - x * z) |
- // | |
- // M = | 2 * (x * y - z * w) 1 - 2 * (x^2 + z^2) 2 * (y * z + x * w) |
- // | |
- // | 2 * (x * z + y * w) 2 * (y * z - x * w) 1 - 2 * (x^2 + y^2) |
- // |
- #ifndef QUAT_H
- #define QUAT_H
- #include "point2d.h"
- class CVector4F;
- class CVector3F;
- class CVector3D;
- class CMatrix4;
- class CMatrix3;
- // Order of rotations for Euler angles:
- //
- // rotates the SCS around the original x by 'roll'
- // rotates the SSC around the original y to 'pitch'
- // rotates the SSC around the original z to 'yaw'.
- // .
- // /|\ yaw axis
- // | __.
- // ._ ___| /| pitch axis
- // _||\ \\ |-. /
- // \|| \_______\_|__\_/_______
- // | _ _ o o o_o_o_o o /_\__ ________\ roll axis
- // // /_______/ /__________/ /
- // /_,-' // /
- // /__,-'
- //
- // Angles are specified in radians
- //************************************************ *******************************
- // NOTA BENE: Quaternion must be unary!!!! And all the vectors and matrices
- // must be normalized. Otherwise it will be bad!!!
- //************************************************ *******************************
- static const U32 LENGTHOFQUAT = 4;
- class CQuaternion
- {
- F32 mQ[LENGTHOFQUAT];
- public:
- /// Creates a unit quaternion (0,0,0,1)
- CQuaternion();
- /// Creates a rotation quaternion from Matrix4
- explicit CQuaternion(const CMatrix4 &mat);
- /// Creates a rotation quaternion from Matrix3
- explicit CQuaternion(const CMatrix3 &mat);
- /// Creates a normalized quaternion based on the vector (x, y, z, w)
- CQuaternion(F32 x, F32 y, F32 z, F32 w);
- /// Creates a quaternion of rotation around the vec axis by angle angle (rad)
- CQuaternion(F32 angle, const CVector4F &vec);
- /// Creates a quaternion of rotation around the vec axis by angle angle (rad)
- CQuaternion(F32 angle, const CVector3F &vec);
- /// Creates a normalized quaternion from an array
- CQuaternion(const F32 *q);
- /// Creates a quaternion from the SCS basis [x_axis ; y_axis ; z_axis]
- CQuaternion(const CVector3F &x_axis,
- const CVector3F &y_axis,
- const CVector3F &z_axis);
- F32 operator[](int idx) const { return mQ[idx]; }
- F32 &operator[](int idx) { return mQ[idx]; }
- // Single?
- bool isIdentity() const;
- // Not single?
- bool isNotIdentity() const;
- /// Checks the finiteness of values
- bool isFinite() const;
- /// rounds values to simulate integer representation
- void quantize16(F32 lower, F32 upper);
- /// rounds values to simulate integer representation
- void quantize8(F32 lower, F32 upper);
- /// unit quaternion
- void loadIdentity();
- const CQuaternion& set(F32 x, F32 y, F32 z, F32 w); /// normalized quaternion based on vector(x, y, z, w)
- const CQuaternion& set(const CQuaternion &quat); /// copy
- const CQuaternion& set(const F32 *q); /// normalized quaternion (quat[VX], quat[VY], quat[VZ], quat[VW])
- const CQuaternion& set(const CMatrix3 &mat); /// Creates a rotation quaternion from a matrix
- const CQuaternion& set(const CMatrix4 &mat); /// Creates a rotation quaternion from a matrix
- /// quaternion corresponding to rotation around the (x,y,z) axis by angle angle (rad)
- const CQuaternion& setAngleAxis(F32 angle, F32 x, F32 y, F32 z);
- /// quaternion corresponding to rotation around the vec axis by angle angle (rad)
- const CQuaternion& setAngleAxis(F32 angle, const CVector3F &vec);
- /// quaternion corresponding to rotation around the vec axis by angle angle (rad)
- const CQuaternion& setAngleAxis(F32 angle, const CVector4F &vec);
- /// quaternion corresponding to Euler angles
- const CQuaternion& setEulerAngles(F32 roll, F32 pitch, F32 yaw);
- CMatrix4 getMatrix4(void) const;
- CMatrix3 getMatrix3(void) const;
- /// Returns the angle and axis of rotation
- void getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const;
- /// Returns the angle and axis of rotation
- void getAngleAxis(F32* angle, CVector3F &vec) const;
- /// Returns the Euler angles
- void getEulerAngles(F32 *roll, F32* pitch, F32 *yaw) const;
- /// Normalizes and returns the previous magnitude
- F32 normalize();
- /// Conjugate quaternion
- const CQuaternion& conjugate(void);
- /// Mirror rotation
- const CQuaternion& transpose();
- /// shortest rotation from direction a to b
- void shortestArc(const CVector3F &a, const CVector3F &b);
- /// trims the rotation to the limits of a cone with a given angle
- const CQuaternion& constrain(F32 radians);
- friend std::ostream& operator<<(std::ostream &s, const CQuaternion &a);
- friend CQuaternion operator+(const CQuaternion &a, const CQuaternion &b);
- friend CQuaternion operator-(const CQuaternion &a, const CQuaternion &b);
- friend CQuaternion operator-(const CQuaternion &a);
- friend CQuaternion operator*(F32 a, const CQuaternion &q);
- friend CQuaternion operator*(const CQuaternion &q, F32 b);
- friend CQuaternion operator*(const CQuaternion &a, const CQuaternion &b);
- friend CQuaternion operator~(const CQuaternion &a); // pairing
- bool operator==(const CQuaternion &b) const;
- bool operator!=(const CQuaternion &b) const;
- friend const CQuaternion& operator*=(CQuaternion &a, const CQuaternion &b);
- // rotation of vector a
- friend CVector4F operator*(const CVector4F &a, const CQuaternion &rot);
- friend CVector3F operator*(const CVector3F &a, const CQuaternion &rot);
- friend CVector3D operator*(const CVector3D &a, const CQuaternion &rot);
- // scalar product
- friend F32 dot(const CQuaternion &a, const CQuaternion &b);
- // Interpolation (t = 0 ... 1) from p to q
- friend CQuaternion lerp(F32 t, const CQuaternion &p, const CQuaternion &q);
- // Interpolation (t = 0 ... 1) from unit square. to q
- friend CQuaternion lerp(F32 t, const CQuaternion &q);
- // Spherical interpolation from p to q
- friend CQuaternion slerp(F32 t, const CQuaternion &p, const CQuaternion &q);
- // Spherical interpolation from unit square. to q
- friend CQuaternion slerp(F32 t, const CQuaternion &q);
- // Normalized interpolation from p to q
- friend CQuaternion nlerp(F32 t, const CQuaternion &p, const CQuaternion &q);
- // Normalized interpolation from unit square. to q
- friend CQuaternion nlerp(F32 t, const CQuaternion &q);
- };
- inline bool CQuaternion::isFinite() const
- {
- return (isfinite(mQ[VX]) && isfinite(mQ[VY]) && isfinite(mQ[VZ]) && isfinite(mQ[VS]));
- }
- inline bool CQuaternion::isIdentity() const
- {
- return
- ( mQ[VX] == 0.f ) &&
- ( mQ[VY] == 0.f ) &&
- ( mQ[VZ] == 0.f ) &&
- ( mQ[VS] == 1.f );
- }
- inline bool CQuaternion::isNotIdentity() const
- {
- return
- ( mQ[VX] != 0.f ) ||
- ( mQ[VY] != 0.f ) ||
- ( mQ[VZ] != 0.f ) ||
- ( mQ[VS] != 1.f );
- }
- inline CQuaternion::CQuaternion(void)
- {
- mQ[VX] = 0.f;
- mQ[VY] = 0.f;
- mQ[VZ] = 0.f;
- mQ[VS] = 1.f;
- }
- inline CQuaternion::CQuaternion(F32 x, F32 y, F32 z, F32 w)
- {
- mQ[VX] = x;
- mQ[VY] = y;
- mQ[VZ] = z;
- mQ[VS] = w;
- //RN:No normalization should be inserted here. Too expensive for temporary values, and rounding adds up
- // set() should be used for normalization
- }
- inline CQuaternion::CQuaternion(const F32 *q)
- {
- mQ[VX] = q[VX];
- mQ[VY] = q[VY];
- mQ[VZ] = q[VZ];
- mQ[VS] = q[VW];
- normalize();
- }
- inline void CQuaternion::loadIdentity()
- {
- mQ[VX] = 0.0f;
- mQ[VY] = 0.0f;
- mQ[VZ] = 0.0f;
- mQ[VW] = 1.0f;
- }
- inline const CQuaternion& CQuaternion::set(F32 x, F32 y, F32 z, F32 w)
- {
- mQ[VX] = x;
- mQ[VY] = y;
- mQ[VZ] = z;
- mQ[VS] = w;
- normalize();
- return (*this);
- }
- inline const CQuaternion& CQuaternion::set(const CQuaternion &quat)
- {
- mQ[VX] = quat.mQ[VX];
- mQ[VY] = quat.mQ[VY];
- mQ[VZ] = quat.mQ[VZ];
- mQ[VW] = quat.mQ[VW];
- normalize();
- return (*this);
- }
- inline const CQuaternion& CQuaternion::set(const F32 *q)
- {
- mQ[VX] = q[VX];
- mQ[VY] = q[VY];
- mQ[VZ] = q[VZ];
- mQ[VS] = q[VW];
- normalize();
- return (*this);
- }
- /// @todo Is it possible to get rid of sqrt... sin_a = VX*VX + VY*VY + VZ*VZ?
- // Copied from Matrix and Quaternion FAQ 1.12
- inline void CQuaternion::getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const
- {
- F32 cos_a = mQ[VW];
- if (cos_a > 1.0f) cos_a = 1.0f;
- if (cos_a < -1.0f) cos_a = -1.0f;
- F32 sin_a = (F32) sqrt( 1.0f - cos_a * cos_a );
- if ( fabs( sin_a ) < 0.0005f )
- sin_a = 1.0f;
- else
- sin_a = 1.f/sin_a;
- F32 temp_angle = 2.0f * (F32) acos( cos_a );
- if (temp_angle > F_PI)
- {
- // Angles should never be outside [PI, -PI]
- // and we need the shortest turn.
- // Because acos is defined on the segment [0, PI], and we multiply the angle by two,
- // then we can go beyond the segment. If you replace the angle with the corresponding one in
- // another half-plane and invert the axis, this will correspond
- // rotate in the other direction (right hand rule)
- *angle = 2.f * F_PI - temp_angle;
- *x = - mQ[VX] * sin_a;
- *y = - mQ[VY] * sin_a;
- *z = - mQ[VZ] * sin_a;
- }
- else
- {
- *angle = temp_angle;
- *x = mQ[VX] * sin_a;
- *y = mQ[VY] * sin_a;
- *z = mQ[VZ] * sin_a;
- }
- }
- inline const CQuaternion& CQuaternion::conjugate()
- {
- mQ[VX] *= -1.f;
- mQ[VY] *= -1.f;
- mQ[VZ] *= -1.f;
- return (*this);
- }
- /// Reflection (mirror rotation)
- inline const CQuaternion& CQuaternion::transpose()
- {
- mQ[VX] *= -1.f;
- mQ[VY] *= -1.f;
- mQ[VZ] *= -1.f;
- return (*this);
- }
- inline CQuaternion operator+(const CQuaternion &a, const CQuaternion &b)
- {
- return CQuaternion(
- a.mQ[VX] + b.mQ[VX],
- a.mQ[VY] + b.mQ[VY],
- a.mQ[VZ] + b.mQ[VZ],
- a.mQ[VW] + b.mQ[VW] );
- }
- inline CQuaternion operator-(const CQuaternion &a, const CQuaternion &b)
- {
- return CQuaternion(
- a.mQ[VX] - b.mQ[VX],
- a.mQ[VY] - b.mQ[VY],
- a.mQ[VZ] - b.mQ[VZ],
- a.mQ[VW] - b.mQ[VW] );
- }
- inline CQuaternion operator-(const CQuaternion &a)
- {
- return CQuaternion(
- -a.mQ[VX],
- -a.mQ[VY],
- -a.mQ[VZ],
- -a.mQ[VW] );
- }
- inline CQuaternion operator*(F32 a, const CQuaternion &q)
- {
- return CQuaternion(
- a * q.mQ[VX],
- a * q.mQ[VY],
- a * q.mQ[VZ],
- a * q.mQ[VW] );
- }
- inline CQuaternion operator*(const CQuaternion &q, F32 a)
- {
- return CQuaternion(
- a * q.mQ[VX],
- a * q.mQ[VY],
- a * q.mQ[VZ],
- a * q.mQ[VW] );
- }
- inline CQuaternion operator~(const CQuaternion &a)
- {
- CQuaternion q(a);
- q.conjugate();
- return q;
- }
- inline bool CQuaternion::operator==(const CQuaternion &b) const
- {
- return ( (mQ[VX] == b.mQ[VX])
- &&(mQ[VY] == b.mQ[VY])
- &&(mQ[VZ] == b.mQ[VZ])
- &&(mQ[VS] == b.mQ[VS]));
- }
- inline bool CQuaternion::operator!=(const CQuaternion &b) const
- {
- return ( (mQ[VX] != b.mQ[VX])
- ||(mQ[VY] != b.mQ[VY])
- ||(mQ[VZ] != b.mQ[VZ])
- ||(mQ[VS] != b.mQ[VS]));
- }
- inline const CQuaternion& operator*=(CQuaternion &a, const CQuaternion &b)
- {
- CQuaternion q(
- b.mQ[3] * a.mQ[0] + b.mQ[0] * a.mQ[3] + b.mQ[1] * a.mQ[2] - b.mQ[2] * a .mQ[1],
- b.mQ[3] * a.mQ[1] + b.mQ[1] * a.mQ[3] + b.mQ[2] * a.mQ[0] - b.mQ[0] * a .mQ[2],
- b.mQ[3] * a.mQ[2] + b.mQ[2] * a.mQ[3] + b.mQ[0] * a.mQ[1] - b.mQ[1] * a .mQ[0],
- b.mQ[3] * a.mQ[3] - b.mQ[0] * a.mQ[0] - b.mQ[1] * a.mQ[1] - b.mQ[2] * a .mQ[2]
- );
- a = q;
- return a;
- }
- const F32 ONE_PART_IN_A_MILLION = 0.000001f;
- inline F32 CQuaternion::normalize()
- {
- F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
- if (mag > FP_MAG_THRESHOLD)
- {
- // Floating point error can prevent some quaternions from achieving
- // exact unity length. When trying to renormalize such quaternions we
- // can oscillate between multiple quantized states. To prevent such
- // drifts we only renomalize if the length is far enough from unity.
- if (fabs(1.f - mag) > ONE_PART_IN_A_MILLION)
- {
- F32 oomag = 1.f/mag;
- mQ[VX] *= oomag;
- mQ[VY] *= oomag;
- mQ[VZ] *= oomag;
- mQ[VS] *= oomag;
- }
- }
- else
- {
- // we were given a very bad quaternion so we set it to identity
- mQ[VX] = 0.f;
- mQ[VY] = 0.f;
- mQ[VZ] = 0.f;
- mQ[VS] = 1.f;
- }
- return mag;
- }
- #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement