// Copyright (c) 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);
///
/// Equality tolerance factor used when comparing quaternions via dot operation.
///
API_FIELD() static constexpr Real Tolerance = 0.999999f;
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::Abs((X * X + Y * Y + Z * Z + W * W) - 1.0f) < 1e-4f;
}
///
/// 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 X == other.X && Y == other.Y && Z == other.Z && W == other.W;
}
///
/// 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 X != other.X || Y != other.Y || Z != other.Z || W != other.W;
}
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) > Tolerance;
}
///
/// 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 = value;
result.Invert();
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 > Tolerance ? 0 : Math::Acos(Math::Min(Math::Abs(dot), 1.0f)) * 2.0f * 57.29578f;
}
// Adds two quaternions.
static void Add(const Quaternion& left, const Quaternion& right, Quaternion& result);
// Subtracts two quaternions.
static void Subtract(const Quaternion& left, const Quaternion& right, Quaternion& result);
// Scales a quaternion by the given value.
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.
static void Multiply(const Quaternion& left, const Quaternion& right, Quaternion& result);
// Reverses the direction of a given quaternion.
static void Negate(const Quaternion& value, Quaternion& result);
// Performs a linear interpolation between two quaternions.
static void Lerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result);
// Performs a linear interpolation between two quaternion.
static Quaternion Lerp(const Quaternion& start, const Quaternion& end, float amount)
{
Quaternion result;
Lerp(start, end, amount, result);
return result;
}
// Creates a quaternion given an angle cosine (radians in range [-1,1]) and an axis of rotation (normalized).
static void RotationAxis(const Float3& axis, float angle, Quaternion& result);
// Creates a quaternion given an angle cosine (radians in range [-1,1]) and an axis of rotation (normalized).
static void RotationCosAxis(const Float3& axis, float cos, Quaternion& result);
// Creates a quaternion given a rotation matrix.
static void RotationMatrix(const Matrix& matrix, Quaternion& result);
// Creates a quaternion given a rotation matrix.
static void RotationMatrix(const Matrix3x3& matrix, Quaternion& result);
// Creates a left-handed, look-at quaternion.
static void LookAt(const Float3& eye, const Float3& target, const Float3& up, Quaternion& result);
// Creates a left-handed, 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.
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.
static Quaternion RotationMatrix(const Matrix& matrix)
{
Quaternion result;
RotationMatrix(matrix, result);
return result;
}
// Interpolates between two quaternions, using spherical linear interpolation.
static Quaternion Slerp(const Quaternion& start, const Quaternion& end, float amount)
{
Quaternion result;
Slerp(start, end, amount, result);
return result;
}
// Interpolates between two quaternions, using spherical linear interpolation.
static void Slerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result);
// Creates a quaternion given a yaw, pitch, and roll value (in degrees).
static Quaternion Euler(float x, float y, float z);
// Creates a quaternion given a yaw, pitch, and roll value in order: X:roll, Y:pitch, Z:yaw (in degrees).
static Quaternion Euler(const Float3& euler);
// Creates a quaternion given a yaw, pitch, and roll value (in radians).
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 (in radians).
static void RotationYawPitchRoll(float yaw, float pitch, float roll, Quaternion& result);
///
/// Gets rotation from a normal in relation to a transform. This function is especially useful for axis aligned faces, and with .
///
/// The normal vector.
/// The reference transform.
/// The rotation from the normal vector.
static Quaternion GetRotationFromNormal(const Vector3& normal, const Transform& reference);
};
///
/// 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);