#pragma once #include "EHS.h" #include "Math.h" #include "Mat4.h" #include "Vec3.h" namespace ehs { template class Quat { public: T w; T x; T y; T z; Quat() : w(0), x(0), y(0), z(0) { } Quat(const T w, const T x, const T y, const T z) : w(w), x(x), y(y), z(z) { } Quat(const T yaw, const T pitch, const T roll) : w(0), x(0), y(0), z(0) { T c1 = cos(yaw / 2); T c2 = cos(pitch / 2); T c3 = cos(roll / 2); T s1 = sin(yaw / 2); T s2 = sin(pitch / 2); T s3 = sin(roll / 2); w = c1 * c2 * c3 - s1 * s2 * s3; x = s1 * s2 * c3 + c1 * c2 * s3; y = s1 * c2 * c3 + c1 * s2 * s3; z = c1 * s2 * c3 - s1 * c2 * s3; } explicit Quat(const Vec3& euler) : w(0), x(0), y(0), z(0) { T c1 = cos(euler.x / 2); T c2 = cos(euler.y / 2); T c3 = cos(euler.z / 2); T s1 = sin(euler.x / 2); T s2 = sin(euler.y / 2); T s3 = sin(euler.z / 2); w = c1 * c2 * c3 - s1 * s2 * s3; x = s1 * s2 * c3 + c1 * c2 * s3; y = s1 * c2 * c3 + c1 * s2 * s3; z = c1 * s2 * c3 - s1 * c2 * s3; } Quat(const Vec3& n, const T a) : w(cosf(a / 2)), x(n.x * sinf(a / 2)), y(n.y * sinf(a / 2)), z(n.z * sinf(a / 2)) { } explicit Quat(const Mat4& rotMatrix) : w(0), x(0), y(0), z(0) { ToQuaternion(rotMatrix); } Quat(const Quat& quat) : w(quat.w), x(quat.x), y(quat.y), z(quat.z) { } explicit Quat(const T scalar) : w(scalar), x(scalar), y(scalar), z(scalar) { } Quat& operator=(const Quat& quat) { if (this == &quat) return *this; w = quat.w; x = quat.x; y = quat.y; z = quat.z; return *this; } Quat& operator=(const Mat4& rotMatrix) { ToQuaternion(rotMatrix); return *this; } Quat operator+(const Quat& other) const { return {w + other.w, x + other.x, y + other.y, z + other.z}; } Quat operator-() const { return {-w, -x, -y, -z}; } Quat operator-(const Quat& other) const { return {w - other.w, x - other.x, y - other.y, z - other.z}; } Quat operator*(const T scalar) { return {w * scalar, x * scalar, x * scalar, x * scalar}; } Quat operator*(const Quat& other) { return Quat ( w * other.w - x * other.x - y * other.y - z * other.z, w * other.x + x * other.w + y * other.z - z * other.y, w * other.y - x * other.z + y * other.w + z * other.x, w * other.z + x * other.y - y * other.x + z * other.w ); } Vec3 operator*(const Vec3& vect) { Quat tmp(0, vect[0], vect[1], vect[2]); Vec3 tmpVect(x, y, z); Vec3 vcV = tmpVect.CrossProduct(vect); return vect + vcV * (2 * w) + tmpVect.CrossProduct(vcV) * 2; } Quat operator^(const T t) { Vec3 n; T a; ToAxisAngle(&n, &a); float at = a * t; return Quat(n, at); } bool operator==(const Quat& quat) const { return w == quat.w && x == quat.x && y == quat.y && z == quat.z; } bool operator!=(const Quat& quat) const { return w != quat.w || x != quat.x || y != quat.y || z == quat.z; } T operator[](const UInt_64 index) const { switch (index) { case 0: return w; case 1: return x; case 2: return y; case 3: return z; default: return w; } } T& operator[](const UInt_64 index) { switch (index) { case 0: return w; case 1: return x; case 2: return y; case 3: return z; default: return w; } } void ToAxisAngle(Vec3* vectAxis, T* flAngle) { Vec3 tmp(x, y, z); if (tmp.GetDis2() < 0.0001f) *vectAxis = Vec3(1, 0, 0); else *vectAxis = tmp.GetNorm(); *flAngle = acosf(w) * 2; *flAngle = Math::Degr(*flAngle); } void ToQuaternion(const Mat4& rotMatrix) { T trace = rotMatrix[0][0] + rotMatrix[1][1] + rotMatrix[2][2]; if (trace > 0) { T s = 0.5f / Math::Sqrt(trace + 1.0f); w = 0.25f / s; x = (rotMatrix[2][1] - rotMatrix[1][2]) * s; y = (rotMatrix[0][2] - rotMatrix[2][0]) * s; z = (rotMatrix[1][0] - rotMatrix[0][1]) * s; } else { if ((rotMatrix[0][0] > rotMatrix[1][1]) && (rotMatrix[0][0] > rotMatrix[2][2])) { T s = 2.0f * Math::Sqrt(1.0f + rotMatrix[0][0] - rotMatrix[1][1] - rotMatrix[2][2]); w = (rotMatrix[2][1] - rotMatrix[1][2]) / s; x = 0.25f * s; y = (rotMatrix[0][1] + rotMatrix[1][0]) / s; z = (rotMatrix[0][2] + rotMatrix[2][0]) / s; } else if (rotMatrix[1][1] > rotMatrix[2][2]) { T s = 2.0f * sqrtf(1.0f + rotMatrix[1][1] - rotMatrix[0][0] - rotMatrix[2][2]); w = (rotMatrix[0][2] - rotMatrix[2][0]) / s; x = (rotMatrix[0][1] + rotMatrix[1][0]) / s; y = 0.25f * s; z = (rotMatrix[1][2] + rotMatrix[2][1]) / s; } else { T s = 2.0f * sqrtf(1.0f + rotMatrix[2][2] - rotMatrix[0][0] - rotMatrix[1][1]); w = (rotMatrix[1][0] - rotMatrix[0][1]) / s; x = (rotMatrix[0][2] + rotMatrix[2][0]) / s; y = (rotMatrix[1][2] + rotMatrix[2][1]) / s; z = 0.25f * s; } } } /* Vec3 ToEulerAngle() const { Vec3 euler; float ysqr = y * y; float t0 = 2 * (w * x + y * z); float t1 = 1 - 2 * (x * x + ysqr); euler.z = std::atan2(t0, t1); float t2 = 2 * (w * y - z * x); t2 = t2 > 1 ? 1 : t2; t2 = t2 < -1 ? -1 : t2; euler.y = std::asin(t2); float t3 = 2 * (w * z + x * y); float t4 = 1 - 2 * (ysqr + z * z); euler.x = std::atan2(t3, t4); return euler; } */ Mat4 ToMatrix() const { Mat4 result; T x2 = x + x; T y2 = y + y; T z2 = z + z; T x2w = x2 * w; T y2w = y2 * w; T z2w = z2 * w; T x2x = x2 * x; T y2x = y2 * x; T z2x = z2 * x; T y2y = y2 * y; T z2y = z2 * y; T z2z = z2 * y; result[0] = T(1) - (y2y + z2z); result[1] = y2x - z2w; result[2] = z2x + y2w; result[3] = T(0); result[4] = y2x + z2w; result[5] = T(1) - (x2x + z2z); result[6] = z2y - x2w; result[7] = T(0); result[8] = z2x - y2w; result[9] = z2y + x2w; result[10] = T(1) - (x2x + y2y); result[11] = T(0); result[12] = T(0); result[13] = T(0); result[14] = T(0); result[15] = T(1); return result; } float GetMagnitude() { return Math::Sqrt(Math::Pow(w, 2) + Math::Pow(x, 2) + Math::Pow(y, 2) + Math::Pow(z, 2)); } Quat GetNormalized() { T mag = GetMagnitude(); return Quat(w / mag, x / mag, y / mag, z / mag); } void Normalize() { T mag = GetMagnitude(); w = w / mag; x = x / mag; y = y / mag; z = z / mag; } T Dot(const Quat& other) const { return w * other.w + x * other.x + y * other.y + z * other.z; } Quat GetConjugate() { return Quat(w, -x, -y, -z); } void Conjugate() { x = -x; y = -y; z = -z; } Quat GetInverse() { return Quat(w, -x, -y, -z); } void Inverse() { x = -x; y = -y; z = -z; } static Quat Slerp(Quat start, Quat finish, const T t) { T cosHalfTheta = start.Dot(finish); if (Math::Abs(cosHalfTheta) >= 1.0f) return start; float halfTheta = Math::ACos(cosHalfTheta); float sinHalfTheta = Math::Sqrt(1.0f - cosHalfTheta * cosHalfTheta); if (Math::Abs(sinHalfTheta) < 0.001f) { return { start.w * 0.5f + finish.w * 0.5f, start.x * 0.5f + finish.x * 0.5f, start.y * 0.5f + finish.y * 0.5f, start.z * 0.5f + finish.z * 0.5f }; } float ratioA = Math::Sin((1 - t) * halfTheta) / sinHalfTheta; float ratioB = Math::Sin(t * halfTheta) / sinHalfTheta; return { start.w * ratioA + finish.w * ratioB, start.x * ratioA + finish.x * ratioB, start.y * ratioA + finish.y * ratioB, start.z * ratioA + finish.z * ratioB }; } }; typedef Quat Quat_f; }