diff --git a/HandmadeMath.h b/HandmadeMath.h index cbe2ccb..5aa6afb 100644 --- a/HandmadeMath.h +++ b/HandmadeMath.h @@ -1,25 +1,11 @@ /* - HandmadeMath.h v1.12.1 + HandmadeMath.h v1.13.0 This is a single header file with a bunch of useful functions for game and graphics math operations. ============================================================================= - 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 @@ -27,7 +13,6 @@ in EXACTLY one C or C++ file that includes this header, BEFORE the include, like this: - #define HANDMADE_MATH_IMPLEMENTATION #define HANDMADE_MATH_NO_SSE #include "HandmadeMath.h" @@ -68,7 +53,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 @@ -167,12 +151,6 @@ extern "C" #define HMM_INLINE static inline -#ifdef HMM_STATIC -#define HMM_DEF static -#else -#define HMM_DEF extern -#endif - #if !defined(HMM_SINF) || !defined(HMM_COSF) || !defined(HMM_TANF) || \ !defined(HMM_SQRTF) || !defined(HMM_EXPF) || !defined(HMM_LOGF) || \ !defined(HMM_ACOSF) || !defined(HMM_ATANF)|| !defined(HMM_ATAN2F) @@ -389,15 +367,16 @@ typedef union hmm_mat4 #ifdef __cplusplus inline hmm_vec4 operator[](const int &Index) { - float* col = Elements[Index]; + hmm_vec4 Result; + float* Column = Elements[Index]; + - hmm_vec4 result; - result.Elements[0] = col[0]; - result.Elements[1] = col[1]; - result.Elements[2] = col[2]; - result.Elements[3] = col[3]; + Result.Elements[0] = Column[0]; + Result.Elements[1] = Column[1]; + Result.Elements[2] = Column[2]; + Result.Elements[3] = Column[3]; - return result; + return Result; } #endif } hmm_mat4; @@ -553,7 +532,29 @@ HMM_INLINE float HMM_PREFIX(RSquareRootF)(float Float) return(Result); } -HMM_DEF float HMM_PREFIX(Power)(float Base, int Exponent); +COVERAGE(HMM_Power, 2) +HMM_INLINE float HMM_PREFIX(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_PREFIX(PowerF)(float Base, float Exponent) @@ -1308,7 +1309,6 @@ HMM_INLINE hmm_mat4 HMM_PREFIX(Mat4d)(float Diagonal) return (Result); } -#ifdef HANDMADE_MATH__USE_SSE COVERAGE(HMM_Transpose, 1) HMM_INLINE hmm_mat4 HMM_PREFIX(Transpose)(hmm_mat4 Matrix) { @@ -1316,15 +1316,24 @@ HMM_INLINE hmm_mat4 HMM_PREFIX(Transpose)(hmm_mat4 Matrix) hmm_mat4 Result = Matrix; +#ifdef HANDMADE_MATH__USE_SSE _MM_TRANSPOSE4_PS(Result.Columns[0], Result.Columns[1], Result.Columns[2], Result.Columns[3]); +#else + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows]; + } + } +#endif + return (Result); } -#else -HMM_DEF hmm_mat4 HMM_PREFIX(Transpose)(hmm_mat4 Matrix); -#endif -#ifdef HANDMADE_MATH__USE_SSE COVERAGE(HMM_AddMat4, 1) HMM_INLINE hmm_mat4 HMM_PREFIX(AddMat4)(hmm_mat4 Left, hmm_mat4 Right) { @@ -1332,18 +1341,27 @@ HMM_INLINE hmm_mat4 HMM_PREFIX(AddMat4)(hmm_mat4 Left, hmm_mat4 Right) hmm_mat4 Result; +#ifdef HANDMADE_MATH__USE_SSE Result.Columns[0] = _mm_add_ps(Left.Columns[0], Right.Columns[0]); Result.Columns[1] = _mm_add_ps(Left.Columns[1], Right.Columns[1]); Result.Columns[2] = _mm_add_ps(Left.Columns[2], Right.Columns[2]); Result.Columns[3] = _mm_add_ps(Left.Columns[3], Right.Columns[3]); - - return (Result); -} #else -HMM_DEF hmm_mat4 HMM_PREFIX(AddMat4)(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]; + } + } #endif -#ifdef HANDMADE_MATH__USE_SSE + + return (Result); +} + COVERAGE(HMM_SubtractMat4, 1) HMM_INLINE hmm_mat4 HMM_PREFIX(SubtractMat4)(hmm_mat4 Left, hmm_mat4 Right) { @@ -1351,20 +1369,61 @@ HMM_INLINE hmm_mat4 HMM_PREFIX(SubtractMat4)(hmm_mat4 Left, hmm_mat4 Right) hmm_mat4 Result; +#ifdef HANDMADE_MATH__USE_SSE Result.Columns[0] = _mm_sub_ps(Left.Columns[0], Right.Columns[0]); Result.Columns[1] = _mm_sub_ps(Left.Columns[1], Right.Columns[1]); Result.Columns[2] = _mm_sub_ps(Left.Columns[2], Right.Columns[2]); Result.Columns[3] = _mm_sub_ps(Left.Columns[3], Right.Columns[3]); +#else + 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]; + } + } +#endif + + return (Result); +} + +COVERAGE(HMM_MultiplyMat4, 1) +HMM_INLINE hmm_mat4 HMM_PREFIX(MultiplyMat4)(hmm_mat4 Left, hmm_mat4 Right) +{ + ASSERT_COVERED(HMM_MultiplyMat4); + + hmm_mat4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.Columns[0] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[0], Left); + Result.Columns[1] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[1], Left); + Result.Columns[2] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[2], Left); + Result.Columns[3] = HMM_PREFIX(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); } -#else -HMM_DEF hmm_mat4 HMM_PREFIX(SubtractMat4)(hmm_mat4 Left, hmm_mat4 Right); -#endif -HMM_DEF hmm_mat4 HMM_PREFIX(MultiplyMat4)(hmm_mat4 Left, hmm_mat4 Right); -#ifdef HANDMADE_MATH__USE_SSE COVERAGE(HMM_MultiplyMat4f, 1) HMM_INLINE hmm_mat4 HMM_PREFIX(MultiplyMat4f)(hmm_mat4 Matrix, float Scalar) { @@ -1372,21 +1431,54 @@ HMM_INLINE hmm_mat4 HMM_PREFIX(MultiplyMat4f)(hmm_mat4 Matrix, float Scalar) hmm_mat4 Result; +#ifdef HANDMADE_MATH__USE_SSE __m128 SSEScalar = _mm_set1_ps(Scalar); Result.Columns[0] = _mm_mul_ps(Matrix.Columns[0], SSEScalar); Result.Columns[1] = _mm_mul_ps(Matrix.Columns[1], SSEScalar); Result.Columns[2] = _mm_mul_ps(Matrix.Columns[2], SSEScalar); Result.Columns[3] = _mm_mul_ps(Matrix.Columns[3], SSEScalar); +#else + 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; + } + } +#endif return (Result); } -#else -HMM_DEF hmm_mat4 HMM_PREFIX(MultiplyMat4f)(hmm_mat4 Matrix, float Scalar); -#endif -HMM_DEF hmm_vec4 HMM_PREFIX(MultiplyMat4ByVec4)(hmm_mat4 Matrix, hmm_vec4 Vector); +COVERAGE(HMM_MultiplyMat4ByVec4, 1) +HMM_INLINE hmm_vec4 HMM_PREFIX(MultiplyMat4ByVec4)(hmm_mat4 Matrix, hmm_vec4 Vector) +{ + ASSERT_COVERED(HMM_MultiplyMat4ByVec4); + + hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE + Result.InternalElementsSSE = HMM_PREFIX(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_PREFIX(DivideMat4f)(hmm_mat4 Matrix, float Scalar) { @@ -1394,18 +1486,26 @@ HMM_INLINE hmm_mat4 HMM_PREFIX(DivideMat4f)(hmm_mat4 Matrix, float Scalar) hmm_mat4 Result; +#ifdef HANDMADE_MATH__USE_SSE __m128 SSEScalar = _mm_set1_ps(Scalar); Result.Columns[0] = _mm_div_ps(Matrix.Columns[0], SSEScalar); Result.Columns[1] = _mm_div_ps(Matrix.Columns[1], SSEScalar); Result.Columns[2] = _mm_div_ps(Matrix.Columns[2], SSEScalar); Result.Columns[3] = _mm_div_ps(Matrix.Columns[3], SSEScalar); +#else + 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; + } + } +#endif return (Result); } -#else -HMM_DEF hmm_mat4 HMM_PREFIX(DivideMat4f)(hmm_mat4 Matrix, float Scalar); -#endif - /* * Common graphics transformations @@ -1465,7 +1565,35 @@ HMM_INLINE hmm_mat4 HMM_PREFIX(Translate)(hmm_vec3 Translation) return (Result); } -HMM_DEF hmm_mat4 HMM_PREFIX(Rotate)(float Angle, hmm_vec3 Axis); +COVERAGE(HMM_Rotate, 1) +HMM_INLINE hmm_mat4 HMM_PREFIX(Rotate)(float Angle, hmm_vec3 Axis) +{ + ASSERT_COVERED(HMM_Rotate); + + hmm_mat4 Result = HMM_PREFIX(Mat4d)(1.0f); + + Axis = HMM_PREFIX(NormalizeVec3)(Axis); + + float SinTheta = HMM_PREFIX(SinF)(HMM_PREFIX(ToRadians)(Angle)); + float CosTheta = HMM_PREFIX(CosF)(HMM_PREFIX(ToRadians)(Angle)); + 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_PREFIX(Scale)(hmm_vec3 Scale) @@ -1481,8 +1609,39 @@ HMM_INLINE hmm_mat4 HMM_PREFIX(Scale)(hmm_vec3 Scale) return (Result); } -HMM_DEF hmm_mat4 HMM_PREFIX(LookAt)(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up); +COVERAGE(HMM_LookAt, 1) +HMM_INLINE hmm_mat4 HMM_PREFIX(LookAt)(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up) +{ + ASSERT_COVERED(HMM_LookAt); + hmm_mat4 Result; + + hmm_vec3 F = HMM_PREFIX(NormalizeVec3)(HMM_PREFIX(SubtractVec3)(Center, Eye)); + hmm_vec3 S = HMM_PREFIX(NormalizeVec3)(HMM_PREFIX(Cross)(F, Up)); + hmm_vec3 U = HMM_PREFIX(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_PREFIX(DotVec3)(S, Eye); + Result.Elements[3][1] = -HMM_PREFIX(DotVec3)(U, Eye); + Result.Elements[3][2] = HMM_PREFIX(DotVec3)(F, Eye); + Result.Elements[3][3] = 1.0f; + + return (Result); +} /* * Quaternion operations @@ -1639,8 +1798,6 @@ HMM_INLINE hmm_quaternion HMM_PREFIX(DivideQuaternionF)(hmm_quaternion Left, flo return (Result); } -HMM_DEF hmm_quaternion HMM_PREFIX(InverseQuaternion)(hmm_quaternion Left); - COVERAGE(HMM_DotQuaternion, 1) HMM_INLINE float HMM_PREFIX(DotQuaternion)(hmm_quaternion Left, hmm_quaternion Right) { @@ -1662,6 +1819,25 @@ HMM_INLINE float HMM_PREFIX(DotQuaternion)(hmm_quaternion Left, hmm_quaternion R return (Result); } + +COVERAGE(HMM_InverseQuaternion, 1) +HMM_INLINE hmm_quaternion HMM_PREFIX(InverseQuaternion)(hmm_quaternion Left) +{ + ASSERT_COVERED(HMM_InverseQuaternion); + + hmm_quaternion Result; + + Result.X = -Left.X; + Result.Y = -Left.Y; + Result.Z = -Left.Z; + Result.W = Left.W; + + Result = HMM_PREFIX(DivideQuaternionF)(Result, (HMM_PREFIX(DotQuaternion)(Left, Left))); + + return (Result); +} + + COVERAGE(HMM_NormalizeQuaternion, 1) HMM_INLINE hmm_quaternion HMM_PREFIX(NormalizeQuaternion)(hmm_quaternion Left) { @@ -1699,10 +1875,163 @@ HMM_INLINE hmm_quaternion HMM_PREFIX(NLerp)(hmm_quaternion Left, float Time, hmm return (Result); } -HMM_DEF hmm_quaternion HMM_PREFIX(Slerp)(hmm_quaternion Left, float Time, hmm_quaternion Right); -HMM_DEF hmm_mat4 HMM_PREFIX(QuaternionToMat4)(hmm_quaternion Left); -HMM_DEF hmm_quaternion HMM_PREFIX(Mat4ToQuaternion)(hmm_mat4 Left); -HMM_DEF hmm_quaternion HMM_PREFIX(QuaternionFromAxisAngle)(hmm_vec3 Axis, float AngleOfRotation); +COVERAGE(HMM_Slerp, 1) +HMM_INLINE hmm_quaternion HMM_PREFIX(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_PREFIX(DotQuaternion)(Left, Right); + float Angle = HMM_PREFIX(ACosF)(Cos_Theta); + + float S1 = HMM_PREFIX(SinF)((1.0f - Time) * Angle); + float S2 = HMM_PREFIX(SinF)(Time * Angle); + float Is = 1.0f / HMM_PREFIX(SinF)(Angle); + + QuaternionLeft = HMM_PREFIX(MultiplyQuaternionF)(Left, S1); + QuaternionRight = HMM_PREFIX(MultiplyQuaternionF)(Right, S2); + + Result = HMM_PREFIX(AddQuaternion)(QuaternionLeft, QuaternionRight); + Result = HMM_PREFIX(MultiplyQuaternionF)(Result, Is); + + return (Result); +} + +COVERAGE(HMM_QuaternionToMat4, 1) +HMM_INLINE hmm_mat4 HMM_PREFIX(QuaternionToMat4)(hmm_quaternion Left) +{ + ASSERT_COVERED(HMM_QuaternionToMat4); + + hmm_mat4 Result; + + hmm_quaternion NormalizedQuaternion = HMM_PREFIX(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_PREFIX(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_PREFIX(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_PREFIX(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_PREFIX(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_PREFIX(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_PREFIX(MultiplyQuaternionF)(Q, 0.5f / HMM_PREFIX(SquareRootF)(T)); + + return Q; +} + +COVERAGE(HMM_QuaternionFromAxisAngle, 1) +HMM_INLINE hmm_quaternion HMM_PREFIX(QuaternionFromAxisAngle)(hmm_vec3 Axis, float AngleOfRotation) +{ + ASSERT_COVERED(HMM_QuaternionFromAxisAngle); + + hmm_quaternion Result; + + hmm_vec3 AxisNormalized = HMM_PREFIX(NormalizeVec3)(Axis); + float SineOfRotation = HMM_PREFIX(SinF)(AngleOfRotation / 2.0f); + + Result.XYZ = HMM_PREFIX(MultiplyVec3f)(AxisNormalized, SineOfRotation); + Result.W = HMM_PREFIX(CosF)(AngleOfRotation / 2.0f); + + return (Result); +} #ifdef __cplusplus } @@ -1716,7 +2045,6 @@ HMM_INLINE float HMM_PREFIX(Length)(hmm_vec2 A) ASSERT_COVERED(HMM_LengthVec2CPP); float Result = HMM_PREFIX(LengthVec2)(A); - return (Result); } @@ -2844,437 +3172,5 @@ HMM_INLINE hmm_vec4 operator-(hmm_vec4 In) #endif /* HANDMADE_MATH_H */ -#ifdef HANDMADE_MATH_IMPLEMENTATION -COVERAGE(HMM_Power, 2) -float HMM_PREFIX(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_PREFIX(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_PREFIX(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_PREFIX(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_PREFIX(MultiplyMat4)(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_MultiplyMat4); - - hmm_mat4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.Columns[0] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[0], Left); - Result.Columns[1] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[1], Left); - Result.Columns[2] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[2], Left); - Result.Columns[3] = HMM_PREFIX(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_PREFIX(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_PREFIX(MultiplyMat4ByVec4)(hmm_mat4 Matrix, hmm_vec4 Vector) -{ - ASSERT_COVERED(HMM_MultiplyMat4ByVec4); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = HMM_PREFIX(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_PREFIX(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_PREFIX(Rotate)(float Angle, hmm_vec3 Axis) -{ - ASSERT_COVERED(HMM_Rotate); - - hmm_mat4 Result = HMM_PREFIX(Mat4d)(1.0f); - - Axis = HMM_PREFIX(NormalizeVec3)(Axis); - - float SinTheta = HMM_PREFIX(SinF)(HMM_PREFIX(ToRadians)(Angle)); - float CosTheta = HMM_PREFIX(CosF)(HMM_PREFIX(ToRadians)(Angle)); - 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_PREFIX(LookAt)(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up) -{ - ASSERT_COVERED(HMM_LookAt); - - hmm_mat4 Result; - - hmm_vec3 F = HMM_PREFIX(NormalizeVec3)(HMM_PREFIX(SubtractVec3)(Center, Eye)); - hmm_vec3 S = HMM_PREFIX(NormalizeVec3)(HMM_PREFIX(Cross)(F, Up)); - hmm_vec3 U = HMM_PREFIX(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_PREFIX(DotVec3)(S, Eye); - Result.Elements[3][1] = -HMM_PREFIX(DotVec3)(U, Eye); - Result.Elements[3][2] = HMM_PREFIX(DotVec3)(F, Eye); - Result.Elements[3][3] = 1.0f; - - return (Result); -} - -COVERAGE(HMM_InverseQuaternion, 1) -hmm_quaternion HMM_PREFIX(InverseQuaternion)(hmm_quaternion Left) -{ - ASSERT_COVERED(HMM_InverseQuaternion); - - hmm_quaternion Result; - - Result.X = -Left.X; - Result.Y = -Left.Y; - Result.Z = -Left.Z; - Result.W = Left.W; - - Result = HMM_PREFIX(DivideQuaternionF)(Result, (HMM_PREFIX(DotQuaternion)(Left, Left))); - - return (Result); -} - -COVERAGE(HMM_Slerp, 1) -hmm_quaternion HMM_PREFIX(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_PREFIX(DotQuaternion)(Left, Right); - float Angle = HMM_PREFIX(ACosF)(Cos_Theta); - - float S1 = HMM_PREFIX(SinF)((1.0f - Time) * Angle); - float S2 = HMM_PREFIX(SinF)(Time * Angle); - float Is = 1.0f / HMM_PREFIX(SinF)(Angle); - - QuaternionLeft = HMM_PREFIX(MultiplyQuaternionF)(Left, S1); - QuaternionRight = HMM_PREFIX(MultiplyQuaternionF)(Right, S2); - - Result = HMM_PREFIX(AddQuaternion)(QuaternionLeft, QuaternionRight); - Result = HMM_PREFIX(MultiplyQuaternionF)(Result, Is); - - return (Result); -} - -COVERAGE(HMM_QuaternionToMat4, 1) -hmm_mat4 HMM_PREFIX(QuaternionToMat4)(hmm_quaternion Left) -{ - ASSERT_COVERED(HMM_QuaternionToMat4); - - hmm_mat4 Result; - - hmm_quaternion NormalizedQuaternion = HMM_PREFIX(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_PREFIX(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_PREFIX(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_PREFIX(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_PREFIX(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_PREFIX(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_PREFIX(MultiplyQuaternionF)(Q, 0.5f / HMM_PREFIX(SquareRootF)(T)); - - return Q; -} - -COVERAGE(HMM_QuaternionFromAxisAngle, 1) -hmm_quaternion HMM_PREFIX(QuaternionFromAxisAngle)(hmm_vec3 Axis, float AngleOfRotation) -{ - ASSERT_COVERED(HMM_QuaternionFromAxisAngle); - - hmm_quaternion Result; - - hmm_vec3 AxisNormalized = HMM_PREFIX(NormalizeVec3)(Axis); - float SineOfRotation = HMM_PREFIX(SinF)(AngleOfRotation / 2.0f); - - Result.XYZ = HMM_PREFIX(MultiplyVec3f)(AxisNormalized, SineOfRotation); - Result.W = HMM_PREFIX(CosF)(AngleOfRotation / 2.0f); - - return (Result); -} - -#endif /* HANDMADE_MATH_IMPLEMENTATION */ diff --git a/README.md b/README.md index 9e1ca2d..646e680 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ To get started, go download [the latest release](https://github.com/HandmadeMath Version | Changes | ----------------|----------------| + +**1.13.0** | Fully inlined HandmadeMath.h. No need for HANDMADE_MATH_IMPLEMENTATION anymore | **1.12.1** | Added extra parentheses around some macros | **1.12.0** | Added Unary Minus operator for `HMM_Vec2`, `HMM_Vec3`, and `HMM_Vec4`. | **1.11.1** | Added HMM_PREFIX macro to a few functions that were missing it. | diff --git a/test/run_test_clang.bat b/test/run_test_clang.bat new file mode 100644 index 0000000..53094d9 --- /dev/null +++ b/test/run_test_clang.bat @@ -0,0 +1,18 @@ +@echo off + +if not exist "build" mkdir build +pushd build + +clang-cl /Fehmm_test_c.exe ..\HandmadeMath.c ..\hmm_test.c +hmm_test_c + +clang-cl /Fehmm_test_c_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.c ..\hmm_test.c +hmm_test_c_no_sse + +clang-cl /Fehmm_test_cpp.exe ..\HandmadeMath.cpp ..\hmm_test.cpp +hmm_test_cpp + +clang-cl /Fehmm_test_cpp_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.cpp ..\hmm_test.cpp +hmm_test_cpp_no_sse + +popd diff --git a/test/test.bat b/test/run_test_msvc.bat similarity index 96% rename from test/test.bat rename to test/run_test_msvc.bat index 1d0b5f5..6f52509 100644 --- a/test/test.bat +++ b/test/run_test_msvc.bat @@ -1,27 +1,27 @@ -@echo off - -if "%1%"=="travis" ( - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" -host_arch=amd64 -arch=amd64 -) else ( - where /q cl - if ERRORLEVEL 1 ( - for /f "delims=" %%a in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -find VC\Auxiliary\Build\vcvarsall.bat') do (%%a x64) - ) -) - -if not exist "build" mkdir build -pushd build - -cl /Fehmm_test_c.exe ..\HandmadeMath.c ..\hmm_test.c -hmm_test_c - -cl /Fehmm_test_c_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.c ..\hmm_test.c -hmm_test_c_no_sse - -cl /Fehmm_test_cpp.exe ..\HandmadeMath.cpp ..\hmm_test.cpp -hmm_test_cpp - -cl /Fehmm_test_cpp_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.cpp ..\hmm_test.cpp -hmm_test_cpp_no_sse - -popd +@echo off + +if "%1%"=="travis" ( + call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" -host_arch=amd64 -arch=amd64 +) else ( + where /q cl + if ERRORLEVEL 1 ( + for /f "delims=" %%a in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -find VC\Auxiliary\Build\vcvarsall.bat') do (%%a x64) + ) +) + +if not exist "build" mkdir build +pushd build + +cl /Fehmm_test_c.exe ..\HandmadeMath.c ..\hmm_test.c +hmm_test_c + +cl /Fehmm_test_c_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.c ..\hmm_test.c +hmm_test_c_no_sse + +cl /Fehmm_test_cpp.exe ..\HandmadeMath.cpp ..\hmm_test.cpp +hmm_test_cpp + +cl /Fehmm_test_cpp_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.cpp ..\hmm_test.cpp +hmm_test_cpp_no_sse + +popd