// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "Math.h"
#include "Engine/Core/Formatting.h"
#include "Engine/Core/Templates.h"
struct Matrix;
struct Matrix3x3;
///
/// Represents a four dimensional mathematical quaternion. Euler angles are stored in: pitch, yaw, roll order (x, y, z).
///
API_STRUCT() struct FLAXENGINE_API Quaternion
{
DECLARE_SCRIPTING_TYPE_MINIMAL(Quaternion);
public:
union
{
struct
{
///
/// The X component of the quaternion.
///
API_FIELD() float X;
///
/// The Y component of the quaternion.
///
API_FIELD() float Y;
///
/// The Z component of the quaternion.
///
API_FIELD() float Z;
///
/// The W component of the quaternion.
///
API_FIELD() float W;
};
///
/// The raw value.
///
float Raw[4];
};
public:
///
/// Quaternion with all components equal 0.
///
static Quaternion Zero;
///
/// Quaternion with all components equal 1.
///
static Quaternion One;
///
/// Identity quaternion (represents no rotation).
///
static Quaternion Identity;
public:
///
/// Empty constructor.
///
Quaternion() = default;
///
/// Init
///
/// X component value.
/// Y component value.
/// Z component value.
/// W component value.
Quaternion(const float x, const float y, const float z, const float w)
: X(x)
, Y(y)
, Z(z)
, W(w)
{
}
///
/// Init
///
/// Vector to set value.
explicit Quaternion(const Float4& value);
public:
String ToString() const;
public:
///
/// Gets a value indicating whether this instance is equivalent to the identity quaternion.
///
bool IsIdentity() const
{
return Math::IsZero(X) && Math::IsZero(Y) && Math::IsZero(Z) && Math::IsOne(W);
}
///
/// Gets a value indicting whether this instance is normalized.
///
bool IsNormalized() const
{
return Math::IsOne(X * X + Y * Y + Z * Z + W * W);
}
///
/// Returns true if quaternion has one or more components is not a number (NaN).
///
bool IsNaN() const
{
return isnan(X) || isnan(Y) || isnan(Z) || isnan(W);
}
///
/// Returns true if quaternion has one or more components equal to +/- infinity.
///
bool IsInfinity() const
{
return isinf(X) || isinf(Y) || isinf(Z) || isinf(W);
}
///
/// Returns true if quaternion has one or more components equal to +/- infinity or NaN.
///
bool IsNanOrInfinity() const
{
return IsInfinity() || IsNaN();
}
///
/// Gets the angle of the quaternion.
///
float GetAngle() const;
///
/// Gets the axis components of the quaternion.
///
Float3 GetAxis() const;
///
/// Calculates the length of the quaternion.
///
float Length() const
{
return Math::Sqrt(X * X + Y * Y + Z * Z + W * W);
}
///
/// Calculates the squared length of the quaternion.
///
float LengthSquared() const
{
return X * X + Y * Y + Z * Z + W * W;
}
///
/// Gets the euler angle (pitch, yaw, roll) in degrees.
///
Float3 GetEuler() const;
///
/// Conjugates the quaternion
///
void Conjugate()
{
X = -X;
Y = -Y;
Z = -Z;
}
///
/// Gets the conjugated quaternion.
///
Quaternion Conjugated() const
{
return { -X, -Y, -Z, W };
}
///
/// Conjugates and renormalizes the quaternion.
///
void Invert()
{
float lengthSq = Math::Sqrt(X * X + Y * Y + Z * Z + W * W);
if (!Math::IsZero(lengthSq))
{
lengthSq = 1.0f / lengthSq;
X = -X * lengthSq;
Y = -Y * lengthSq;
Z = -Z * lengthSq;
W = W * lengthSq;
}
}
///
/// Converts the quaternion into a unit quaternion.
///
void Normalize()
{
const float length = Math::Sqrt(X * X + Y * Y + Z * Z + W * W);
if (!Math::IsZero(length))
{
const float inv = 1.0f / length;
X *= inv;
Y *= inv;
Z *= inv;
W *= inv;
}
}
///
/// Scales a quaternion by the given value.
///
/// The amount by which to scale the quaternion.
void Multiply(float scale)
{
X *= scale;
Y *= scale;
Z *= scale;
W *= scale;
}
///
/// Multiplies a quaternion by another.
///
/// The other quaternion to multiply by.
void Multiply(const Quaternion& other);
public:
///
/// Adds two quaternions.
///
/// The quaternion to add.
/// The sum of the two quaternions.
inline Quaternion operator+(const Quaternion& b) const
{
return Quaternion(X + b.X, Y + b.Y, Z + b.Z, W + b.W);
}
///
/// Subtracts two quaternions.
///
/// The quaternion to subtract.
/// The difference of the two quaternions.
inline Quaternion operator-(const Quaternion& b) const
{
return Quaternion(X - b.X, Y - b.Y, Z - b.Z, W - b.W);
}
///
/// Multiplies two quaternions.
///
/// The quaternion to multiply.
/// The multiplied quaternion.
inline Quaternion operator*(const Quaternion& b) const
{
Quaternion result;
Multiply(*this, b, result);
return result;
}
///
/// Adds two quaternions.
///
/// The quaternion to add.
/// The sum of the two quaternions.
inline Quaternion& operator+=(const Quaternion& b)
{
X += b.X;
Y += b.Y;
Z += b.Z;
W += b.W;
return *this;
}
///
/// Subtracts two quaternions.
///
/// The quaternion to subtract.
/// The difference of the two quaternions.
inline Quaternion& operator-=(const Quaternion& b)
{
X -= b.X;
Y -= b.Y;
Z -= b.Z;
W -= b.W;
return *this;
}
///
/// Multiplies two quaternions.
///
/// The quaternion to multiply.
/// The multiplied quaternion.
inline Quaternion& operator*=(const Quaternion& b)
{
Multiply(b);
return *this;
}
///
/// Scales a quaternion by the given value.
///
/// The amount by which to scale the quaternion.
/// The scaled quaternion.
inline Quaternion operator*(float scale) const
{
Quaternion q = *this;
q.Multiply(scale);
return q;
}
///
/// Transforms a vector by the given rotation.
///
/// The vector to transform.
/// The scaled vector.
Float3 operator*(const Float3& vector) const;
///
/// Scales a quaternion by the given value.
///
/// The amount by which to scale the quaternion.
/// This instance.
FORCE_INLINE Quaternion& operator*=(float scale)
{
Multiply(scale);
return *this;
}
///
/// Determines whether the specified is equal to this instance.
///
/// The to compare with this instance.
/// true if the specified is equal to this instance; otherwise, false.
FORCE_INLINE bool operator==(const Quaternion& other) const
{
return Dot(*this, other) > 0.9999999f;
}
///
/// Determines whether the specified is equal to this instance.
///
/// The to compare with this instance.
/// true if the specified isn't equal to this instance; otherwise, false.
FORCE_INLINE bool operator!=(const Quaternion& other) const
{
return !(*this == other);
}
public:
///
/// Determines whether the specified structures are equal.
///
/// The first to compare.
/// The second to compare.
/// true if the specified structures are equal; otherwise, false.
static bool NearEqual(const Quaternion& a, const Quaternion& b)
{
return Dot(a, b) > 0.9999999f;
}
///
/// Determines whether the specified structures are equal.
///
/// The first to compare.
/// The second to compare.
/// The comparision threshold value.
/// true if the specified structures are equal within the specified epsilon range; otherwise, false.
static bool NearEqual(const Quaternion& a, const Quaternion& b, float epsilon)
{
return Dot(a, b) > 1.0f - epsilon;
}
public:
///
/// Calculates the inverse of the specified quaternion.
///
/// The quaternion whose inverse is to be calculated.
/// The inverse of the specified quaternion.
static Quaternion Invert(const Quaternion& value)
{
Quaternion result;
Invert(value, result);
return result;
}
///
/// Calculates the inverse of the specified quaternion.
///
/// The quaternion whose inverse is to be calculated.
/// When the method completes, contains the inverse of the specified quaternion.
static void Invert(const Quaternion& value, Quaternion& result)
{
result = value;
result.Invert();
}
///
/// Calculates the dot product of two quaternions.
///
/// The first source quaternion.
/// The second source quaternion.
/// The dot product of the two quaternions.
static float Dot(const Quaternion& left, const Quaternion& right)
{
return left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W;
}
///
/// Calculates the angle between two quaternions.
///
/// First source quaternion.
/// Second source quaternion.
/// Returns the angle in degrees between two rotations a and b.
static float AngleBetween(const Quaternion& a, const Quaternion& b)
{
const float dot = Dot(a, b);
return dot > 0.9999999f ? 0 : Math::Acos(Math::Min(Math::Abs(dot), 1.0f)) * 2.0f * 57.29578f;
}
// Adds two quaternions
// @param left The first quaternion to add
// @param right The second quaternion to add
// @param result When the method completes, contains the sum of the two quaternions
static void Add(const Quaternion& left, const Quaternion& right, Quaternion& result)
{
result.X = left.X + right.X;
result.Y = left.Y + right.Y;
result.Z = left.Z + right.Z;
result.W = left.W + right.W;
}
// Subtracts two quaternions
// @param left The first quaternion to subtract
// @param right The second quaternion to subtract
// @param result When the method completes, contains the difference of the two quaternions
static void Subtract(const Quaternion& left, const Quaternion& right, Quaternion& result)
{
result.X = left.X - right.X;
result.Y = left.Y - right.Y;
result.Z = left.Z - right.Z;
result.W = left.W - right.W;
}
// Scales a quaternion by the given value
// @param value The quaternion to scale
// @param scale The amount by which to scale the quaternion
// @param result When the method completes, contains the scaled quaternion
static void Multiply(const Quaternion& value, float scale, Quaternion& result)
{
result.X = value.X * scale;
result.Y = value.Y * scale;
result.Z = value.Z * scale;
result.W = value.W * scale;
}
// Multiplies a quaternion by another
// @param left The first quaternion to multiply
// @param right The second quaternion to multiply
// @param result When the method completes, contains the multiplied quaternion
static void Multiply(const Quaternion& left, const Quaternion& right, Quaternion& result);
// Reverses the direction of a given quaternion
// @param value The quaternion to negate
// @param result When the method completes, contains a quaternion facing in the opposite direction
static void Negate(const Quaternion& value, Quaternion& result)
{
result.X = -value.X;
result.Y = -value.Y;
result.Z = -value.Z;
result.W = -value.W;
}
// Performs a linear interpolation between two quaternions
// @param start Start quaternion
// @param end End quaternion
// @param amount Value between 0 and 1 indicating the weight of end
// @param result When the method completes, contains the linear interpolation of the two quaternions
static void Lerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result);
// Performs a linear interpolation between two quaternion
// @param start Start quaternion
// @param end End quaternion
// @param amount Value between 0 and 1 indicating the weight of end
// @returns The linear interpolation of the two quaternions
static Quaternion Lerp(const Quaternion& start, const Quaternion& end, float amount)
{
Quaternion result;
Lerp(start, end, amount, result);
return result;
}
// Creates a quaternion given a rotation and an axis
// @param axis The axis of rotation
// @param angle The angle of rotation (in radians).
// @param result When the method completes, contains the newly created quaternion
static void RotationAxis(const Float3& axis, float angle, Quaternion& result);
// Creates a quaternion given a angle cosine and an axis
// @param axis The axis of rotation
// @param cos The angle cosine, it must be within [-1,1] range (in radians).
// @param result When the method completes, contains the newly created quaternion
static void RotationCosAxis(const Float3& axis, float cos, Quaternion& result);
// Creates a quaternion given a rotation matrix
// @param matrix The rotation matrix
// @param result When the method completes, contains the newly created quaternion
static void RotationMatrix(const Matrix& matrix, Quaternion& result);
// Creates a quaternion given a rotation matrix
// @param matrix The rotation matrix
// @param result When the method completes, contains the newly created quaternion
static void RotationMatrix(const Matrix3x3& matrix, Quaternion& result);
// Creates a left-handed, look-at quaternion
// @param eye The position of the viewer's eye
// @param target The camera look-at target
// @param up The camera's up vector
// @param result When the method completes, contains the created look-at quaternion
static void LookAt(const Float3& eye, const Float3& target, const Float3& up, Quaternion& result);
// Creates a left-handed, look-at quaternion
// @param forward The camera's forward direction
// @param up The camera's up vector
// @param result When the method completes, contains the created look-at quaternion
static void RotationLookAt(const Float3& forward, const Float3& up, Quaternion& result);
// Creates a left-handed spherical billboard that rotates around a specified object position
// @param objectPosition The position of the object around which the billboard will rotate
// @param cameraPosition The position of the camera
// @param cameraUpFloat The up vector of the camera
// @param cameraForwardFloat The forward vector of the camera
// @param result When the method completes, contains the created billboard quaternion
static void Billboard(const Float3& objectPosition, const Float3& cameraPosition, const Float3& cameraUpFloat, const Float3& cameraForwardFloat, Quaternion& result);
///
/// Calculates the orientation from the direction vector.
///
/// The direction vector (normalized).
/// The orientation.
static Quaternion FromDirection(const Float3& direction);
///
/// Creates a rotation with the specified forward and upwards directions.
///
/// The forward direction. Direction to orient towards.
/// The up direction. Constrains y axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.
/// The calculated quaternion.
static void LookRotation(const Float3& forward, const Float3& up, Quaternion& result);
///
/// Creates a rotation with the specified forward and upwards directions.
///
/// The forward direction. Direction to orient towards.
/// Up direction. Constrains y axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.
/// The calculated quaternion.
static Quaternion LookRotation(const Float3& forward, const Float3& up)
{
Quaternion result;
LookRotation(forward, up, result);
return result;
}
///
/// Gets the shortest arc quaternion to rotate this vector to the destination vector.
///
/// The source vector.
/// The destination vector.
/// The result.
/// The fallback axis.
static void GetRotationFromTo(const Float3& from, const Float3& to, Quaternion& result, const Float3& fallbackAxis);
///
/// Gets the shortest arc quaternion to rotate this vector to the destination vector.
///
/// The source vector.
/// The destination vector.
/// The fallback axis.
/// The rotation.
static Quaternion GetRotationFromTo(const Float3& from, const Float3& to, const Float3& fallbackAxis)
{
Quaternion result;
GetRotationFromTo(from, to, result, fallbackAxis);
return result;
}
///
/// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized.
///
/// The source vector.
/// The destination vector.
/// The result.
static void FindBetween(const Float3& from, const Float3& to, Quaternion& result);
///
/// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized.
///
/// The source vector.
/// The destination vector.
/// The rotation.
static Quaternion FindBetween(const Float3& from, const Float3& to)
{
Quaternion result;
FindBetween(from, to, result);
return result;
}
// Creates a quaternion given a rotation matrix
// @param matrix The rotation matrix
// @returns The newly created quaternion
static Quaternion RotationMatrix(const Matrix& matrix)
{
Quaternion result;
RotationMatrix(matrix, result);
return result;
}
// Interpolates between two quaternions, using spherical linear interpolation
// @param start Start quaternion
// @param end End quaternion
// @param amount Value between 0 and 1 indicating the weight of end
// @param result When the method completes, contains the spherical linear interpolation of the two quaternions
static void Slerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result);
// Creates a quaternion given a yaw, pitch, and roll value (is using degrees)
// @param x Roll (in degrees)
// @param x Pitch (in degrees)
// @param x Yaw (in degrees)
// @returns Result rotation
static Quaternion Euler(float x, float y, float z);
// Creates a quaternion given a yaw, pitch, and roll value (is using degrees)
// @param euler Euler angle rotation with values in order: X:roll, Y:pitch, Z:yaw (in degrees)
// @returns Result rotation
static Quaternion Euler(const Float3& euler);
// Creates a quaternion given a yaw, pitch, and roll value (is using radians)
// @param yaw The yaw of rotation (in radians)
// @param pitch The pitch of rotation (in radians)
// @param roll The roll of rotation (in radians)
// @param result When the method completes, contains the newly created quaternion
static Quaternion RotationYawPitchRoll(float yaw, float pitch, float roll)
{
Quaternion result;
RotationYawPitchRoll(yaw, pitch, roll, result);
return result;
}
// Creates a quaternion given a yaw, pitch, and roll value (is using radians)
// @param yaw The yaw of rotation (in radians)
// @param pitch The pitch of rotation (in radians)
// @param roll The roll of rotation (in radians)
// @param result When the method completes, contains the newly created quaternion
static void RotationYawPitchRoll(float yaw, float pitch, float roll, Quaternion& result);
};
///
/// Scales a quaternion by the given value.
///
/// The amount by which to scale the quaternion.
/// The quaternion to scale.
/// The scaled quaternion.
inline Quaternion operator*(float a, const Quaternion& b)
{
return b * a;
}
namespace Math
{
FORCE_INLINE static bool NearEqual(const Quaternion& a, const Quaternion& b)
{
return Quaternion::NearEqual(a, b);
}
}
template<>
struct TIsPODType
{
enum { Value = true };
};
DEFINE_DEFAULT_FORMATTING(Quaternion, "X:{0} Y:{1} Z:{2} W:{3}", v.X, v.Y, v.Z, v.W);