#pragma once #include #include "util/helpers/helpers.h" #include "util/math/vector3.h" #define DEG2RAD(__d__) ((__d__ * M_PI) / 180.0f ) #define RAD2DEG(__r__) ((__r__ * 180.0f) / M_PI) template class Quaternion { public: T w; T x; T y; T z; Quaternion(); Quaternion(const T& w, const T& x, const T& y, const T& z); Quaternion(float x, float y, float z); void Assign(float w, float x, float y, float z) { this->w = w; this->x = x; this->y = y; this->z = z; } static Quaternion FromAngleAxis(float inAngle, float inX, float inY, float inZ); std::tuple, Vector3, Vector3> GetRotationMatrix(); std::tuple, Vector3, Vector3> GetTransposedRotationMatrix(); // normalize but keep W void NormalizeXYZ() { const T xyzTargetLength = sqrtf((T)1.0 - w * w); const T lengthScaler = xyzTargetLength / sqrtf(x * x + y * y + z * z); x *= lengthScaler; y *= lengthScaler; z *= lengthScaler; } void NormalizeXYZW() { const T lengthScaler = 1.0f / sqrtf(w * w + x * x + y * y + z * z); w *= lengthScaler; x *= lengthScaler; y *= lengthScaler; z *= lengthScaler; } Vector3 GetVectorX() const { return Vector3( 2.0f * (w * w + x * x) - 1.0f, 2.0f * (x * y - w * z), 2.0f * (x * z + w * y)); } Vector3 GetVectorY() const { return Vector3( 2.0f * (x * y + w * z), 2.0f * (w * w + y * y) - 1.0f, 2.0f * (y * z - w * x) ); } Vector3 GetVectorZ() const { return Vector3( 2.0f * (x * z - w * y), 2.0f * (y * z + w * x), 2.0f * (w * w + z * z) - 1.0f); } Quaternion& operator*=(const Quaternion& rhs) { Assign(w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z, w * rhs.x + x * rhs.w + y * rhs.z - z * rhs.y, w * rhs.y - x * rhs.z + y * rhs.w + z * rhs.x, w * rhs.z + x * rhs.y - y * rhs.x + z * rhs.w); return *this; } Quaternion& operator+=(const Quaternion& rhs) { w += rhs.w; x += rhs.x; y += rhs.y; z += rhs.z; return *this; } friend Quaternion operator*(Quaternion lhs, const Quaternion& rhs) { lhs *= rhs; return lhs; } }; template Quaternion::Quaternion() : w(), x(), y(), z() {} template Quaternion::Quaternion(const T& w, const T& x, const T& y, const T& z) : w(w), x(x), y(y), z(z) {} template Quaternion::Quaternion(float x, float y, float z) { float pitch = DEG2RAD(x); float yaw = DEG2RAD(y); float roll = DEG2RAD(z); float cyaw = cos(0.5f * yaw); float cpitch = cos(0.5f * pitch); float croll = cos(0.5f * roll); float syaw = sin(0.5f * yaw); float spitch = sin(0.5f * pitch); float sroll = sin(0.5f * roll); float cyawcpitch = cyaw * cpitch; float syawspitch = syaw * spitch; float cyawspitch = cyaw * spitch; float syawcpitch = syaw * cpitch; this->w = cyawcpitch * croll + syawspitch * sroll; this->x = cyawspitch * croll + syawcpitch * sroll; this->y = syawcpitch * croll - cyawspitch * sroll; this->z = cyawcpitch * sroll - syawspitch * croll; } template Quaternion Quaternion::FromAngleAxis(float inAngle, float inX, float inY, float inZ) { Quaternion result = Quaternion(cosf(inAngle * 0.5f), inX, inY, inZ); result.NormalizeXYZ(); return result; } template std::tuple, Vector3, Vector3> Quaternion::GetRotationMatrix() { float sqw = w*w; float sqx = x*x; float sqy = y*y; float sqz = z*z; // invs (inverse square length) is only required if quaternion is not already normalised float invs = 1.0f / (sqx + sqy + sqz + sqw); Vector3 v1, v2, v3; v1.x = (sqx - sqy - sqz + sqw) * invs; // since sqw + sqx + sqy + sqz =1/invs*invs v2.y = (-sqx + sqy - sqz + sqw) * invs; v3.z = (-sqx - sqy + sqz + sqw) * invs; float tmp1 = x*y; float tmp2 = z*w; v2.x = 2.0 * (tmp1 + tmp2)*invs; v1.y = 2.0 * (tmp1 - tmp2)*invs; tmp1 = x*z; tmp2 = y*w; v3.x = 2.0 * (tmp1 - tmp2)*invs; v1.z = 2.0 * (tmp1 + tmp2)*invs; tmp1 = y*z; tmp2 = x*w; v3.y = 2.0 * (tmp1 + tmp2)*invs; v2.z = 2.0 * (tmp1 - tmp2)*invs; return std::make_tuple(v1, v2, v3); } template std::tuple, Vector3, Vector3> Quaternion::GetTransposedRotationMatrix() { float sqw = w*w; float sqx = x*x; float sqy = y*y; float sqz = z*z; // invs (inverse square length) is only required if quaternion is not already normalised float invs = 1.0f / (sqx + sqy + sqz + sqw); Vector3 v1, v2, v3; v1.x = (sqx - sqy - sqz + sqw) * invs; // since sqw + sqx + sqy + sqz =1/invs*invs v2.y = (-sqx + sqy - sqz + sqw) * invs; v3.z = (-sqx - sqy + sqz + sqw) * invs; float tmp1 = x*y; float tmp2 = z*w; v1.y = 2.0 * (tmp1 + tmp2)*invs; v2.x = 2.0 * (tmp1 - tmp2)*invs; tmp1 = x*z; tmp2 = y*w; v1.z = 2.0 * (tmp1 - tmp2)*invs; v3.x = 2.0 * (tmp1 + tmp2)*invs; tmp1 = y*z; tmp2 = x*w; v2.z = 2.0 * (tmp1 + tmp2)*invs; v3.y = 2.0 * (tmp1 - tmp2)*invs; return std::make_tuple(v1, v2, v3); } using Quaternionf = Quaternion;