From ad3039186d3a48caeff99fdd3a148e6bfdcfa78e Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Wed, 4 Jan 2017 18:25:12 -0600 Subject: [PATCH] Initial quaternion implementation (#49) * Various changes, and started work on Quaternions * Worked on Quaternions (#46) * Working on quaternions * Finished operations, onto slerp * Working on slerp * Finished slerp implementation, next is NLerp * Cleaned HandmadeMath.cpp * Removed gbmath includes * fixed minor issue. * Fixed function names * Fixed DotQuat call * Did a style check * Double Style Check * Triple Style Check * Fixed all the bullshit I did wrong * Fixed more bs * Added new functions * Finished function implements * Fixed some compile errors * Fixed UTF-8 Error * Added in most operator overrides * Changed instances of HMM_PI to HMM_Pi32 * Fixed a warning * Fixed a major issue with quaternion to matrix function where rotations about the y axis caused strange warping. * Revert "Fixed a warning" This reverts commit 043decab0d47ed7b4cdc5cd155cc78e29d9e09bd. * Revert repo * Fixed Quaternion to Matrix function. * Fixed some prevalent issues, and now pulls triganometric funcitions from the std instead of implementing them. * Fixed tab formatting, added in reverse multiplication function with Quaternions * Removed error suppression cleanup, as it was causing more warnings. . . * Added documentation * Changed ATanf2 to ATan2f * Fixed some typos and added additional documentation (#48) * Add tests for quaternions * Remove (very wrong) quaternion division in favor of multiplying by inverse * Put back newlines at ends of files * Make tweaks for PR * Add assigning arithmetic operators for quaternions * Add NLerp Just to make Jonathan Blow happy: http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/ * Rearrange parameters of HMM_Slerp It now matches HMM_Lerp and HMM_NLerp. * Update README.md --- .gitignore | 1 + HandmadeMath.h | 543 ++++++++++++++++++++++++++++++++++++++++++++-- README.md | 1 + test/hmm_test.cpp | 311 ++++++++++++++++++++++++++ 4 files changed, 837 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 1a4d940..bf14777 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.lo *.o *.obj +*.vs # Precompiled Headers *.gch diff --git a/HandmadeMath.h b/HandmadeMath.h index d2abbc7..1ce290d 100644 --- a/HandmadeMath.h +++ b/HandmadeMath.h @@ -1,5 +1,5 @@ /* - HandmadeMath.h v1.0 + HandmadeMath.h v1.1 This is a single header file with a bunch of useful functions for basic game math operations. @@ -79,8 +79,11 @@ #define HMM_TANF MyTanF #define HMM_EXPF MyExpF #define HMM_LOGF MyLogF + #define HMM_ACOSF MyACosF + #define HMM_ATANF MyATanF + #define HMM_ATAN2F MYATan2F - Provide your own implementations of SinF, CosF, TanF, ExpF and LogF + Provide your own implementations of SinF, CosF, TanF, ACosF, ATanF, ATan2F, ExpF and LogF in EXACTLY one C or C++ file that includes this header, BEFORE the include, like this: @@ -89,6 +92,9 @@ #define HMM_TANF MyTanF #define HMM_EXPF MyExpF #define HMM_LOGF MyLogF + #define HMM_ACOSF MyACosF + #define HMM_ATANF MyATanF + #define HMM_ATAN2F MyATan2F #define HANDMADE_MATH_IMPLEMENTATION #define HANDMADE_MATH_CPP_MODE #include "HandmadeMath.h" @@ -147,6 +153,23 @@ (*) Added HMM_NormalizeVec4 1.0 (*) Lots of testing! + + 1.1 + (*) Quaternion support + (*) Added type hmm_quaternion + (*) Added HMM_Quaternion + (*) Added HMM_QuaternionV4 + (*) Added HMM_AddQuaternion + (*) Added HMM_SubtractQuaternion + (*) Added HMM_MultiplyQuaternion + (*) Added HMM_MultiplyQuaternionF + (*) Added HMM_DivideQuaternionF + (*) Added HMM_InverseQuaternion + (*) Added HMM_DotQuaternion + (*) Added HMM_NormalizeQuaternion + (*) Added HMM_Slerp + (*) Added HMM_QuaternionToMat4 + (*) Added HMM_QuaternionFromAxisAngle LICENSE @@ -164,6 +187,7 @@ FieryDrake (@fierydrake) Gingerbill (@TheGingerBill) Ben Visness (@bvisness) + Trinton Bullard (@Peliex_Dev) Fixes: Jeroen van Rijn (@J_vanRijn) @@ -171,7 +195,9 @@ Insofaras (@insofaras) */ +#ifndef HANDMADE_NO_SSE #include +#endif #ifndef HANDMADE_MATH_H #define HANDMADE_MATH_H @@ -189,7 +215,7 @@ extern "C" { #endif -#ifdef HANDMADEMATH_STATIC +#ifdef HANDMADE_MATH_STATIC #define HMMDEF static #else #define HMMDEF extern @@ -204,7 +230,8 @@ extern "C" #endif #if !defined(HMM_SINF) || !defined(HMM_COSF) || !defined(HMM_TANF) || \ - !defined(HMM_EXPF) || !defined(HMM_LOGF) + !defined(HMM_EXPF) || !defined(HMM_LOGF) || !defined(HMM_ACOSF) || \ + !defined(HMM_ATANF)|| !defined(HMM_ATAN2F) #include #endif @@ -228,12 +255,24 @@ extern "C" #define HMM_LOGF logf #endif +#ifndef HMM_ACOSF +#define HMM_ACOSF acosf +#endif + +#ifndef HMM_ATANF +#define HMM_ATANF atanf +#endif + +#ifndef HMM_ATAN2F +#define HMM_ATAN2F atan2f +#endif + #define HMM_PI32 3.14159265359f #define HMM_PI 3.14159265358979323846 #define HMM_MIN(a, b) (a) > (b) ? (b) : (a) #define HMM_MAX(a, b) (a) < (b) ? (b) : (a) -#define HMN_ABS(a) (a) < 0 ? -(a) : (a) +#define HMM_ABS(a) ((a) > 0 ? (a) : -(a)) #define HMM_MOD(a, m) ((a) % (m)) >= 0 ? ((a) % (m)) : (((a) % (m)) + (m)) #define HMM_SQUARE(x) ((x) * (x)) @@ -359,6 +398,25 @@ typedef union hmm_mat4 float Elements[4][4]; } hmm_mat4; +typedef union hmm_quaternion +{ + struct + { + union + { + hmm_vec3 XYZ; + struct + { + float X, Y, Z; + }; + }; + + float W; + }; + + float Elements[4]; +} hmm_quaternion; + typedef hmm_vec2 hmm_v2; typedef hmm_vec3 hmm_v3; typedef hmm_vec4 hmm_v4; @@ -366,7 +424,10 @@ typedef hmm_mat4 hmm_m4; HMMDEF float HMM_SinF(float Angle); HMMDEF float HMM_TanF(float Angle); +HMMDEF float HMM_ATanF(float Theta); +HMMDEF float HMM_ATan2F(float Theta, float Theta2); HMMDEF float HMM_CosF(float Angle); +HMMDEF float HMM_ACosF(float Theta); HMMDEF float HMM_ExpF(float Float); HMMDEF float HMM_LogF(float Float); @@ -397,8 +458,8 @@ HMMDEF float HMM_DotVec4(hmm_vec4 VecOne, hmm_vec4 VecTwo); HMMDEF hmm_vec3 HMM_Cross(hmm_vec3 VecOne, hmm_vec3 VecTwo); -HMMDEF hmm_vec2 HMM_Vec2i(int X, int Y); HMMDEF hmm_vec2 HMM_Vec2(float X, float Y); +HMMDEF hmm_vec2 HMM_Vec2i(int X, int Y); HMMDEF hmm_vec3 HMM_Vec3(float X, float Y, float Z); HMMDEF hmm_vec3 HMM_Vec3i(int X, int Y, int Z); HMMDEF hmm_vec4 HMM_Vec4(float X, float Y, float Z, float W); @@ -447,6 +508,21 @@ HMMDEF hmm_mat4 HMM_Scale(hmm_vec3 Scale); HMMDEF hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up); +HMMDEF hmm_quaternion HMM_Quaternion(float X, float Y, float Z, float W); +HMMDEF hmm_quaternion HMM_QuaternionV4(hmm_vec4 Vector); +HMMDEF hmm_quaternion HMM_AddQuaternion(hmm_quaternion Left, hmm_quaternion Right); +HMMDEF hmm_quaternion HMM_SubtractQuaternion(hmm_quaternion Left, hmm_quaternion Right); +HMMDEF hmm_quaternion HMM_MultiplyQuaternion(hmm_quaternion Left, hmm_quaternion Right); +HMMDEF hmm_quaternion HMM_MultiplyQuaternionF(hmm_quaternion Left, float Multiplicative); +HMMDEF hmm_quaternion HMM_DivideQuaternionF(hmm_quaternion Left, float Dividend); +HMMDEF hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left); +HMMDEF float HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right); +HMMDEF hmm_quaternion HMM_NormalizeQuaternion(hmm_quaternion Left); +HMMDEF hmm_quaternion HMM_NLerp(hmm_quaternion Left, float Time, hmm_quaternion Right); +HMMDEF hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right); +HMMDEF hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left); +HMMDEF hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotation); + #ifdef __cplusplus } #endif @@ -464,20 +540,24 @@ HMMDEF float HMM_LengthSquared(hmm_vec4 A); HMMDEF hmm_vec2 HMM_Normalize(hmm_vec2 A); HMMDEF hmm_vec3 HMM_Normalize(hmm_vec3 A); HMMDEF hmm_vec4 HMM_Normalize(hmm_vec4 A); +HMMDEF hmm_quaternion HMM_Normalize(hmm_quaternion A); HMMDEF float HMM_Dot(hmm_vec2 VecOne, hmm_vec2 VecTwo); HMMDEF float HMM_Dot(hmm_vec3 VecOne, hmm_vec3 VecTwo); HMMDEF float HMM_Dot(hmm_vec4 VecOne, hmm_vec4 VecTwo); +HMMDEF float HMM_Dot(hmm_quaternion QuatOne, hmm_quaternion QuatTwo); HMMDEF hmm_vec2 HMM_Add(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 HMM_Add(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 HMM_Add(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_mat4 HMM_Add(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_quaternion HMM_Add(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_vec2 HMM_Subtract(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 HMM_Subtract(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 HMM_Subtract(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_mat4 HMM_Subtract(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_quaternion HMM_Subtract(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_vec2 HMM_Multiply(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec2 HMM_Multiply(hmm_vec2 Left, float Right); @@ -488,6 +568,8 @@ HMMDEF hmm_vec4 HMM_Multiply(hmm_vec4 Left, float Right); HMMDEF hmm_mat4 HMM_Multiply(hmm_mat4 Left, hmm_mat4 Right); HMMDEF hmm_mat4 HMM_Multiply(hmm_mat4 Left, float Right); HMMDEF hmm_vec4 HMM_Multiply(hmm_mat4 Matrix, hmm_vec4 Vector); +HMMDEF hmm_quaternion HMM_Multiply(hmm_quaternion Left, hmm_quaternion Right); +HMMDEF hmm_quaternion HMM_Multiply(hmm_quaternion Left, float Right); HMMDEF hmm_vec2 HMM_Divide(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec2 HMM_Divide(hmm_vec2 Left, float Right); @@ -496,31 +578,38 @@ HMMDEF hmm_vec3 HMM_Divide(hmm_vec3 Left, float Right); HMMDEF hmm_vec4 HMM_Divide(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_vec4 HMM_Divide(hmm_vec4 Left, float Right); HMMDEF hmm_mat4 HMM_Divide(hmm_mat4 Left, float Right); +HMMDEF hmm_quaternion HMM_Divide(hmm_quaternion Left, hmm_quaternion Right); +HMMDEF hmm_quaternion HMM_Divide(hmm_quaternion Left, float Right); HMMDEF hmm_vec2 operator+(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 operator+(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 operator+(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_mat4 operator+(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_quaternion operator+(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_vec2 operator-(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 operator-(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 operator-(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_mat4 operator-(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_quaternion operator-(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_vec2 operator*(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 operator*(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 operator*(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_mat4 operator*(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_quaternion operator*(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_vec2 operator*(hmm_vec2 Left, float Right); HMMDEF hmm_vec3 operator*(hmm_vec3 Left, float Right); HMMDEF hmm_vec4 operator*(hmm_vec4 Left, float Right); HMMDEF hmm_mat4 operator*(hmm_mat4 Left, float Right); +HMMDEF hmm_quaternion operator*(hmm_quaternion Left, float Right); HMMDEF hmm_vec2 operator*(float Left, hmm_vec2 Right); HMMDEF hmm_vec3 operator*(float Left, hmm_vec3 Right); HMMDEF hmm_vec4 operator*(float Left, hmm_vec4 Right); HMMDEF hmm_mat4 operator*(float Left, hmm_mat4 Right); +HMMDEF hmm_quaternion operator*(float Left, hmm_quaternion Right); HMMDEF hmm_vec4 operator*(hmm_mat4 Matrix, hmm_vec4 Vector); @@ -532,16 +621,19 @@ HMMDEF hmm_vec2 operator/(hmm_vec2 Left, float Right); HMMDEF hmm_vec3 operator/(hmm_vec3 Left, float Right); HMMDEF hmm_vec4 operator/(hmm_vec4 Left, float Right); HMMDEF hmm_mat4 operator/(hmm_mat4 Left, float Right); +HMMDEF hmm_quaternion operator/(hmm_quaternion Left, float Right); HMMDEF hmm_vec2 &operator+=(hmm_vec2 &Left, hmm_vec2 Right); HMMDEF hmm_vec3 &operator+=(hmm_vec3 &Left, hmm_vec3 Right); HMMDEF hmm_vec4 &operator+=(hmm_vec4 &Left, hmm_vec4 Right); HMMDEF hmm_mat4 &operator+=(hmm_mat4 &Left, hmm_mat4 Right); +HMMDEF hmm_quaternion &operator+=(hmm_quaternion &Left, hmm_quaternion Right); HMMDEF hmm_vec2 &operator-=(hmm_vec2 &Left, hmm_vec2 Right); HMMDEF hmm_vec3 &operator-=(hmm_vec3 &Left, hmm_vec3 Right); HMMDEF hmm_vec4 &operator-=(hmm_vec4 &Left, hmm_vec4 Right); HMMDEF hmm_mat4 &operator-=(hmm_mat4 &Left, hmm_mat4 Right); +HMMDEF hmm_quaternion &operator-=(hmm_quaternion &Left, hmm_quaternion Right); HMMDEF hmm_vec2 &operator*=(hmm_vec2 &Left, hmm_vec2 Right); HMMDEF hmm_vec3 &operator*=(hmm_vec3 &Left, hmm_vec3 Right); @@ -551,6 +643,7 @@ HMMDEF hmm_vec2 &operator*=(hmm_vec2 &Left, float Right); HMMDEF hmm_vec3 &operator*=(hmm_vec3 &Left, float Right); HMMDEF hmm_vec4 &operator*=(hmm_vec4 &Left, float Right); HMMDEF hmm_mat4 &operator*=(hmm_mat4 &Left, float Right); +HMMDEF hmm_quaternion &operator*=(hmm_quaternion &Left, float Right); HMMDEF hmm_vec2 &operator/=(hmm_vec2 &Left, hmm_vec2 Right); HMMDEF hmm_vec3 &operator/=(hmm_vec3 &Left, hmm_vec3 Right); @@ -560,6 +653,7 @@ HMMDEF hmm_vec2 &operator/=(hmm_vec2 &Left, float Right); HMMDEF hmm_vec3 &operator/=(hmm_vec3 &Left, float Right); HMMDEF hmm_vec4 &operator/=(hmm_vec4 &Left, float Right); HMMDEF hmm_mat4 &operator/=(hmm_mat4 &Left, float Right); +HMMDEF hmm_quaternion &operator/=(hmm_quaternion &Left, float Right); #endif /* HANDMADE_MATH_CPP */ @@ -594,6 +688,33 @@ HMM_TanF(float Radians) return (Result); } +HINLINE float +HMM_ACosF(float Radians) +{ + float Result = 0.0f; + + Result = HMM_ACOSF(Radians); + return (Result); +} + +HINLINE float +HMM_ATanF(float Radians) +{ + float Result = 0.0f; + + Result = HMM_ATANF(Radians); + return (Result); +} + +HINLINE float +HMM_Atan2F(float Left, float Right) +{ + float Result = 0.0f; + + Result = HMM_ATAN2F(Left, Right); + return (Result); +} + HINLINE float HMM_ExpF(float Float) { @@ -1205,7 +1326,7 @@ HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right) return (Result); } -hmm_mat4 +HINLINE hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_Mat4(); @@ -1230,7 +1351,7 @@ HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right) return (Result); } -hmm_mat4 +HINLINE hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar) { hmm_mat4 Result = HMM_Mat4(); @@ -1248,7 +1369,7 @@ HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar) return (Result); } -hmm_vec4 +HINLINE hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector) { hmm_vec4 Result = {0}; @@ -1268,7 +1389,7 @@ HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector) return (Result); } -hmm_mat4 +HINLINE hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar) { hmm_mat4 Result = HMM_Mat4(); @@ -1286,7 +1407,7 @@ HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar) return (Result); } -hmm_mat4 +HINLINE hmm_mat4 HMM_Transpose(hmm_mat4 Matrix) { hmm_mat4 Result = HMM_Mat4(); @@ -1304,7 +1425,7 @@ HMM_Transpose(hmm_mat4 Matrix) return (Result); } -hmm_mat4 +HINLINE hmm_mat4 HMM_Orthographic(float Left, float Right, float Bottom, float Top, float Near, float Far) { hmm_mat4 Result = HMM_Mat4d(1.0f); @@ -1320,7 +1441,7 @@ HMM_Orthographic(float Left, float Right, float Bottom, float Top, float Near, f return (Result); } -hmm_mat4 +HINLINE hmm_mat4 HMM_Perspective(float FOV, float AspectRatio, float Near, float Far) { hmm_mat4 Result = HMM_Mat4d(1.0f); @@ -1337,7 +1458,7 @@ HMM_Perspective(float FOV, float AspectRatio, float Near, float Far) return (Result); } -hmm_mat4 +HINLINE hmm_mat4 HMM_Translate(hmm_vec3 Translation) { hmm_mat4 Result = HMM_Mat4d(1.0f); @@ -1349,7 +1470,7 @@ HMM_Translate(hmm_vec3 Translation) return (Result); } -hmm_mat4 +HINLINE hmm_mat4 HMM_Rotate(float Angle, hmm_vec3 Axis) { hmm_mat4 Result = HMM_Mat4d(1.0f); @@ -1375,7 +1496,7 @@ HMM_Rotate(float Angle, hmm_vec3 Axis) return (Result); } -hmm_mat4 +HINLINE hmm_mat4 HMM_Scale(hmm_vec3 Scale) { hmm_mat4 Result = HMM_Mat4d(1.0f); @@ -1387,7 +1508,7 @@ HMM_Scale(hmm_vec3 Scale) return (Result); } -hmm_mat4 +HINLINE hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up) { hmm_mat4 Result = {0}; @@ -1416,6 +1537,236 @@ HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up) return (Result); } + +HMMDEF hmm_quaternion +HMM_Quaternion(float X, float Y, float Z, float W) +{ + hmm_quaternion Result = {0}; + + Result.X = X; + Result.Y = Y; + Result.Z = Z; + Result.W = W; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_QuaternionV4(hmm_vec4 Vector) +{ + hmm_quaternion Result = {0}; + + Result.X = Vector.X; + Result.Y = Vector.Y; + Result.Z = Vector.Z; + Result.W = Vector.W; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_AddQuaternion(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + Result.Z = Left.Z + Right.Z; + Result.W = Left.W + Right.W; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_SubtractQuaternion(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + Result.Z = Left.Z - Right.Z; + Result.W = Left.W - Right.W; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_MultiplyQuaternion(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result.X = (Left.X * Right.W) + (Left.Y * Right.Z) - (Left.Z * Right.Y) + (Left.W * Right.X); + Result.Y = (-Left.X * Right.Z) + (Left.Y * Right.W) + (Left.Z * Right.X) + (Left.W * Right.Y); + Result.Z = (Left.X * Right.Y) - (Left.Y * Right.X) + (Left.Z * Right.W) + (Left.W * Right.Z); + Result.W = (-Left.X * Right.X) - (Left.Y * Right.Y) - (Left.Z * Right.Z) + (Left.W * Right.W); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_MultiplyQuaternionF(hmm_quaternion Left, float Multiplicative) +{ + hmm_quaternion Result = {0}; + + Result.X = Left.X * Multiplicative; + Result.Y = Left.Y * Multiplicative; + Result.Z = Left.Z * Multiplicative; + Result.W = Left.W * Multiplicative; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_DivideQuaternionF(hmm_quaternion Left, float Dividend) +{ + hmm_quaternion Result = {0}; + + Result.X = Left.X / Dividend; + Result.Y = Left.Y / Dividend; + Result.Z = Left.Z / Dividend; + Result.W = Left.W / Dividend; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_InverseQuaternion(hmm_quaternion Left) +{ + hmm_quaternion Conjugate = {0}; + hmm_quaternion Result = {0}; + float Norm = 0; + float NormSquared = 0; + + Conjugate.X = -Left.X; + Conjugate.Y = -Left.Y; + Conjugate.Z = -Left.Z; + Conjugate.W = Left.W; + + Norm = HMM_SquareRootF(HMM_DotQuaternion(Left, Left)); + NormSquared = Norm * Norm; + + Result.X = Conjugate.X / NormSquared; + Result.Y = Conjugate.Y / NormSquared; + Result.Z = Conjugate.Z / NormSquared; + Result.W = Conjugate.W / NormSquared; + + return(Result); +} + +HINLINE float +HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right) +{ + float Result = 0.0f; + + Result = (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z) + (Left.W * Right.W); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_NormalizeQuaternion(hmm_quaternion Left) +{ + hmm_quaternion Result = {0}; + + float Length = HMM_SquareRootF(HMM_DotQuaternion(Left, Left)); + Result = HMM_DivideQuaternionF(Left, Length); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_NLerp(hmm_quaternion Left, float Time, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result.X = HMM_Lerp(Left.X, Time, Right.X); + Result.Y = HMM_Lerp(Left.Y, Time, Right.Y); + Result.Z = HMM_Lerp(Left.Z, Time, Right.Z); + Result.W = HMM_Lerp(Left.W, Time, Right.W); + + Result = HMM_Normalize(Result); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + hmm_quaternion QuaternionLeft = {0}; + hmm_quaternion QuaternionRight = {0}; + + float Cos_Theta = HMM_DotQuaternion(Left, Right); + float Angle = HMM_ACosF(Cos_Theta); + + float S1 = HMM_SinF((1.0f - Time) * Angle); + float S2 = HMM_SinF(Time * Angle); + float Is = 1.0f / HMM_SinF(Angle); + + QuaternionLeft = HMM_MultiplyQuaternionF(Left, S1); + QuaternionRight = HMM_MultiplyQuaternionF(Right, S2); + + Result = HMM_AddQuaternion(QuaternionLeft, QuaternionRight); + Result = HMM_MultiplyQuaternionF(Result, Is); + + return(Result); +} + +HINLINE hmm_mat4 +HMM_QuaternionToMat4(hmm_quaternion Left) +{ + hmm_mat4 Result = {0}; + Result = HMM_Mat4d(1); + + hmm_quaternion NormalizedQuaternion = HMM_NormalizeQuaternion(Left); + + float XX, YY, ZZ, + XY, XZ, YZ, + WX, WY, WZ; + + XX = NormalizedQuaternion.X * NormalizedQuaternion.X; + YY = NormalizedQuaternion.Y * NormalizedQuaternion.Y; + ZZ = NormalizedQuaternion.Z * NormalizedQuaternion.Z; + XY = NormalizedQuaternion.X * NormalizedQuaternion.Y; + XZ = NormalizedQuaternion.X * NormalizedQuaternion.Z; + YZ = NormalizedQuaternion.Y * NormalizedQuaternion.Z; + WX = NormalizedQuaternion.W * NormalizedQuaternion.X; + WY = NormalizedQuaternion.W * NormalizedQuaternion.Y; + WZ = NormalizedQuaternion.W * NormalizedQuaternion.Z; + + Result.Elements[0][0] = 1.0f - 2.0f * (YY + ZZ); + Result.Elements[0][1] = 2.0f * (XY + WZ); + Result.Elements[0][2] = 2.0f * (XZ - WY); + + Result.Elements[1][0] = 2.0f * (XY - WZ); + Result.Elements[1][1] = 1.0f - 2.0f * (XX + ZZ); + Result.Elements[1][2] = 2.0f * (YZ + WX); + + Result.Elements[2][0] = 2.0f * (XZ + WY); + Result.Elements[2][1] = 2.0f * (YZ - WX); + Result.Elements[2][2] = 1.0f - 2.0f * (XX + YY); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotation) +{ + hmm_quaternion Result = {0}; + float AxisNorm = 0; + float SineOfRotation = 0; + hmm_vec3 RotatedVector = {0}; + + AxisNorm = HMM_SquareRootF(HMM_DotVec3(Axis, Axis)); + SineOfRotation = HMM_SinF(AngleOfRotation / 2.0f); + RotatedVector = HMM_MultiplyVec3f(Axis, SineOfRotation); + + Result.W = HMM_CosF(AngleOfRotation / 2.0f); + Result.XYZ = HMM_DivideVec3f(RotatedVector, AxisNorm); + + return(Result); +} + #ifdef HANDMADE_MATH_CPP_MODE HMMDEF float @@ -1508,6 +1859,16 @@ HMM_Normalize(hmm_vec4 A) return(Result); } +HINLINE hmm_quaternion +HMM_Normalize(hmm_quaternion A) +{ + hmm_quaternion Result = {0}; + + Result = HMM_NormalizeQuaternion(A); + + return(Result); +} + HINLINE float HMM_Dot(hmm_vec2 VecOne, hmm_vec2 VecTwo) { @@ -1538,6 +1899,16 @@ HMM_Dot(hmm_vec4 VecOne, hmm_vec4 VecTwo) return(Result); } +HINLINE float +HMM_Dot(hmm_quaternion QuatOne, hmm_quaternion QuatTwo) +{ + float Result = 0; + + Result = HMM_DotQuaternion(QuatOne, QuatTwo); + + return(Result); +} + HINLINE hmm_vec2 HMM_Add(hmm_vec2 Left, hmm_vec2 Right) { @@ -1574,6 +1945,15 @@ HMM_Add(hmm_mat4 Left, hmm_mat4 Right) return (Result); } +HINLINE hmm_quaternion +HMM_Add(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_AddQuaternion(Left, Right); + return(Result); +} + HINLINE hmm_vec2 HMM_Subtract(hmm_vec2 Left, hmm_vec2 Right) { @@ -1610,6 +1990,15 @@ HMM_Subtract(hmm_mat4 Left, hmm_mat4 Right) return (Result); } +HINLINE hmm_quaternion +HMM_Subtract(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_SubtractQuaternion(Left, Right); + return (Result); +} + HINLINE hmm_vec2 HMM_Multiply(hmm_vec2 Left, hmm_vec2 Right) { @@ -1691,6 +2080,33 @@ HMM_Multiply(hmm_mat4 Matrix, hmm_vec4 Vector) return (Result); } +HINLINE hmm_quaternion +HMM_Multiply(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_MultiplyQuaternion(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +HMM_Multiply(hmm_quaternion Left, float Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_MultiplyQuaternionF(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +HMM_Multiply(float Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_MultiplyQuaternionF(Right, Left); + return (Result); +} + HINLINE hmm_vec2 HMM_Divide(hmm_vec2 Left, hmm_vec2 Right) { @@ -1754,6 +2170,15 @@ HMM_Divide(hmm_mat4 Left, float Right) return (Result); } +HINLINE hmm_quaternion +HMM_Divide(hmm_quaternion Left, float Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_DivideQuaternionF(Left, Right); + return (Result); +} + HINLINE hmm_vec2 operator+(hmm_vec2 Left, hmm_vec2 Right) { @@ -1790,6 +2215,15 @@ operator+(hmm_mat4 Left, hmm_mat4 Right) return (Result); } +HINLINE hmm_quaternion +operator+(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Add(Left, Right); + return (Result); +} + HINLINE hmm_vec2 operator-(hmm_vec2 Left, hmm_vec2 Right) { @@ -1826,6 +2260,15 @@ operator-(hmm_mat4 Left, hmm_mat4 Right) return (Result); } +HINLINE hmm_quaternion +operator-(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Subtract(Left, Right); + return (Result); +} + HINLINE hmm_vec2 operator*(hmm_vec2 Left, hmm_vec2 Right) { @@ -1942,6 +2385,33 @@ operator*(hmm_mat4 Matrix, hmm_vec4 Vector) return (Result); } +HINLINE hmm_quaternion +operator*(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +operator*(hmm_quaternion Left, float Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +operator*(float Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Multiply(Right, Left); + return (Result); +} + HINLINE hmm_vec2 operator/(hmm_vec2 Left, hmm_vec2 Right) { @@ -1954,7 +2424,9 @@ operator/(hmm_vec2 Left, hmm_vec2 Right) HINLINE hmm_vec3 operator/(hmm_vec3 Left, hmm_vec3 Right) { - hmm_vec3 Result = HMM_Divide(Left, Right); + hmm_vec3 Result = {0}; + + Result = HMM_Divide(Left, Right); return (Result); } @@ -2004,6 +2476,15 @@ operator/(hmm_mat4 Left, float Right) return (Result); } +HINLINE hmm_quaternion +operator/(hmm_quaternion Left, float Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Divide(Left, Right); + return (Result); +} + HINLINE hmm_vec2 & operator+=(hmm_vec2 &Left, hmm_vec2 Right) { @@ -2028,6 +2509,12 @@ operator+=(hmm_mat4 &Left, hmm_mat4 Right) return (Left = Left + Right); } +HINLINE hmm_quaternion & +operator+=(hmm_quaternion &Left, hmm_quaternion Right) +{ + return (Left = Left + Right); +} + HINLINE hmm_vec2 & operator-=(hmm_vec2 &Left, hmm_vec2 Right) { @@ -2052,6 +2539,12 @@ operator-=(hmm_mat4 &Left, hmm_mat4 Right) return (Left = Left - Right); } +HINLINE hmm_quaternion & +operator-=(hmm_quaternion &Left, hmm_quaternion Right) +{ + return (Left = Left - Right); +} + HINLINE hmm_vec2 & operator/=(hmm_vec2 &Left, hmm_vec2 Right) { @@ -2094,6 +2587,12 @@ operator/=(hmm_mat4 &Left, float Right) return (Left = Left / Right); } +HINLINE hmm_quaternion & +operator/=(hmm_quaternion &Left, float Right) +{ + return (Left = Left / Right); +} + HINLINE hmm_vec2 & operator*=(hmm_vec2 &Left, hmm_vec2 Right) { @@ -2136,6 +2635,12 @@ operator*=(hmm_mat4 &Left, float Right) return (Left = Left * Right); } +HINLINE hmm_quaternion & +operator*=(hmm_quaternion &Left, float Right) +{ + return (Left = Left * Right); +} + #endif /* HANDMADE_MATH_CPP_MODE */ -#endif /* HANDMADE_MATH_IMPLEMENTATION */ \ No newline at end of file +#endif /* HANDMADE_MATH_IMPLEMENTATION */ diff --git a/README.md b/README.md index 50c15a7..991b040 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ _This library is free and will stay free, but if you would like to support devel Version | Changes | ----------------|----------------| +**1.1** | Quaternions! | **1.0** | Lots of testing | **0.7** | Added HMM_Vec2, and HMM_Vec4 versions of HMM_LengthSquared, HMM_Length, and HMM_Normalize. | **0.6** | Made HMM_Power faster, Fixed possible efficiency problem with HMM_Normalize, RENAMED HMM_LengthSquareRoot to HMM_LengthSquared, RENAMED HMM_RSqrtF to HMM_RSquareRootF, RENAMED HMM_SqrtF to HMM_SquareRootF, REMOVED Inner function (user should use Dot now), REMOVED HMM_FastInverseSquareRoot function declaration | diff --git a/test/hmm_test.cpp b/test/hmm_test.cpp index d175a39..9cc0cff 100644 --- a/test/hmm_test.cpp +++ b/test/hmm_test.cpp @@ -157,6 +157,29 @@ TEST(Initialization, MatrixDiagonal) } } +TEST(Initialization, Quaternion) +{ + hmm_quaternion q = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f); + + EXPECT_FLOAT_EQ(q.X, 1.0f); + EXPECT_FLOAT_EQ(q.Y, 2.0f); + EXPECT_FLOAT_EQ(q.Z, 3.0f); + EXPECT_FLOAT_EQ(q.W, 4.0f); + + EXPECT_FLOAT_EQ(q.Elements[0], 1.0f); + EXPECT_FLOAT_EQ(q.Elements[1], 2.0f); + EXPECT_FLOAT_EQ(q.Elements[2], 3.0f); + EXPECT_FLOAT_EQ(q.Elements[3], 4.0f); + + hmm_vec4 v = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f); + hmm_quaternion qv = HMM_QuaternionV4(v); + + EXPECT_FLOAT_EQ(qv.X, 1.0f); + EXPECT_FLOAT_EQ(qv.Y, 2.0f); + EXPECT_FLOAT_EQ(qv.Z, 3.0f); + EXPECT_FLOAT_EQ(qv.W, 4.0f); +} + TEST(VectorOps, LengthSquared) { hmm_vec2 v2 = HMM_Vec2(1.0f, -2.0f); @@ -314,6 +337,119 @@ TEST(MatrixOps, Transpose) EXPECT_FLOAT_EQ(result.Elements[3][3], 16.0f); } +TEST(QuaternionOps, Inverse) +{ + hmm_quaternion q1 = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f); + hmm_quaternion inverse = HMM_InverseQuaternion(q1); + + hmm_quaternion result = HMM_MultiplyQuaternion(q1, inverse); + + EXPECT_FLOAT_EQ(result.X, 0.0f); + EXPECT_FLOAT_EQ(result.Y, 0.0f); + EXPECT_FLOAT_EQ(result.Z, 0.0f); + EXPECT_FLOAT_EQ(result.W, 1.0f); +} + +TEST(QuaternionOps, Dot) +{ + hmm_quaternion q1 = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f); + hmm_quaternion q2 = HMM_Quaternion(5.0f, 6.0f, 7.0f, 8.0f); + + { + float result = HMM_DotQuaternion(q1, q2); + EXPECT_FLOAT_EQ(result, 70.0f); + } + { + float result = HMM_Dot(q1, q2); + EXPECT_FLOAT_EQ(result, 70.0f); + } +} + +TEST(QuaternionOps, Normalize) +{ + hmm_quaternion q = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f); + + { + hmm_quaternion result = HMM_NormalizeQuaternion(q); + EXPECT_FLOAT_EQ(result.X, 0.1825741858f); + EXPECT_FLOAT_EQ(result.Y, 0.3651483717f); + EXPECT_FLOAT_EQ(result.Z, 0.5477225575f); + EXPECT_FLOAT_EQ(result.W, 0.7302967433f); + } + { + hmm_quaternion result = HMM_Normalize(q); + EXPECT_FLOAT_EQ(result.X, 0.1825741858f); + EXPECT_FLOAT_EQ(result.Y, 0.3651483717f); + EXPECT_FLOAT_EQ(result.Z, 0.5477225575f); + EXPECT_FLOAT_EQ(result.W, 0.7302967433f); + } +} + +TEST(QuaternionOps, NLerp) +{ + hmm_quaternion from = HMM_Quaternion(0.0f, 0.0f, 0.0f, 1.0f); + hmm_quaternion to = HMM_Quaternion(0.5f, 0.5f, -0.5f, 0.5f); + + hmm_quaternion result = HMM_NLerp(from, 0.5f, to); + EXPECT_FLOAT_EQ(result.X, 0.28867513f); + EXPECT_FLOAT_EQ(result.Y, 0.28867513f); + EXPECT_FLOAT_EQ(result.Z, -0.28867513f); + EXPECT_FLOAT_EQ(result.W, 0.86602540f); +} + +TEST(QuaternionOps, Slerp) +{ + hmm_quaternion from = HMM_Quaternion(0.0f, 0.0f, 0.0f, 1.0f); + hmm_quaternion to = HMM_Quaternion(0.5f, 0.5f, -0.5f, 0.5f); + + hmm_quaternion result = HMM_Slerp(from, 0.5f, to); + EXPECT_FLOAT_EQ(result.X, 0.28867513f); + EXPECT_FLOAT_EQ(result.Y, 0.28867513f); + EXPECT_FLOAT_EQ(result.Z, -0.28867513f); + EXPECT_FLOAT_EQ(result.W, 0.86602540f); +} + +TEST(QuaternionOps, ToMat4) +{ + const float abs_error = 0.0001f; + + hmm_quaternion rot = HMM_Quaternion(0.707107f, 0.0f, 0.0f, 0.707107f); + + hmm_mat4 result = HMM_QuaternionToMat4(rot); + + EXPECT_NEAR(result.Elements[0][0], 1.0f, abs_error); + EXPECT_NEAR(result.Elements[0][1], 0.0f, abs_error); + EXPECT_NEAR(result.Elements[0][2], 0.0f, abs_error); + EXPECT_NEAR(result.Elements[0][3], 0.0f, abs_error); + + EXPECT_NEAR(result.Elements[1][0], 0.0f, abs_error); + EXPECT_NEAR(result.Elements[1][1], 0.0f, abs_error); + EXPECT_NEAR(result.Elements[1][2], 1.0f, abs_error); + EXPECT_NEAR(result.Elements[1][3], 0.0f, abs_error); + + EXPECT_NEAR(result.Elements[2][0], 0.0f, abs_error); + EXPECT_NEAR(result.Elements[2][1], -1.0f, abs_error); + EXPECT_NEAR(result.Elements[2][2], 0.0f, abs_error); + EXPECT_NEAR(result.Elements[2][3], 0.0f, abs_error); + + EXPECT_NEAR(result.Elements[3][0], 0.0f, abs_error); + EXPECT_NEAR(result.Elements[3][1], 0.0f, abs_error); + EXPECT_NEAR(result.Elements[3][2], 0.0f, abs_error); + EXPECT_NEAR(result.Elements[3][3], 1.0f, abs_error); +} + +TEST(QuaternionOps, FromAxisAngle) +{ + hmm_vec3 axis = HMM_Vec3(1.0f, 0.0f, 0.0f); + float angle = HMM_PI32 / 2.0f; + + hmm_quaternion result = HMM_QuaternionFromAxisAngle(axis, angle); + EXPECT_FLOAT_EQ(result.X, 0.707107f); + EXPECT_FLOAT_EQ(result.Y, 0.0f); + EXPECT_FLOAT_EQ(result.Z, 0.0f); + EXPECT_FLOAT_EQ(result.W, 0.707107f); +} + TEST(Addition, Vec2) { hmm_vec2 v2_1 = HMM_Vec2(1.0f, 2.0f); @@ -478,6 +614,40 @@ TEST(Addition, Mat4) } } +TEST(Addition, Quaternion) +{ + hmm_quaternion q1 = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f); + hmm_quaternion q2 = HMM_Quaternion(5.0f, 6.0f, 7.0f, 8.0f); + + { + hmm_quaternion result = HMM_AddQuaternion(q1, q2); + EXPECT_FLOAT_EQ(result.X, 6.0f); + EXPECT_FLOAT_EQ(result.Y, 8.0f); + EXPECT_FLOAT_EQ(result.Z, 10.0f); + EXPECT_FLOAT_EQ(result.W, 12.0f); + } + { + hmm_quaternion result = HMM_Add(q1, q2); + EXPECT_FLOAT_EQ(result.X, 6.0f); + EXPECT_FLOAT_EQ(result.Y, 8.0f); + EXPECT_FLOAT_EQ(result.Z, 10.0f); + EXPECT_FLOAT_EQ(result.W, 12.0f); + } + { + hmm_quaternion result = q1 + q2; + EXPECT_FLOAT_EQ(result.X, 6.0f); + EXPECT_FLOAT_EQ(result.Y, 8.0f); + EXPECT_FLOAT_EQ(result.Z, 10.0f); + EXPECT_FLOAT_EQ(result.W, 12.0f); + } + + q1 += q2; + EXPECT_FLOAT_EQ(q1.X, 6.0f); + EXPECT_FLOAT_EQ(q1.Y, 8.0f); + EXPECT_FLOAT_EQ(q1.Z, 10.0f); + EXPECT_FLOAT_EQ(q1.W, 12.0f); +} + TEST(Subtraction, Vec2) { hmm_vec2 v2_1 = HMM_Vec2(1.0f, 2.0f); @@ -634,6 +804,40 @@ TEST(Subtraction, Mat4) } } +TEST(Subtraction, Quaternion) +{ + hmm_quaternion q1 = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f); + hmm_quaternion q2 = HMM_Quaternion(5.0f, 6.0f, 7.0f, 8.0f); + + { + hmm_quaternion result = HMM_SubtractQuaternion(q1, q2); + EXPECT_FLOAT_EQ(result.X, -4.0f); + EXPECT_FLOAT_EQ(result.Y, -4.0f); + EXPECT_FLOAT_EQ(result.Z, -4.0f); + EXPECT_FLOAT_EQ(result.W, -4.0f); + } + { + hmm_quaternion result = HMM_Subtract(q1, q2); + EXPECT_FLOAT_EQ(result.X, -4.0f); + EXPECT_FLOAT_EQ(result.Y, -4.0f); + EXPECT_FLOAT_EQ(result.Z, -4.0f); + EXPECT_FLOAT_EQ(result.W, -4.0f); + } + { + hmm_quaternion result = q1 - q2; + EXPECT_FLOAT_EQ(result.X, -4.0f); + EXPECT_FLOAT_EQ(result.Y, -4.0f); + EXPECT_FLOAT_EQ(result.Z, -4.0f); + EXPECT_FLOAT_EQ(result.W, -4.0f); + } + + q1 -= q2; + EXPECT_FLOAT_EQ(q1.X, -4.0f); + EXPECT_FLOAT_EQ(q1.Y, -4.0f); + EXPECT_FLOAT_EQ(q1.Z, -4.0f); + EXPECT_FLOAT_EQ(q1.W, -4.0f); +} + TEST(Multiplication, Vec2Vec2) { hmm_vec2 v2_1 = HMM_Vec2(1.0f, 2.0f); @@ -1074,6 +1278,79 @@ TEST(Multiplication, Mat4Vec4) // *= makes no sense for this particular case. } +TEST(Multiplication, QuaternionQuaternion) +{ + hmm_quaternion q1 = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f); + hmm_quaternion q2 = HMM_Quaternion(5.0f, 6.0f, 7.0f, 8.0f); + + { + hmm_quaternion result = HMM_MultiplyQuaternion(q1, q2); + EXPECT_FLOAT_EQ(result.X, 24.0f); + EXPECT_FLOAT_EQ(result.Y, 48.0f); + EXPECT_FLOAT_EQ(result.Z, 48.0f); + EXPECT_FLOAT_EQ(result.W, -6.0f); + } + { + hmm_quaternion result = HMM_Multiply(q1, q2); + EXPECT_FLOAT_EQ(result.X, 24.0f); + EXPECT_FLOAT_EQ(result.Y, 48.0f); + EXPECT_FLOAT_EQ(result.Z, 48.0f); + EXPECT_FLOAT_EQ(result.W, -6.0f); + } + { + hmm_quaternion result = q1 * q2; + EXPECT_FLOAT_EQ(result.X, 24.0f); + EXPECT_FLOAT_EQ(result.Y, 48.0f); + EXPECT_FLOAT_EQ(result.Z, 48.0f); + EXPECT_FLOAT_EQ(result.W, -6.0f); + } + + // Like with matrices, we're not implementing the *= + // operator for quaternions because quaternion multiplication + // is not commutative. +} + +TEST(Multiplication, QuaternionScalar) +{ + hmm_quaternion q = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f); + float f = 2.0f; + + { + hmm_quaternion result = HMM_MultiplyQuaternionF(q, f); + EXPECT_FLOAT_EQ(result.X, 2.0f); + EXPECT_FLOAT_EQ(result.Y, 4.0f); + EXPECT_FLOAT_EQ(result.Z, 6.0f); + EXPECT_FLOAT_EQ(result.W, 8.0f); + } + { + hmm_quaternion result = HMM_Multiply(q, f); + EXPECT_FLOAT_EQ(result.X, 2.0f); + EXPECT_FLOAT_EQ(result.Y, 4.0f); + EXPECT_FLOAT_EQ(result.Z, 6.0f); + EXPECT_FLOAT_EQ(result.W, 8.0f); + } + { + hmm_quaternion result = q * f; + EXPECT_FLOAT_EQ(result.X, 2.0f); + EXPECT_FLOAT_EQ(result.Y, 4.0f); + EXPECT_FLOAT_EQ(result.Z, 6.0f); + EXPECT_FLOAT_EQ(result.W, 8.0f); + } + { + hmm_quaternion result = f * q; + EXPECT_FLOAT_EQ(result.X, 2.0f); + EXPECT_FLOAT_EQ(result.Y, 4.0f); + EXPECT_FLOAT_EQ(result.Z, 6.0f); + EXPECT_FLOAT_EQ(result.W, 8.0f); + } + + q *= f; + EXPECT_FLOAT_EQ(q.X, 2.0f); + EXPECT_FLOAT_EQ(q.Y, 4.0f); + EXPECT_FLOAT_EQ(q.Z, 6.0f); + EXPECT_FLOAT_EQ(q.W, 8.0f); +} + TEST(Division, Vec2Vec2) { hmm_vec2 v2_1 = HMM_Vec2(1.0f, 3.0f); @@ -1348,6 +1625,40 @@ TEST(Division, Mat4Scalar) EXPECT_FLOAT_EQ(m4.Elements[3][3], 8.0f); } +TEST(Division, QuaternionScalar) +{ + hmm_quaternion q = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f); + float f = 2.0f; + + { + hmm_quaternion result = HMM_DivideQuaternionF(q, f); + EXPECT_FLOAT_EQ(result.X, 0.5f); + EXPECT_FLOAT_EQ(result.Y, 1.0f); + EXPECT_FLOAT_EQ(result.Z, 1.5f); + EXPECT_FLOAT_EQ(result.W, 2.0f); + } + { + hmm_quaternion result = HMM_Divide(q, f); + EXPECT_FLOAT_EQ(result.X, 0.5f); + EXPECT_FLOAT_EQ(result.Y, 1.0f); + EXPECT_FLOAT_EQ(result.Z, 1.5f); + EXPECT_FLOAT_EQ(result.W, 2.0f); + } + { + hmm_quaternion result = q / f; + EXPECT_FLOAT_EQ(result.X, 0.5f); + EXPECT_FLOAT_EQ(result.Y, 1.0f); + EXPECT_FLOAT_EQ(result.Z, 1.5f); + EXPECT_FLOAT_EQ(result.W, 2.0f); + } + + q /= f; + EXPECT_FLOAT_EQ(q.X, 0.5f); + EXPECT_FLOAT_EQ(q.Y, 1.0f); + EXPECT_FLOAT_EQ(q.Z, 1.5f); + EXPECT_FLOAT_EQ(q.W, 2.0f); +} + TEST(Projection, Orthographic) { hmm_mat4 projection = HMM_Orthographic(-10.0f, 10.0f, -5.0f, 5.0f, 0.0f, -10.0f);