Files
2021-08-09 16:40:35 -07:00

257 lines
15 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
// include required headers
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector2.h>
#include "StandardHeaders.h"
#include "FastMath.h"
#include "Vector.h"
#include "Algorithms.h"
namespace MCore
{
/**
* The dual quaternion class.
* Dual quaternions contain two internal vectors. The real and dual part. They behave very similar to regular quaternions in usage.
* The difference is that the dual quaternion can represent both a rotation and translation or displacement.
* The dual quaternion consists of a real part, which you can see as the rotation quaternion, and a dual part, which you can see as the translation part.
* One of the advantages of dual quaternions is that they can be used to replace the linear skin deformations with a volume preserving method.
* Using dual quaternions in skinning fixes issues such as the famous candy-wrapper effect. It handles twisting of bones much nicer compared to the linear method.
* This is often referred to as dual quaternion skinning.
*/
class MCORE_API DualQuaternion
{
public:
/**
* The default constructor.
* This automatically initializes the dual quaternion to identity.
*/
MCORE_INLINE DualQuaternion()
: m_real(0.0f, 0.0f, 0.0f, 1.0f)
, m_dual(0.0f, 0.0f, 0.0f, 0.0f) {}
/**
* Copy constructor.
* @param other The dual quaternion to copy the data from.
*/
MCORE_INLINE DualQuaternion(const DualQuaternion& other)
: m_real(other.m_real)
, m_dual(other.m_dual) {}
/**
* Extended constructor.
* @param real The real part, which is the rotation part.
* @param dual The dual part, which is you can see as the translation part.
* @note Do not directly put in the translation into the dual part, if you want to convert from a rotation and translation, please use the other constructor
* or use the FromRotationTranslation method.
*/
MCORE_INLINE DualQuaternion(const AZ::Quaternion& real, const AZ::Quaternion& dual)
: m_real(real)
, m_dual(dual) {}
/**
* Constructor which takes a matrix as input parameter.
* This converts the rotation of the specified matrix into a quaternion. Please keep in mind that the matrix may NOT contain
* any scaling, so if it does, please normalize your matrix first!
* @param matrix The matrix to initialize the quaternion from.
*/
MCORE_INLINE DualQuaternion(const AZ::Transform& transform) { FromTransform(transform); }
/**
* Extended constructor, which initializes this dual quaternion from a rotation and translation.
* @param rotation The rotation quaternion, which does not need to be normalized, unless you want this to be a normalized dual quaternion.
* @param translation The translation vector.
*/
MCORE_INLINE DualQuaternion(const AZ::Quaternion& rotation, const AZ::Vector3& translation);
/**
* Set the real and dual part of the dual quaternion.
* @param real The real part, which represents the rotation.
* @param dual The dual part, which represents the translation.
* @note Please keep in mind that you should not set the translation directly into the dual part. If you want to initialize the dual quaternion from
* a rotation and translation, please use the special constructor for this, or the FromRotationTranslation method.
*/
MCORE_INLINE void Set(const AZ::Quaternion& real, const AZ::Quaternion& dual) { m_real = real; m_dual = dual; }
/**
* Normalize the dual quaternion.
* @result A reference to this same quaternion, but now normalized.
* @note Please keep in mind that zero length quaternions will result in a division by zero!
*/
DualQuaternion& Normalize();
/**
* Calculate the normalized version of this dual quaternion.
* @result A copy of this dual quaternion, but then normalized.
* @note Please keep in mind that zero length quaternions will result in a division by zero!
*/
MCORE_INLINE DualQuaternion Normalized() const { DualQuaternion result(*this); result.Normalize(); return result; }
/**
* Set the dual quaternion to identity, so that it has basically no transform.
* The default constructor already puts the dual quaternion in its identity transform.
* @result A reference to this quaternion, but now having an identity transform.
*/
MCORE_INLINE DualQuaternion& Identity() { m_real = AZ::Quaternion::CreateIdentity(); m_dual.Set(0.0f, 0.0f, 0.0f, 0.0f); return *this; }
/**
* Get the dot product between the two dual quaternions.
* This basically performs two dot products: One on the real part and one on the dual part.
* That means that it also results in two float values, which is why a Vector2 is returned.
* @param other The other dual quaternion to perform the dot product with.
* @result A 2D vector containing the result of the dot products. The x component contains the result of the dot between the real part and the y component
* contains the result of the dot product between the dual parts.
*/
MCORE_INLINE AZ::Vector2 Dot(const DualQuaternion& other) const { return AZ::Vector2(m_real.Dot(other.m_real), m_dual.Dot(other.m_dual)); }
/**
* Calculate the length of the dual quaternion.
* This results in a 2D vector, because it calculates the length for both the real and dual parts.
* The result of the real part will be stored in the x component of the 2D vector, and the result of the dual part will be stored in the y component.
* @result The 2D vector containing the length of the real and dual part.
*/
MCORE_INLINE AZ::Vector2 Length() const { const float realLen = m_real.GetLength(); return AZ::Vector2(realLen, m_real.Dot(m_dual) / realLen); }
/**
* Inverse this dual quaternion.
* @result A reference to this dual quaternion, but now inversed.
*/
DualQuaternion& Inverse();
/**
* Calculate an inversed version of this dual quaternion.
* @result A copy of this dual quaternion, but then inversed.
*/
DualQuaternion Inversed() const;
/**
* Conjugate this dual quaternion.
* @result A reference to this dual quaternion, but now conjugaged.
* @note If you want to inverse a unit quaternion, you can use the conjugate instead, as that gives the same result, but is much faster to calculate.
*/
MCORE_INLINE DualQuaternion& Conjugate() { m_real = m_real.GetConjugate(); m_dual = m_dual.GetConjugate(); return *this; }
/**
* Calculate a conjugated version of this dual quaternion.
* @result A copy of this dual quaternion, but conjugated.
* @note If you want to inverse a unit quaternion, you can use the conjugate instead, as that gives the same result, but is much faster to calculate.
*/
MCORE_INLINE DualQuaternion Conjugated() const { return DualQuaternion(m_real.GetConjugate(), m_dual.GetConjugate()); }
/**
* Initialize the current quaternion from a specified matrix.
* Please note that the matrix may not contain any scaling!
* So make sure the matrix has been normalized before, if it contains any scale.
* @param m The matrix to initialize the quaternion from.
*/
MCORE_INLINE void FromTransform(const AZ::Transform& transform) { *this = DualQuaternion::ConvertFromTransform(transform); }
/**
* Initialize this dual quaternion from a rotation and translation.
* @param rot The rotation quaternion.
* @param pos The translation vector.
* @note It is allowed to pass an un-normalized quaternion to the rotation parameter. This however will also result in a non-normalized dual quaternion.
*/
MCORE_INLINE void FromRotationTranslation(const AZ::Quaternion& rot, const AZ::Vector3& pos) { *this = DualQuaternion::ConvertFromRotationTranslation(rot, pos); }
/**
* Convert this dual quaternion into a 4x4 matrix.
* @result A matrix, representing the same transformation as this dual quaternion.
*/
AZ::Transform ToTransform() const;
/**
* Transform a 3D point with this dual quaternion.
* This applies both a rotation and possible translation to the point.
* @param point The 3D point to transform.
* @result The transformed point.
* @note If you want to transform a vector instead of a point, please use the TransformVector method.
*/
MCORE_INLINE AZ::Vector3 TransformPoint(const AZ::Vector3& point) const;
/**
* Transform a 3D vector with this dual quaternion.
* This will apply only the rotation part of this dual quaternion. So it will not apply any displacement caused by the dual part of this dual quaternion.
* You should use this method when transforming normals and tangents for example.
* You can use the TransformPoint method to transform 3D points.
* This vector transform is also faster than the TransformPoint method, as it has to do fewer calculations.
* @param v The input vector.
* @result The transformed vector.
*/
MCORE_INLINE AZ::Vector3 TransformVector(const AZ::Vector3& v) const;
/**
* Extract the rotation and translation from this dual quaternion.
* This method handles non-unit dual quaternions fine. If you are sure your dual quaternion is normalized, you should use
* the faster version of this method, which is called NormalizedToRotationTranslation.
* @param outRot A pointer to the quaternion in which we will store the output rotation.
* @param outPos A pointer to the vector in which we will store the output translation.
*/
void ToRotationTranslation(AZ::Quaternion* outRot, AZ::Vector3* outPos) const;
/**
* Extract the rotation and translation from this dual normalized quaternion.
* This method assumes that this dual quaternion is normalized. If it isn't, the resulting output will be incorrect!
* If you are not sure this quaternion is not normalized, or if you know it is not, please use the ToRotationTranslation method.
* @param outRot A pointer to the quaternion in which we will store the output rotation.
* @param outPos A pointer to the vector in which we will store the output translation.
*/
void NormalizedToRotationTranslation(AZ::Quaternion* outRot, AZ::Vector3* outPos) const;
/**
* Convert a matrix into a quaternion.
* Please keep in mind that the specified matrix may NOT contain any scaling!
* So make sure the matrix has been normalized before, if it contains any scale.
* @param m The matrix to extract the rotation from.
* @result The quaternion, now containing the rotation of the given matrix, in quaternion form.
*/
static DualQuaternion ConvertFromTransform(const AZ::Transform& transform);
/**
* Convert a rotation and translation into a dual quaternion.
* @param rotation The rotation quaternion, which does not need to be normalized.
* @param translation The translation.
* @result A dual quaternion representing the same rotation and translation.
* @note If your input quaternion is not normalized, then the dual quaternion will also not be normalized.
*/
static DualQuaternion ConvertFromRotationTranslation(const AZ::Quaternion& rotation, const AZ::Vector3& translation);
// operators
MCORE_INLINE const DualQuaternion& operator=(const AZ::Transform& transform) { FromTransform(transform); return *this; }
MCORE_INLINE const DualQuaternion& operator=(const DualQuaternion& other) { m_real = other.m_real; m_dual = other.m_dual; return *this; }
MCORE_INLINE DualQuaternion operator-() const { return DualQuaternion(-m_real, -m_dual); }
MCORE_INLINE const DualQuaternion& operator+=(const DualQuaternion& q) { m_real += q.m_real; m_dual += q.m_dual; return *this; }
MCORE_INLINE const DualQuaternion& operator-=(const DualQuaternion& q) { m_real -= q.m_real; m_dual -= q.m_dual; return *this; }
MCORE_INLINE const DualQuaternion& operator*=(const DualQuaternion& q) { const AZ::Quaternion orgReal(m_real); m_real *= q.m_real; m_dual = orgReal * q.m_dual + q.m_real * m_dual; return *this; }
MCORE_INLINE const DualQuaternion& operator*=(float f) { m_real *= f; m_dual *= f; return *this; }
// attributes
AZ::Quaternion m_real; /**< The real value, which you can see as the regular rotation quaternion. */
AZ::Quaternion m_dual; /**< The dual part, which you can see as the translation part. */
};
// operators
MCORE_INLINE DualQuaternion operator*(const DualQuaternion& a, float f) { return DualQuaternion(a.m_real * f, a.m_dual * f); }
MCORE_INLINE DualQuaternion operator*(float f, const DualQuaternion& b) { return DualQuaternion(b.m_real * f, b.m_dual * f); }
MCORE_INLINE DualQuaternion operator+(const DualQuaternion& a, const DualQuaternion& b) { return DualQuaternion(a.m_real + b.m_real, a.m_dual + b.m_dual); }
MCORE_INLINE DualQuaternion operator-(const DualQuaternion& a, const DualQuaternion& b) { return DualQuaternion(a.m_real - b.m_real, a.m_dual - b.m_dual); }
MCORE_INLINE DualQuaternion operator*(const DualQuaternion& a, const DualQuaternion& b)
{
return DualQuaternion(a.m_real * b.m_real, a.m_real * b.m_dual + b.m_real * a.m_dual);
}
// include the inline code
#include "DualQuaternion.inl"
} // namespace MCore