/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #pragma once // include required headers #include <AzCore/RTTI/TypeInfo.h> #include <AzCore/Math/Vector2.h> #include "StandardHeaders.h" #include "FastMath.h" #include "Vector.h" #include "Matrix4.h" #include "Algorithms.h" namespace MCore { /** * Depracated. Please use AZ::Quaternion instead. * The quaternion class in MCore. * Quaternions are mostly used to represent rotations in 3D applications. * The advantages of quaternions over matrices are that they take up less space and that interpolation between * two quaternions is easier to perform. Instead of a 3x3 rotation matrix, which is 9 floats or doubles, a quaternion * only uses 4 floats or doubles. This template/class provides you with methods to perform all kind of operations on * these quaternions, from interpolation to conversion to matrices and other rotation representations. */ class MCORE_API Quaternion { public: AZ_TYPE_INFO(MCore::Quaternion, "{1807CD22-EBB5-45E8-8113-3B1DABB53F12}") /** * Default constructor. Sets x, y and z to 0 and w to 1. */ MCORE_INLINE Quaternion() : x(0.0f) , y(0.0f) , z(0.0f) , w(1.0f) {} /** * Constructor which sets the x, y, z and w. * @param xVal The value of x. * @param yVal The value of y. * @param zVal The value of z. * @param wVal The value of w. */ MCORE_INLINE Quaternion(float xVal, float yVal, float zVal, float wVal) : x(xVal) , y(yVal) , z(zVal) , w(wVal) {} /** * Copy constructor. Copies the x, y, z, w values from the other quaternion. * @param other The quaternion to copy the attributes from. */ MCORE_INLINE Quaternion(const Quaternion& other) : x(other.x) , y(other.y) , z(other.z) , w(other.w) {} /** * Constructor which creates a quaternion from a pitch, yaw and roll. * @param pitch Rotation around the x-axis, in radians. * @param yaw Rotation around the y-axis, in radians. * @param roll Rotation around the z-axis, in radians. */ MCORE_INLINE Quaternion(float pitch, float yaw, float roll) { SetEuler(pitch, yaw, roll); } /** * Constructor which takes a matrix as input parameter. * This converts the rotation of the specified matrix into a quaternion. Please keep in mind that the matrix may NOT contain * any scaling, so if it does, please normalize your matrix first! * @param matrix The matrix to initialize the quaternion from. */ MCORE_INLINE Quaternion(const Matrix& matrix) { FromMatrix(matrix); } /** * Constructor which creates a quaternion from a spherical rotation. * @param spherical The spherical coordinates in radians, which creates an axis to rotate around. * @param angle The angle to rotate around this axis. */ Quaternion(const AZ::Vector2& spherical, float angle); /** * Constructor which creates a quaternion from an axis and angle. * @param axis The axis to rotate around. * @param angle The angle in radians to rotate around the given axis. */ Quaternion(const AZ::Vector3& axis, float angle); /** * Set the quaternion x/y/z/w component values. * @param vx The value of x. * @param vy The value of y. * @param vz The value of z. * @param vw The value of w. */ MCORE_INLINE void Set(float vx, float vy, float vz, float vw) { x = vx; y = vy; z = vz; w = vw; } /** * Calculates the square length of the quaternion. * @result The square length (length*length). */ MCORE_INLINE float SquareLength() const { return (x * x + y * y + z * z + w * w); } /** * Calculates the length of the quaternion. * It's safe, since it prevents a division by 0. * @result The length of the quaternion. */ MCORE_INLINE float Length() const; /** * Performs a dot product on the quaternions. * @param q The quaternion to multiply (dot product) this quaternion with. * @result The quaternion which is the result of the dot product. */ MCORE_INLINE float Dot(const Quaternion& q) const { return (x * q.x + y * q.y + z * q.z + w * q.w); } /** * Normalize the quaternion. * @result The normalized quaternion. It modifies itself, so no new quaternion is returned. */ MCORE_INLINE Quaternion& Normalize(); /** * Sets the quaternion to identity. Where x, y and z are set to 0 and w is set to 1. * @result The quaternion, now set to identity. */ MCORE_INLINE Quaternion& Identity() { x = 0.0f; y = 0.0f; z = 0.0f; w = 1.0f; return *this; } /** * Calculate the inversed version of this quaternion. * @result The inversed version of this quaternion. */ MCORE_INLINE Quaternion& Inverse() { const float len = 1.0f / SquareLength(); x = -x * len; y = -y * len; z = -z * len; w = w * len; return *this; } /** * Conjugate this quaternion. * @result Returns itself Conjugated. */ MCORE_INLINE Quaternion& Conjugate() { x = -x; y = -y; z = -z; return *this; } /** * Calculate the inversed version of this quaternion. * @result The inversed version of this quaternion. */ MCORE_INLINE Quaternion Inversed() const { const float len = 1.0f / SquareLength(); return Quaternion(-x * len, -y * len, -z * len, w * len); } /** * Returns the normalized version of this quaternion. * @result The normalized version of this quaternion. */ MCORE_INLINE Quaternion Normalized() const { Quaternion result(*this); result.Normalize(); return result; } /** * Return the conjugated version of this quaternion. * @result The conjugated version of this quaternion. */ MCORE_INLINE Quaternion Conjugated() const { return Quaternion(-x, -y, -z, w); } /** * Calculate the exponent of this quaternion. * @result The resulting quaternion of the exp. */ MCORE_INLINE Quaternion Exp() const { const float r = Math::Sqrt(x * x + y * y + z * z); const float expW = Math::Exp(w); const float s = (r >= 0.00001f) ? expW* Math::Sin(r) / r : 0.0f; return Quaternion(s * x, s * y, s * z, expW * Math::Cos(r)); } /** * Calculate the log of the quaternion. * @result The resulting quaternion of the log. */ MCORE_INLINE Quaternion LogN() const { const float r = Math::Sqrt(x * x + y * y + z * z); float t = (r > 0.00001f) ? Math::ATan2(r, w) / r : 0.0f; return Quaternion(t * x, t * y, t * z, 0.5f * Math::Log(SquareLength())); } /** * Calculate and get the right basis vector. * @result The basis vector pointing to the right. This assumes x+ points to the right. */ MCORE_INLINE AZ::Vector3 CalcRightAxis() const; /** * Calculate and get the up basis vector. * @result The basis vector pointing upwards. This assumes z+ points up. */ MCORE_INLINE AZ::Vector3 CalcUpAxis() const; /** * Calculate and get the forward basis vector. * @result The basis vector pointing forward. This assumes y+ points forward, into the depth. */ MCORE_INLINE AZ::Vector3 CalcForwardAxis() const; /** * Initialize the current quaternion from a specified matrix. * Please note that the matrix may not contain any scaling! * So make sure the matrix has been normalized before, if it contains any scale. * @param m The matrix to initialize the quaternion from. */ MCORE_INLINE void FromMatrix(const Matrix& m) { *this = Quaternion::ConvertFromMatrix(m); } /** * Setup the quaternion from a pitch, yaw and roll. * @param pitch The rotation around the x-axis, in radians. * @param yaw The rotation around the y-axis, in radians. * @param roll The rotation around the z-axis in radians. * @result The quaternion, now initialized with the given pitch, yaw, roll rotation. */ Quaternion& SetEuler(float pitch, float yaw, float roll); /** * Convert the quaternion to an axis and angle. Which represents a rotation of the resulting angle around the resulting axis. * @param axis Pointer to the vector to store the axis in. * @param angle Pointer to the variable to store the angle in (will be in radians). */ void ToAxisAngle(AZ::Vector3* axis, float* angle) const; /** * Convert the quaternion to a spherical rotation. * @param spherical A pointer to the 2D vector to store the spherical coordinates in radians, which build the axis. * @param angle The pointer to the variable to store the angle around this axis in radians. */ void ToSpherical(AZ::Vector2* spherical, float* angle) const; /** * Extract the euler angles in radians. * The x component of the resulting vector represents the rotation around the x-axis (pitch). * The y component results the rotation around the y-axis (yaw) and the z component represents * the rotation around the z-axis (roll). * @result The 3D vector containing the euler angles in radians, around each axis. */ AZ::Vector3 ToEuler() const; /** * Returns the angle of rotation about the z axis. This is same as * the z component of the vector returned by the ToEuler method. It * is just more efficient to call this when one is interested only in rotation about the z axis. * @result The angle of rotation about z axis in radians. */ float GetEulerZ() const; /** * Convert this quaternion into a matrix. * @result The matrix representing the rotation of this quaternion. */ Matrix ToMatrix() const; /** * Convert a matrix into a quaternion. * Please keep in mind that the specified matrix may NOT contain any scaling! * So make sure the matrix has been normalized before, if it contains any scale. * @param m The matrix to extract the rotation from. * @result The quaternion, now containing the rotation of the given matrix, in quaternion form. */ static Quaternion ConvertFromMatrix(const Matrix& m); /** * Create a delta rotation that rotates one vector onto another vector. * @param fromVector The normalized vector to start from. This must be normalized! * @param toVector The normalized vector to rotate towards. This must be normalized as well! * @result The delta rotation quaternion. */ static Quaternion CreateDeltaRotation(const AZ::Vector3& fromVector, const AZ::Vector3& toVector); /** * Create a delta rotation that rotates one vector onto another vector. * If the angle is bigger than the max allowed angle that is specified it will rotate with an angle of the maximum specified angle. * So if the angle between the vectors is 40 degrees and you maxAngleRadians equals 10 degrees (in radians) it will only rotate 10 degrees. * @param fromVector The normalized vector to start from. This must be normalized! * @param toVector The normalized vector to rotate towards. This must be normalized as well! * @param maxAngleRadians The maximum rotation angle on the plane defined by the two vectors. This cannot be more than Math::pi (180 degrees). * @result The delta rotation quaternion. */ static Quaternion CreateDeltaRotation(const AZ::Vector3& fromVector, const AZ::Vector3& toVector, float maxAngleRadians); /** * Init this quaternion as a delta rotation that rotates one vector onto another vector. * @param fromVector The normalized vector to start from. This must be normalized! * @param toVector The normalized vector to rotate towards. This must be normalized as well! */ void SetAsDeltaRotation(const AZ::Vector3& fromVector, const AZ::Vector3& toVector); /** * Init this quaternion as a delta rotation that rotates one vector onto another vector. * If the angle is bigger than the max allowed angle that is specified it will rotate with an angle of the maximum specified angle. * So if the angle between the vectors is 40 degrees and you maxAngleRadians equals 10 degrees (in radians) it will only rotate 10 degrees. * @param fromVector The normalized vector to start from. This must be normalized! * @param toVector The normalized vector to rotate towards. This must be normalized as well! * @param maxAngleRadians The maximum rotation angle on the plane defined by the two vectors. This cannot be more than Math::pi (180 degrees). */ void SetAsDeltaRotation(const AZ::Vector3& fromVector, const AZ::Vector3& toVector, float maxAngleRadians); /** * Rotate this current quaternion using a given delta that is calculated from two vectors. * The rotation axis used is the cross product between the from and to vector. The rotation angle is the angle between these two vectors. * @param fromVector The current direction vector, must be normalized. * @param toVector The desired new direction vector, must be normalized. */ void RotateFromTo(const AZ::Vector3& fromVector, const AZ::Vector3& toVector); /** * Decompose into swing and twist. * The original rotation quat can be reassembled by doing swing * twist. * @param direction The direction vector to get the twist from. * @param outSwing This will contain the swing quaternion. * @param outTwist This will contain the twist quaternion. */ void DecomposeSwingTwist(const AZ::Vector3& direction, Quaternion* outSwing, Quaternion* outTwist) const; /** * Linear interpolate between this and another quaternion. * @param to The quaternion to interpolate towards. * @param t The time value, between 0 and 1. * @result The quaternion at the given time in the interpolation process. */ Quaternion Lerp(const Quaternion& to, float t) const; /** * Linear interpolate between this and another quaternion, and normalize afterwards. * @param to The quaternion to interpolate towards. * @param t The time value, between 0 and 1. * @result The normalized quaternion at the given time in the interpolation process. */ Quaternion NLerp(const Quaternion& to, float t) const; /** * Spherical Linear interpolate between this and another quaternion. * @param to The quaternion to interpolate towards. * @param t The time value, between 0 and 1. * @result The quaternion at the given time in the interpolation process. */ Quaternion Slerp(const Quaternion& to, float t) const; /** * Spherical cubic interpolate. * @param p The first quaternion. * @param a The second quaternion. * @param b The third quaternion. * @param q The fourth quaternion. * @param t The time value, between 0 and 1. * @result The quaternion at the given time in the interpolation process. */ static Quaternion Squad(const Quaternion& p, const Quaternion& a, const Quaternion& b, const Quaternion& q, float t); // operators MCORE_INLINE const Quaternion& operator=(const Matrix& m) { FromMatrix(m); return *this; } MCORE_INLINE const Quaternion& operator=(const Quaternion& other) { x = other.x; y = other.y; z = other.z; w = other.w; return *this; } MCORE_INLINE Quaternion operator-() const { return Quaternion(-x, -y, -z, -w); } MCORE_INLINE const Quaternion& operator+=(const Quaternion& q) { x += q.x; y += q.y; z += q.z; w += q.w; return *this; } MCORE_INLINE const Quaternion& operator-=(const Quaternion& q) { x -= q.x; y -= q.y; z -= q.z; w -= q.w; return *this; } MCORE_INLINE const Quaternion& operator*=(const Quaternion& q); MCORE_INLINE const Quaternion& operator*=(float f) { x *= f; y *= f; z *= f; w *= f; return *this; } //MCORE_INLINE const Quaternion& operator*=(double f) { x*=f; y*=f; z*=f; w*=f; return *this; } MCORE_INLINE bool operator==(const Quaternion& q) const { return ((q.x == x) && (q.y == y) && (q.z == z) && (q.w == w)); } MCORE_INLINE bool operator!=(const Quaternion& q) const { return ((q.x != x) || (q.y != y) || (q.z != z) || (q.w != w)); } //MCORE_INLINE float& operator[](int32 row) { return ((float*)&x)[row]; } MCORE_INLINE operator float*() { return (float*)&x; } MCORE_INLINE operator const float*() const { return (const float*)&x; } MCORE_INLINE AZ::Vector3 operator*(const AZ::Vector3& p) const; // multiply a vector by a quaternion MCORE_INLINE Quaternion operator/(const Quaternion& q) const; // returns the ratio of two quaternions // attributes float x, y, z, w; }; // operators MCORE_INLINE Quaternion operator*(const Quaternion& a, float f) { return Quaternion(a.x * f, a.y * f, a.z * f, a.w * f); } MCORE_INLINE Quaternion operator*(float f, const Quaternion& b) { return Quaternion(f * b.x, f * b.y, f * b.z, f * b.w); } //MCORE_INLINE Quaternion operator*(const Quaternion& a, double f) { return Quaternion(a.x*f, a.y*f, a.z*f, a.w*f); } //MCORE_INLINE Quaternion operator*(double f, const Quaternion& b) { return Quaternion(f*b.x, f*b.y, f*b.z, f*b.w); } MCORE_INLINE Quaternion operator+(const Quaternion& a, const Quaternion& b) { return Quaternion(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); } MCORE_INLINE Quaternion operator-(const Quaternion& a, const Quaternion& b) { return Quaternion(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); } MCORE_INLINE Quaternion operator*(const Quaternion& a, const Quaternion& b) { return Quaternion(a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, a.w * b.y + a.y * b.w + a.z * b.x - a.x * b.z, a.w * b.z + a.z * b.w + a.x * b.y - a.y * b.x, a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z); } // include the inline code #include "Quaternion.inl" } // namespace MCore