From d3d09f8352c47b011f82c89ce12d74d0fc20c681 Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Sat, 11 Apr 2020 18:41:26 -0500 Subject: [PATCH] Make all functions static inline (#117) * Make all functions static inline * Updated email in credits Co-authored-by: Zakary Strange --- CONTRIBUTING.md | 6 +- HandmadeMath.h | 895 ++++++++++++++++++++------------------------ test/HandmadeMath.c | 2 - test/test.bat | 8 +- 4 files changed, 409 insertions(+), 502 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b57a6f6..7ab16f8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,6 @@ # Understanding the structure of Handmade Math -Most of the functions in Handmade Math are very short, and are the kind of functions you want to have inlined. Because of this, most functions in Handmade Math are defined with `HINLINE`, which is defined as `static inline`. - -The exceptions are functions like `HMM_Rotate`, which are long enough that it doesn't make sense to inline them. These functions are defined with an `HEXTERN` prototype, and implemented in the `#ifdef HANDMADE_MATH_IMPLEMENTATION` block. +Most of the functions in Handmade Math are very short, and all are the kind of functions you want to be easily inlined for performance. Because of this, all functions in Handmade Math are defined with `HMM_INLINE`, which is defined as `static inline`. # Quick style guide @@ -14,7 +12,7 @@ The exceptions are functions like `HMM_Rotate`, which are long enough that it do 0.5f; 1.0f; 3.14159f; - + // Bad 1.f .0f diff --git a/HandmadeMath.h b/HandmadeMath.h index e428b2e..fc80e99 100644 --- a/HandmadeMath.h +++ b/HandmadeMath.h @@ -8,28 +8,12 @@ ============================================================================= - You MUST - - #define HANDMADE_MATH_IMPLEMENTATION - - in EXACTLY one C or C++ file that includes this header, BEFORE the - include, like this: - - #define HANDMADE_MATH_IMPLEMENTATION - #include "HandmadeMath.h" - - All other files should just #include "HandmadeMath.h" without the #define. - - ============================================================================= - To disable SSE intrinsics, you MUST #define HANDMADE_MATH_NO_SSE - in EXACTLY one C or C++ file that includes this header, BEFORE the - include, like this: + BEFORE the include, like this: - #define HANDMADE_MATH_IMPLEMENTATION #define HANDMADE_MATH_NO_SSE #include "HandmadeMath.h" @@ -48,8 +32,7 @@ #define HMM_ATAN2F MYATan2F 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: + ExpF, and LogF BEFORE the include, like this: #define HMM_SINF MySinF #define HMM_COSF MyCosF @@ -60,7 +43,6 @@ #define HMM_ACOSF MyACosF #define HMM_ATANF MyATanF #define HMM_ATAN2F MyATan2F - #define HANDMADE_MATH_IMPLEMENTATION #include "HandmadeMath.h" If you do not define all of these, HandmadeMath.h will use the @@ -76,14 +58,15 @@ CREDITS - Written by Zakary Strange (zak@strangedev.net && @strangezak) + Written by: + Zakary Strange (zakarystrange@gmail.com && @strangezak) + Ben Visness (ben@bvisness.me && @its_bvisness) Functionality: Matt Mascarenhas (@miblo_) Aleph FieryDrake (@fierydrake) Gingerbill (@TheGingerBill) - Ben Visness (@bvisness) Trinton Bullard (@Peliex_Dev) @AntonDan @@ -150,7 +133,6 @@ extern "C" #endif #define HMM_INLINE static inline -#define HMM_EXTERN extern #if !defined(HMM_SINF) || !defined(HMM_COSF) || !defined(HMM_TANF) || \ !defined(HMM_SQRTF) || !defined(HMM_EXPF) || !defined(HMM_LOGF) || \ @@ -529,7 +511,29 @@ HMM_INLINE float HMM_RSquareRootF(float Float) return(Result); } -HMM_EXTERN float HMM_Power(float Base, int Exponent); +COVERAGE(HMM_Power, 2) +HMM_INLINE float HMM_Power(float Base, int Exponent) +{ + ASSERT_COVERED(HMM_Power); + + float Result = 1.0f; + float Mul = Exponent < 0 ? 1.f / Base : Base; + int X = Exponent < 0 ? -Exponent : Exponent; + while (X) + { + if (X & 1) + { + ASSERT_COVERED(HMM_Power); + + Result *= Mul; + } + + Mul *= Mul; + X >>= 1; + } + + return (Result); +} COVERAGE(HMM_PowerF, 1) HMM_INLINE float HMM_PowerF(float Base, float Exponent) @@ -1293,28 +1297,40 @@ HMM_INLINE hmm_mat4 HMM_Mat4d(float Diagonal) return (Result); } -#ifdef HANDMADE_MATH__USE_SSE COVERAGE(HMM_Transpose, 1) HMM_INLINE hmm_mat4 HMM_Transpose(hmm_mat4 Matrix) { ASSERT_COVERED(HMM_Transpose); +#ifdef HANDMADE_MATH__USE_SSE hmm_mat4 Result = Matrix; _MM_TRANSPOSE4_PS(Result.Columns[0], Result.Columns[1], Result.Columns[2], Result.Columns[3]); return (Result); -} #else -HMM_EXTERN hmm_mat4 HMM_Transpose(hmm_mat4 Matrix); -#endif + hmm_mat4 Result; + + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows]; + } + } + + return (Result); +#endif +} -#ifdef HANDMADE_MATH__USE_SSE COVERAGE(HMM_AddMat4, 1) HMM_INLINE hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right) { ASSERT_COVERED(HMM_AddMat4); +#ifdef HANDMADE_MATH__USE_SSE hmm_mat4 Result; Result.Columns[0] = _mm_add_ps(Left.Columns[0], Right.Columns[0]); @@ -1323,17 +1339,29 @@ HMM_INLINE hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right) Result.Columns[3] = _mm_add_ps(Left.Columns[3], Right.Columns[3]); return (Result); -} #else -HMM_EXTERN hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right); -#endif + hmm_mat4 Result; + + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] + Right.Elements[Columns][Rows]; + } + } + + return (Result); +#endif +} -#ifdef HANDMADE_MATH__USE_SSE COVERAGE(HMM_SubtractMat4, 1) HMM_INLINE hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right) { ASSERT_COVERED(HMM_SubtractMat4); +#ifdef HANDMADE_MATH__USE_SSE hmm_mat4 Result; Result.Columns[0] = _mm_sub_ps(Left.Columns[0], Right.Columns[0]); @@ -1342,19 +1370,63 @@ HMM_INLINE hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right) Result.Columns[3] = _mm_sub_ps(Left.Columns[3], Right.Columns[3]); return (Result); -} #else -HMM_EXTERN hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right); -#endif + hmm_mat4 Result; -HMM_EXTERN hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right); + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] - Right.Elements[Columns][Rows]; + } + } + + return (Result); +#endif +} + +COVERAGE(HMM_MultiplyMat4, 1) +HMM_INLINE hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right) +{ + ASSERT_COVERED(HMM_MultiplyMat4); + + hmm_mat4 Result; #ifdef HANDMADE_MATH__USE_SSE + Result.Columns[0] = HMM_LinearCombineSSE(Right.Columns[0], Left); + Result.Columns[1] = HMM_LinearCombineSSE(Right.Columns[1], Left); + Result.Columns[2] = HMM_LinearCombineSSE(Right.Columns[2], Left); + Result.Columns[3] = HMM_LinearCombineSSE(Right.Columns[3], Left); +#else + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + float Sum = 0; + int CurrentMatrice; + for(CurrentMatrice = 0; CurrentMatrice < 4; ++CurrentMatrice) + { + Sum += Left.Elements[CurrentMatrice][Rows] * Right.Elements[Columns][CurrentMatrice]; + } + + Result.Elements[Columns][Rows] = Sum; + } + } +#endif + + return (Result); +} + COVERAGE(HMM_MultiplyMat4f, 1) HMM_INLINE hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar) { ASSERT_COVERED(HMM_MultiplyMat4f); +#ifdef HANDMADE_MATH__USE_SSE hmm_mat4 Result; __m128 SSEScalar = _mm_set1_ps(Scalar); @@ -1364,19 +1436,55 @@ HMM_INLINE hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar) Result.Columns[3] = _mm_mul_ps(Matrix.Columns[3], SSEScalar); return (Result); -} #else -HMM_EXTERN hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar); -#endif + hmm_mat4 Result; -HMM_EXTERN hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector); + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] * Scalar; + } + } + + return (Result); +#endif +} + +COVERAGE(HMM_MultiplyMat4ByVec4, 1) +HMM_INLINE hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector) +{ + ASSERT_COVERED(HMM_MultiplyMat4ByVec4); + + hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE + Result.InternalElementsSSE = HMM_LinearCombineSSE(Vector.InternalElementsSSE, Matrix); +#else + int Columns, Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + float Sum = 0; + for(Columns = 0; Columns < 4; ++Columns) + { + Sum += Matrix.Elements[Columns][Rows] * Vector.Elements[Columns]; + } + + Result.Elements[Rows] = Sum; + } +#endif + + return (Result); +} + COVERAGE(HMM_DivideMat4f, 1) HMM_INLINE hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar) { ASSERT_COVERED(HMM_DivideMat4f); +#ifdef HANDMADE_MATH__USE_SSE hmm_mat4 Result; __m128 SSEScalar = _mm_set1_ps(Scalar); @@ -1386,10 +1494,22 @@ HMM_INLINE hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar) Result.Columns[3] = _mm_div_ps(Matrix.Columns[3], SSEScalar); return (Result); -} #else -HMM_EXTERN hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar); + hmm_mat4 Result; + + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] / Scalar; + } + } + + return (Result); #endif +} /* @@ -1450,7 +1570,33 @@ HMM_INLINE hmm_mat4 HMM_Translate(hmm_vec3 Translation) return (Result); } -HMM_EXTERN hmm_mat4 HMM_Rotate(float AngleRadians, hmm_vec3 Axis); +COVERAGE(HMM_Rotate, 1) +HMM_INLINE hmm_mat4 HMM_Rotate(float AngleRadians, hmm_vec3 Axis) +{ + ASSERT_COVERED(HMM_Rotate); + + hmm_mat4 Result = HMM_Mat4d(1.0f); + + Axis = HMM_NormalizeVec3(Axis); + + float SinTheta = HMM_SinF(AngleRadians); + float CosTheta = HMM_CosF(AngleRadians); + float CosValue = 1.0f - CosTheta; + + Result.Elements[0][0] = (Axis.X * Axis.X * CosValue) + CosTheta; + Result.Elements[0][1] = (Axis.X * Axis.Y * CosValue) + (Axis.Z * SinTheta); + Result.Elements[0][2] = (Axis.X * Axis.Z * CosValue) - (Axis.Y * SinTheta); + + Result.Elements[1][0] = (Axis.Y * Axis.X * CosValue) - (Axis.Z * SinTheta); + Result.Elements[1][1] = (Axis.Y * Axis.Y * CosValue) + CosTheta; + Result.Elements[1][2] = (Axis.Y * Axis.Z * CosValue) + (Axis.X * SinTheta); + + Result.Elements[2][0] = (Axis.Z * Axis.X * CosValue) + (Axis.Y * SinTheta); + Result.Elements[2][1] = (Axis.Z * Axis.Y * CosValue) - (Axis.X * SinTheta); + Result.Elements[2][2] = (Axis.Z * Axis.Z * CosValue) + CosTheta; + + return (Result); +} COVERAGE(HMM_Scale, 1) HMM_INLINE hmm_mat4 HMM_Scale(hmm_vec3 Scale) @@ -1466,7 +1612,39 @@ HMM_INLINE hmm_mat4 HMM_Scale(hmm_vec3 Scale) return (Result); } -HMM_EXTERN hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up); +COVERAGE(HMM_LookAt, 1) +HMM_INLINE hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up) +{ + ASSERT_COVERED(HMM_LookAt); + + hmm_mat4 Result; + + hmm_vec3 F = HMM_NormalizeVec3(HMM_SubtractVec3(Center, Eye)); + hmm_vec3 S = HMM_NormalizeVec3(HMM_Cross(F, Up)); + hmm_vec3 U = HMM_Cross(S, F); + + Result.Elements[0][0] = S.X; + Result.Elements[0][1] = U.X; + Result.Elements[0][2] = -F.X; + Result.Elements[0][3] = 0.0f; + + Result.Elements[1][0] = S.Y; + Result.Elements[1][1] = U.Y; + Result.Elements[1][2] = -F.Y; + Result.Elements[1][3] = 0.0f; + + Result.Elements[2][0] = S.Z; + Result.Elements[2][1] = U.Z; + Result.Elements[2][2] = -F.Z; + Result.Elements[2][3] = 0.0f; + + Result.Elements[3][0] = -HMM_DotVec3(S, Eye); + Result.Elements[3][1] = -HMM_DotVec3(U, Eye); + Result.Elements[3][2] = HMM_DotVec3(F, Eye); + Result.Elements[3][3] = 1.0f; + + return (Result); +} /* @@ -1624,8 +1802,6 @@ HMM_INLINE hmm_quaternion HMM_DivideQuaternionF(hmm_quaternion Left, float Divid return (Result); } -HMM_EXTERN hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left); - COVERAGE(HMM_DotQuaternion, 1) HMM_INLINE float HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right) { @@ -1647,6 +1823,29 @@ HMM_INLINE float HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right) return (Result); } +COVERAGE(HMM_InverseQuaternion, 1) +HMM_INLINE hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left) +{ + ASSERT_COVERED(HMM_InverseQuaternion); + + hmm_quaternion Conjugate; + hmm_quaternion Result; + 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 = HMM_DivideQuaternionF(Conjugate, NormSquared); + + return (Result); +} + COVERAGE(HMM_NormalizeQuaternion, 1) HMM_INLINE hmm_quaternion HMM_NormalizeQuaternion(hmm_quaternion Left) { @@ -1684,10 +1883,163 @@ HMM_INLINE hmm_quaternion HMM_NLerp(hmm_quaternion Left, float Time, hmm_quatern return (Result); } -HMM_EXTERN hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right); -HMM_EXTERN hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left); -HMM_EXTERN hmm_quaternion HMM_Mat4ToQuaternion(hmm_mat4 Left); -HMM_EXTERN hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotationRadians); +COVERAGE(HMM_Slerp, 1) +HMM_INLINE hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right) +{ + ASSERT_COVERED(HMM_Slerp); + + hmm_quaternion Result; + hmm_quaternion QuaternionLeft; + hmm_quaternion QuaternionRight; + + 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); +} + +COVERAGE(HMM_QuaternionToMat4, 1) +HMM_INLINE hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left) +{ + ASSERT_COVERED(HMM_QuaternionToMat4); + + hmm_mat4 Result; + + 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[0][3] = 0.0f; + + 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[1][3] = 0.0f; + + 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); + Result.Elements[2][3] = 0.0f; + + Result.Elements[3][0] = 0.0f; + Result.Elements[3][1] = 0.0f; + Result.Elements[3][2] = 0.0f; + Result.Elements[3][3] = 1.0f; + + return (Result); +} + +// This method taken from Mike Day at Insomniac Games. +// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf +// +// Note that as mentioned at the top of the paper, the paper assumes the matrix +// would be *post*-multiplied to a vector to rotate it, meaning the matrix is +// the transpose of what we're dealing with. But, because our matrices are +// stored in column-major order, the indices *appear* to match the paper. +// +// For example, m12 in the paper is row 1, column 2. We need to transpose it to +// row 2, column 1. But, because the column comes first when referencing +// elements, it looks like M.Elements[1][2]. +// +// Don't be confused! Or if you must be confused, at least trust this +// comment. :) +COVERAGE(HMM_Mat4ToQuaternion, 4) +HMM_INLINE hmm_quaternion HMM_Mat4ToQuaternion(hmm_mat4 M) +{ + float T; + hmm_quaternion Q; + + if (M.Elements[2][2] < 0.0f) { + if (M.Elements[0][0] > M.Elements[1][1]) { + ASSERT_COVERED(HMM_Mat4ToQuaternion); + + T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2]; + Q = HMM_Quaternion( + T, + M.Elements[0][1] + M.Elements[1][0], + M.Elements[2][0] + M.Elements[0][2], + M.Elements[1][2] - M.Elements[2][1] + ); + } else { + ASSERT_COVERED(HMM_Mat4ToQuaternion); + + T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2]; + Q = HMM_Quaternion( + M.Elements[0][1] + M.Elements[1][0], + T, + M.Elements[1][2] + M.Elements[2][1], + M.Elements[2][0] - M.Elements[0][2] + ); + } + } else { + if (M.Elements[0][0] < -M.Elements[1][1]) { + ASSERT_COVERED(HMM_Mat4ToQuaternion); + + T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2]; + Q = HMM_Quaternion( + M.Elements[2][0] + M.Elements[0][2], + M.Elements[1][2] + M.Elements[2][1], + T, + M.Elements[0][1] - M.Elements[1][0] + ); + } else { + ASSERT_COVERED(HMM_Mat4ToQuaternion); + + T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2]; + Q = HMM_Quaternion( + M.Elements[1][2] - M.Elements[2][1], + M.Elements[2][0] - M.Elements[0][2], + M.Elements[0][1] - M.Elements[1][0], + T + ); + } + } + + Q = HMM_MultiplyQuaternionF(Q, 0.5f / HMM_SquareRootF(T)); + + return Q; +} + +COVERAGE(HMM_QuaternionFromAxisAngle, 1) +HMM_INLINE hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotationRadians) +{ + ASSERT_COVERED(HMM_QuaternionFromAxisAngle); + + hmm_quaternion Result; + + hmm_vec3 AxisNormalized = HMM_NormalizeVec3(Axis); + float SineOfRotation = HMM_SinF(AngleOfRotationRadians / 2.0f); + + Result.XYZ = HMM_MultiplyVec3f(AxisNormalized, SineOfRotation); + Result.W = HMM_CosF(AngleOfRotationRadians / 2.0f); + + return (Result); +} #ifdef __cplusplus } @@ -2792,444 +3144,3 @@ HMM_INLINE hmm_bool operator!=(hmm_vec4 Left, hmm_vec4 Right) #endif #endif /* HANDMADE_MATH_H */ - -#ifdef HANDMADE_MATH_IMPLEMENTATION - -COVERAGE(HMM_Power, 2) -float HMM_Power(float Base, int Exponent) -{ - ASSERT_COVERED(HMM_Power); - - float Result = 1.0f; - float Mul = Exponent < 0 ? 1.f / Base : Base; - int X = Exponent < 0 ? -Exponent : Exponent; - while (X) - { - if (X & 1) - { - ASSERT_COVERED(HMM_Power); - - Result *= Mul; - } - - Mul *= Mul; - X >>= 1; - } - - return (Result); -} - -#ifndef HANDMADE_MATH__USE_SSE -COVERAGE(HMM_Transpose, 1) -hmm_mat4 HMM_Transpose(hmm_mat4 Matrix) -{ - ASSERT_COVERED(HMM_Transpose); - - hmm_mat4 Result; - - int Columns; - for(Columns = 0; Columns < 4; ++Columns) - { - int Rows; - for(Rows = 0; Rows < 4; ++Rows) - { - Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows]; - } - } - - return (Result); -} -#endif - -#ifndef HANDMADE_MATH__USE_SSE -COVERAGE(HMM_AddMat4, 1) -hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_AddMat4); - - hmm_mat4 Result; - - int Columns; - for(Columns = 0; Columns < 4; ++Columns) - { - int Rows; - for(Rows = 0; Rows < 4; ++Rows) - { - Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] + Right.Elements[Columns][Rows]; - } - } - - return (Result); -} -#endif - -#ifndef HANDMADE_MATH__USE_SSE -COVERAGE(HMM_SubtractMat4, 1) -hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_SubtractMat4); - - hmm_mat4 Result; - - int Columns; - for(Columns = 0; Columns < 4; ++Columns) - { - int Rows; - for(Rows = 0; Rows < 4; ++Rows) - { - Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] - Right.Elements[Columns][Rows]; - } - } - - return (Result); -} -#endif - -COVERAGE(HMM_MultiplyMat4, 1) -hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_MultiplyMat4); - - hmm_mat4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.Columns[0] = HMM_LinearCombineSSE(Right.Columns[0], Left); - Result.Columns[1] = HMM_LinearCombineSSE(Right.Columns[1], Left); - Result.Columns[2] = HMM_LinearCombineSSE(Right.Columns[2], Left); - Result.Columns[3] = HMM_LinearCombineSSE(Right.Columns[3], Left); -#else - int Columns; - for(Columns = 0; Columns < 4; ++Columns) - { - int Rows; - for(Rows = 0; Rows < 4; ++Rows) - { - float Sum = 0; - int CurrentMatrice; - for(CurrentMatrice = 0; CurrentMatrice < 4; ++CurrentMatrice) - { - Sum += Left.Elements[CurrentMatrice][Rows] * Right.Elements[Columns][CurrentMatrice]; - } - - Result.Elements[Columns][Rows] = Sum; - } - } -#endif - - return (Result); -} - -#ifndef HANDMADE_MATH__USE_SSE -COVERAGE(HMM_MultiplyMat4f, 1) -hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar) -{ - ASSERT_COVERED(HMM_MultiplyMat4f); - - hmm_mat4 Result; - - int Columns; - for(Columns = 0; Columns < 4; ++Columns) - { - int Rows; - for(Rows = 0; Rows < 4; ++Rows) - { - Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] * Scalar; - } - } - - return (Result); -} -#endif - -COVERAGE(HMM_MultiplyMat4ByVec4, 1) -hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector) -{ - ASSERT_COVERED(HMM_MultiplyMat4ByVec4); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = HMM_LinearCombineSSE(Vector.InternalElementsSSE, Matrix); -#else - int Columns, Rows; - for(Rows = 0; Rows < 4; ++Rows) - { - float Sum = 0; - for(Columns = 0; Columns < 4; ++Columns) - { - Sum += Matrix.Elements[Columns][Rows] * Vector.Elements[Columns]; - } - - Result.Elements[Rows] = Sum; - } -#endif - - return (Result); -} - -#ifndef HANDMADE_MATH__USE_SSE -COVERAGE(HMM_DivideMat4f, 1); -hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar) -{ - ASSERT_COVERED(HMM_DivideMat4f); - - hmm_mat4 Result; - - int Columns; - for(Columns = 0; Columns < 4; ++Columns) - { - int Rows; - for(Rows = 0; Rows < 4; ++Rows) - { - Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] / Scalar; - } - } - - return (Result); -} -#endif - -COVERAGE(HMM_Rotate, 1) -hmm_mat4 HMM_Rotate(float AngleRadians, hmm_vec3 Axis) -{ - ASSERT_COVERED(HMM_Rotate); - - hmm_mat4 Result = HMM_Mat4d(1.0f); - - Axis = HMM_NormalizeVec3(Axis); - - float SinTheta = HMM_SinF(AngleRadians); - float CosTheta = HMM_CosF(AngleRadians); - float CosValue = 1.0f - CosTheta; - - Result.Elements[0][0] = (Axis.X * Axis.X * CosValue) + CosTheta; - Result.Elements[0][1] = (Axis.X * Axis.Y * CosValue) + (Axis.Z * SinTheta); - Result.Elements[0][2] = (Axis.X * Axis.Z * CosValue) - (Axis.Y * SinTheta); - - Result.Elements[1][0] = (Axis.Y * Axis.X * CosValue) - (Axis.Z * SinTheta); - Result.Elements[1][1] = (Axis.Y * Axis.Y * CosValue) + CosTheta; - Result.Elements[1][2] = (Axis.Y * Axis.Z * CosValue) + (Axis.X * SinTheta); - - Result.Elements[2][0] = (Axis.Z * Axis.X * CosValue) + (Axis.Y * SinTheta); - Result.Elements[2][1] = (Axis.Z * Axis.Y * CosValue) - (Axis.X * SinTheta); - Result.Elements[2][2] = (Axis.Z * Axis.Z * CosValue) + CosTheta; - - return (Result); -} - -COVERAGE(HMM_LookAt, 1) -hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up) -{ - ASSERT_COVERED(HMM_LookAt); - - hmm_mat4 Result; - - hmm_vec3 F = HMM_NormalizeVec3(HMM_SubtractVec3(Center, Eye)); - hmm_vec3 S = HMM_NormalizeVec3(HMM_Cross(F, Up)); - hmm_vec3 U = HMM_Cross(S, F); - - Result.Elements[0][0] = S.X; - Result.Elements[0][1] = U.X; - Result.Elements[0][2] = -F.X; - Result.Elements[0][3] = 0.0f; - - Result.Elements[1][0] = S.Y; - Result.Elements[1][1] = U.Y; - Result.Elements[1][2] = -F.Y; - Result.Elements[1][3] = 0.0f; - - Result.Elements[2][0] = S.Z; - Result.Elements[2][1] = U.Z; - Result.Elements[2][2] = -F.Z; - Result.Elements[2][3] = 0.0f; - - Result.Elements[3][0] = -HMM_DotVec3(S, Eye); - Result.Elements[3][1] = -HMM_DotVec3(U, Eye); - Result.Elements[3][2] = HMM_DotVec3(F, Eye); - Result.Elements[3][3] = 1.0f; - - return (Result); -} - -COVERAGE(HMM_InverseQuaternion, 1) -hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left) -{ - ASSERT_COVERED(HMM_InverseQuaternion); - - hmm_quaternion Conjugate; - hmm_quaternion Result; - 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 = HMM_DivideQuaternionF(Conjugate, NormSquared); - - return (Result); -} - -COVERAGE(HMM_Slerp, 1) -hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_Slerp); - - hmm_quaternion Result; - hmm_quaternion QuaternionLeft; - hmm_quaternion QuaternionRight; - - 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); -} - -COVERAGE(HMM_QuaternionToMat4, 1) -hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left) -{ - ASSERT_COVERED(HMM_QuaternionToMat4); - - hmm_mat4 Result; - - 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[0][3] = 0.0f; - - 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[1][3] = 0.0f; - - 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); - Result.Elements[2][3] = 0.0f; - - Result.Elements[3][0] = 0.0f; - Result.Elements[3][1] = 0.0f; - Result.Elements[3][2] = 0.0f; - Result.Elements[3][3] = 1.0f; - - return (Result); -} - -// This method taken from Mike Day at Insomniac Games. -// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf -// -// Note that as mentioned at the top of the paper, the paper assumes the matrix -// would be *post*-multiplied to a vector to rotate it, meaning the matrix is -// the transpose of what we're dealing with. But, because our matrices are -// stored in column-major order, the indices *appear* to match the paper. -// -// For example, m12 in the paper is row 1, column 2. We need to transpose it to -// row 2, column 1. But, because the column comes first when referencing -// elements, it looks like M.Elements[1][2]. -// -// Don't be confused! Or if you must be confused, at least trust this -// comment. :) -COVERAGE(HMM_Mat4ToQuaternion, 4) -hmm_quaternion HMM_Mat4ToQuaternion(hmm_mat4 M) -{ - float T; - hmm_quaternion Q; - - if (M.Elements[2][2] < 0.0f) { - if (M.Elements[0][0] > M.Elements[1][1]) { - ASSERT_COVERED(HMM_Mat4ToQuaternion); - - T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2]; - Q = HMM_Quaternion( - T, - M.Elements[0][1] + M.Elements[1][0], - M.Elements[2][0] + M.Elements[0][2], - M.Elements[1][2] - M.Elements[2][1] - ); - } else { - ASSERT_COVERED(HMM_Mat4ToQuaternion); - - T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2]; - Q = HMM_Quaternion( - M.Elements[0][1] + M.Elements[1][0], - T, - M.Elements[1][2] + M.Elements[2][1], - M.Elements[2][0] - M.Elements[0][2] - ); - } - } else { - if (M.Elements[0][0] < -M.Elements[1][1]) { - ASSERT_COVERED(HMM_Mat4ToQuaternion); - - T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2]; - Q = HMM_Quaternion( - M.Elements[2][0] + M.Elements[0][2], - M.Elements[1][2] + M.Elements[2][1], - T, - M.Elements[0][1] - M.Elements[1][0] - ); - } else { - ASSERT_COVERED(HMM_Mat4ToQuaternion); - - T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2]; - Q = HMM_Quaternion( - M.Elements[1][2] - M.Elements[2][1], - M.Elements[2][0] - M.Elements[0][2], - M.Elements[0][1] - M.Elements[1][0], - T - ); - } - } - - Q = HMM_MultiplyQuaternionF(Q, 0.5f / HMM_SquareRootF(T)); - - return Q; -} - -COVERAGE(HMM_QuaternionFromAxisAngle, 1) -hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotationRadians) -{ - ASSERT_COVERED(HMM_QuaternionFromAxisAngle); - - hmm_quaternion Result; - - hmm_vec3 AxisNormalized = HMM_NormalizeVec3(Axis); - float SineOfRotation = HMM_SinF(AngleOfRotationRadians / 2.0f); - - Result.XYZ = HMM_MultiplyVec3f(AxisNormalized, SineOfRotation); - Result.W = HMM_CosF(AngleOfRotationRadians / 2.0f); - - return (Result); -} - -#endif /* HANDMADE_MATH_IMPLEMENTATION */ diff --git a/test/HandmadeMath.c b/test/HandmadeMath.c index 2bb61ae..7cb4e41 100644 --- a/test/HandmadeMath.c +++ b/test/HandmadeMath.c @@ -2,6 +2,4 @@ #include "HandmadeTest.h" #endif -#define HANDMADE_MATH_IMPLEMENTATION -#define HANDMADE_MATH_NO_INLINE #include "../HandmadeMath.h" diff --git a/test/test.bat b/test/test.bat index 1d0b5f5..ed0474b 100644 --- a/test/test.bat +++ b/test/test.bat @@ -12,16 +12,16 @@ if "%1%"=="travis" ( if not exist "build" mkdir build pushd build -cl /Fehmm_test_c.exe ..\HandmadeMath.c ..\hmm_test.c +cl /Fehmm_test_c.exe ..\HandmadeMath.c ..\hmm_test.c || popd && exit /B hmm_test_c -cl /Fehmm_test_c_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.c ..\hmm_test.c +cl /Fehmm_test_c_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.c ..\hmm_test.c || popd && exit /B hmm_test_c_no_sse -cl /Fehmm_test_cpp.exe ..\HandmadeMath.cpp ..\hmm_test.cpp +cl /Fehmm_test_cpp.exe ..\HandmadeMath.cpp ..\hmm_test.cpp || popd && exit /B hmm_test_cpp -cl /Fehmm_test_cpp_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.cpp ..\hmm_test.cpp +cl /Fehmm_test_cpp_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.cpp ..\hmm_test.cpp || popd && exit /B hmm_test_cpp_no_sse popd