Compare commits
7 Commits
Release-3.
...
3.4.2
Author | SHA1 | Date | |
---|---|---|---|
13a68eea45 | |||
79e617b780 | |||
aaea5ff53e | |||
2caa4c8412 | |||
bb1b1b5a13 | |||
80b752128c | |||
3fc9ca3954 |
@@ -84,6 +84,9 @@ namespace J3ML::Math::Constants {
|
||||
constexpr float RecipSqrt2Pi = 0.3989422804014326779399460599343818684758586311649346576659258296706579258993018385012523339073069364;
|
||||
/// pi - https://www.mathsisfun.com/numbers/pi.html
|
||||
constexpr float Pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
|
||||
/// pi * 0.5.
|
||||
constexpr float HalfPi = 1.5707963267948966192313216916397514420985846996875529104874722961539082031431044993140174126710585;
|
||||
|
||||
/// e - https://www.mathsisfun.com/numbers/e-eulers-number.html
|
||||
constexpr float EulersNumber = 2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274;
|
||||
/// 2pi - The ratio of a circle's circumferecne to its radius, and the number of radians in one turn.
|
||||
|
@@ -2,28 +2,23 @@
|
||||
|
||||
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
|
||||
#include <J3ML/LinearAlgebra/Quaternion.hpp>
|
||||
#include <J3ML/LinearAlgebra/AxisAngle.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector3.hpp>
|
||||
|
||||
namespace J3ML::LinearAlgebra
|
||||
{
|
||||
namespace J3ML::LinearAlgebra {
|
||||
class AxisAngle;
|
||||
}
|
||||
|
||||
/// Transitional datatype, not useful for internal representation of rotation
|
||||
/// But has uses for conversion and manipulation.
|
||||
class AxisAngle {
|
||||
public:
|
||||
Vector3 axis;
|
||||
float angle;
|
||||
public:
|
||||
AxisAngle();
|
||||
explicit AxisAngle(const Quaternion& q);
|
||||
explicit AxisAngle(const EulerAngle& e);
|
||||
/// Transitional datatype, not useful for internal representation of rotation
|
||||
/// But has uses for conversion and manipulation.
|
||||
class J3ML::LinearAlgebra::AxisAngle {
|
||||
public:
|
||||
Vector3 axis;
|
||||
// Radians.
|
||||
float angle;
|
||||
public:
|
||||
AxisAngle();
|
||||
explicit AxisAngle(const Quaternion& q);
|
||||
explicit AxisAngle(const EulerAngleXYZ& e);
|
||||
AxisAngle(const Vector3& axis, float angle);
|
||||
|
||||
AxisAngle(const Vector3 &axis, float angle);
|
||||
|
||||
EulerAngle ToEulerAngleXYZ() const;
|
||||
|
||||
Quaternion ToQuaternion() const;
|
||||
static AxisAngle FromEulerAngleXYZ(const EulerAngle&);
|
||||
};
|
||||
}
|
||||
};
|
20
include/J3ML/LinearAlgebra/DirectionVector.hpp
Normal file
20
include/J3ML/LinearAlgebra/DirectionVector.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include <J3ML/LinearAlgebra/Vector3.hpp>
|
||||
|
||||
namespace J3ML::LinearAlgebra {
|
||||
class DirectionVectorRH;
|
||||
}
|
||||
|
||||
/// Direction vector of a given Matrix3x3 RotationMatrix in a Right-handed coordinate space.
|
||||
class J3ML::LinearAlgebra::DirectionVectorRH : public Vector3 {
|
||||
private:
|
||||
// This is purposefully not exposed because these types aren't usually convertable.
|
||||
explicit DirectionVectorRH(const Vector3& rhs);
|
||||
public:
|
||||
static DirectionVectorRH Forward(const Matrix3x3& rhs);
|
||||
static DirectionVectorRH Backward(const Matrix3x3& rhs);
|
||||
static DirectionVectorRH Left(const Matrix3x3& rhs);
|
||||
static DirectionVectorRH Right(const Matrix3x3& rhs);
|
||||
static DirectionVectorRH Up(const Matrix3x3& rhs);
|
||||
static DirectionVectorRH Down(const Matrix3x3& rhs);
|
||||
};
|
@@ -5,48 +5,19 @@
|
||||
#include <J3ML/LinearAlgebra/AxisAngle.hpp>
|
||||
|
||||
namespace J3ML::LinearAlgebra {
|
||||
class EulerAngleXYZ;
|
||||
}
|
||||
|
||||
class AxisAngle;
|
||||
|
||||
// Essential Reading:
|
||||
// http://www.essentialmath.com/GDC2012/GDC2012_JMV_Rotations.pdf
|
||||
class EulerAngle {
|
||||
class J3ML::LinearAlgebra::EulerAngleXYZ {
|
||||
public:
|
||||
EulerAngle();
|
||||
EulerAngle(float pitch, float yaw, float roll);
|
||||
EulerAngle(const Vector3& vec) : pitch(vec.x), yaw(vec.y), roll(vec.z) {}
|
||||
|
||||
AxisAngle ToAxisAngle() const;
|
||||
|
||||
[[nodiscard]] Quaternion ToQuaternion() const;
|
||||
|
||||
|
||||
explicit EulerAngle(const Quaternion& rhs);
|
||||
explicit EulerAngle(const AxisAngle& rhs);
|
||||
|
||||
/// TODO: Implement separate upper and lower bounds
|
||||
/// Preserves internal value of euler angles, normalizes and clamps the output.
|
||||
/// This does not solve gimbal lock!!!
|
||||
float GetPitch(float pitch_limit) const;
|
||||
float GetYaw(float yaw_limit) const;
|
||||
float GetRoll(float roll_limit) const;
|
||||
|
||||
bool operator==(const EulerAngle& a) const;
|
||||
void clamp();
|
||||
|
||||
// TODO: Euler Angles do not represent a vector, length doesn't apply, nor is this information meaningful for this data type.
|
||||
// If you need a meaningful representation of length in 3d space, use a vector!!
|
||||
[[nodiscard]] float length() const {
|
||||
return 0;
|
||||
}
|
||||
// TODO: Implement
|
||||
Vector3 unitVector() const;
|
||||
|
||||
EulerAngle movementAngle() const;
|
||||
public:
|
||||
float pitch;
|
||||
float yaw;
|
||||
float roll;
|
||||
float roll = 0; // X
|
||||
float pitch = 0; // Y
|
||||
float yaw = 0; // Z
|
||||
public:
|
||||
EulerAngleXYZ(float roll, float pitch, float yaw);
|
||||
public:
|
||||
explicit EulerAngleXYZ(const Quaternion& rhs);
|
||||
explicit EulerAngleXYZ(const AxisAngle& rhs);
|
||||
explicit EulerAngleXYZ(const Matrix3x3& rhs);
|
||||
};
|
||||
|
||||
}
|
@@ -7,7 +7,7 @@ namespace J3ML::LinearAlgebra
|
||||
class Vector3; // A type representing a position in a 3-dimensional coordinate space.
|
||||
class Vector4; // A type representing a position in a 4-dimensional coordinate space.
|
||||
class Angle2D; // Uses x,y components to represent a 2D rotation.
|
||||
class EulerAngle; // Uses pitch,yaw,roll components to represent a 3D orientation.
|
||||
class EulerAngleXYZ; // Uses pitch,yaw,roll components to represent a 3D orientation.
|
||||
class AxisAngle; //
|
||||
class CoordinateFrame; //
|
||||
class Matrix2x2;
|
||||
@@ -15,6 +15,7 @@ namespace J3ML::LinearAlgebra
|
||||
class Matrix4x4;
|
||||
class Transform2D;
|
||||
class Transform3D;
|
||||
class DirectionVectorRH; // A type representing a direction in 3D space.
|
||||
class Quaternion;
|
||||
|
||||
|
||||
|
@@ -63,7 +63,10 @@ namespace J3ML::LinearAlgebra {
|
||||
/// Constructs this matrix3x3 from the given quaternion.
|
||||
explicit Matrix3x3(const Quaternion& orientation);
|
||||
/// Constructs this matrix3x3 from the given euler angle.
|
||||
explicit Matrix3x3(const EulerAngle& orientation);
|
||||
|
||||
explicit Matrix3x3(const EulerAngleXYZ& orientation);
|
||||
|
||||
explicit Matrix3x3(const AxisAngle& orientation);
|
||||
|
||||
/// Constructs this Matrix3x3 from a pointer to an array of floats.
|
||||
explicit Matrix3x3(const float *data);
|
||||
@@ -153,6 +156,7 @@ namespace J3ML::LinearAlgebra {
|
||||
|
||||
/// Sets this matrix to perform a rotation about the given axis and angle.
|
||||
void SetRotatePart(const Vector3& a, float angle);
|
||||
|
||||
void SetRotatePart(const AxisAngle& axisAngle);
|
||||
/// Sets this matrix to perform the rotation expressed by the given quaternion.
|
||||
void SetRotatePart(const Quaternion& quat);
|
||||
@@ -239,17 +243,10 @@ namespace J3ML::LinearAlgebra {
|
||||
inline float* ptr() { return &elems[0][0];}
|
||||
[[nodiscard]] inline const float* ptr() const {return &elems[0][0];}
|
||||
|
||||
|
||||
/// Convers this rotation matrix to a quaternion.
|
||||
/// This function assumes that the matrix is orthonormal (no shear or scaling) and does not perform any mirroring (determinant > 0)
|
||||
[[nodiscard]] Quaternion ToQuat() const;
|
||||
/// Attempts to convert this matrix to a quaternion. Returns false if the conversion cannot succeed (this matrix was not a rotation
|
||||
/// matrix, and there is scaling ,shearing, or mirroring in this matrix)
|
||||
bool TryConvertToQuat(Quaternion& q) const;
|
||||
|
||||
/// Converts this rotation matrix to an Euler Angle.
|
||||
[[nodiscard]] EulerAngle ToEulerAngle() const;
|
||||
|
||||
/// Returns the main diagonal.
|
||||
/// The main diagonal consists of the elements at m[0][0], m[1][1], m[2][2]
|
||||
[[nodiscard]] Vector3 Diagonal() const;
|
||||
|
@@ -71,8 +71,7 @@ namespace J3ML::LinearAlgebra {
|
||||
|
||||
/// Constructs this Matrix4x4 from the given quaternion.
|
||||
explicit Matrix4x4(const Quaternion& orientation);
|
||||
/// Constructs this Matrix4x4 from the given Euler Angle.
|
||||
explicit Matrix4x4(const EulerAngle& orientation);
|
||||
|
||||
|
||||
/// Constructs this float4x4 from the given quaternion and translation.
|
||||
/// Logically, the translation occurs after the rotation has been performed.
|
||||
@@ -567,8 +566,6 @@ namespace J3ML::LinearAlgebra {
|
||||
|
||||
[[nodiscard]] Quaternion ToQuat() const;
|
||||
|
||||
[[nodiscard]] EulerAngle ToEulerAngle() const;
|
||||
|
||||
/// Returns true if this Matrix4x4 is equal to the given Matrix4x4, up to given per-element epsilon.
|
||||
bool Equals(const Matrix4x4& other, float epsilon = 1e-3f) const;
|
||||
|
||||
|
@@ -4,258 +4,237 @@
|
||||
#include <J3ML/Algorithm/RNG.hpp>
|
||||
#include <cmath>
|
||||
|
||||
namespace J3ML::LinearAlgebra
|
||||
{
|
||||
class Quaternion {
|
||||
public:
|
||||
/// The identity quaternion performs no rotation when applied to a vector.
|
||||
static const Quaternion Identity;
|
||||
/// A compile-time constant Quaternion with the value (NAN, NAN, NAN, NAN).
|
||||
/// For this constant, each element has the value of quiet NAN, or Not-A-Number.
|
||||
/// @note Never compare a Quaternion to this value! Due to how IEEE floats work, "nan == nan" returns false!
|
||||
/// That is, nothing is equal to NaN, not even NaN itself!
|
||||
static const Quaternion NaN;
|
||||
public:
|
||||
/// The default constructor does not initialize any member values.
|
||||
Quaternion();
|
||||
/// Copy constructor
|
||||
Quaternion(const Quaternion &rhs) = default;
|
||||
/// Constructs a quaternion from the given data buffer.
|
||||
/// @param data An array of four floats to use for the quaternion, in the order 'x,y,z,w.' (== 'i,j,k,w')
|
||||
explicit Quaternion(const float *data);
|
||||
namespace J3ML::LinearAlgebra {
|
||||
class Quaternion;
|
||||
}
|
||||
|
||||
explicit Quaternion(const Matrix3x3 &rotationMtrx);
|
||||
explicit Quaternion(const Matrix4x4 &rotationMtrx);
|
||||
class J3ML::LinearAlgebra::Quaternion {
|
||||
public:
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
public:
|
||||
/// The identity quaternion performs no rotation when applied to a vector.
|
||||
static const Quaternion Identity;
|
||||
/// A compile-time constant Quaternion with the value (NAN, NAN, NAN, NAN).
|
||||
/// For this constant, each element has the value of quiet NAN, or Not-A-Number.
|
||||
/// @note Never compare a Quaternion to this value! Due to how IEEE floats work, "nan == nan" returns false!
|
||||
/// That is, nothing is equal to NaN, not even NaN itself!
|
||||
static const Quaternion NaN;
|
||||
public:
|
||||
/// The default constructor does not initialize any member values.
|
||||
Quaternion() = default;
|
||||
/// Copy constructor
|
||||
Quaternion(const Quaternion &rhs);
|
||||
/// Quaternion from Matrix3x3
|
||||
explicit Quaternion(const Matrix3x3& ro_mat);
|
||||
/// Quaternion from Matrix4x4 RotatePart.
|
||||
explicit Quaternion(const Matrix4x4& ro_mat);
|
||||
/// Quaternion from EulerAngleXYZ.
|
||||
explicit Quaternion(const EulerAngleXYZ& rhs);
|
||||
/// Quaternion from AxisAngle.
|
||||
explicit Quaternion(const AxisAngle& angle);
|
||||
/// Quaternion from Vector4 (no conversion).
|
||||
explicit Quaternion(const Vector4& vector4);
|
||||
|
||||
/// @param x The factor of i.
|
||||
/// @param y The factor of j.
|
||||
/// @param z The factor of k.
|
||||
/// @param w The scalar factor (or 'w').
|
||||
/// @note The input data is not normalized after construction, this has to be done manually.
|
||||
Quaternion(float X, float Y, float Z, float W);
|
||||
/// @param x The factor of i.
|
||||
/// @param y The factor of j.
|
||||
/// @param z The factor of k.
|
||||
/// @param w The scalar factor (or 'w').
|
||||
/// @note The input data is not normalized after construction, this has to be done manually.
|
||||
Quaternion(float X, float Y, float Z, float W);
|
||||
|
||||
/// Constructs this quaternion by specifying a rotation axis and the amount of rotation to be performed about that axis
|
||||
/// @param rotationAxis The normalized rotation axis to rotate about. If using Vector4 version of the constructor, the w component of this vector must be 0.
|
||||
/// @param rotationAngleRadians The angle to rotate by, in radians. For example, Pi/4.f equals to 45 degrees, Pi/2.f is 90 degrees, etc.
|
||||
/// @see DegToRad()
|
||||
Quaternion(const Vector3 &rotationAxis, float rotationAngleRadians);
|
||||
Quaternion(const Vector4 &rotationAxis, float rotationAngleRadians);
|
||||
/// Creates a LookAt quaternion.
|
||||
/** A LookAt quaternion is a quaternion that orients an object to face towards a specified target direction.
|
||||
@param localForward Specifies the forward direction in the local space of the object. This is the direction
|
||||
the model is facing at in its own local/object space, often +X(1,0,0), +Y(0,1,0), or +Z(0,0,1).
|
||||
The vector to pass in here depends on the conventions you or your modeling software is using, and it is best
|
||||
to pick one convention for all your objects, and be consistent.
|
||||
This input parameter must be a normalized vector.
|
||||
@param targetDirection Specifies the desired world space direction the object should look at. This function
|
||||
will compute a quaternion which will rotate the localForward vector to orient towards this targetDirection
|
||||
vector. This input parameter must be a normalized vector.
|
||||
@param localUp Specifies the up direction in the local space of the object. This is the up direction the model
|
||||
was authored in, often +Y (0,1,0) or +Z (0,0,1). The vector to pass in here depends on the conventions you
|
||||
or your modeling software is using, and it is best to pick one convention for all your objects, and be
|
||||
consistent. This input parameter must be a normalized vector. This vector must be perpendicular to the
|
||||
vector localForward, i.e. localForward.Dot(localUp) == 0.
|
||||
@param worldUp Specifies the global up direction of the scene in world space. Simply rotating one vector to
|
||||
coincide with another (localForward->targetDirection) would cause the up direction of the resulting
|
||||
orientation to drift (e.g. the model could be looking at its target its head slanted sideways). To keep
|
||||
the up direction straight, this function orients the localUp direction of the model to point towards
|
||||
the specified worldUp direction (as closely as possible). The worldUp and targetDirection vectors cannot be
|
||||
collinear, but they do not need to be perpendicular either.
|
||||
@return A quaternion that maps the given local space forward direction vector to point towards the given target
|
||||
direction, and the given local up direction towards the given target world up direction. For the returned
|
||||
quaternion Q it holds that M * localForward = targetDirection, and M * localUp lies in the plane spanned
|
||||
by the vectors targetDirection and worldUp.
|
||||
@see RotateFromTo() */
|
||||
static Quaternion LookAt(const Vector3& localForward, const Vector3& targetDirection, const Vector3& localUp, const Vector3& worldUp);
|
||||
|
||||
explicit Quaternion(const Vector4& vector4);
|
||||
explicit Quaternion(const EulerAngle& angle);
|
||||
explicit Quaternion(const AxisAngle& angle);
|
||||
/// Creates a new quaternion that rotates about the positive X axis by the given rotation.
|
||||
static Quaternion RotateX(float rad);
|
||||
/// Creates a new quaternion that rotates about the positive Y axis by the given rotation.
|
||||
static Quaternion RotateY(float rad);
|
||||
/// Creates a new quaternion that rotates about the positive Z axis by the given rotation.
|
||||
static Quaternion RotateZ(float rad);
|
||||
|
||||
/// Creates a LookAt quaternion.
|
||||
/** A LookAt quaternion is a quaternion that orients an object to face towards a specified target direction.
|
||||
@param localForward Specifies the forward direction in the local space of the object. This is the direction
|
||||
the model is facing at in its own local/object space, often +X(1,0,0), +Y(0,1,0), or +Z(0,0,1).
|
||||
The vector to pass in here depends on the conventions you or your modeling software is using, and it is best
|
||||
to pick one convention for all your objects, and be consistent.
|
||||
This input parameter must be a normalized vector.
|
||||
@param targetDirection Specifies the desired world space direction the object should look at. This function
|
||||
will compute a quaternion which will rotate the localForward vector to orient towards this targetDirection
|
||||
vector. This input parameter must be a normalized vector.
|
||||
@param localUp Specifies the up direction in the local space of the object. This is the up direction the model
|
||||
was authored in, often +Y (0,1,0) or +Z (0,0,1). The vector to pass in here depends on the conventions you
|
||||
or your modeling software is using, and it is best to pick one convention for all your objects, and be
|
||||
consistent. This input parameter must be a normalized vector. This vector must be perpendicular to the
|
||||
vector localForward, i.e. localForward.Dot(localUp) == 0.
|
||||
@param worldUp Specifies the global up direction of the scene in world space. Simply rotating one vector to
|
||||
coincide with another (localForward->targetDirection) would cause the up direction of the resulting
|
||||
orientation to drift (e.g. the model could be looking at its target its head slanted sideways). To keep
|
||||
the up direction straight, this function orients the localUp direction of the model to point towards
|
||||
the specified worldUp direction (as closely as possible). The worldUp and targetDirection vectors cannot be
|
||||
collinear, but they do not need to be perpendicular either.
|
||||
@return A quaternion that maps the given local space forward direction vector to point towards the given target
|
||||
direction, and the given local up direction towards the given target world up direction. For the returned
|
||||
quaternion Q it holds that M * localForward = targetDirection, and M * localUp lies in the plane spanned
|
||||
by the vectors targetDirection and worldUp.
|
||||
@see RotateFromTo() */
|
||||
static Quaternion LookAt(const Vector3& localForward, const Vector3& targetDirection, const Vector3& localUp, const Vector3& worldUp);
|
||||
/// Creates a new quaternion that rotates sourceDirection vector (in world space) to coincide with the
|
||||
/// targetDirection vector (in world space).
|
||||
/// Rotation is performed about the origin.
|
||||
/// The vectors sourceDirection and targetDirection are assumed to be normalized.
|
||||
/// @note There are multiple such rotations - this function returns the rotation that has the shortest angle
|
||||
/// (when decomposed to axis-angle notation).
|
||||
static Quaternion RotateFromTo(const Vector3& sourceDirection, const Vector3& targetDirection);
|
||||
static Quaternion RotateFromTo(const Vector4& sourceDirection, const Vector4& targetDirection);
|
||||
|
||||
/// Creates a new Quaternion that rotates about the given axis by the given angle.
|
||||
static Quaternion RotateAxisAngle(const AxisAngle& axisAngle);
|
||||
|
||||
/// Creates a new quaternion that rotates about the positive X axis by the given rotation.
|
||||
static Quaternion RotateX(float angleRadians);
|
||||
/// Creates a new quaternion that rotates about the positive Y axis by the given rotation.
|
||||
static Quaternion RotateY(float angleRadians);
|
||||
/// Creates a new quaternion that rotates about the positive Z axis by the given rotation.
|
||||
static Quaternion RotateZ(float angleRadians);
|
||||
|
||||
/// Creates a new quaternion that rotates sourceDirection vector (in world space) to coincide with the
|
||||
/// targetDirection vector (in world space).
|
||||
/// Rotation is performed about the origin.
|
||||
/// The vectors sourceDirection and targetDirection are assumed to be normalized.
|
||||
/// @note There are multiple such rotations - this function returns the rotation that has the shortest angle
|
||||
/// (when decomposed to axis-angle notation).
|
||||
static Quaternion RotateFromTo(const Vector3& sourceDirection, const Vector3& targetDirection);
|
||||
static Quaternion RotateFromTo(const Vector4& sourceDirection, const Vector4& targetDirection);
|
||||
|
||||
/// Creates a new quaternion that
|
||||
/// 1. rotates sourceDirection vector to coincide with the targetDirection vector, and then
|
||||
/// 2. rotates sourceDirection2 (which was transformed by 1.) to targetDirection2, but keeping the constraint that
|
||||
/// sourceDirection must look at targetDirection
|
||||
static Quaternion RotateFromTo(const Vector3& sourceDirection, const Vector3& targetDirection, const Vector3& sourceDirection2, const Vector3& targetDirection2);
|
||||
/// Creates a new quaternion that
|
||||
/// 1. rotates sourceDirection vector to coincide with the targetDirection vector, and then
|
||||
/// 2. rotates sourceDirection2 (which was transformed by 1.) to targetDirection2, but keeping the constraint that
|
||||
/// sourceDirection must look at targetDirection
|
||||
static Quaternion RotateFromTo(const Vector3& sourceDirection, const Vector3& targetDirection, const Vector3& sourceDirection2, const Vector3& targetDirection2);
|
||||
|
||||
|
||||
/// Returns a uniformly random unitary quaternion.
|
||||
static Quaternion RandomRotation(RNG &rng);
|
||||
public:
|
||||
void SetFromAxisAngle(const Vector3 &vector3, float between);
|
||||
/// Returns a uniformly random unitary quaternion.
|
||||
static Quaternion RandomRotation(RNG &rng);
|
||||
public:
|
||||
/// Inverses this quaternion in-place.
|
||||
/// @note For optimization purposes, this function assumes that the quaternion is unitary, in which
|
||||
/// case the inverse of the quaternion is simply just the same as its conjugate.
|
||||
/// This function does not detect whether the operation succeeded or failed.
|
||||
void Inverse();
|
||||
|
||||
void SetFromAxisAngle(const Vector4 &vector4, float between);
|
||||
void SetFrom(const AxisAngle& angle);
|
||||
/// Returns an inverted copy of this quaternion.
|
||||
[[nodiscard]] Quaternion Inverted() const;
|
||||
/// Computes the conjugate of this quaternion in-place.
|
||||
void Conjugate();
|
||||
/// Returns a conjugated copy of this quaternion.
|
||||
[[nodiscard]] Quaternion Conjugated() const;
|
||||
|
||||
/// Inverses this quaternion in-place.
|
||||
/// @note For optimization purposes, this function assumes that the quaternion is unitary, in which
|
||||
/// case the inverse of the quaternion is simply just the same as its conjugate.
|
||||
/// This function does not detect whether the operation succeeded or failed.
|
||||
void Inverse();
|
||||
/// Inverses this quaternion in-place.
|
||||
/// Call this function when the quaternion is not known beforehand to be normalized.
|
||||
/// This function computes the inverse proper, and normalizes the result.
|
||||
/// @note Because of the normalization, it does not necessarily hold that q * q.InverseAndNormalize() == id.
|
||||
/// @return Returns the old length of this quaternion (not the old length of the inverse quaternion).
|
||||
float InverseAndNormalize();
|
||||
|
||||
/// Returns an inverted copy of this quaternion.
|
||||
[[nodiscard]] Quaternion Inverted() const;
|
||||
/// Computes the conjugate of this quaternion in-place.
|
||||
void Conjugate();
|
||||
/// Returns a conjugated copy of this quaternion.
|
||||
[[nodiscard]] Quaternion Conjugated() const;
|
||||
/// Returns the local +X axis in the post-transformed coordinate space. This is the same as transforming the vector (1,0,0) by this quaternion.
|
||||
[[nodiscard]] Vector3 WorldX() const;
|
||||
/// Returns the local +Y axis in the post-transformed coordinate space. This is the same as transforming the vector (0,1,0) by this quaternion.
|
||||
[[nodiscard]] Vector3 WorldY() const;
|
||||
/// Returns the local +Z axis in the post-transformed coordinate space. This is the same as transforming the vector (0,0,1) by this quaternion.
|
||||
[[nodiscard]] Vector3 WorldZ() const;
|
||||
/// Returns the axis of rotation for this quaternion.
|
||||
[[nodiscard]] Vector3 Axis() const;
|
||||
|
||||
/// Inverses this quaternion in-place.
|
||||
/// Call this function when the quaternion is not known beforehand to be normalized.
|
||||
/// This function computes the inverse proper, and normalizes the result.
|
||||
/// @note Because of the normalization, it does not necessarily hold that q * q.InverseAndNormalize() == id.
|
||||
/// @return Returns the old length of this quaternion (not the old length of the inverse quaternion).
|
||||
float InverseAndNormalize();
|
||||
/// Returns the angle of rotation for this quaternion, in radians.
|
||||
[[nodiscard]] float Angle() const;
|
||||
|
||||
/// Returns the local +X axis in the post-transformed coordinate space. This is the same as transforming the vector (1,0,0) by this quaternion.
|
||||
[[nodiscard]] Vector3 WorldX() const;
|
||||
/// Returns the local +Y axis in the post-transformed coordinate space. This is the same as transforming the vector (0,1,0) by this quaternion.
|
||||
[[nodiscard]] Vector3 WorldY() const;
|
||||
/// Returns the local +Z axis in the post-transformed coordinate space. This is the same as transforming the vector (0,0,1) by this quaternion.
|
||||
[[nodiscard]] Vector3 WorldZ() const;
|
||||
[[nodiscard]] float LengthSquared() const;
|
||||
[[nodiscard]] float Length() const;
|
||||
|
||||
/// Returns the axis of rotation for this quaternion.
|
||||
[[nodiscard]] Vector3 Axis() const;
|
||||
[[nodiscard]] Matrix3x3 ToMatrix3x3() const;
|
||||
[[nodiscard]] Matrix4x4 ToMatrix4x4() const;
|
||||
|
||||
/// Returns the angle of rotation for this quaternion, in radians.
|
||||
[[nodiscard]] float Angle() const;
|
||||
[[nodiscard]] Matrix4x4 ToMatrix4x4(const Vector3 &translation) const;
|
||||
|
||||
[[nodiscard]] float LengthSquared() const;
|
||||
[[nodiscard]] float Length() const;
|
||||
[[nodiscard]] Vector3 Transform(const Vector3& vec) const;
|
||||
[[nodiscard]] Vector3 Transform(float X, float Y, float Z) const;
|
||||
// Note: We only transform the x,y,z components of 4D vectors, w is left untouched
|
||||
[[nodiscard]] Vector4 Transform(const Vector4& vec) const;
|
||||
[[nodiscard]] Vector4 Transform(float X, float Y, float Z, float W) const;
|
||||
|
||||
[[nodiscard]] EulerAngle ToEulerAngle() const;
|
||||
[[nodiscard]] Quaternion Lerp(const Quaternion& b, float t) const;
|
||||
static Quaternion Lerp(const Quaternion &source, const Quaternion& target, float t);
|
||||
[[nodiscard]] Quaternion Slerp(const Quaternion& q2, float t) const;
|
||||
static Quaternion Slerp(const Quaternion &source, const Quaternion& target, float t);
|
||||
|
||||
/// Returns the 'from' vector rotated towards the 'to' vector by the given normalized time parameter.
|
||||
/** This function slerps the given 'form' vector toward the 'to' vector.
|
||||
@param from A normalized direction vector specifying the direction of rotation at t=0
|
||||
@param to A normalized direction vector specifying the direction of rotation at t=1
|
||||
@param t The interpolation time parameter, in the range [0, 1]. Input values outside this range are
|
||||
silently clamped to the [0, 1] interval.
|
||||
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
|
||||
static Vector3 SlerpVector(const Vector3& from, const Vector3& to, float t);
|
||||
|
||||
/// Returns the 'from' vector rotated towards the 'to' vector by the given absolute angle, in radians.
|
||||
/** This function slerps the given 'from' vector towards the 'to' vector.
|
||||
@param from A normalized direction vector specifying the direction of rotation at angleRadians=0.
|
||||
@param to A normalized direction vector specifying the target direction to rotate towards.
|
||||
@param angleRadians The maximum angle to rotate the 'from' vector by, in the range [0, pi]. If the
|
||||
angle between 'from' and 'to' is smaller than this angle, then the vector 'to' is returned.
|
||||
Input values outside this range are silently clamped to the [0, pi] interval.
|
||||
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
|
||||
static Vector3 SlerpVectorAbs(const Vector3 &from, const Vector3& to, float angleRadians);
|
||||
|
||||
/// Normalizes this quaternion in-place.
|
||||
/// @returns false if failure, true if success.
|
||||
[[nodiscard]] bool Normalize();
|
||||
/// Returns a normalized copy of this quaternion.
|
||||
[[nodiscard]] Quaternion Normalized() const;
|
||||
|
||||
/// Returns true if the length of this quaternion is one.
|
||||
[[nodiscard]] bool IsNormalized(float epsilon = 1e-5f) const;
|
||||
[[nodiscard]] bool IsInvertible(float epsilon = 1e-3f) const;
|
||||
|
||||
/// Returns true if the entries of this quaternion are all finite.
|
||||
[[nodiscard]] bool IsFinite() const;
|
||||
|
||||
/// Returns true if this quaternion equals rhs, up to the given epsilon.
|
||||
[[nodiscard]] bool Equals(const Quaternion& rhs, float epsilon = 1e-3f) const;
|
||||
|
||||
/// Compares whether this Quaternion and the given Quaternion are identical bit-by-bit in the underlying representation.
|
||||
/// @note Prefer using this over e.g. memcmp, since there can be SSE-related padding in the structures.
|
||||
bool BitEquals(const Quaternion& rhs) const;
|
||||
|
||||
/// @return A pointer to the first element (x). The data is contiguous in memory.
|
||||
/// ptr[0] gives x, ptr[1] gives y, ptr[2] gives z, ptr[3] gives w.
|
||||
inline float *ptr() { return &x; }
|
||||
[[nodiscard]] inline const float *ptr() const { return &x; }
|
||||
|
||||
|
||||
[[nodiscard]] Matrix3x3 ToMatrix3x3() const;
|
||||
[[nodiscard]] Matrix4x4 ToMatrix4x4() const;
|
||||
// Multiplies two quaternions together.
|
||||
// The product q1 * q2 returns a quaternion that concatenates the two orientation rotations.
|
||||
// The rotation q2 is applied first before q1.
|
||||
Quaternion operator * (const Quaternion& rhs) const;
|
||||
|
||||
[[nodiscard]] Matrix4x4 ToMatrix4x4(const Vector3 &translation) const;
|
||||
// Unsafe
|
||||
Quaternion operator * (float scalar) const;
|
||||
|
||||
[[nodiscard]] Vector3 Transform(const Vector3& vec) const;
|
||||
[[nodiscard]] Vector3 Transform(float X, float Y, float Z) const;
|
||||
// Note: We only transform the x,y,z components of 4D vectors, w is left untouched
|
||||
[[nodiscard]] Vector4 Transform(const Vector4& vec) const;
|
||||
[[nodiscard]] Vector4 Transform(float X, float Y, float Z, float W) const;
|
||||
// Unsafe
|
||||
Quaternion operator / (float scalar) const;
|
||||
|
||||
[[nodiscard]] Quaternion Lerp(const Quaternion& b, float t) const;
|
||||
static Quaternion Lerp(const Quaternion &source, const Quaternion& target, float t);
|
||||
[[nodiscard]] Quaternion Slerp(const Quaternion& q2, float t) const;
|
||||
static Quaternion Slerp(const Quaternion &source, const Quaternion& target, float t);
|
||||
// Transforms the given vector by this Quaternion.
|
||||
Vector3 operator * (const Vector3& rhs) const;
|
||||
|
||||
/// Returns the 'from' vector rotated towards the 'to' vector by the given normalized time parameter.
|
||||
/** This function slerps the given 'form' vector toward the 'to' vector.
|
||||
@param from A normalized direction vector specifying the direction of rotation at t=0
|
||||
@param to A normalized direction vector specifying the direction of rotation at t=1
|
||||
@param t The interpolation time parameter, in the range [0, 1]. Input values outside this range are
|
||||
silently clamped to the [0, 1] interval.
|
||||
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
|
||||
static Vector3 SlerpVector(const Vector3& from, const Vector3& to, float t);
|
||||
Vector4 operator * (const Vector4& rhs) const;
|
||||
|
||||
/// Returns the 'from' vector rotated towards the 'to' vector by the given absolute angle, in radians.
|
||||
/** This function slerps the given 'from' vector towards the 'to' vector.
|
||||
@param from A normalized direction vector specifying the direction of rotation at angleRadians=0.
|
||||
@param to A normalized direction vector specifying the target direction to rotate towards.
|
||||
@param angleRadians The maximum angle to rotate the 'from' vector by, in the range [0, pi]. If the
|
||||
angle between 'from' and 'to' is smaller than this angle, then the vector 'to' is returned.
|
||||
Input values outside this range are silently clamped to the [0, pi] interval.
|
||||
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
|
||||
static Vector3 SlerpVectorAbs(const Vector3 &from, const Vector3& to, float angleRadians);
|
||||
|
||||
/// Normalizes this quaternion in-place.
|
||||
/// Returns the old length of this quaternion, or 0 if normalization failed.
|
||||
float Normalize();
|
||||
/// Returns a normalized copy of this quaternion.
|
||||
[[nodiscard]] Quaternion Normalized() const;
|
||||
|
||||
/// Returns true if the length of this quaternion is one.
|
||||
[[nodiscard]] bool IsNormalized(float epsilon = 1e-5f) const;
|
||||
[[nodiscard]] bool IsInvertible(float epsilon = 1e-3f) const;
|
||||
|
||||
/// Returns true if the entries of this quaternion are all finite.
|
||||
[[nodiscard]] bool IsFinite() const;
|
||||
|
||||
/// Returns true if this quaternion equals rhs, up to the given epsilon.
|
||||
[[nodiscard]] bool Equals(const Quaternion& rhs, float epsilon = 1e-3f) const;
|
||||
|
||||
/// Compares whether this Quaternion and the given Quaternion are identical bit-by-bit in the underlying representation.
|
||||
/// @note Prefer using this over e.g. memcmp, since there can be SSE-related padding in the structures.
|
||||
bool BitEquals(const Quaternion& rhs) const;
|
||||
|
||||
/// @return A pointer to the first element (x). The data is contiguous in memory.
|
||||
/// ptr[0] gives x, ptr[1] gives y, ptr[2] gives z, ptr[3] gives w.
|
||||
inline float *ptr() { return &x; }
|
||||
[[nodiscard]] inline const float *ptr() const { return &x; }
|
||||
|
||||
|
||||
// Multiplies two quaternions together.
|
||||
// The product q1 * q2 returns a quaternion that concatenates the two orientation rotations.
|
||||
// The rotation q2 is applied first before q1.
|
||||
Quaternion operator * (const Quaternion& rhs) const;
|
||||
|
||||
// Unsafe
|
||||
Quaternion operator * (float scalar) const;
|
||||
|
||||
// Unsafe
|
||||
Quaternion operator / (float scalar) const;
|
||||
|
||||
// Transforms the given vector by this Quaternion.
|
||||
Vector3 operator * (const Vector3& rhs) const;
|
||||
|
||||
Vector4 operator * (const Vector4& rhs) const;
|
||||
|
||||
// Divides a quaternion by another. Divison "a / b" results in a quaternion that rotates the orientation b to coincide with orientation of
|
||||
Quaternion operator / (const Quaternion& rhs) const;
|
||||
Quaternion operator + (const Quaternion& rhs) const;
|
||||
// Divides a quaternion by another. Divison "a / b" results in a quaternion that rotates the orientation b to coincide with orientation of
|
||||
Quaternion operator / (const Quaternion& rhs) const;
|
||||
Quaternion operator + (const Quaternion& rhs) const;
|
||||
|
||||
|
||||
|
||||
Quaternion operator + () const;
|
||||
Quaternion operator - () const;
|
||||
Quaternion operator + () const;
|
||||
Quaternion operator - () const;
|
||||
|
||||
/// Computes the dot product of this and the given quaternion.
|
||||
/// Dot product is commutative.
|
||||
[[nodiscard]] float Dot(const Quaternion &quaternion) const;
|
||||
/// Computes the dot product of this and the given quaternion.
|
||||
/// Dot product is commutative.
|
||||
[[nodiscard]] float Dot(const Quaternion &quaternion) const;
|
||||
|
||||
/// Returns the angle between this and the target orientation (the shortest route) in radians.
|
||||
[[nodiscard]] float AngleBetween(const Quaternion& target) const;
|
||||
/// Returns the axis of rotation to get from this orientation to target orientation (the shortest route).
|
||||
[[nodiscard]] Vector3 AxisFromTo(const Quaternion& target) const;
|
||||
/// Returns the angle between this and the target orientation (the shortest route) in radians.
|
||||
[[nodiscard]] float AngleBetween(const Quaternion& target) const;
|
||||
/// Returns the axis of rotation to get from this orientation to target orientation (the shortest route).
|
||||
[[nodiscard]] Vector3 AxisFromTo(const Quaternion& target) const;
|
||||
|
||||
|
||||
[[nodiscard]] AxisAngle ToAxisAngle() const;
|
||||
void SetFromAxisAngle(const AxisAngle& axisAngle);
|
||||
/// Sets this quaternion to represent the same rotation as the given matrix.
|
||||
void Set(const Matrix3x3& matrix);
|
||||
void Set(const Matrix4x4& matrix);
|
||||
void Set(float x, float y, float z, float w);
|
||||
void Set(const Quaternion& q);
|
||||
void Set(const Vector4& v);
|
||||
|
||||
/// Sets this quaternion to represent the same rotation as the given matrix.
|
||||
void Set(const Matrix3x3& matrix);
|
||||
void Set(const Matrix4x4& matrix);
|
||||
void Set(float x, float y, float z, float w);
|
||||
void Set(const Quaternion& q);
|
||||
void Set(const Vector4& v);
|
||||
|
||||
public:
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
};
|
||||
}
|
||||
};
|
@@ -68,15 +68,15 @@ public:
|
||||
/** @note Due to static data initialization order being undefined in C++, do NOT use this
|
||||
member to initialize other static data in other compilation units! */
|
||||
static const Vector3 NegativeInfinity;
|
||||
/// Specifies a compile-time constant Vector3 with value (1,1,1).
|
||||
/// Specifies a compile-time constant Vector3 with value (1,0,0).
|
||||
/** @note Due to static data initialization order being undefined in C++, do NOT use this
|
||||
member to initialize other static data in other compilation units! */
|
||||
static const Vector3 UnitX;
|
||||
/// Specifies a compile-time constant Vector3 with value (1,1,1).
|
||||
/// Specifies a compile-time constant Vector3 with value (0,1,0).
|
||||
/** @note Due to static data initialization order being undefined in C++, do NOT use this
|
||||
member to initialize other static data in other compilation units! */
|
||||
static const Vector3 UnitY;
|
||||
/// Specifies a compile-time constant Vector3 with value (1,1,1).
|
||||
/// Specifies a compile-time constant Vector3 with value (0,0,1).
|
||||
/** @note Due to static data initialization order being undefined in C++, do NOT use this
|
||||
member to initialize other static data in other compilation units! */
|
||||
static const Vector3 UnitZ;
|
||||
|
@@ -2,60 +2,22 @@
|
||||
#include <J3ML/LinearAlgebra/Quaternion.hpp>
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
AxisAngle::AxisAngle() : axis(Vector3::Zero) {}
|
||||
AxisAngle::AxisAngle() : axis(Vector3::Zero), angle(0) {}
|
||||
|
||||
AxisAngle::AxisAngle(const Vector3 &axis, float angle) : axis(axis), angle(angle) {}
|
||||
AxisAngle::AxisAngle(const Vector3& axis, float angle) : axis(axis), angle(angle) {}
|
||||
|
||||
Quaternion AxisAngle::ToQuaternion() const {
|
||||
return {
|
||||
axis.x * std::sin(angle/2),
|
||||
axis.y * std::sin(angle/2),
|
||||
axis.z * std::sin(angle/2),
|
||||
std::cos(angle/2)
|
||||
};
|
||||
|
||||
AxisAngle::AxisAngle(const Quaternion& rhs) {
|
||||
float halfAngle = std::acos(rhs.w);
|
||||
angle = halfAngle * 2.f;
|
||||
float reciprocalSinAngle = 1.f / std::sqrt(1.f - rhs.w*rhs.w);
|
||||
|
||||
axis = { rhs.x*reciprocalSinAngle, rhs.y*reciprocalSinAngle, rhs.z*reciprocalSinAngle };
|
||||
}
|
||||
|
||||
AxisAngle::AxisAngle(const Quaternion &q) {
|
||||
auto theta = std::acos(q.w) * 2.f;
|
||||
auto ax = q.x / std::sin(std::acos(theta));
|
||||
auto ay = q.y / std::sin(std::acos(theta));
|
||||
auto az = q.z / std::sin(std::acos(theta));
|
||||
}
|
||||
|
||||
AxisAngle::AxisAngle(const EulerAngle &e) {
|
||||
|
||||
// Assuming the angles are in radians
|
||||
|
||||
float heading = e.pitch;
|
||||
float attitude = e.yaw;
|
||||
float bank = e.roll;
|
||||
|
||||
float c1 = std::cos(heading / 2.f);
|
||||
float s1 = std::sin(heading / 2.f);
|
||||
float c2 = std::cos(attitude / 2.f);
|
||||
float s2 = std::sin(attitude / 2.f);
|
||||
float c3 = std::cos(bank / 2.f);
|
||||
float s3 = std::sin(bank / 2.f);
|
||||
|
||||
float w = c1*c2*c3 - s1*s2*s3;
|
||||
float x = c1*c2*c3 + s1*s2*s3;
|
||||
float y = s1*c2*c3 + c1*s2*s3;
|
||||
float z = c1*s2*c3 - s1*c2*s3;
|
||||
|
||||
angle = 2.f * std::acos(w);
|
||||
|
||||
double norm = x*x + y*y + z*z;
|
||||
if (norm < 0.001) { // when all euler angles are zero angle=0, so
|
||||
// we can set axis to anything to avoid divide by zero
|
||||
x = 1;
|
||||
y = z = 0;
|
||||
} else {
|
||||
norm = std::sqrt(norm);
|
||||
x /= norm;
|
||||
y /= norm;
|
||||
z /= norm;
|
||||
}
|
||||
|
||||
axis = {x, y, z};
|
||||
AxisAngle::AxisAngle(const EulerAngleXYZ& e) {
|
||||
auto a = AxisAngle(Quaternion(e));
|
||||
axis = a.axis;
|
||||
angle = a.angle;
|
||||
}
|
||||
}
|
38
src/J3ML/LinearAlgebra/DirectionVector.cpp
Normal file
38
src/J3ML/LinearAlgebra/DirectionVector.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <J3ML/LinearAlgebra/DirectionVector.hpp>
|
||||
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
|
||||
|
||||
DirectionVectorRH::DirectionVectorRH(const Vector3& rhs) {
|
||||
x = rhs.x;
|
||||
y = rhs.y;
|
||||
z = rhs.z;
|
||||
}
|
||||
|
||||
DirectionVectorRH DirectionVectorRH::Forward(const Matrix3x3& rhs) {
|
||||
return DirectionVectorRH(rhs.Col(2));
|
||||
}
|
||||
|
||||
DirectionVectorRH DirectionVectorRH::Backward(const Matrix3x3& rhs) {
|
||||
return DirectionVectorRH(-rhs.Col(2));
|
||||
}
|
||||
|
||||
DirectionVectorRH DirectionVectorRH::Left(const Matrix3x3& rhs) {
|
||||
return DirectionVectorRH(-rhs.Col(0));
|
||||
}
|
||||
|
||||
DirectionVectorRH DirectionVectorRH::Right(const Matrix3x3& rhs) {
|
||||
return DirectionVectorRH(rhs.Col(0));
|
||||
}
|
||||
|
||||
DirectionVectorRH DirectionVectorRH::Up(const Matrix3x3 &rhs) {
|
||||
return DirectionVectorRH(rhs.Col(1));
|
||||
}
|
||||
|
||||
DirectionVectorRH DirectionVectorRH::Down(const Matrix3x3& rhs) {
|
||||
return DirectionVectorRH(-rhs.Col(1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,147 +1,45 @@
|
||||
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
|
||||
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace J3ML::LinearAlgebra {
|
||||
EulerAngle::EulerAngle(float pitch, float yaw, float roll): pitch(pitch), yaw(yaw), roll(roll)
|
||||
{}
|
||||
|
||||
float EulerAngle::GetPitch(float pitch_limit) const
|
||||
{ return std::clamp( std::remainderf(pitch,360.f), -pitch_limit, pitch_limit); }
|
||||
|
||||
float EulerAngle::GetYaw(float yaw_limit) const
|
||||
{ return std::clamp(std::remainderf(yaw, 360.f), -yaw_limit, yaw_limit); }
|
||||
|
||||
float EulerAngle::GetRoll(float pitch_limit) const
|
||||
{ return std::clamp( std::remainderf(pitch,360.f), -pitch_limit, pitch_limit); }
|
||||
|
||||
bool EulerAngle::operator==(const EulerAngle& a) const
|
||||
{
|
||||
return (pitch == a.pitch) && (yaw == a.yaw) && (roll == a.roll);
|
||||
EulerAngleXYZ::EulerAngleXYZ(float roll, float pitch, float yaw) {
|
||||
this->roll = roll;
|
||||
this->pitch = pitch;
|
||||
this->yaw = yaw;
|
||||
}
|
||||
|
||||
void EulerAngle::clamp()
|
||||
{
|
||||
if (this->pitch > 89.0f)
|
||||
this->pitch = 89.0f;
|
||||
if (this->pitch <= -89.0f)
|
||||
this->pitch = -89.0f;
|
||||
//TODO: Make this entirely seamless by getting the amount they rotated passed -180 and +180 by.
|
||||
if (this->yaw <= -180.0f)
|
||||
this->yaw = 180.0f;
|
||||
if (this->yaw >= 180.01f)
|
||||
this->yaw = -179.9f;
|
||||
if (this->roll >= 360.0f)
|
||||
this->roll = 0.0;
|
||||
if (this->roll <= -360.0f)
|
||||
this->roll = 0.0;
|
||||
EulerAngleXYZ::EulerAngleXYZ(const AxisAngle& rhs) {
|
||||
*this = EulerAngleXYZ(Quaternion(rhs));
|
||||
}
|
||||
|
||||
EulerAngle EulerAngle::movementAngle() const
|
||||
{
|
||||
EulerAngle a;
|
||||
a.pitch = (cos(Math::Radians(yaw)) * cos(Math::Radians(pitch)));
|
||||
a.yaw = -sin(Math::Radians(pitch));
|
||||
a.roll = (sin(Math::Radians(yaw)) * cos(Math::Radians(pitch)));
|
||||
return a;
|
||||
EulerAngleXYZ::EulerAngleXYZ(const Quaternion& q) {
|
||||
float sy = 2 * q.x * q.z + 2 * q.y * q.w;
|
||||
bool gimbal_lock = std::abs(sy) > 0.99999f;
|
||||
|
||||
if (!gimbal_lock)
|
||||
roll = Math::Degrees(std::atan2(-(2 * q.y * q.z - 2 * q.x * q.w),2 * q.w * q.w + 2 * q.z * q.z - 1));
|
||||
else
|
||||
roll = Math::Degrees(std::atan2(2 * q.y * q.z + 2 * q.x * q.w,2 * q.w * q.w + 2 * q.y * q.y - 1));
|
||||
|
||||
pitch = Math::Degrees(std::asin(sy));
|
||||
|
||||
if (!gimbal_lock)
|
||||
yaw = Math::Degrees(std::atan2(-(2 * q.x * q.y - 2 * q.z * q.w),2 * q.w * q.w + 2 * q.x * q.x - 1));
|
||||
else
|
||||
yaw = 0;
|
||||
}
|
||||
|
||||
EulerAngle::EulerAngle() : pitch(0), yaw(0), roll(0) {}
|
||||
EulerAngleXYZ::EulerAngleXYZ(const Matrix3x3& rhs) {
|
||||
auto m = rhs.Transposed();
|
||||
auto sy = m.At(0, 2);
|
||||
auto unlocked = std::abs(sy) < 0.99999f;
|
||||
|
||||
EulerAngle::EulerAngle(const AxisAngle &rhs) {
|
||||
|
||||
float x = rhs.axis.x;
|
||||
float y = rhs.axis.y;
|
||||
float z = rhs.axis.z;
|
||||
float angle = rhs.angle;
|
||||
|
||||
double s = std::sin(rhs.angle);
|
||||
|
||||
double c = std::cos(rhs.angle);
|
||||
|
||||
double t = 1-c;
|
||||
|
||||
// if axis is not already normalized then uncomment this
|
||||
|
||||
// double magnitude = std::sqrt(x*x + y*y + z*z);
|
||||
// if (magnitude == 0) throw error;
|
||||
// x /= magnitude;
|
||||
// y /= magnitude;
|
||||
// z /= magnitude;
|
||||
|
||||
if ((x*y*t + z*s) > 0.998) { // North pole singularity detected
|
||||
pitch = 2 * std::atan2(x * std::sin(angle/2.f), std::cos(angle/2.f));
|
||||
yaw = Math::Pi / 2.f;
|
||||
roll = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((x*y*t + z*s) < -0.998) { // South pole singularity detected
|
||||
pitch = -2 * std::atan2(x * std::sin(angle/2.f), std::cos(angle/2.f));
|
||||
yaw = -Math::Pi / 2.f;
|
||||
roll = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
pitch = std::atan2(y * s-x * z * t, 1 - (y*y + z*z) * t);
|
||||
yaw = std::asin(x * y * t + z * s);
|
||||
roll = std::atan2(x * s - y * z * t, 1 - (x*x + z*z) * t);
|
||||
}
|
||||
|
||||
AxisAngle EulerAngle::ToAxisAngle() const {
|
||||
auto c1 = std::cos(yaw / 2);
|
||||
auto c2 = std::cos(pitch / 2);
|
||||
auto c3 = std::cos(roll / 2);
|
||||
auto s1 = std::sin(yaw / 2);
|
||||
auto s2 = std::sin(pitch / 2);
|
||||
auto s3 = std::sin(roll / 2);
|
||||
|
||||
auto angle = 2 * std::acos(c1*c2*c3 - s1*s2*s3);
|
||||
|
||||
auto x = s1*s2*c3 + c1*c2*s3;
|
||||
auto y = s1*c2*c3 + c1*s2*s3;
|
||||
auto z = c1*s2*c3 - s1*c2*s3;
|
||||
|
||||
// todo: normalize?
|
||||
// sqrt(x^2 + y^2 + z^2) = sqrt((s1 s2 c3 +c1 c2 s3)^2+(s1 c2 c3 + c1 s2 s3)^2+(c1 s2 c3 - s1 c2 s3)^2)
|
||||
|
||||
return {{x,y,z}, angle};
|
||||
}
|
||||
|
||||
Quaternion EulerAngle::ToQuaternion() const {
|
||||
auto c1 = std::cos(yaw / 2);
|
||||
auto c2 = std::cos(pitch / 2);
|
||||
auto c3 = std::cos(roll / 2);
|
||||
auto s1 = std::sin(yaw / 2);
|
||||
auto s2 = std::sin(pitch / 2);
|
||||
auto s3 = std::sin(roll / 2);
|
||||
|
||||
auto w = c1*c2*c3 - s1*s2*s3;
|
||||
auto x = s1*s2*c3 + c1*c2*s3;
|
||||
auto y = s1*c2*c3 + c1*s2*s3;
|
||||
auto z = c1*s2*c3 - s1*c2*s3;
|
||||
|
||||
return {w,x,y,z};
|
||||
}
|
||||
|
||||
EulerAngle::EulerAngle(const Quaternion &rhs) {
|
||||
double test = rhs.x * rhs.y + rhs.z * rhs.w;
|
||||
if (test > 0.499) { // Singularity at north pole
|
||||
pitch = 2 * std::atan2(rhs.x, rhs.w);
|
||||
yaw = Math::Pi / 2.f;
|
||||
roll = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (test < -0.499) { // Singularity at south pole
|
||||
pitch = -2 * std::atan2(rhs.x, rhs.y);
|
||||
yaw = - Math::Pi / 2.f;
|
||||
roll = 0;
|
||||
return;
|
||||
}
|
||||
float sqx = rhs.x * rhs.x;
|
||||
float sqy = rhs.y * rhs.y;
|
||||
float sqz = rhs.z * rhs.z;
|
||||
roll = Math::Degrees(unlocked ? std::atan2(-m.At(1, 2), m.At(2, 2)) : std::atan2(m.At(2, 1), m.At(1, 1)));
|
||||
pitch = Math::Degrees(std::asin(sy));
|
||||
yaw = Math::Degrees(unlocked ? std::atan2(-m.At(0, 1), m.At(0, 0)) : 0);
|
||||
}
|
||||
}
|
@@ -109,27 +109,8 @@ namespace J3ML::LinearAlgebra {
|
||||
//this->elems[2][2] = r3.z;
|
||||
}
|
||||
|
||||
Matrix3x3::Matrix3x3(const Quaternion &orientation) {
|
||||
SetRotatePart(orientation);
|
||||
}
|
||||
|
||||
Matrix3x3::Matrix3x3(const EulerAngle &orientation) {
|
||||
auto sa = std::sin(orientation.pitch);
|
||||
auto ca = std::cos(orientation.pitch);
|
||||
auto sb = std::sin(orientation.roll);
|
||||
auto cb = std::cos(orientation.roll);
|
||||
auto sh = std::sin(orientation.yaw);
|
||||
auto ch = std::cos(orientation.yaw);
|
||||
|
||||
At(0, 0) = ch*ca;
|
||||
At(0, 1) = -ch*sa*cb + sh*sh;
|
||||
At(0, 2) = ch*sa*sb + sh*cb;
|
||||
At(1, 0) = sa;
|
||||
At(1, 1) = ca*cb;
|
||||
At(1, 2) = -ca*cb;
|
||||
At(2, 0) = -sh*ca;
|
||||
At(2, 1) = sh*sa*cb + ch*sb;
|
||||
At(2, 2) = -sh*sa*sb + ch*cb;
|
||||
Matrix3x3::Matrix3x3(const Quaternion& orientation) {
|
||||
*this = Matrix3x3(EulerAngleXYZ(orientation));
|
||||
}
|
||||
|
||||
float Matrix3x3::Determinant() const {
|
||||
@@ -207,28 +188,7 @@ namespace J3ML::LinearAlgebra {
|
||||
};
|
||||
}
|
||||
|
||||
Quaternion Matrix3x3::ToQuat() const {
|
||||
auto m00 = At(0,0);
|
||||
auto m01 = At(0, 1);
|
||||
auto m02 = At(0, 2);
|
||||
auto m10 = At(1,0);
|
||||
auto m11 = At(1, 1);
|
||||
auto m12 = At(1, 2);
|
||||
auto m20 = At(2,0);
|
||||
auto m21 = At(2, 1);
|
||||
auto m22 = At(2, 2);
|
||||
|
||||
auto w = std::sqrt(1.f + m00 + m11 + m22) / 2.f;
|
||||
float w4 = (4.f * w);
|
||||
return {
|
||||
(m21 - m12) / w4,
|
||||
(m02 - m20) / w4,
|
||||
(m10 - m01) / w4,
|
||||
w
|
||||
};
|
||||
}
|
||||
|
||||
void Matrix3x3::SetRotatePart(const Vector3 &a, float angle) {
|
||||
void Matrix3x3::SetRotatePart(const Vector3& a, float angle) {
|
||||
float s = std::sin(angle);
|
||||
float c = std::cos(angle);
|
||||
|
||||
@@ -371,9 +331,9 @@ namespace J3ML::LinearAlgebra {
|
||||
return m;
|
||||
}
|
||||
|
||||
Matrix3x3 Matrix3x3::FromScale(float sx, float sy, float sz) {
|
||||
Matrix3x3 Matrix3x3::FromScale(float sin_roll, float sy, float sz) {
|
||||
Matrix3x3 m;
|
||||
m.At(0,0) = sx;
|
||||
m.At(0,0) = sin_roll;
|
||||
m.At(1,1) = sy;
|
||||
m.At(2,2) = sz;
|
||||
return m;
|
||||
@@ -992,25 +952,12 @@ namespace J3ML::LinearAlgebra {
|
||||
|
||||
bool Matrix3x3::TryConvertToQuat(Quaternion &q) const {
|
||||
if (IsColOrthogonal() && HasUnitaryScale() && !HasNegativeScale()) {
|
||||
q = ToQuat();
|
||||
q = Quaternion(*this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EulerAngle Matrix3x3::ToEulerAngle() const {
|
||||
auto heading = std::atan2(-At(2, 0), At(0, 0));
|
||||
auto attitude = std::asin(At(1, 0));
|
||||
auto bank = std::atan2(-At(1,2), At(1,1));
|
||||
if (At(1, 0) == 1 || At(1, 0) == -1) // North Pole || South Pole
|
||||
{
|
||||
heading = std::atan2(At(0, 2), At(2,2));
|
||||
bank = 0;
|
||||
}
|
||||
|
||||
return {attitude, heading, bank};
|
||||
}
|
||||
|
||||
void Matrix3x3::BatchTransform(Vector3 *pointArray, int numPoints, int stride) const {
|
||||
assert(pointArray || numPoints == 0);
|
||||
assert(stride >= (int)sizeof(Vector3));
|
||||
@@ -1119,6 +1066,25 @@ namespace J3ML::LinearAlgebra {
|
||||
return m;
|
||||
}
|
||||
|
||||
Matrix3x3::Matrix3x3(const EulerAngleXYZ& e) {
|
||||
float cos_roll = std::cos(Math::Radians(e.roll));
|
||||
float sin_roll = std::sin(Math::Radians(e.roll));
|
||||
float cos_pitch = std::cos(Math::Radians(e.pitch));
|
||||
float sin_pitch = std::sin(Math::Radians(e.pitch));
|
||||
float cos_yaw = std::cos(Math::Radians(e.yaw));
|
||||
float sin_yaw = std::sin(Math::Radians(e.yaw));
|
||||
|
||||
Matrix3x3 m;
|
||||
m.SetRow(0, Vector3(cos_pitch * cos_yaw, sin_roll * sin_pitch *cos_yaw + cos_roll * sin_yaw, -cos_roll * sin_pitch *cos_yaw + sin_roll * sin_yaw));
|
||||
m.SetRow(1, Vector3(-cos_pitch * sin_yaw, -sin_roll * sin_pitch * sin_yaw + cos_roll *cos_yaw, cos_roll * sin_pitch * sin_yaw + sin_roll *cos_yaw));
|
||||
m.SetRow(2, Vector3(sin_pitch, -sin_roll * cos_pitch, cos_roll * cos_pitch));
|
||||
*this = m;
|
||||
}
|
||||
|
||||
Matrix3x3::Matrix3x3(const AxisAngle& orientation) {
|
||||
*this = Matrix3x3(Quaternion(orientation));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@@ -86,10 +86,6 @@ namespace J3ML::LinearAlgebra {
|
||||
Set3x3Part(Matrix3x3(orientation));
|
||||
}
|
||||
|
||||
Matrix4x4::Matrix4x4(const EulerAngle &orientation) {
|
||||
Set3x3Part(Matrix3x3(orientation));
|
||||
}
|
||||
|
||||
void Matrix4x4::SetTranslatePart(float translateX, float translateY, float translateZ) {
|
||||
elems[0][3] = translateX;
|
||||
elems[1][3] = translateY;
|
||||
@@ -777,19 +773,6 @@ namespace J3ML::LinearAlgebra {
|
||||
};
|
||||
}
|
||||
|
||||
EulerAngle Matrix4x4::ToEulerAngle() const {
|
||||
auto heading = std::atan2(-At(2, 0), At(0, 0));
|
||||
auto attitude = std::asin(At(1, 0));
|
||||
auto bank = std::atan2(-At(1,2), At(1,1));
|
||||
if (At(1, 0) == 1 || At(1, 0) == -1) // North Pole || South Pole
|
||||
{
|
||||
heading = std::atan2(At(0, 2), At(2,2));
|
||||
bank = 0;
|
||||
}
|
||||
|
||||
return {attitude, heading, bank};
|
||||
}
|
||||
|
||||
bool Matrix4x4::InverseOrthogonalUniformScale() {
|
||||
assert(!ContainsProjection());
|
||||
assert(IsColOrthogonal(1e-3f));
|
||||
|
@@ -1,23 +1,46 @@
|
||||
#include <J3ML/LinearAlgebra/Vector3.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector4.hpp>
|
||||
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
|
||||
#include <J3ML/LinearAlgebra/Matrix4x4.hpp>
|
||||
#include <J3ML/LinearAlgebra/Quaternion.hpp>
|
||||
#include <J3ML/LinearAlgebra/AxisAngle.hpp>
|
||||
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector4.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector3.hpp>
|
||||
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
const Quaternion Quaternion::Identity = Quaternion(0.f, 0.f, 0.f, 1.f);
|
||||
const Quaternion Quaternion::NaN = Quaternion(NAN, NAN, NAN, NAN);
|
||||
|
||||
Quaternion Quaternion::operator-() const
|
||||
{
|
||||
Quaternion Quaternion::operator-() const {
|
||||
return {-x, -y, -z, -w};
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(const Matrix3x3 &rotationMtrx) {}
|
||||
Quaternion::Quaternion(const Matrix3x3& ro_mat) {
|
||||
auto m = ro_mat.Transposed();
|
||||
auto m00 = m.At(0,0);
|
||||
auto m01 = m.At(0, 1);
|
||||
auto m02 = m.At(0, 2);
|
||||
auto m10 = m.At(1,0);
|
||||
auto m11 = m.At(1, 1);
|
||||
auto m12 = m.At(1, 2);
|
||||
auto m20 = m.At(2,0);
|
||||
auto m21 = m.At(2, 1);
|
||||
auto m22 = m.At(2, 2);
|
||||
|
||||
Quaternion::Quaternion(const Matrix4x4 &rotationMtrx) {}
|
||||
auto field_w = std::sqrt(1.f + m00 + m11 + m22) / 2.f;
|
||||
float w4 = (4.f * field_w);
|
||||
|
||||
x = (m21 - m12) / w4;
|
||||
y = (m02 - m20) / w4;
|
||||
z = (m10 - m01) / w4;
|
||||
w = field_w;
|
||||
Normalize();
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(const Matrix4x4& ro_mat) {
|
||||
auto q = Quaternion(ro_mat.GetRotatePart());
|
||||
x = q.x; y = q.y; z = q.z; w = q.w;
|
||||
}
|
||||
|
||||
Vector3 Quaternion::WorldX() const { return Transform(1.f, 0.f, 0.f); }
|
||||
|
||||
@@ -50,22 +73,6 @@ namespace J3ML::LinearAlgebra {
|
||||
return (*this * (t - 1.f) + b * t).Normalized();
|
||||
}
|
||||
|
||||
void Quaternion::SetFromAxisAngle(const Vector3 &axis, float angle) {
|
||||
float sinz, cosz;
|
||||
sinz = std::sin(angle*0.5f);
|
||||
cosz = std::cos(angle*0.5f);
|
||||
|
||||
x = axis.x * sinz;
|
||||
y = axis.y * sinz;
|
||||
z = axis.z * sinz;
|
||||
w = cosz;
|
||||
}
|
||||
|
||||
void Quaternion::SetFromAxisAngle(const Vector4 &axis, float angle)
|
||||
{
|
||||
SetFromAxisAngle(Vector3(axis.x, axis.y, axis.z), angle);
|
||||
}
|
||||
|
||||
Quaternion Quaternion::operator*(float scalar) const {
|
||||
return Quaternion(x * scalar, y * scalar, z * scalar, w * scalar);
|
||||
}
|
||||
@@ -82,8 +89,6 @@ namespace J3ML::LinearAlgebra {
|
||||
|
||||
Quaternion Quaternion::operator+() const { return *this; }
|
||||
|
||||
Quaternion::Quaternion() {}
|
||||
|
||||
Quaternion::Quaternion(float X, float Y, float Z, float W) : x(X), y(Y), z(Z), w(W) {}
|
||||
|
||||
// TODO: implement
|
||||
@@ -148,20 +153,6 @@ namespace J3ML::LinearAlgebra {
|
||||
return (*this * (a * sign) + q2 * b).Normalized();
|
||||
}
|
||||
|
||||
AxisAngle Quaternion::ToAxisAngle() const {
|
||||
float halfAngle = std::acos(w);
|
||||
float angle = halfAngle * 2.f;
|
||||
// TODO: Can Implement Fast Inverted Sqrt Here
|
||||
float reciprocalSinAngle = 1.f / std::sqrt(1.f - w*w);
|
||||
|
||||
Vector3 axis = {
|
||||
x*reciprocalSinAngle,
|
||||
y*reciprocalSinAngle,
|
||||
z*reciprocalSinAngle
|
||||
};
|
||||
return AxisAngle(axis, angle);
|
||||
}
|
||||
|
||||
float Quaternion::AngleBetween(const Quaternion &target) const {
|
||||
Quaternion delta = target / *this;
|
||||
return delta.Normalized().Angle();
|
||||
@@ -212,47 +203,13 @@ namespace J3ML::LinearAlgebra {
|
||||
return Vector3(x, y, z) * rcpSinAngle;
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(const Vector3 &rotationAxis, float rotationAngleRadians) {
|
||||
SetFromAxisAngle(rotationAxis, rotationAngleRadians);
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(const Vector4 &rotationAxis, float rotationAngleRadians) {
|
||||
SetFromAxisAngle(rotationAxis, rotationAngleRadians);
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(const AxisAngle &angle) {
|
||||
Quaternion::Quaternion(const AxisAngle& angle) {
|
||||
double s = std::sin(angle.angle / 2);
|
||||
x = angle.axis.x * s;
|
||||
y = angle.axis.y * s;
|
||||
z = angle.axis.z * s;
|
||||
w = std::cos(angle.angle / 2);
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(const EulerAngle &angle) {
|
||||
// Abbreviations for the various angular functions
|
||||
double cr = std::cos(angle.roll * 0.5);
|
||||
double sr = std::sin(angle.roll * 0.5);
|
||||
double cp = std::cos(angle.pitch * 0.5);
|
||||
double sp = std::sin(angle.pitch * 0.5);
|
||||
double cy = std::cos(angle.yaw * 0.5);
|
||||
double sy = std::sin(angle.yaw * 0.5);
|
||||
|
||||
w = cr * cp * cy + sr * sp * sy;
|
||||
x = sr * cp * cy - cr * sp * sy;
|
||||
y = cr * sp * cy + sr * cp * sy;
|
||||
z = cr * cp * sy - sr * sp * cy;
|
||||
}
|
||||
|
||||
void Quaternion::SetFrom(const AxisAngle &angle) {
|
||||
double s = std::sin(angle.angle / 2);
|
||||
x = angle.axis.x * s;
|
||||
y = angle.axis.y * s;
|
||||
z = angle.axis.z * s;
|
||||
w = std::cos(angle.angle / 2);
|
||||
}
|
||||
|
||||
EulerAngle Quaternion::ToEulerAngle() const {
|
||||
return EulerAngle(*this);
|
||||
Normalize();
|
||||
}
|
||||
|
||||
Quaternion Quaternion::RandomRotation(RNG &rng) {
|
||||
@@ -271,16 +228,16 @@ namespace J3ML::LinearAlgebra {
|
||||
return Quaternion::Identity;
|
||||
}
|
||||
|
||||
float Quaternion::Normalize() {
|
||||
bool Quaternion::Normalize() {
|
||||
float length = Length();
|
||||
if (length < 1e-4f)
|
||||
return 0.f;
|
||||
return false;
|
||||
float rcpLength = 1.f / length;
|
||||
x *= rcpLength;
|
||||
y *= rcpLength;
|
||||
z *= rcpLength;
|
||||
w *= rcpLength;
|
||||
return length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Quaternion::IsNormalized(float epsilon) const {
|
||||
@@ -325,9 +282,10 @@ namespace J3ML::LinearAlgebra {
|
||||
|
||||
Quaternion Quaternion::LookAt(const Vector3 &localForward, const Vector3 &targetDirection, const Vector3 &localUp,
|
||||
const Vector3 &worldUp) {
|
||||
return Matrix3x3::LookAt(localForward, targetDirection, localUp, worldUp).ToQuat();
|
||||
return Quaternion(Matrix3x3::LookAt(localForward, targetDirection, localUp, worldUp));
|
||||
}
|
||||
|
||||
/*
|
||||
Quaternion Quaternion::RotateX(float angleRadians) {
|
||||
return {{1,0,0}, angleRadians};
|
||||
}
|
||||
@@ -343,6 +301,7 @@ namespace J3ML::LinearAlgebra {
|
||||
Quaternion Quaternion::RotateAxisAngle(const AxisAngle &axisAngle) {
|
||||
return {axisAngle.axis, axisAngle.angle};
|
||||
}
|
||||
*/
|
||||
|
||||
Quaternion Quaternion::RotateFromTo(const Vector3 &sourceDirection, const Vector3 &targetDirection) {
|
||||
assert(sourceDirection.IsNormalized());
|
||||
@@ -369,14 +328,6 @@ namespace J3ML::LinearAlgebra {
|
||||
return Quaternion::RotateFromTo(sourceDirection.XYZ(), targetDirection.XYZ());
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(const float *data) {
|
||||
assert(data);
|
||||
x = data[0];
|
||||
y = data[1];
|
||||
z = data[2];
|
||||
w = data[3];
|
||||
}
|
||||
|
||||
Quaternion Quaternion::Lerp(const Quaternion &source, const Quaternion &target, float t) { return source.Lerp(target, t);}
|
||||
|
||||
Quaternion Quaternion::Slerp(const Quaternion &source, const Quaternion &target, float t) { return source.Slerp(target, t);}
|
||||
@@ -401,4 +352,28 @@ namespace J3ML::LinearAlgebra {
|
||||
float Quaternion::LengthSquared() const { return x*x + y*y + z*z + w*w;}
|
||||
|
||||
float Quaternion::Length() const { return std::sqrt(LengthSquared()); }
|
||||
|
||||
Quaternion::Quaternion(const EulerAngleXYZ& rhs) {
|
||||
float cos_roll = Math::Cos(0.5f * Math::Radians(rhs.roll));
|
||||
float sin_roll = Math::Sin(0.5f * Math::Radians(rhs.roll));
|
||||
|
||||
float cos_pitch = Math::Cos(0.5f * Math::Radians(rhs.pitch));
|
||||
float sin_pitch = Math::Sin(0.5f * Math::Radians(rhs.pitch));
|
||||
|
||||
float cos_yaw = Math::Cos(0.5f * Math::Radians(rhs.yaw));
|
||||
float sin_yaw = Math::Sin(0.5f * Math::Radians(rhs.yaw));
|
||||
|
||||
x = cos_roll * sin_pitch * sin_yaw + sin_roll * cos_pitch * cos_yaw;
|
||||
y = -sin_roll * cos_pitch * sin_yaw + cos_roll * sin_pitch * cos_yaw;
|
||||
z = cos_roll * cos_pitch * sin_yaw + sin_roll * sin_pitch * cos_yaw;
|
||||
w = -sin_roll * sin_pitch * sin_yaw + cos_roll * cos_pitch * cos_yaw;
|
||||
Normalize();
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(const Quaternion& rhs) {
|
||||
x = rhs.x;
|
||||
y = rhs.y;
|
||||
z = rhs.z;
|
||||
w = rhs.w;
|
||||
}
|
||||
}
|
@@ -10,6 +10,7 @@
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
const Vector3 Vector3::Zero = {0,0,0};
|
||||
const Vector3 Vector3::One = {1, 1, 1};
|
||||
const Vector3 Vector3::Up = {0, -1, 0};
|
||||
const Vector3 Vector3::Down = {0, 1, 0};
|
||||
const Vector3 Vector3::Left = {-1, 0, 0};
|
||||
|
25
tests/LinearAlgebra/AxisAngleTests.hpp
Normal file
25
tests/LinearAlgebra/AxisAngleTests.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <jtest/jtest.hpp>
|
||||
#include <jtest/Unit.hpp>
|
||||
|
||||
jtest::Unit AxisAngleUnit {"AxisAngle"};
|
||||
|
||||
namespace AxisAngleTests {
|
||||
inline void Define() {
|
||||
using namespace jtest;
|
||||
|
||||
AxisAngleUnit += Test("From_Quaternion", [] {
|
||||
AxisAngle expected_result({0.3860166, 0.4380138, 0.8118714}, 0.6742209);
|
||||
Quaternion q(0.1276794, 0.1448781, 0.2685358, 0.9437144);
|
||||
|
||||
AxisAngle from_quaternion(q);
|
||||
|
||||
jtest::check(Math::EqualAbs(expected_result.axis.x, from_quaternion.axis.x, 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.axis.y, from_quaternion.axis.y, 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.axis.z, from_quaternion.axis.z, 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.angle, from_quaternion.angle, 1e-6f));
|
||||
});
|
||||
}
|
||||
inline void Run() {
|
||||
AxisAngleUnit.RunAll();
|
||||
}
|
||||
}
|
@@ -5,14 +5,21 @@
|
||||
#include <jtest/jtest.hpp>
|
||||
#include <jtest/Unit.hpp>
|
||||
|
||||
jtest::Unit EulerAngleUnit {"EulerAngle"};
|
||||
jtest::Unit EulerAngleUnit {"EulerAngle_XYZ"};
|
||||
|
||||
namespace EulerAngleTests {
|
||||
inline void Define() {
|
||||
using namespace jtest;
|
||||
|
||||
EulerAngleUnit += Test("Not Implemented", [] {
|
||||
throw("Not Implemented");
|
||||
EulerAngleUnit += Test("From_Quaternion", [] {
|
||||
EulerAngleXYZ expected_result(-170, 88, -160);
|
||||
Quaternion q(0.1840604, 0.6952024, 0.1819093, 0.6706149);
|
||||
|
||||
EulerAngleXYZ from_quaternion(q);
|
||||
|
||||
jtest::check(Math::EqualAbs(Math::Radians(expected_result.roll), Math::Radians(from_quaternion.roll), 1e-5f));
|
||||
jtest::check(Math::EqualAbs(Math::Radians(expected_result.pitch), Math::Radians(from_quaternion.pitch), 1e-5f));
|
||||
jtest::check(Math::EqualAbs(Math::Radians(expected_result.yaw), Math::Radians(from_quaternion.yaw), 1e-5f));
|
||||
});
|
||||
}
|
||||
inline void Run() {
|
||||
|
@@ -11,6 +11,42 @@ namespace Matrix3x3Tests
|
||||
using namespace jtest;
|
||||
using namespace J3ML::LinearAlgebra;
|
||||
|
||||
Matrix3x3Unit += Test("AngleTypeRound-TripConversion", [] {
|
||||
EulerAngleXYZ expected_result(8, 60, -27);
|
||||
|
||||
Matrix3x3 m(expected_result);
|
||||
AxisAngle a(expected_result);
|
||||
Quaternion q(a);
|
||||
Matrix3x3 m2(q);
|
||||
Quaternion q2(m2);
|
||||
AxisAngle a2(q2);
|
||||
EulerAngleXYZ round_trip(a2);
|
||||
|
||||
jtest::check(Math::EqualAbs(Math::Radians(expected_result.roll), Math::Radians(round_trip.roll), 1e-6f));
|
||||
jtest::check(Math::EqualAbs(Math::Radians(expected_result.pitch), Math::Radians(round_trip.pitch), 1e-6f));
|
||||
jtest::check(Math::EqualAbs(Math::Radians(expected_result.yaw), Math::Radians(round_trip.yaw), 1e-6f));
|
||||
});
|
||||
|
||||
Matrix3x3Unit += Test("From_EulerAngleXYZ", []{
|
||||
Matrix3x3 expected_result(Vector3(0.4455033, 0.2269952, 0.8660254),
|
||||
Vector3(-0.3421816, 0.9370536, -0.0695866),
|
||||
Vector3(-0.8273081, -0.2653369, 0.4951340)
|
||||
);
|
||||
|
||||
EulerAngleXYZ e(8, 60, -27);
|
||||
Matrix3x3 from_euler(e);
|
||||
|
||||
jtest::check(Math::EqualAbs(expected_result.At(0, 0), from_euler.At(0, 0), 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.At(0, 1), from_euler.At(0, 1), 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.At(0, 2), from_euler.At(0, 2), 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.At(1, 0), from_euler.At(1, 0), 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.At(1, 1), from_euler.At(1, 1), 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.At(1, 2), from_euler.At(1, 2), 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.At(2, 0), from_euler.At(2, 0), 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.At(2, 1), from_euler.At(2, 1), 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.At(2, 2), from_euler.At(2, 2), 1e-6f));
|
||||
});
|
||||
|
||||
Matrix3x3Unit += Test("Add_Unary", []
|
||||
{
|
||||
Matrix3x3 m(1,2,3, 4,5,6, 7,8,9);
|
||||
|
@@ -111,20 +111,6 @@ namespace Matrix4x4Tests {
|
||||
Matrix4x4Unit += Test("InverseOrthonormal", [] {});
|
||||
Matrix4x4Unit += Test("DeterminantCorrectness", [] { });
|
||||
Matrix4x4Unit += Test("MulMat3x3", [] {});
|
||||
|
||||
|
||||
Matrix4x4Unit += Test("AngleTypeRoundtripConversion", [] {
|
||||
Matrix4x4 matrix;
|
||||
EulerAngle a(Math::Radians(45), Math::Radians(45), Math::Radians(45));
|
||||
Quaternion q(a);
|
||||
matrix.SetRotatePart(q);
|
||||
//matrix.SetRotatePartX(a.pitch);
|
||||
//matrix.SetRotatePartY(a.yaw);
|
||||
//matrix.SetRotatePartZ(a.roll);
|
||||
EulerAngle fromMatrix = matrix.GetRotatePart().ToQuat().ToEulerAngle();
|
||||
jtest::check(a == fromMatrix);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
inline void Run() {
|
||||
|
@@ -4,10 +4,14 @@
|
||||
#include <jtest/jtest.hpp>
|
||||
#include <jtest/Unit.hpp>
|
||||
#include <J3ML/LinearAlgebra/Quaternion.hpp>
|
||||
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
|
||||
#include <J3ML/Algorithm/RNG.hpp>
|
||||
|
||||
jtest::Unit QuaternionUnit {"Quaternion"};
|
||||
namespace QuaternionTests {
|
||||
|
||||
// This is here to check the accuracy of the Slerp inside the Quaternion class.
|
||||
// Although you don't jtest::check anything :shrug: - Redacted.
|
||||
Quaternion PreciseSlerp(const Quaternion &a, const Quaternion& b, float t)
|
||||
{
|
||||
double angle = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
@@ -69,6 +73,28 @@ namespace QuaternionTests {
|
||||
Quaternion lerp = q.Lerp(q2, t);
|
||||
}
|
||||
});
|
||||
QuaternionUnit += Test("From_EulerAngleXYZ", [] {
|
||||
Quaternion expected_result(0.1819093, 0.6706149, 0.1840604, 0.6952024);
|
||||
EulerAngleXYZ e(10, 88, 20);
|
||||
Quaternion from_euler(e);
|
||||
|
||||
jtest::check(Math::EqualAbs(expected_result.x, from_euler.x, 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.y, from_euler.y, 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.z, from_euler.z, 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.w, from_euler.w, 1e-6f));
|
||||
});
|
||||
|
||||
QuaternionUnit += Test("From_AxisAngle", [] {
|
||||
Quaternion expected_result(0.0579133, 0.0782044, 0.1765667, 0.9794664);
|
||||
AxisAngle a({0.2872573, 0.3879036, 0.8757934}, 0.4059981);
|
||||
Quaternion from_axis(a);
|
||||
|
||||
jtest::check(Math::EqualAbs(expected_result.x, from_axis.x, 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.y, from_axis.y, 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.z, from_axis.z, 1e-6f));
|
||||
jtest::check(Math::EqualAbs(expected_result.w, from_axis.w, 1e-6f));
|
||||
});
|
||||
|
||||
QuaternionUnit += Test("Mat4x4Conversion", [] { throw("Not Implemented"); });
|
||||
QuaternionUnit += Test("MulOpQuat", [] { throw("Not Implemented"); });
|
||||
QuaternionUnit += Test("DivOpQuat", [] { throw("Not Implemented"); });
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include "Geometry/FrustumTests.hpp"
|
||||
|
||||
#include "LinearAlgebra/EulerAngleTests.hpp"
|
||||
#include "LinearAlgebra/AxisAngleTests.hpp"
|
||||
#include "LinearAlgebra/Matrix2x2Tests.hpp"
|
||||
#include "LinearAlgebra/Matrix3x3Tests.hpp"
|
||||
#include "LinearAlgebra/Matrix4x4Tests.hpp"
|
||||
@@ -64,10 +65,11 @@ namespace LinearAlgebraTests
|
||||
{
|
||||
void Define()
|
||||
{
|
||||
EulerAngleTests::Define();
|
||||
Vector2Tests::Define();
|
||||
Vector3Tests::Define();
|
||||
Vector4Tests::Define();
|
||||
AxisAngleTests::Define();
|
||||
EulerAngleTests::Define();
|
||||
QuaternionTests::Define();
|
||||
Matrix2x2Tests::Define();
|
||||
Matrix3x3Tests::Define();
|
||||
@@ -76,10 +78,11 @@ namespace LinearAlgebraTests
|
||||
}
|
||||
void Run()
|
||||
{
|
||||
EulerAngleTests::Run();
|
||||
Vector2Tests::Run();
|
||||
Vector3Tests::Run();
|
||||
Vector4Tests::Run();
|
||||
AxisAngleTests::Run();
|
||||
EulerAngleTests::Run();
|
||||
QuaternionTests::Run();
|
||||
Matrix2x2Tests::Run();
|
||||
Matrix3x3Tests::Run();
|
||||
|
Reference in New Issue
Block a user