/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #pragma once // include required headers #include #include #include #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() : mReal(0.0f, 0.0f, 0.0f, 1.0f) , mDual(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) : mReal(other.mReal) , mDual(other.mDual) {} /** * 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) : mReal(real) , mDual(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) { mReal = real; mDual = 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() { mReal = AZ::Quaternion::CreateIdentity(); mDual.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(mReal.Dot(other.mReal), mDual.Dot(other.mDual)); } /** * 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 = mReal.GetLength(); return AZ::Vector2(realLen, mReal.Dot(mDual) / 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() { mReal = mReal.GetConjugate(); mDual = mDual.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(mReal.GetConjugate(), mDual.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) { mReal = other.mReal; mDual = other.mDual; return *this; } MCORE_INLINE DualQuaternion operator-() const { return DualQuaternion(-mReal, -mDual); } MCORE_INLINE const DualQuaternion& operator+=(const DualQuaternion& q) { mReal += q.mReal; mDual += q.mDual; return *this; } MCORE_INLINE const DualQuaternion& operator-=(const DualQuaternion& q) { mReal -= q.mReal; mDual -= q.mDual; return *this; } MCORE_INLINE const DualQuaternion& operator*=(const DualQuaternion& q) { const AZ::Quaternion orgReal(mReal); mReal *= q.mReal; mDual = orgReal * q.mDual + q.mReal * mDual; return *this; } MCORE_INLINE const DualQuaternion& operator*=(float f) { mReal *= f; mDual *= f; return *this; } //MCORE_INLINE const DualQuaternion& operator*=(double f) { mReal*=f; mDual*=f; return *this; } // attributes AZ::Quaternion mReal; /**< The real value, which you can see as the regular rotation quaternion. */ AZ::Quaternion mDual; /**< The dual part, which you can see as the translation part. */ }; // operators MCORE_INLINE DualQuaternion operator*(const DualQuaternion& a, float f) { return DualQuaternion(a.mReal * f, a.mDual * f); } MCORE_INLINE DualQuaternion operator*(float f, const DualQuaternion& b) { return DualQuaternion(b.mReal * f, b.mDual * f); } MCORE_INLINE DualQuaternion operator+(const DualQuaternion& a, const DualQuaternion& b) { return DualQuaternion(a.mReal + b.mReal, a.mDual + b.mDual); } MCORE_INLINE DualQuaternion operator-(const DualQuaternion& a, const DualQuaternion& b) { return DualQuaternion(a.mReal - b.mReal, a.mDual - b.mDual); } MCORE_INLINE DualQuaternion operator*(const DualQuaternion& a, const DualQuaternion& b) { return DualQuaternion(a.mReal * b.mReal, a.mReal * b.mDual + b.mReal * a.mDual); } // include the inline code #include "DualQuaternion.inl" } // namespace MCore