Compare commits

..

7 Commits

Author SHA1 Message Date
Ben Visness
422bc588e9 Fix inverse perspective 2023-02-20 13:03:46 -06:00
Ben Visness
beb837a3c6 Tweak docs, add tests, find bugs 2023-02-02 19:18:24 -06:00
Ben Visness
50ab55b3bc Rewrite the update tool in Python
Less than 200 lines, properly cross platform, actually outputs error
messages if things break, better flag handling. Everyone has
Python anyway.
2023-01-27 03:39:38 -06:00
Ben Visness
22d743ce3d Change naming convention for NO/ZO (the update tool needs updating) 2023-01-26 22:27:09 -06:00
Logan Forman
d4918a514e Drop Projection Matrix Config; Make explicit. (#154)
* Add N0/Z0 projection

Remove configuration macro

* Update update_hmm.c

Co-authored-by: Ben Visness <bvisness@users.noreply.github.com>
2023-01-26 21:01:31 -06:00
Logan Forman
37aa3fa6a0 Unroll sse (#153)
* Unroll loops and SSE consistency

* Fix tranposes
2023-01-26 20:56:28 -06:00
dev_dwarf
7e493a5481 Fix LH perspective matrix
Off by a sign before.
2023-01-26 19:55:21 -06:00
10 changed files with 724 additions and 1028 deletions

View File

@@ -6,8 +6,8 @@
both C and C++.
=============================================================================
CONFIG
=============================================================================
By default, all angles in Handmade Math are specified in radians. However, it
can be configured to use degrees or turns instead. Use one of the following
@@ -24,25 +24,13 @@
HMM_AngleDeg(degrees)
HMM_AngleTurn(turns)
-----------------------------------------------------------------------------
By default, Handmade Math's projection matrices use a Normalized Device
Coordinates (NDC) range of X: [-1, 1], Y: [-1, 1], Z: [-1, 1], as is standard
in OpenGL. However, other graphics APIs require the range with Z: [0, 1], as
this has better numerical properties for depth buffers.
To use NDC with Z: [0, 1], you must define HANDMADE_MATH_USE_NDC_Z01 before
including this header, which will make HMM_Perspective and HMM_Orthographic
functions project Z to [0, 1]:
#define HANDMADE_MATH_USE_NDC_Z01
#include "HandmadeMath.h"
The definitions of these functions change depending on the default unit.
-----------------------------------------------------------------------------
Handmade Math ships with SSE (SIMD) implementations of several common
operations. To disable the use of SSE intrinsics, you must
define HANDMADE_MATH_NO_SSE before including this file:
operations. To disable the use of SSE intrinsics, you must define
HANDMADE_MATH_NO_SSE before including this file:
#define HANDMADE_MATH_NO_SSE
#include "HandmadeMath.h"
@@ -541,13 +529,7 @@ static inline float HMM_InvSqrtF(float Float)
float Result;
#ifdef HANDMADE_MATH__USE_SSE
__m128 In = _mm_set_ss(Float);
__m128 Out = _mm_rsqrt_ss(In);
Result = _mm_cvtss_f32(Out);
#else
Result = 1.0f/HMM_SqrtF(Float);
#endif
return Result;
}
@@ -974,7 +956,7 @@ static inline float HMM_DotV4(HMM_Vec4 Left, HMM_Vec4 Right)
SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo);
_mm_store_ss(&Result, SSEResultOne);
#else
Result = (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z) + (Left.W * Right.W);
Result = ((Left.X * Right.X) + (Left.Z * Right.Z)) + ((Left.Y * Right.Y) + (Left.W * Right.W));
#endif
return Result;
@@ -1102,16 +1084,25 @@ static inline HMM_Vec4 HMM_LinearCombineV4M4(HMM_Vec4 Left, HMM_Mat4 Right)
Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xaa), Right.Columns[2].SSE));
Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xff), Right.Columns[3].SSE));
#else
int Columns, Rows;
for(Rows = 0; Rows < 4; ++Rows)
{
float Sum = 0;
for(Columns = 0; Columns < 4; ++Columns)
{
Sum += Left.Elements[Columns]*Right.Elements[Columns][Rows];
}
Result.Elements[Rows] = Sum;
}
Result.X = Left.Elements[0] * Right.Columns[0].X;
Result.Y = Left.Elements[0] * Right.Columns[0].Y;
Result.Z = Left.Elements[0] * Right.Columns[0].Z;
Result.W = Left.Elements[0] * Right.Columns[0].W;
Result.X += Left.Elements[1] * Right.Columns[1].X;
Result.Y += Left.Elements[1] * Right.Columns[1].Y;
Result.Z += Left.Elements[1] * Right.Columns[1].Z;
Result.W += Left.Elements[1] * Right.Columns[1].W;
Result.X += Left.Elements[2] * Right.Columns[2].X;
Result.Y += Left.Elements[2] * Right.Columns[2].Y;
Result.Z += Left.Elements[2] * Right.Columns[2].Z;
Result.W += Left.Elements[2] * Right.Columns[2].W;
Result.X += Left.Elements[3] * Right.Columns[3].X;
Result.Y += Left.Elements[3] * Right.Columns[3].Y;
Result.Z += Left.Elements[3] * Right.Columns[3].Z;
Result.W += Left.Elements[3] * Right.Columns[3].W;
#endif
return Result;
@@ -1146,16 +1137,10 @@ static inline HMM_Mat2 HMM_TransposeM2(HMM_Mat2 Matrix)
{
ASSERT_COVERED(HMM_TransposeM2);
HMM_Mat2 Result;
HMM_Mat2 Result = Matrix;
int Columns, Rows;
for(Columns = 0; Columns < 2; ++Columns)
{
for(Rows = 0; Rows < 2; ++Rows)
{
Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows];
}
}
Result.Elements[0][1] = Matrix.Elements[1][0];
Result.Elements[1][0] = Matrix.Elements[0][1];
return Result;
}
@@ -1166,15 +1151,11 @@ static inline HMM_Mat2 HMM_AddM2(HMM_Mat2 Left, HMM_Mat2 Right)
ASSERT_COVERED(HMM_AddM2);
HMM_Mat2 Result;
int Columns;
for(Columns = 0; Columns < 2; ++Columns)
{
int Rows;
for(Rows = 0; Rows < 2; ++Rows)
{
Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] + Right.Elements[Columns][Rows];
}
}
Result.Elements[0][0] = Left.Elements[0][0] + Right.Elements[0][0];
Result.Elements[0][1] = Left.Elements[0][1] + Right.Elements[0][1];
Result.Elements[1][0] = Left.Elements[1][0] + Right.Elements[1][0];
Result.Elements[1][1] = Left.Elements[1][1] + Right.Elements[1][1];
return Result;
}
@@ -1185,16 +1166,12 @@ static inline HMM_Mat2 HMM_SubM2(HMM_Mat2 Left, HMM_Mat2 Right)
ASSERT_COVERED(HMM_SubM2);
HMM_Mat2 Result;
int Columns;
for(Columns = 0; Columns < 2; ++Columns)
{
int Rows;
for(Rows = 0; Rows < 2; ++Rows)
{
Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] - Right.Elements[Columns][Rows];
}
}
Result.Elements[0][0] = Left.Elements[0][0] - Right.Elements[0][0];
Result.Elements[0][1] = Left.Elements[0][1] - Right.Elements[0][1];
Result.Elements[1][0] = Left.Elements[1][0] - Right.Elements[1][0];
Result.Elements[1][1] = Left.Elements[1][1] - Right.Elements[1][1];
return Result;
}
@@ -1204,16 +1181,12 @@ static inline HMM_Vec2 HMM_MulM2V2(HMM_Mat2 Matrix, HMM_Vec2 Vector)
ASSERT_COVERED(HMM_MulM2V2);
HMM_Vec2 Result;
int Columns, Rows;
for(Rows = 0; Rows < 2; ++Rows)
{
float Sum = 0.0f;
for(Columns = 0; Columns < 2; ++Columns)
{
Sum += Matrix.Elements[Columns][Rows] * Vector.Elements[Columns];
}
Result.Elements[Rows] = Sum;
}
Result.X = Vector.Elements[0] * Matrix.Columns[0].X;
Result.Y = Vector.Elements[0] * Matrix.Columns[0].Y;
Result.X += Vector.Elements[1] * Matrix.Columns[1].X;
Result.Y += Vector.Elements[1] * Matrix.Columns[1].Y;
return Result;
}
@@ -1236,16 +1209,12 @@ static inline HMM_Mat2 HMM_MulM2F(HMM_Mat2 Matrix, float Scalar)
ASSERT_COVERED(HMM_MulM2F);
HMM_Mat2 Result;
int Columns;
for(Columns = 0; Columns < 2; ++Columns)
{
int Rows;
for(Rows = 0; Rows < 2; ++Rows)
{
Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] * Scalar;
}
}
Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar;
Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar;
Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar;
Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar;
return Result;
}
@@ -1255,15 +1224,11 @@ static inline HMM_Mat2 HMM_DivM2F(HMM_Mat2 Matrix, float Scalar)
ASSERT_COVERED(HMM_DivM2F);
HMM_Mat2 Result;
int Columns;
for(Columns = 0; Columns < 2; ++Columns)
{
int Rows;
for(Rows = 0; Rows < 2; ++Rows)
{
Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] / Scalar;
}
}
Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar;
Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar;
Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar;
Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar;
return Result;
}
@@ -1321,17 +1286,14 @@ static inline HMM_Mat3 HMM_TransposeM3(HMM_Mat3 Matrix)
{
ASSERT_COVERED(HMM_TransposeM3);
HMM_Mat3 Result;
HMM_Mat3 Result = Matrix;
int Columns;
for(Columns = 0; Columns < 3; ++Columns)
{
int Rows;
for(Rows = 0; Rows < 3; ++Rows)
{
Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows];
}
}
Result.Elements[0][1] = Matrix.Elements[1][0];
Result.Elements[0][2] = Matrix.Elements[2][0];
Result.Elements[1][0] = Matrix.Elements[0][1];
Result.Elements[1][2] = Matrix.Elements[2][1];
Result.Elements[2][1] = Matrix.Elements[1][2];
Result.Elements[2][0] = Matrix.Elements[0][2];
return Result;
}
@@ -1342,16 +1304,17 @@ static inline HMM_Mat3 HMM_AddM3(HMM_Mat3 Left, HMM_Mat3 Right)
ASSERT_COVERED(HMM_AddM3);
HMM_Mat3 Result;
int Columns;
for(Columns = 0; Columns < 3; ++Columns)
{
int Rows;
for(Rows = 0; Rows < 3; ++Rows)
{
Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] + Right.Elements[Columns][Rows];
}
}
Result.Elements[0][0] = Left.Elements[0][0] + Right.Elements[0][0];
Result.Elements[0][1] = Left.Elements[0][1] + Right.Elements[0][1];
Result.Elements[0][2] = Left.Elements[0][2] + Right.Elements[0][2];
Result.Elements[1][0] = Left.Elements[1][0] + Right.Elements[1][0];
Result.Elements[1][1] = Left.Elements[1][1] + Right.Elements[1][1];
Result.Elements[1][2] = Left.Elements[1][2] + Right.Elements[1][2];
Result.Elements[2][0] = Left.Elements[2][0] + Right.Elements[2][0];
Result.Elements[2][1] = Left.Elements[2][1] + Right.Elements[2][1];
Result.Elements[2][2] = Left.Elements[2][2] + Right.Elements[2][2];
return Result;
}
@@ -1361,15 +1324,16 @@ static inline HMM_Mat3 HMM_SubM3(HMM_Mat3 Left, HMM_Mat3 Right)
ASSERT_COVERED(HMM_SubM3);
HMM_Mat3 Result;
int Columns;
for(Columns = 0; Columns < 3; ++Columns)
{
int Rows;
for(Rows = 0; Rows < 3; ++Rows)
{
Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] - Right.Elements[Columns][Rows];
}
}
Result.Elements[0][0] = Left.Elements[0][0] - Right.Elements[0][0];
Result.Elements[0][1] = Left.Elements[0][1] - Right.Elements[0][1];
Result.Elements[0][2] = Left.Elements[0][2] - Right.Elements[0][2];
Result.Elements[1][0] = Left.Elements[1][0] - Right.Elements[1][0];
Result.Elements[1][1] = Left.Elements[1][1] - Right.Elements[1][1];
Result.Elements[1][2] = Left.Elements[1][2] - Right.Elements[1][2];
Result.Elements[2][0] = Left.Elements[2][0] - Right.Elements[2][0];
Result.Elements[2][1] = Left.Elements[2][1] - Right.Elements[2][1];
Result.Elements[2][2] = Left.Elements[2][2] - Right.Elements[2][2];
return Result;
}
@@ -1380,17 +1344,19 @@ static inline HMM_Vec3 HMM_MulM3V3(HMM_Mat3 Matrix, HMM_Vec3 Vector)
ASSERT_COVERED(HMM_MulM3V3);
HMM_Vec3 Result;
int Columns, Rows;
for(Rows = 0; Rows < 3; ++Rows)
{
float Sum = 0.0f;
for(Columns = 0; Columns < 3; ++Columns)
{
Sum += Matrix.Elements[Columns][Rows] * Vector.Elements[Columns];
}
Result.Elements[Rows] = Sum;
}
Result.X = Vector.Elements[0] * Matrix.Columns[0].X;
Result.Y = Vector.Elements[0] * Matrix.Columns[0].Y;
Result.Z = Vector.Elements[0] * Matrix.Columns[0].Z;
Result.X += Vector.Elements[1] * Matrix.Columns[1].X;
Result.Y += Vector.Elements[1] * Matrix.Columns[1].Y;
Result.Z += Vector.Elements[1] * Matrix.Columns[1].Z;
Result.X += Vector.Elements[2] * Matrix.Columns[2].X;
Result.Y += Vector.Elements[2] * Matrix.Columns[2].Y;
Result.Z += Vector.Elements[2] * Matrix.Columns[2].Z;
return Result;
}
@@ -1413,15 +1379,16 @@ static inline HMM_Mat3 HMM_MulM3F(HMM_Mat3 Matrix, float Scalar)
ASSERT_COVERED(HMM_MulM3F);
HMM_Mat3 Result;
int Columns;
for(Columns = 0; Columns < 3; ++Columns)
{
int Rows;
for(Rows = 0; Rows < 3; ++Rows)
{
Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] * Scalar;
}
}
Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar;
Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar;
Result.Elements[0][2] = Matrix.Elements[0][2] * Scalar;
Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar;
Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar;
Result.Elements[1][2] = Matrix.Elements[1][2] * Scalar;
Result.Elements[2][0] = Matrix.Elements[2][0] * Scalar;
Result.Elements[2][1] = Matrix.Elements[2][1] * Scalar;
Result.Elements[2][2] = Matrix.Elements[2][2] * Scalar;
return Result;
}
@@ -1432,15 +1399,16 @@ static inline HMM_Mat3 HMM_DivM3F(HMM_Mat3 Matrix, float Scalar)
ASSERT_COVERED(HMM_DivM3);
HMM_Mat3 Result;
int Columns;
for(Columns = 0; Columns < 3; ++Columns)
{
int Rows;
for(Rows = 0; Rows < 3; ++Rows)
{
Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] / Scalar;
}
}
Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar;
Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar;
Result.Elements[0][2] = Matrix.Elements[0][2] / Scalar;
Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar;
Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar;
Result.Elements[1][2] = Matrix.Elements[1][2] / Scalar;
Result.Elements[2][0] = Matrix.Elements[2][0] / Scalar;
Result.Elements[2][1] = Matrix.Elements[2][1] / Scalar;
Result.Elements[2][2] = Matrix.Elements[2][2] / Scalar;
return Result;
}
@@ -1509,20 +1477,22 @@ static inline HMM_Mat4 HMM_TransposeM4(HMM_Mat4 Matrix)
{
ASSERT_COVERED(HMM_TransposeM4);
#ifdef HANDMADE_MATH__USE_SSE
HMM_Mat4 Result = Matrix;
#ifdef HANDMADE_MATH__USE_SSE
_MM_TRANSPOSE4_PS(Result.Columns[0].SSE, Result.Columns[1].SSE, Result.Columns[2].SSE, Result.Columns[3].SSE);
#else
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];
}
}
Result.Elements[0][1] = Matrix.Elements[1][0];
Result.Elements[0][2] = Matrix.Elements[2][0];
Result.Elements[0][3] = Matrix.Elements[3][0];
Result.Elements[1][0] = Matrix.Elements[0][1];
Result.Elements[1][2] = Matrix.Elements[2][1];
Result.Elements[1][3] = Matrix.Elements[3][1];
Result.Elements[2][1] = Matrix.Elements[1][2];
Result.Elements[2][0] = Matrix.Elements[0][2];
Result.Elements[2][3] = Matrix.Elements[3][2];
Result.Elements[3][1] = Matrix.Elements[1][3];
Result.Elements[3][2] = Matrix.Elements[2][3];
Result.Elements[3][0] = Matrix.Elements[0][3];
#endif
return Result;
@@ -1541,15 +1511,22 @@ static inline HMM_Mat4 HMM_AddM4(HMM_Mat4 Left, HMM_Mat4 Right)
Result.Columns[2].SSE = _mm_add_ps(Left.Columns[2].SSE, Right.Columns[2].SSE);
Result.Columns[3].SSE = _mm_add_ps(Left.Columns[3].SSE, Right.Columns[3].SSE);
#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];
}
}
Result.Elements[0][0] = Left.Elements[0][0] + Right.Elements[0][0];
Result.Elements[0][1] = Left.Elements[0][1] + Right.Elements[0][1];
Result.Elements[0][2] = Left.Elements[0][2] + Right.Elements[0][2];
Result.Elements[0][3] = Left.Elements[0][3] + Right.Elements[0][3];
Result.Elements[1][0] = Left.Elements[1][0] + Right.Elements[1][0];
Result.Elements[1][1] = Left.Elements[1][1] + Right.Elements[1][1];
Result.Elements[1][2] = Left.Elements[1][2] + Right.Elements[1][2];
Result.Elements[1][3] = Left.Elements[1][3] + Right.Elements[1][3];
Result.Elements[2][0] = Left.Elements[2][0] + Right.Elements[2][0];
Result.Elements[2][1] = Left.Elements[2][1] + Right.Elements[2][1];
Result.Elements[2][2] = Left.Elements[2][2] + Right.Elements[2][2];
Result.Elements[2][3] = Left.Elements[2][3] + Right.Elements[2][3];
Result.Elements[3][0] = Left.Elements[3][0] + Right.Elements[3][0];
Result.Elements[3][1] = Left.Elements[3][1] + Right.Elements[3][1];
Result.Elements[3][2] = Left.Elements[3][2] + Right.Elements[3][2];
Result.Elements[3][3] = Left.Elements[3][3] + Right.Elements[3][3];
#endif
return Result;
@@ -1568,15 +1545,22 @@ static inline HMM_Mat4 HMM_SubM4(HMM_Mat4 Left, HMM_Mat4 Right)
Result.Columns[2].SSE = _mm_sub_ps(Left.Columns[2].SSE, Right.Columns[2].SSE);
Result.Columns[3].SSE = _mm_sub_ps(Left.Columns[3].SSE, Right.Columns[3].SSE);
#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];
}
}
Result.Elements[0][0] = Left.Elements[0][0] - Right.Elements[0][0];
Result.Elements[0][1] = Left.Elements[0][1] - Right.Elements[0][1];
Result.Elements[0][2] = Left.Elements[0][2] - Right.Elements[0][2];
Result.Elements[0][3] = Left.Elements[0][3] - Right.Elements[0][3];
Result.Elements[1][0] = Left.Elements[1][0] - Right.Elements[1][0];
Result.Elements[1][1] = Left.Elements[1][1] - Right.Elements[1][1];
Result.Elements[1][2] = Left.Elements[1][2] - Right.Elements[1][2];
Result.Elements[1][3] = Left.Elements[1][3] - Right.Elements[1][3];
Result.Elements[2][0] = Left.Elements[2][0] - Right.Elements[2][0];
Result.Elements[2][1] = Left.Elements[2][1] - Right.Elements[2][1];
Result.Elements[2][2] = Left.Elements[2][2] - Right.Elements[2][2];
Result.Elements[2][3] = Left.Elements[2][3] - Right.Elements[2][3];
Result.Elements[3][0] = Left.Elements[3][0] - Right.Elements[3][0];
Result.Elements[3][1] = Left.Elements[3][1] - Right.Elements[3][1];
Result.Elements[3][2] = Left.Elements[3][2] - Right.Elements[3][2];
Result.Elements[3][3] = Left.Elements[3][3] - Right.Elements[3][3];
#endif
return Result;
@@ -1610,15 +1594,22 @@ static inline HMM_Mat4 HMM_MulM4F(HMM_Mat4 Matrix, float Scalar)
Result.Columns[2].SSE = _mm_mul_ps(Matrix.Columns[2].SSE, SSEScalar);
Result.Columns[3].SSE = _mm_mul_ps(Matrix.Columns[3].SSE, 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;
}
}
Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar;
Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar;
Result.Elements[0][2] = Matrix.Elements[0][2] * Scalar;
Result.Elements[0][3] = Matrix.Elements[0][3] * Scalar;
Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar;
Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar;
Result.Elements[1][2] = Matrix.Elements[1][2] * Scalar;
Result.Elements[1][3] = Matrix.Elements[1][3] * Scalar;
Result.Elements[2][0] = Matrix.Elements[2][0] * Scalar;
Result.Elements[2][1] = Matrix.Elements[2][1] * Scalar;
Result.Elements[2][2] = Matrix.Elements[2][2] * Scalar;
Result.Elements[2][3] = Matrix.Elements[2][3] * Scalar;
Result.Elements[3][0] = Matrix.Elements[3][0] * Scalar;
Result.Elements[3][1] = Matrix.Elements[3][1] * Scalar;
Result.Elements[3][2] = Matrix.Elements[3][2] * Scalar;
Result.Elements[3][3] = Matrix.Elements[3][3] * Scalar;
#endif
return Result;
@@ -1645,15 +1636,22 @@ static inline HMM_Mat4 HMM_DivM4F(HMM_Mat4 Matrix, float Scalar)
Result.Columns[2].SSE = _mm_div_ps(Matrix.Columns[2].SSE, SSEScalar);
Result.Columns[3].SSE = _mm_div_ps(Matrix.Columns[3].SSE, 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;
}
}
Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar;
Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar;
Result.Elements[0][2] = Matrix.Elements[0][2] / Scalar;
Result.Elements[0][3] = Matrix.Elements[0][3] / Scalar;
Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar;
Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar;
Result.Elements[1][2] = Matrix.Elements[1][2] / Scalar;
Result.Elements[1][3] = Matrix.Elements[1][3] / Scalar;
Result.Elements[2][0] = Matrix.Elements[2][0] / Scalar;
Result.Elements[2][1] = Matrix.Elements[2][1] / Scalar;
Result.Elements[2][2] = Matrix.Elements[2][2] / Scalar;
Result.Elements[2][3] = Matrix.Elements[2][3] / Scalar;
Result.Elements[3][0] = Matrix.Elements[3][0] / Scalar;
Result.Elements[3][1] = Matrix.Elements[3][1] / Scalar;
Result.Elements[3][2] = Matrix.Elements[3][2] / Scalar;
Result.Elements[3][3] = Matrix.Elements[3][3] / Scalar;
#endif
return Result;
@@ -1673,6 +1671,8 @@ static inline float HMM_DeterminantM4(HMM_Mat4 Matrix)
}
COVERAGE(HMM_InvGeneralM4, 1)
// Returns a general-purpose inverse of an HMM_Mat4. Note that special-purpose inverses of many transformations
// are available and will be more efficient.
static inline HMM_Mat4 HMM_InvGeneralM4(HMM_Mat4 Matrix)
{
ASSERT_COVERED(HMM_InvGeneralM4);
@@ -1701,43 +1701,81 @@ static inline HMM_Mat4 HMM_InvGeneralM4(HMM_Mat4 Matrix)
* Common graphics transformations
*/
COVERAGE(HMM_Orthographic_RH, 1)
static inline HMM_Mat4 HMM_Orthographic_RH(float Left, float Right, float Bottom, float Top, float Near, float Far)
COVERAGE(HMM_Orthographic_RH_NO, 1)
// Produces a right-handed orthographic projection matrix with Z ranging from -1 to 1 (the GL convention).
// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes.
// Near and Far specify the distances to the near and far clipping planes.
static inline HMM_Mat4 HMM_Orthographic_RH_NO(float Left, float Right, float Bottom, float Top, float Near, float Far)
{
ASSERT_COVERED(HMM_Orthographic_RH);
ASSERT_COVERED(HMM_Orthographic_RH_NO);
HMM_Mat4 Result = {0};
Result.Elements[0][0] = 2.0f / (Right - Left);
Result.Elements[1][1] = 2.0f / (Top - Bottom);
Result.Elements[2][2] = 2.0f / (Near - Far);
Result.Elements[3][3] = 1.0f;
Result.Elements[3][0] = (Left + Right) / (Left - Right);
Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top);
#ifdef HANDMADE_MATH_USE_NDC_Z01
Result.Elements[2][2] = 1.0f / (Near - Far);
Result.Elements[3][2] = (Near) / (Near - Far);
#else
Result.Elements[2][2] = 2.0f / (Near - Far);
Result.Elements[3][2] = (Far + Near) / (Near - Far);
#endif
Result.Elements[3][2] = (Near + Far) / (Near - Far);
return Result;
}
COVERAGE(HMM_Orthographic_LH, 1)
static inline HMM_Mat4 HMM_Orthographic_LH(float Left, float Right, float Bottom, float Top, float Near, float Far)
COVERAGE(HMM_Orthographic_RH_ZO, 1)
// Produces a right-handed orthographic projection matrix with Z ranging from 0 to 1 (the DirectX convention).
// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes.
// Near and Far specify the distances to the near and far clipping planes.
static inline HMM_Mat4 HMM_Orthographic_RH_ZO(float Left, float Right, float Bottom, float Top, float Near, float Far)
{
ASSERT_COVERED(HMM_Orthographic_LH);
ASSERT_COVERED(HMM_Orthographic_RH_ZO);
HMM_Mat4 Result = HMM_Orthographic_RH(Left, Right, Bottom, Top, Near, Far);
HMM_Mat4 Result = {0};
Result.Elements[0][0] = 2.0f / (Right - Left);
Result.Elements[1][1] = 2.0f / (Top - Bottom);
Result.Elements[2][2] = 1.0f / (Near - Far);
Result.Elements[3][3] = 1.0f;
Result.Elements[3][0] = (Left + Right) / (Left - Right);
Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top);
Result.Elements[3][2] = (Near) / (Near - Far);
return Result;
}
COVERAGE(HMM_Orthographic_LH_NO, 1)
// Produces a left-handed orthographic projection matrix with Z ranging from -1 to 1 (the GL convention).
// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes.
// Near and Far specify the distances to the near and far clipping planes.
static inline HMM_Mat4 HMM_Orthographic_LH_NO(float Left, float Right, float Bottom, float Top, float Near, float Far)
{
ASSERT_COVERED(HMM_Orthographic_LH_NO);
HMM_Mat4 Result = HMM_Orthographic_RH_NO(Left, Right, Bottom, Top, Near, Far);
Result.Elements[2][2] = -Result.Elements[2][2];
return Result;
}
COVERAGE(HMM_InvOrthographic, 1)
COVERAGE(HMM_Orthographic_LH_ZO, 1)
// Produces a left-handed orthographic projection matrix with Z ranging from 0 to 1 (the DirectX convention).
// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes.
// Near and Far specify the distances to the near and far clipping planes.
static inline HMM_Mat4 HMM_Orthographic_LH_ZO(float Left, float Right, float Bottom, float Top, float Near, float Far)
{
ASSERT_COVERED(HMM_Orthographic_LH_ZO);
HMM_Mat4 Result = HMM_Orthographic_RH_ZO(Left, Right, Bottom, Top, Near, Far);
Result.Elements[2][2] = -Result.Elements[2][2];
return Result;
}
COVERAGE(HMM_InvOrthographic, 1)
// Returns an inverse for the given orthographic projection matrix. Works for all orthographic
// projection matrices, regardless of handedness or NDC convention.
static inline HMM_Mat4 HMM_InvOrthographic(HMM_Mat4 OrthoMatrix)
{
ASSERT_COVERED(HMM_InvOrthographic);
@@ -1755,48 +1793,74 @@ static inline HMM_Mat4 HMM_InvOrthographic(HMM_Mat4 OrthoMatrix)
return Result;
}
COVERAGE(HMM_Perspective_RH, 1)
static inline HMM_Mat4 HMM_Perspective_RH(float FOV, float AspectRatio, float Near, float Far)
COVERAGE(HMM_Perspective_RH_NO, 1)
static inline HMM_Mat4 HMM_Perspective_RH_NO(float FOV, float AspectRatio, float Near, float Far)
{
ASSERT_COVERED(HMM_Perspective_RH);
ASSERT_COVERED(HMM_Perspective_RH_NO);
HMM_Mat4 Result = {0};
// See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
float Cotangent = 1.0f / HMM_TanF(FOV / 2.0f);
Result.Elements[0][0] = Cotangent / AspectRatio;
Result.Elements[1][1] = Cotangent;
Result.Elements[2][3] = -1.0f;
#ifdef HANDMADE_MATH_USE_NDC_Z01
Result.Elements[2][2] = (Far) / (Near - Far);
Result.Elements[3][2] = (Near * Far) / (Near - Far);
#else
Result.Elements[2][2] = (Near + Far) / (Near - Far);
Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far);
#endif
return Result;
}
COVERAGE(HMM_Perspective_LH, 1)
static inline HMM_Mat4 HMM_Perspective_LH(float FOV, float AspectRatio, float Near, float Far)
{
ASSERT_COVERED(HMM_Perspective_LH);
HMM_Mat4 Result = HMM_Perspective_RH(FOV, AspectRatio, Near, Far);
Result.Elements[2][3] = +1.0f;
return Result;
}
COVERAGE(HMM_InvPerspective, 1)
static inline HMM_Mat4 HMM_InvPerspective(HMM_Mat4 PerspectiveMatrix)
COVERAGE(HMM_Perspective_RH_ZO, 1)
static inline HMM_Mat4 HMM_Perspective_RH_ZO(float FOV, float AspectRatio, float Near, float Far)
{
ASSERT_COVERED(HMM_InvPerspective);
ASSERT_COVERED(HMM_Perspective_RH_ZO);
HMM_Mat4 Result = {0};
// See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
float Cotangent = 1.0f / HMM_TanF(FOV / 2.0f);
Result.Elements[0][0] = Cotangent / AspectRatio;
Result.Elements[1][1] = Cotangent;
Result.Elements[2][3] = -1.0f;
Result.Elements[2][2] = (Far) / (Near - Far);
Result.Elements[3][2] = (Near * Far) / (Near - Far);
return Result;
}
COVERAGE(HMM_Perspective_LH_NO, 1)
static inline HMM_Mat4 HMM_Perspective_LH_NO(float FOV, float AspectRatio, float Near, float Far)
{
ASSERT_COVERED(HMM_Perspective_LH_NO);
HMM_Mat4 Result = HMM_Perspective_RH_NO(FOV, AspectRatio, Near, Far);
Result.Elements[2][2] = -Result.Elements[2][2];
Result.Elements[2][3] = -Result.Elements[2][3];
return Result;
}
COVERAGE(HMM_Perspective_LH_ZO, 1)
static inline HMM_Mat4 HMM_Perspective_LH_ZO(float FOV, float AspectRatio, float Near, float Far)
{
ASSERT_COVERED(HMM_Perspective_LH_ZO);
HMM_Mat4 Result = HMM_Perspective_RH_ZO(FOV, AspectRatio, Near, Far);
Result.Elements[2][2] = -Result.Elements[2][2];
Result.Elements[2][3] = -Result.Elements[2][3];
return Result;
}
COVERAGE(HMM_InvPerspective_RH, 1)
static inline HMM_Mat4 HMM_InvPerspective_RH(HMM_Mat4 PerspectiveMatrix)
{
ASSERT_COVERED(HMM_InvPerspective_RH);
HMM_Mat4 Result = {0};
Result.Elements[0][0] = 1.0f / PerspectiveMatrix.Elements[0][0];
@@ -1810,6 +1874,23 @@ static inline HMM_Mat4 HMM_InvPerspective(HMM_Mat4 PerspectiveMatrix)
return Result;
}
COVERAGE(HMM_InvPerspective_LH, 1)
static inline HMM_Mat4 HMM_InvPerspective_LH(HMM_Mat4 PerspectiveMatrix)
{
ASSERT_COVERED(HMM_InvPerspective_LH);
HMM_Mat4 Result = {0};
Result.Elements[0][0] = 1.0f / PerspectiveMatrix.Elements[0][0];
Result.Elements[1][1] = 1.0f / PerspectiveMatrix.Elements[1][1];
Result.Elements[2][2] = 0.0f;
Result.Elements[2][3] = 1.0f / PerspectiveMatrix.Elements[3][2];
Result.Elements[3][3] = PerspectiveMatrix.Elements[2][2] * -Result.Elements[2][3];
Result.Elements[3][2] = PerspectiveMatrix.Elements[2][3];
return Result;
}
COVERAGE(HMM_Translate, 1)
static inline HMM_Mat4 HMM_Translate(HMM_Vec3 Translation)
{
@@ -2088,10 +2169,25 @@ static inline HMM_Quat HMM_MulQ(HMM_Quat Left, HMM_Quat Right)
SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(3, 2, 1, 0));
Result.SSE = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne));
#else
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);
Result.X = Right.Elements[3] * +Left.Elements[0];
Result.Y = Right.Elements[2] * -Left.Elements[0];
Result.Z = Right.Elements[1] * +Left.Elements[0];
Result.W = Right.Elements[0] * -Left.Elements[0];
Result.X += Right.Elements[2] * +Left.Elements[1];
Result.Y += Right.Elements[3] * +Left.Elements[1];
Result.Z += Right.Elements[0] * -Left.Elements[1];
Result.W += Right.Elements[1] * -Left.Elements[1];
Result.X += Right.Elements[1] * -Left.Elements[2];
Result.Y += Right.Elements[0] * +Left.Elements[2];
Result.Z += Right.Elements[3] * +Left.Elements[2];
Result.W += Right.Elements[2] * -Left.Elements[2];
Result.X += Right.Elements[0] * +Left.Elements[3];
Result.Y += Right.Elements[1] * +Left.Elements[3];
Result.Z += Right.Elements[2] * +Left.Elements[3];
Result.W += Right.Elements[3] * +Left.Elements[3];
#endif
return Result;
@@ -2152,7 +2248,7 @@ static inline float HMM_DotQ(HMM_Quat Left, HMM_Quat Right)
SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo);
_mm_store_ss(&Result, SSEResultOne);
#else
Result = (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z) + (Left.W * Right.W);
Result = ((Left.X * Right.X) + (Left.Z * Right.Z)) + ((Left.Y * Right.Y) + (Left.W * Right.W));
#endif
return Result;

View File

@@ -1,22 +1,23 @@
# Handmade Math
A single-file, cross-platform, public domain game math library for both C and C++. Supports vectors, matrices, quaternions, and all the utilities you'd expect.
A single-file, cross-platform, public domain graphics math library for both C and C++. Supports vectors, matrices, quaternions, and all the utilities you'd expect.
To get started, go download [the latest release](https://github.com/HandmadeMath/HandmadeMath/releases).
> If you are upgrading to version 2 of Handmade Math, save yourself some time and use our [automatic update tool](./update).
> If you are upgrading to Handmade Math 2.0, save yourself some time and use our [automatic update tool](./update).
Here's what sets Handmade Math apart:
- **A simple single-header library.** Just `#include "HandmadeMath.h"`.
- **Supports both C and C++.** While libraries like GLM only support C++, Handmade Math supports both C and C++, with convenient overloads wherever possible. For example, C++ codebases get operator overloading, and C11 codebases get `_Generic` versions of common operations.
- **Supports all graphics APIs.** Handmade Math has left- and right-handed versions of each operation, as well as support for zero-to-one and negative-one-to-one NDC conventions.
- **Swizzling, sort of.** Handmade Math's vector types use unions to provide several ways of accessing the same underlying data. For example, the components of an `HMM_Vec3` can be accessed as `XYZ`, `RGB`, or `UVW` - or subsets can be accessed like `.XY` and `.YZ`.
- **Your choice of angle unit.** While Handmade Math uses radians by default, you can configure it to use degrees or [turns](https://www.computerenhance.com/p/turns-are-better-than-radians) instead.
## Usage
Simply `#include "HandmadeMath.h"`. All functions are `static inline`, so no need for an "implementation" file as with some other single-header libraries.
Simply `#include "HandmadeMath.h"`. All functions are `static inline`, so there is no need for an "implementation" file as with some other single-header libraries.
A few config options are available. See the header comment in [the source](./HandmadeMath.h) for details.
@@ -29,7 +30,7 @@ This library is in the public domain. You can do whatever you want with it.
**Where can I contact you to ask questions?**
Feel free to make Github issues for any questions, concerns, or problems you encounter.
Feel free to make GitHub issues for any questions, concerns, or problems you encounter.
**What if I don't want the `HMM_` prefix?**

View File

@@ -148,25 +148,35 @@ INITIALIZER(_HMT_COVERCASE_FUNCNAME_INIT(name)) { \
} \
} \
#define HMT_EXPECT_FLOAT_EQ(_actual, _expected) { \
#define HMT_EXPECT_FLOAT_EQ_MSG(_actual, _expected, _msg) { \
_HMT_CASE_START(); \
float actual = (_actual); \
float diff = actual - (_expected); \
if (diff < -FLT_EPSILON || FLT_EPSILON < diff) { \
_HMT_CASE_FAIL(); \
printf("Expected %f, got %f", (_expected), actual); \
if ((_msg)[0] == 0) { \
printf("Expected %f, got %f", (_expected), actual); \
} else { \
printf("%s: Expected %f, got %f", (_msg), (_expected), actual); \
} \
} \
} \
}
#define HMT_EXPECT_FLOAT_EQ(_actual, _expected) HMT_EXPECT_FLOAT_EQ_MSG(_actual, _expected, "");
#define HMT_EXPECT_NEAR(_actual, _expected, _epsilon) { \
#define HMT_EXPECT_NEAR_MSG(_actual, _expected, _epsilon, _msg) { \
_HMT_CASE_START(); \
float actual = (_actual); \
float diff = actual - (_expected); \
if (diff < -(_epsilon) || (_epsilon) < diff) { \
_HMT_CASE_FAIL(); \
printf("Expected %f, got %f", (_expected), actual); \
if ((_msg)[0] == 0) { \
printf("Expected %f, got %f", (_expected), actual); \
} else { \
printf("%s: Expected %f, got %f", (_msg), (_expected), actual); \
} \
} \
} \
}
#define HMT_EXPECT_NEAR(_actual, _expected, _epsilon) HMT_EXPECT_NEAR_MSG(_actual, _expected, _epsilon, "");
#define HMT_EXPECT_LT(_actual, _expected) { \
_HMT_CASE_START(); \
@@ -192,7 +202,46 @@ INITIALIZER(_HMT_COVERCASE_FUNCNAME_INIT(name)) { \
#define EXPECT_TRUE(_actual) HMT_EXPECT_TRUE(_actual)
#define EXPECT_FALSE(_actual) HMT_EXPECT_FALSE(_actual)
#define EXPECT_FLOAT_EQ(_actual, _expected) HMT_EXPECT_FLOAT_EQ(_actual, _expected)
#define EXPECT_V4_EQ(_actual, _expected) \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.X, _expected.X, "incorrect X"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Y, _expected.Y, "incorrect Y"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Z, _expected.Z, "incorrect Z"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.W, _expected.W, "incorrect W");
#define EXPECT_M4_EQ(_actual, _expected) \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[0][0], _expected.Elements[0][0], "incorrect [0][0]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[0][1], _expected.Elements[0][1], "incorrect [0][1]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[0][2], _expected.Elements[0][2], "incorrect [0][2]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[0][3], _expected.Elements[0][3], "incorrect [0][3]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[1][0], _expected.Elements[1][0], "incorrect [1][0]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[1][1], _expected.Elements[1][1], "incorrect [1][1]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[1][2], _expected.Elements[1][2], "incorrect [1][2]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[1][3], _expected.Elements[1][3], "incorrect [1][3]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[2][0], _expected.Elements[2][0], "incorrect [2][0]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[2][1], _expected.Elements[2][1], "incorrect [2][1]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[2][2], _expected.Elements[2][2], "incorrect [2][2]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[2][3], _expected.Elements[2][3], "incorrect [2][3]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[3][0], _expected.Elements[3][0], "incorrect [3][0]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[3][1], _expected.Elements[3][1], "incorrect [3][1]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[3][2], _expected.Elements[3][2], "incorrect [3][2]"); \
HMT_EXPECT_FLOAT_EQ_MSG(_actual.Elements[3][3], _expected.Elements[3][3], "incorrect [3][3]");
#define EXPECT_NEAR(_actual, _expected, _epsilon) HMT_EXPECT_NEAR(_actual, _expected, _epsilon)
#define EXPECT_M4_NEAR(_actual, _expected, _epsilon) \
HMT_EXPECT_NEAR_MSG(_actual.Elements[0][0], _expected.Elements[0][0], _epsilon, "incorrect [0][0]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[0][1], _expected.Elements[0][1], _epsilon, "incorrect [0][1]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[0][2], _expected.Elements[0][2], _epsilon, "incorrect [0][2]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[0][3], _expected.Elements[0][3], _epsilon, "incorrect [0][3]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[1][0], _expected.Elements[1][0], _epsilon, "incorrect [1][0]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[1][1], _expected.Elements[1][1], _epsilon, "incorrect [1][1]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[1][2], _expected.Elements[1][2], _epsilon, "incorrect [1][2]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[1][3], _expected.Elements[1][3], _epsilon, "incorrect [1][3]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[2][0], _expected.Elements[2][0], _epsilon, "incorrect [2][0]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[2][1], _expected.Elements[2][1], _epsilon, "incorrect [2][1]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[2][2], _expected.Elements[2][2], _epsilon, "incorrect [2][2]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[2][3], _expected.Elements[2][3], _epsilon, "incorrect [2][3]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[3][0], _expected.Elements[3][0], _epsilon, "incorrect [3][0]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[3][1], _expected.Elements[3][1], _epsilon, "incorrect [3][1]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[3][2], _expected.Elements[3][2], _epsilon, "incorrect [3][2]"); \
HMT_EXPECT_NEAR_MSG(_actual.Elements[3][3], _expected.Elements[3][3], _epsilon, "incorrect [3][3]");
#define EXPECT_LT(_actual, _expected) HMT_EXPECT_LT(_actual, _expected)
#define EXPECT_GT(_actual, _expected) HMT_EXPECT_GT(_actual, _expected)
#endif // HMT_SAFE_MACROS

View File

@@ -246,60 +246,56 @@ TEST(InvMatrix, InvGeneral)
}
}
TEST(InvMatrix, Mat4Inverses)
TEST(InvMatrix, InvOrthographic)
{
{
HMM_Mat4 Matrix = HMM_Orthographic_RH(-160+100, 160+100, -90+200, 90+200, 10, 10000);
HMM_Mat4 Expect = HMM_M4D(1.0f);
HMM_Mat4 Matrix = HMM_Orthographic_RH_NO(-160+100, 160+100, -90+200, 90+200, 10, 10000);
HMM_Mat4 Inverse = HMM_InvOrthographic(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);
EXPECT_FLOAT_EQ(Result.Elements[0][0], Expect.Elements[0][0]);
EXPECT_FLOAT_EQ(Result.Elements[0][1], Expect.Elements[0][1]);
EXPECT_FLOAT_EQ(Result.Elements[0][2], Expect.Elements[0][2]);
EXPECT_FLOAT_EQ(Result.Elements[0][3], Expect.Elements[0][3]);
EXPECT_FLOAT_EQ(Result.Elements[1][0], Expect.Elements[1][0]);
EXPECT_FLOAT_EQ(Result.Elements[1][1], Expect.Elements[1][1]);
EXPECT_FLOAT_EQ(Result.Elements[1][2], Expect.Elements[1][2]);
EXPECT_FLOAT_EQ(Result.Elements[1][3], Expect.Elements[1][3]);
EXPECT_FLOAT_EQ(Result.Elements[2][0], Expect.Elements[2][0]);
EXPECT_FLOAT_EQ(Result.Elements[2][1], Expect.Elements[2][1]);
EXPECT_FLOAT_EQ(Result.Elements[2][2], Expect.Elements[2][2]);
EXPECT_FLOAT_EQ(Result.Elements[2][3], Expect.Elements[2][3]);
EXPECT_FLOAT_EQ(Result.Elements[3][0], Expect.Elements[3][0]);
EXPECT_FLOAT_EQ(Result.Elements[3][1], Expect.Elements[3][1]);
EXPECT_FLOAT_EQ(Result.Elements[3][2], Expect.Elements[3][2]);
EXPECT_FLOAT_EQ(Result.Elements[3][3], Expect.Elements[3][3]);
EXPECT_M4_EQ(HMM_MulM4(Matrix, Inverse), HMM_M4D(1.0f));
}
{
HMM_Mat4 Matrix = HMM_Perspective_RH(HMM_AngleDeg(120), 16.0/9.0, 10, 10000);
HMM_Mat4 Expect = HMM_M4D(1.0f);
HMM_Mat4 Inverse = HMM_InvPerspective(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);
EXPECT_FLOAT_EQ(Result.Elements[0][0], Expect.Elements[0][0]);
EXPECT_FLOAT_EQ(Result.Elements[0][1], Expect.Elements[0][1]);
EXPECT_FLOAT_EQ(Result.Elements[0][2], Expect.Elements[0][2]);
EXPECT_FLOAT_EQ(Result.Elements[0][3], Expect.Elements[0][3]);
EXPECT_FLOAT_EQ(Result.Elements[1][0], Expect.Elements[1][0]);
EXPECT_FLOAT_EQ(Result.Elements[1][1], Expect.Elements[1][1]);
EXPECT_FLOAT_EQ(Result.Elements[1][2], Expect.Elements[1][2]);
EXPECT_FLOAT_EQ(Result.Elements[1][3], Expect.Elements[1][3]);
EXPECT_FLOAT_EQ(Result.Elements[2][0], Expect.Elements[2][0]);
EXPECT_FLOAT_EQ(Result.Elements[2][1], Expect.Elements[2][1]);
EXPECT_FLOAT_EQ(Result.Elements[2][2], Expect.Elements[2][2]);
EXPECT_FLOAT_EQ(Result.Elements[2][3], Expect.Elements[2][3]);
EXPECT_FLOAT_EQ(Result.Elements[3][0], Expect.Elements[3][0]);
EXPECT_FLOAT_EQ(Result.Elements[3][1], Expect.Elements[3][1]);
EXPECT_FLOAT_EQ(Result.Elements[3][2], Expect.Elements[3][2]);
EXPECT_FLOAT_EQ(Result.Elements[3][3], Expect.Elements[3][3]);
HMM_Mat4 Matrix = HMM_Orthographic_RH_ZO(-160+100, 160+100, -90+200, 90+200, 10, 10000);
HMM_Mat4 Inverse = HMM_InvOrthographic(Matrix);
EXPECT_M4_EQ(HMM_MulM4(Matrix, Inverse), HMM_M4D(1.0f));
}
{
HMM_Mat4 Matrix = HMM_Orthographic_LH_NO(-160+100, 160+100, -90+200, 90+200, 10, 10000);
HMM_Mat4 Inverse = HMM_InvOrthographic(Matrix);
EXPECT_M4_EQ(HMM_MulM4(Matrix, Inverse), HMM_M4D(1.0f));
}
{
HMM_Mat4 Matrix = HMM_Orthographic_LH_ZO(-160+100, 160+100, -90+200, 90+200, 10, 10000);
HMM_Mat4 Inverse = HMM_InvOrthographic(Matrix);
EXPECT_M4_EQ(HMM_MulM4(Matrix, Inverse), HMM_M4D(1.0f));
}
}
TEST(InvMatrix, InvPerspective)
{
{
HMM_Mat4 Matrix = HMM_Perspective_RH_NO(HMM_AngleDeg(120), 16.0/9.0, 10, 10000);
HMM_Mat4 Inverse = HMM_InvPerspective_RH(Matrix);
EXPECT_M4_EQ(HMM_MulM4(Matrix, Inverse), HMM_M4D(1.0f));
}
{
HMM_Mat4 Matrix = HMM_Perspective_RH_ZO(HMM_AngleDeg(120), 16.0/9.0, 10, 10000);
HMM_Mat4 Inverse = HMM_InvPerspective_RH(Matrix);
EXPECT_M4_EQ(HMM_MulM4(Matrix, Inverse), HMM_M4D(1.0f));
}
{
HMM_Mat4 Matrix = HMM_Perspective_LH_NO(HMM_AngleDeg(120), 16.0/9.0, 10, 10000);
HMM_Mat4 Inverse = HMM_InvPerspective_LH(Matrix);
EXPECT_M4_EQ(HMM_MulM4(Matrix, Inverse), HMM_M4D(1.0f));
}
{
HMM_Mat4 Matrix = HMM_Perspective_LH_ZO(HMM_AngleDeg(120), 16.0/9.0, 10, 10000);
HMM_Mat4 Inverse = HMM_InvPerspective_LH(Matrix);
EXPECT_M4_EQ(HMM_MulM4(Matrix, Inverse), HMM_M4D(1.0f));
}
}
TEST(InvMatrix, InvLookAt)
{
{
HMM_Vec3 Eye = {10.0f, 10.0f, 10.0f};
HMM_Vec3 Center = {100.0f, 200.0f, 30.0f};
@@ -308,106 +304,56 @@ TEST(InvMatrix, Mat4Inverses)
HMM_Mat4 Expect = HMM_M4D(1.0f);
HMM_Mat4 Inverse = HMM_InvLookAt(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);
EXPECT_NEAR(Result.Elements[0][0], Expect.Elements[0][0], 0.001f);
EXPECT_NEAR(Result.Elements[0][1], Expect.Elements[0][1], 0.001f);
EXPECT_NEAR(Result.Elements[0][2], Expect.Elements[0][2], 0.001f);
EXPECT_NEAR(Result.Elements[0][3], Expect.Elements[0][3], 0.001f);
EXPECT_NEAR(Result.Elements[1][0], Expect.Elements[1][0], 0.001f);
EXPECT_NEAR(Result.Elements[1][1], Expect.Elements[1][1], 0.001f);
EXPECT_NEAR(Result.Elements[1][2], Expect.Elements[1][2], 0.001f);
EXPECT_NEAR(Result.Elements[1][3], Expect.Elements[1][3], 0.001f);
EXPECT_NEAR(Result.Elements[2][0], Expect.Elements[2][0], 0.001f);
EXPECT_NEAR(Result.Elements[2][1], Expect.Elements[2][1], 0.001f);
EXPECT_NEAR(Result.Elements[2][2], Expect.Elements[2][2], 0.001f);
EXPECT_NEAR(Result.Elements[2][3], Expect.Elements[2][3], 0.001f);
EXPECT_NEAR(Result.Elements[3][0], Expect.Elements[3][0], 0.001f);
EXPECT_NEAR(Result.Elements[3][1], Expect.Elements[3][1], 0.001f);
EXPECT_NEAR(Result.Elements[3][2], Expect.Elements[3][2], 0.001f);
EXPECT_NEAR(Result.Elements[3][3], Expect.Elements[3][3], 0.001f);
EXPECT_M4_NEAR(Result, Expect, 0.001f);
}
{
HMM_Vec3 Eye = {10.0f, 10.0f, 10.0f};
HMM_Vec3 Center = {100.0f, 200.0f, 30.0f};
HMM_Vec3 Up = {0.0f, 0.0f, 1.0f};
HMM_Mat4 Matrix = HMM_LookAt_LH(Eye, Center, Up);
HMM_Mat4 Expect = HMM_M4D(1.0f);
HMM_Mat4 Inverse = HMM_InvLookAt(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);
EXPECT_M4_NEAR(Result, Expect, 0.001f);
}
}
TEST(InvMatrix, InvRotate)
{
{
HMM_Vec3 Axis = {1.0f, -1.0f, 0.5f};
HMM_Mat4 Matrix = HMM_Rotate_RH(HMM_AngleDeg(30), HMM_NormV3(Axis));
HMM_Mat4 Expect = HMM_M4D(1.0f);
HMM_Mat4 Inverse = HMM_InvRotate(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);
EXPECT_NEAR(Result.Elements[0][0], Expect.Elements[0][0], 0.001f);
EXPECT_NEAR(Result.Elements[0][1], Expect.Elements[0][1], 0.001f);
EXPECT_NEAR(Result.Elements[0][2], Expect.Elements[0][2], 0.001f);
EXPECT_NEAR(Result.Elements[0][3], Expect.Elements[0][3], 0.001f);
EXPECT_NEAR(Result.Elements[1][0], Expect.Elements[1][0], 0.001f);
EXPECT_NEAR(Result.Elements[1][1], Expect.Elements[1][1], 0.001f);
EXPECT_NEAR(Result.Elements[1][2], Expect.Elements[1][2], 0.001f);
EXPECT_NEAR(Result.Elements[1][3], Expect.Elements[1][3], 0.001f);
EXPECT_NEAR(Result.Elements[2][0], Expect.Elements[2][0], 0.001f);
EXPECT_NEAR(Result.Elements[2][1], Expect.Elements[2][1], 0.001f);
EXPECT_NEAR(Result.Elements[2][2], Expect.Elements[2][2], 0.001f);
EXPECT_NEAR(Result.Elements[2][3], Expect.Elements[2][3], 0.001f);
EXPECT_NEAR(Result.Elements[3][0], Expect.Elements[3][0], 0.001f);
EXPECT_NEAR(Result.Elements[3][1], Expect.Elements[3][1], 0.001f);
EXPECT_NEAR(Result.Elements[3][2], Expect.Elements[3][2], 0.001f);
EXPECT_NEAR(Result.Elements[3][3], Expect.Elements[3][3], 0.001f);
EXPECT_M4_NEAR(Result, Expect, 0.001f);
}
{
HMM_Vec3 Scale = {1.0f, -1.0f, 0.5f};
HMM_Mat4 Matrix = HMM_Scale(Scale);
HMM_Vec3 Axis = {1.0f, -1.0f, 0.5f};
HMM_Mat4 Matrix = HMM_Rotate_LH(HMM_AngleDeg(30), HMM_NormV3(Axis));
HMM_Mat4 Expect = HMM_M4D(1.0f);
HMM_Mat4 Inverse = HMM_InvScale(Matrix);
HMM_Mat4 Inverse = HMM_InvRotate(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);
EXPECT_FLOAT_EQ(Result.Elements[0][0], Expect.Elements[0][0]);
EXPECT_FLOAT_EQ(Result.Elements[0][1], Expect.Elements[0][1]);
EXPECT_FLOAT_EQ(Result.Elements[0][2], Expect.Elements[0][2]);
EXPECT_FLOAT_EQ(Result.Elements[0][3], Expect.Elements[0][3]);
EXPECT_FLOAT_EQ(Result.Elements[1][0], Expect.Elements[1][0]);
EXPECT_FLOAT_EQ(Result.Elements[1][1], Expect.Elements[1][1]);
EXPECT_FLOAT_EQ(Result.Elements[1][2], Expect.Elements[1][2]);
EXPECT_FLOAT_EQ(Result.Elements[1][3], Expect.Elements[1][3]);
EXPECT_FLOAT_EQ(Result.Elements[2][0], Expect.Elements[2][0]);
EXPECT_FLOAT_EQ(Result.Elements[2][1], Expect.Elements[2][1]);
EXPECT_FLOAT_EQ(Result.Elements[2][2], Expect.Elements[2][2]);
EXPECT_FLOAT_EQ(Result.Elements[2][3], Expect.Elements[2][3]);
EXPECT_FLOAT_EQ(Result.Elements[3][0], Expect.Elements[3][0]);
EXPECT_FLOAT_EQ(Result.Elements[3][1], Expect.Elements[3][1]);
EXPECT_FLOAT_EQ(Result.Elements[3][2], Expect.Elements[3][2]);
EXPECT_FLOAT_EQ(Result.Elements[3][3], Expect.Elements[3][3]);
EXPECT_M4_NEAR(Result, Expect, 0.001f);
}
{
HMM_Vec3 Move = {1.0f, -1.0f, 0.5f};
HMM_Mat4 Matrix = HMM_Translate(Move);
HMM_Mat4 Expect = HMM_M4D(1.0f);
HMM_Mat4 Inverse = HMM_InvTranslate(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);
EXPECT_FLOAT_EQ(Result.Elements[0][0], Expect.Elements[0][0]);
EXPECT_FLOAT_EQ(Result.Elements[0][1], Expect.Elements[0][1]);
EXPECT_FLOAT_EQ(Result.Elements[0][2], Expect.Elements[0][2]);
EXPECT_FLOAT_EQ(Result.Elements[0][3], Expect.Elements[0][3]);
EXPECT_FLOAT_EQ(Result.Elements[1][0], Expect.Elements[1][0]);
EXPECT_FLOAT_EQ(Result.Elements[1][1], Expect.Elements[1][1]);
EXPECT_FLOAT_EQ(Result.Elements[1][2], Expect.Elements[1][2]);
EXPECT_FLOAT_EQ(Result.Elements[1][3], Expect.Elements[1][3]);
EXPECT_FLOAT_EQ(Result.Elements[2][0], Expect.Elements[2][0]);
EXPECT_FLOAT_EQ(Result.Elements[2][1], Expect.Elements[2][1]);
EXPECT_FLOAT_EQ(Result.Elements[2][2], Expect.Elements[2][2]);
EXPECT_FLOAT_EQ(Result.Elements[2][3], Expect.Elements[2][3]);
EXPECT_FLOAT_EQ(Result.Elements[3][0], Expect.Elements[3][0]);
EXPECT_FLOAT_EQ(Result.Elements[3][1], Expect.Elements[3][1]);
EXPECT_FLOAT_EQ(Result.Elements[3][2], Expect.Elements[3][2]);
EXPECT_FLOAT_EQ(Result.Elements[3][3], Expect.Elements[3][3]);
}
}
TEST(InvMatrix, InvScale)
{
HMM_Vec3 Scale = {1.0f, -1.0f, 0.5f};
HMM_Mat4 Matrix = HMM_Scale(Scale);
HMM_Mat4 Expect = HMM_M4D(1.0f);
HMM_Mat4 Inverse = HMM_InvScale(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);
EXPECT_M4_EQ(Result, Expect);
}
TEST(InvMatrix, InvTranslate)
{
HMM_Vec3 Move = {1.0f, -1.0f, 0.5f};
HMM_Mat4 Matrix = HMM_Translate(Move);
HMM_Mat4 Expect = HMM_M4D(1.0f);
HMM_Mat4 Inverse = HMM_InvTranslate(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);
EXPECT_M4_EQ(Result, Expect);
}

View File

@@ -2,48 +2,84 @@
TEST(Projection, Orthographic)
{
#define ORTHO_BOUNDS -8.0f, 12.0f, 5.0f, 10.0f, 1.0f, 100.0f
// Right-handed
{
HMM_Mat4 projection = HMM_Orthographic_RH(-10.0f, 10.0f, -5.0f, 5.0f, 0.0f, -10.0f);
// Near and far distances correspond to negative Z, hence the Z coordinates here are negative.
HMM_Vec4 minCorner = HMM_V4(-8.0f, 5.0f, -1.0f, 1.0);
HMM_Vec4 maxCorner = HMM_V4(12.0f, 10.0f, -100.0f, 1.0);
HMM_Vec3 original = HMM_V3(5.0f, 5.0f, -5.0f);
HMM_Vec4 projected = HMM_MulM4V4(projection, HMM_V4V(original, 1));
// Z from -1 to 1 (GL convention)
{
HMM_Mat4 projection = HMM_Orthographic_RH_NO(ORTHO_BOUNDS);
EXPECT_V4_EQ(HMM_MulM4V4(projection, minCorner), HMM_V4(-1.0f, -1.0f, -1.0f, 1.0f));
EXPECT_V4_EQ(HMM_MulM4V4(projection, maxCorner), HMM_V4(1.0f, 1.0f, 1.0f, 1.0f));
}
EXPECT_FLOAT_EQ(projected.X, 0.5f);
EXPECT_FLOAT_EQ(projected.Y, 1.0f);
EXPECT_FLOAT_EQ(projected.Z, -2.0f);
EXPECT_FLOAT_EQ(projected.W, 1.0f);
// Z from 0 to 1 (DX convention)
{
HMM_Mat4 projection = HMM_Orthographic_RH_ZO(ORTHO_BOUNDS);
EXPECT_V4_EQ(HMM_MulM4V4(projection, minCorner), HMM_V4(-1.0f, -1.0f, 0.0f, 1.0f));
EXPECT_V4_EQ(HMM_MulM4V4(projection, maxCorner), HMM_V4(1.0f, 1.0f, 1.0f, 1.0f));
}
}
// Left-handed
{
HMM_Mat4 projection = HMM_Orthographic_LH(-10.0f, 10.0f, -5.0f, 5.0f, 0.0f, 10.0f);
// Near and far distances correspond to positive Z, hence the Z coordinates here are positive.
HMM_Vec4 minCorner = HMM_V4(-8.0f, 5.0f, 1.0f, 1.0);
HMM_Vec4 maxCorner = HMM_V4(12.0f, 10.0f, 100.0f, 1.0);
HMM_Vec3 original = HMM_V3(5.0f, 5.0f, -5.0f);
HMM_Vec4 projected = HMM_MulM4V4(projection, HMM_V4V(original, 1));
// Z from -1 to 1 (GL convention)
{
HMM_Mat4 projection = HMM_Orthographic_LH_NO(ORTHO_BOUNDS);
EXPECT_V4_EQ(HMM_MulM4V4(projection, minCorner), HMM_V4(-1.0f, -1.0f, -1.0f, 1.0f));
EXPECT_V4_EQ(HMM_MulM4V4(projection, maxCorner), HMM_V4(1.0f, 1.0f, 1.0f, 1.0f));
}
EXPECT_FLOAT_EQ(projected.X, 0.5f);
EXPECT_FLOAT_EQ(projected.Y, 1.0f);
EXPECT_FLOAT_EQ(projected.Z, -2.0f);
EXPECT_FLOAT_EQ(projected.W, 1.0f);
// Z from 0 to 1 (DX convention)
{
HMM_Mat4 projection = HMM_Orthographic_LH_ZO(ORTHO_BOUNDS);
EXPECT_V4_EQ(HMM_MulM4V4(projection, minCorner), HMM_V4(-1.0f, -1.0f, 0.0f, 1.0f));
EXPECT_V4_EQ(HMM_MulM4V4(projection, maxCorner), HMM_V4(1.0f, 1.0f, 1.0f, 1.0f));
}
}
}
TEST(Projection, Perspective)
{
// Right-handed
{
HMM_Mat4 projection = HMM_Perspective_RH(HMM_AngleDeg(90.0f), 2.0f, 5.0f, 15.0f);
HMM_Vec3 original = HMM_V3(5.0f, 5.0f, -15.0f);
HMM_Vec4 projected = HMM_MulM4V4(projection, HMM_V4V(original, 1));
EXPECT_FLOAT_EQ(projected.X, 2.5f);
EXPECT_FLOAT_EQ(projected.Y, 5.0f);
EXPECT_FLOAT_EQ(projected.Z, 15.0f);
EXPECT_FLOAT_EQ(projected.W, 15.0f);
// Z from -1 to 1 (GL convention)
{
HMM_Mat4 projection = HMM_Perspective_RH_NO(HMM_AngleDeg(90.0f), 2.0f, 1.0f, 15.0f);
HMM_Vec4 original = HMM_V4(5.0f, 5.0f, -1.0f, 1.0f);
EXPECT_V4_EQ(HMM_MulM4V4(projection, original), HMM_V4(2.5f, 5.0f, -1.0f, 1.0f));
}
// Z from 0 to 1 (DX convention)
{
HMM_Mat4 projection = HMM_Perspective_RH_ZO(HMM_AngleDeg(90.0f), 2.0f, 1.0f, 15.0f);
HMM_Vec4 original = HMM_V4(5.0f, 5.0f, -1.0f, 1.0f);
EXPECT_V4_EQ(HMM_MulM4V4(projection, original), HMM_V4(2.5f, 5.0f, 0.0f, 1.0f));
}
}
// Left-handed
{
HMM_Mat4 projection = HMM_Perspective_LH(HMM_AngleDeg(90.0f), 2.0f, 5.0f, 15.0f);
HMM_Vec3 original = HMM_V3(5.0f, 5.0f, -15.0f);
HMM_Vec4 projected = HMM_MulM4V4(projection, HMM_V4V(original, 1));
EXPECT_FLOAT_EQ(projected.X, 2.5f);
EXPECT_FLOAT_EQ(projected.Y, 5.0f);
EXPECT_FLOAT_EQ(projected.Z, 15.0f);
EXPECT_FLOAT_EQ(projected.W, -15.0f);
// Z from -1 to 1 (GL convention)
{
HMM_Mat4 projection = HMM_Perspective_LH_NO(HMM_AngleDeg(90.0f), 2.0f, 1.0f, 15.0f);
HMM_Vec4 original = HMM_V4(5.0f, 5.0f, 1.0f, 1.0f);
EXPECT_V4_EQ(HMM_MulM4V4(projection, original), HMM_V4(2.5f, 5.0f, -1.0f, 1.0f));
}
// Z from 0 to 1 (DX convention)
{
HMM_Mat4 projection = HMM_Perspective_LH_ZO(HMM_AngleDeg(90.0f), 2.0f, 1.0f, 15.0f);
HMM_Vec4 original = HMM_V4(5.0f, 5.0f, 1.0f, 1.0f);
EXPECT_V4_EQ(HMM_MulM4V4(projection, original), HMM_V4(2.5f, 5.0f, 0.0f, 1.0f));
}
}
}

View File

@@ -1,36 +1,24 @@
# Handmade Math 2.0 Update Tool
Due to the large number of breaking naming changes in Handmade Math 2, we provide a small tool to update your programs automatically. It's a C program that takes a list of files and updates their text, along with some scripts to recursively run the program on all code in a directory.
Due to the large number of breaking naming changes in Handmade Math 2, we provide a small Python script to update your programs automatically. It can run on individual files or on all files in a directory (recursively).
You can compile the tool yourself with any C/C++ compiler:
**Warning!** This tool is not very smart! Please ensure that your work is committed and backed up, in case you have to revert this tool's changes.
```bash
# MSVC (Windows)
cl update_hmm.c
# gcc
gcc update_hmm.c -o update_hmm
# clang
clang update_hmm.c -o update_hmm
```
# see usage info and options
> python3 update_hmm.py -h
usage: update_hmm [-h] [--exts .foo [.foo ...]] filename [filename ...]
...
Once built, the tool can be run on any C or C++ files:
# run on individual files
> python3 update_hmm.py MyPlatformLayer.c MyPlatformLayer.h
Updating: MyPlatformLayer.c
Updating: MyPlatformLayer.h
Updated 2 files with 0 warnings.
```bash
# Windows
update_hmm.exe MyGame.cpp MyPlatformLayer.cpp
# Other platforms
update_hmm MyGame.cpp MyPlatformLayer.cpp
```
Or, update all C/C++ files in a directory by running one of the provided shell scripts:
```bash
# Windows
update_hmm_all.bat "path\to\project"
# Other platforms
update_hmm_all.sh path/to/project
# run on a whole directory
> python3 update_hmm.py projects/MyCoolGame
Updating: projects/MyCoolGame/src/MyPlatformLayer.c
Updating: projects/MyCoolGame/include/MyPlatformLayer.h
...
```

View File

@@ -1,562 +0,0 @@
/* Compile:
Windows (MSVC): cl update_hmm.c
Linux (GCC): gcc update_hmm.c -o update_hmm
*/
/** LCF stuff **/
/* I used my personally library when writing this so I am dumping the necessary things here
so that it's all in one file. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Types */
#define global static
#define internal static
typedef int32_t s32; global s32 s32_MAX = 0x7FFFFFFF; global s32 s32_MIN = -1 - 0x7FFFFFFF;
typedef int64_t s64; global s64 s64_MAX = 0x7FFFFFFFFFFFFFFF; global s64 s64_MIN = -1 - 0x7FFFFFFFFFFFFFFF;
typedef uint32_t u32; global u32 u32_MAX = 0xFFFFFFFF; global u32 u32_MIN = 0;
typedef uint64_t u64; global u64 u64_MAX = 0xFFFFFFFFFFFFFFFF; global u64 u64_MIN = 0;
typedef u32 b32;
typedef u64 b64;
#define MIN(a,b) (((a)<(b))?(a):(b))
#define CLAMPTOP(a,b) MIN(a,b)
/* Memory */
struct lcf_Arena {
u64 pos;
u64 size;
u64 alignment;
u64 commited_pos;
};
typedef struct lcf_Arena Arena;
#define KB(x) ((x) << 10)
#define GB(x) ((x) << 30)
Arena* Arena_create(u64 size);
void* Arena_take(Arena *a, u64 size);
void* Arena_take_custom(Arena *a, u64 size, u64 alignment);
#define Arena_take_array(a, type, count) ((type*) Arena_take(a, sizeof(type)*count))
void Arena_reset_all(Arena *a);
#define LCF_MEMORY_PROVIDE_MEMORY "stdlib"
#define LCF_MEMORY_RESERVE_MEMORY(name) void* name(u64 size)
#define LCF_MEMORY_COMMIT_MEMORY(name) b32 name(void* memory, u64 size)
#define LCF_MEMORY_DECOMMIT_MEMORY(name) void name(void* memory, u64 size)
#define LCF_MEMORY_FREE_MEMORY(name) void name(void* memory, u64 size)
/* This implementation of an arena doesn't take advantage of virtual memory at all.
It's just convenient to have something portable so I can use the Arena API I'm used to. */
internal LCF_MEMORY_RESERVE_MEMORY(_lcf_memory_default_reserve) {
return malloc(size);
}
internal LCF_MEMORY_COMMIT_MEMORY(_lcf_memory_default_commit) {
(void) size, memory;
return 1; /* malloc commits memory automatically */
}
internal LCF_MEMORY_DECOMMIT_MEMORY(_lcf_memory_default_decommit) {
(void) size, memory;
return;
}
internal LCF_MEMORY_FREE_MEMORY(_lcf_memory_default_free) {
(void) size;
free(memory);
}
#define LCF_MEMORY_reserve _lcf_memory_default_reserve
#define LCF_MEMORY_commit _lcf_memory_default_commit
#define LCF_MEMORY_decommit _lcf_memory_default_decommit
#define LCF_MEMORY_free _lcf_memory_default_free
#define LCF_MEMORY_RESERVE_SIZE GB(1)
#define LCF_MEMORY_COMMIT_SIZE KB(4)
#define LCF_MEMORY_ALIGNMENT (sizeof(void*))
Arena* Arena_create(u64 size) {
Arena* a = (Arena*) LCF_MEMORY_reserve(size);
LCF_MEMORY_commit(a, LCF_MEMORY_COMMIT_SIZE);
a->size = size;
a->pos = sizeof(Arena);
a->commited_pos = LCF_MEMORY_COMMIT_SIZE;
a->alignment = LCF_MEMORY_ALIGNMENT;
return a;
}
#define B_PTR(p) (u8*)(p)
internal b32 is_power_of_2(u64 x) {
return ((x & (x-1)) == 0);
}
internal u64 next_alignment(u64 ptr, u64 alignment) {
/* Fast replacement for mod because alignment is power of 2 */
u64 modulo = ptr & (alignment-1);
if (modulo != 0) {
ptr += alignment - modulo;
}
return ptr;
}
void* Arena_take_custom(Arena *a, u64 size, u64 alignment) {
void* result = 0;
/* Align pos pointer to check if "size" can fit */
u64 mem = (u64) a;
u64 aligned_pos = next_alignment(mem + a->pos, alignment) - mem;
u64 new_pos = aligned_pos + size;
/* Check that there is space */
if (new_pos < a->size) {
u64 commited_pos = a->commited_pos;
/* Commit memory if needed */
if (new_pos > commited_pos) {
u64 new_commited_pos = next_alignment(mem + new_pos, LCF_MEMORY_COMMIT_SIZE)-mem;
if (LCF_MEMORY_commit(a, new_commited_pos)) {
a->commited_pos = commited_pos = new_commited_pos;
}
}
/* If enough memory is commited, set result and pos. */
if (new_pos <= commited_pos) {
result = (void*)(mem + aligned_pos);
a->pos = new_pos;
}
}
return result;
}
void* Arena_take(Arena *a, u64 size) {
return Arena_take_custom(a, size, LCF_MEMORY_ALIGNMENT);
}
void Arena_reset_all(Arena *a) {
a->pos = 0;
}
/* String */
typedef char chr8;
struct str8 {
u64 len;
chr8 *str;
};
typedef struct str8 str8;
#define str8_PRINTF_ARGS(s) (int)(s).len, (s).str
#define str8_lit(s) str8_from((chr8*)(s),(u64)sizeof(s)-1) /* -1 to exclude null character */
#define str8_is_empty(s) ((b32)((s).len == 0))
#define LCF_STRING_NO_MATCH 0x8000000000000000
#define str8_iter_custom(s, i, c) \
s64 i = 0; \
chr8 c = s.str[i]; \
for (; (i < (s64) s.len); i++, c = s.str[i])
#define str8_iter(s) str8_iter_custom(s, i, c)
str8 str8_from(chr8* s, u64 len);
str8 str8_from_cstring(chr8 *cstr);
str8 str8_first(str8 s, u64 len);
str8 str8_skip(str8 s, u64 len);
b32 chr8_is_whitespace(chr8 c);
b32 str8_contains_char(str8 s, chr8 c);
u64 str8_char_location(str8 s, chr8 c);
#define RET_STR8(s,l) \
str8 _str8; \
_str8.str = (s); \
_str8.len = (l); \
return _str8
str8 str8_from(chr8* s, u64 len) {
RET_STR8(s, len);
}
str8 str8_from_cstring(chr8 *cstr) {
chr8* p2 = cstr;
while(*p2 != 0)
p2++;
RET_STR8(cstr, (u64)(p2 - cstr));
}
str8 str8_first(str8 s, u64 len) {
u64 len_clamped = CLAMPTOP(len, s.len);
RET_STR8(s.str, len_clamped);
}
str8 str8_skip(str8 s, u64 len) {
u64 len_clamped = CLAMPTOP(len, s.len);
RET_STR8(s.str + len_clamped, s.len - len_clamped);
}
b32 chr8_is_whitespace(chr8 c) {
switch (c) {
case ' ':
case '\n':
case '\t':
case '\r':
return 1;
default:
return 0;
}
}
b32 str8_contains_char(str8 s, chr8 find) {
return str8_char_location(s,find) != LCF_STRING_NO_MATCH;
}
u64 str8_char_location(str8 s, chr8 find) {
str8_iter(s) {
if (c == find) {
return i;
}
}
return LCF_STRING_NO_MATCH;
}
#undef RET_STR8
struct Str8Node {
struct Str8Node *next;
struct str8 str;
};
struct Str8List {
struct Str8Node *first;
struct Str8Node *last;
u64 count;
u64 total_len;
};
typedef struct Str8Node Str8Node;
typedef struct Str8List Str8List;
void Str8List_add_node(Str8List *list, Str8Node *n);
void Str8List_add(Arena *arena, Str8List *list, str8 str);
void Str8List_add_node(Str8List *list, Str8Node *n) {
if (list->last) {
list->last->next = n;
} else {
list->first = n;
}
list->last = n;
list->count++;
list->total_len += n->str.len;
}
void Str8List_add(Arena *arena, Str8List *list, str8 str) {
Str8Node *n = Arena_take_array(arena, Str8Node, 1);
n->str = str;
n->next = 0;
Str8List_add_node(list, n);
}
/* CRT - stdio */
str8 stdio_load_entire_file(Arena *arena, str8 filepath);
b32 stdio_write_file(str8 filepath, Str8List text);
str8 stdio_load_entire_file(Arena *arena, str8 filepath) {
str8 file_content = {0};
FILE *file = fopen(filepath.str, "rb");
if (file != 0) {
fseek(file, 0, SEEK_END);
u64 file_len = ftell(file);
fseek(file, 0, SEEK_SET);
file_content.str = (chr8*) Arena_take(arena, file_len+1);
if (file_content.str != 0) {
file_content.len = file_len;
fread(file_content.str, 1, file_len, file);
file_content.str[file_content.len] = 0;
}
fclose(file);
}
return file_content;
}
b32 stdio_write_file(str8 filepath, Str8List text) {
u64 bytes_written = 0;
FILE *file = fopen(filepath.str, "wb");
if (file != 0) {
Str8Node* n = text.first;
for (s64 i = 0; i < text.count; i++, n = n->next) {
if (!fwrite(n->str.str, n->str.len, 1, file)) {
break;
}
bytes_written += n->str.len;
}
fclose(file);
}
return bytes_written == text.total_len;
}
/** HMM2.0 Update Tool **/
enum Targets {
/* hmm_ and HMM_ prefixes */
PREFIX_TYPE, PREFIX_FUNCTION,
PREFIXES_Size,
/* Struct/Union types */
TYPE_VEC, TYPE_MAT, TYPE_QUATERNION, TYPE_BOOL,
TYPE_INTERNAL_ELEMENTS_SSE,
TYPES_Size,
/* Types in Function Names */
FUN_VEC, FUN_MAT, FUN_QUATERNION,
/* Function Names for Common Operations */
FUN_EQUALS, FUN_SUBTRACT, FUN_MULTIPLY, FUN_DIVIDE,
FUN_INVERSE, FUN_R_SQUARE_ROOT, FUN_SQUARE_ROOT,
FUN_LENGTH_SQUARED, FUN_LENGTH, FUN_FAST_NORM, FUN_NORM,
FUN_SLERP, FUN_BY,
FUN_LINEAR_COMBINE_SSE, FUN_TRANSPOSE,
FUNCTIONS_Size,
/* Handedness */
HAND_PERSPECTIVE, HAND_ROTATE,
HAND_ORTHO, HAND_LOOK_AT, HAND_QUAT_AXIS_ANGLE, HAND_MAT_TO_QUAT,
HAND_Size,
};
Str8List update_file_content(Arena* arena, str8 file_content) {
Str8List out = {0};
str8 Find[HAND_Size];
str8 Repl[HAND_Size];
{ /* NOTE: Initialization */
Find[PREFIX_TYPE] = str8_lit("hmm_");
Find[PREFIX_FUNCTION] = str8_lit("HMM_");
Repl[PREFIX_TYPE] = Find[PREFIX_FUNCTION];
Find[TYPE_VEC] = str8_lit("vec");
Repl[TYPE_VEC] = str8_lit("Vec");
Find[TYPE_MAT] = str8_lit("mat");
Repl[TYPE_MAT] = str8_lit("Mat");
Find[TYPE_QUATERNION] = str8_lit("quaternion");
Repl[TYPE_QUATERNION] = str8_lit("Quat");
Find[TYPE_BOOL] = str8_lit("bool");
Repl[TYPE_BOOL] = str8_lit("Bool");
Find[TYPE_INTERNAL_ELEMENTS_SSE] = str8_lit(".InternalElementsSSE");
Repl[TYPE_INTERNAL_ELEMENTS_SSE] = str8_lit(".SSE");
Find[FUN_VEC] = str8_lit("Vec");
Repl[FUN_VEC] = str8_lit("V");
Find[FUN_MAT] = str8_lit("Mat");
Repl[FUN_MAT] = str8_lit("M");
Find[FUN_QUATERNION] = str8_lit("Quaternion");
Repl[FUN_QUATERNION] = str8_lit("Q");
Find[FUN_EQUALS] = str8_lit("Equals");
Repl[FUN_EQUALS] = str8_lit("Eq");
Find[FUN_SUBTRACT] = str8_lit("Subtract");
Repl[FUN_SUBTRACT] = str8_lit("Sub");
Find[FUN_MULTIPLY] = str8_lit("Multiply");
Repl[FUN_MULTIPLY] = str8_lit("Mul");
Find[FUN_DIVIDE] = str8_lit("Divide");
Repl[FUN_DIVIDE] = str8_lit("Div");
Find[FUN_INVERSE] = str8_lit("Inverse");
Repl[FUN_INVERSE] = str8_lit("Inv");
Find[FUN_R_SQUARE_ROOT] = str8_lit("RSquareRoot");
Repl[FUN_R_SQUARE_ROOT] = str8_lit("InvSqrt");
Find[FUN_SQUARE_ROOT] = str8_lit("SquareRoot");
Repl[FUN_SQUARE_ROOT] = str8_lit("Sqrt");
Find[FUN_LENGTH_SQUARED] = str8_lit("Squared");
Repl[FUN_LENGTH_SQUARED] = str8_lit("Sqr"); /* FIXME: not working for some reason */
Find[FUN_LENGTH] = str8_lit("Length");
Repl[FUN_LENGTH] = str8_lit("Len");
Find[FUN_SLERP] = str8_lit("Slerp");
Repl[FUN_SLERP] = str8_lit("SLerp");
Find[FUN_BY] = str8_lit("By");
Repl[FUN_BY] = str8_lit("");
Find[FUN_LINEAR_COMBINE_SSE] = str8_lit("LinearCombineSSE"); /* TODO: emit warning */
Repl[FUN_LINEAR_COMBINE_SSE] = str8_lit("LinearCombineV4M4");
Find[FUN_TRANSPOSE] = str8_lit("Transpose");
Repl[FUN_TRANSPOSE] = str8_lit("TransposeM4");
Find[FUN_FAST_NORM] = str8_lit("Fast"); /* TODO: emit warning, lower precision. */
Repl[FUN_FAST_NORM] = str8_lit("");
Find[FUN_NORM] = str8_lit("Normalize");
Repl[FUN_NORM] = str8_lit("Norm");
Find[HAND_PERSPECTIVE] = str8_lit("Perspective");
Find[HAND_ROTATE] = str8_lit("Rotate");
Find[HAND_ORTHO] = str8_lit("Orthographic");
Find[HAND_LOOK_AT] = str8_lit("LookAt");
Find[HAND_QUAT_AXIS_ANGLE] = str8_lit("FromAxisAngle");
Find[HAND_MAT_TO_QUAT] = str8_lit("ToQuaternion");
}
/* Match with a bunch of sliding windows, skipping when there can't be a match */
u64 MatchProgress[HAND_Size] = {0};
b32 FoundTypePrefix = 0;
b32 FoundFunctionPrefix = 0;
u32 Line = 1;
str8_iter(file_content) {
if (c == '\n') {
Line++;
}
if (FoundTypePrefix || FoundFunctionPrefix) {
if (chr8_is_whitespace(c)
|| str8_contains_char(str8_lit("(){}[]:;,.<>~?!@#$%^&+-*/'\""), c)) {
FoundTypePrefix = 0;
FoundFunctionPrefix = 0;
}
}
for (u32 t = 0; t < PREFIXES_Size; t++) {
if (c == Find[t].str[MatchProgress[t]]) {
MatchProgress[t]++;
if (MatchProgress[t] == Find[t].len) {
if (t == PREFIX_TYPE) {
FoundTypePrefix = 1;
} else if (t == PREFIX_FUNCTION) {
FoundFunctionPrefix = 1;
}
MatchProgress[t] = 0;
}
} else {
MatchProgress[t] = 0;
}
}
/* Replace hmm_ types */
if (FoundTypePrefix) {
for (u32 t = PREFIXES_Size+1; t < TYPES_Size; t++) {
if (c == Find[t].str[MatchProgress[t]]) {
MatchProgress[t]++;
if (MatchProgress[t] == Find[t].len) {
MatchProgress[t] = 0;
printf("\t[%u]: Find: %.*s, Repl: %.*s.\n", Line, str8_PRINTF_ARGS(Find[t]), str8_PRINTF_ARGS(Repl[t]));
Str8List_add(arena, &out,
str8_first(file_content,
i + 1 - (Find[t].len + Find[PREFIX_TYPE].len)));
Str8List_add(arena, &out, Repl[PREFIX_TYPE]);
Str8List_add(arena, &out, Repl[t]);
file_content = str8_skip(file_content, i+1);
i = -1;
}
} else {
MatchProgress[t] = 0;
}
}
}
/* If in a HMM_ function, do function name replacements */
if (FoundFunctionPrefix) {
for (u32 t = TYPES_Size+1; t < FUNCTIONS_Size; t++) {
if (c == Find[t].str[MatchProgress[t]]) {
MatchProgress[t]++;
if (MatchProgress[t] == Find[t].len) {
MatchProgress[t] = 0;
printf("\t[%u]: Find: %.*s, Repl: %.*s.\n", Line, str8_PRINTF_ARGS(Find[t]), str8_PRINTF_ARGS(Repl[t]));
Str8List_add(arena, &out, str8_first(file_content, i + 1 - Find[t].len));
Str8List_add(arena, &out, Repl[t]);
file_content = str8_skip(file_content, i+1);
i = -1;
/* NOTE(lcf): Special case because Find[] overlaps here */
if (t == FUN_R_SQUARE_ROOT) {
MatchProgress[FUN_SQUARE_ROOT] = 0;
}
if (t == FUN_LINEAR_COMBINE_SSE) {
printf("\t[%u]: HMM_LinearCombineSSE is now HMM_LinearCombineV4M4, and will now use a fallback method when SSE is not available. \n\tYou no longer need to check for the availability of SSE.\n", Line);
}
if (t == FUN_VEC) {
/* NOTE(lcf): if pattern is Vec2i, this is now i */
c = file_content.str[1];
if (c == 'i') {
Str8List_add(arena, &out, str8_first(file_content, 1));
Str8List_add(arena, &out, str8_lit("I"));
file_content = str8_skip(file_content, 2);
} else if (c == 'v') {
Str8List_add(arena, &out, str8_first(file_content, 1));
Str8List_add(arena, &out, str8_lit("V"));
file_content = str8_skip(file_content, 2);
} else if (c == 'f') {
Str8List_add(arena, &out, str8_first(file_content, 1));
Str8List_add(arena, &out, str8_lit("F"));
file_content = str8_skip(file_content, 2);
}
} else if (t == FUN_MAT) {
/* if pattern is Mat4d, this is now d */
c = file_content.str[1];
if (c == 'd') {
Str8List_add(arena, &out, str8_first(file_content, 1));
Str8List_add(arena, &out, str8_lit("D"));
file_content = str8_skip(file_content, 2);
} else if (c == 'f') {
Str8List_add(arena, &out, str8_first(file_content, 1));
Str8List_add(arena, &out, str8_lit("F"));
file_content = str8_skip(file_content, 2);
}
}
}
} else {
MatchProgress[t] = 0;
}
}
}
/* Handedness cases. */
if (FoundFunctionPrefix) {
for (u32 t = FUNCTIONS_Size+1; t < HAND_Size; t++) {
if (c == Find[t].str[MatchProgress[t]]) {
MatchProgress[t]++;
if (MatchProgress[t] == Find[t].len) {
MatchProgress[t] = 0;
chr8 check = file_content.str[i+1];
if (check == '(') {
printf("\t[%u]: Find: %.*s, Appending: _RH for old default handedness.\n", Line, str8_PRINTF_ARGS(Find[t]));
Str8List_add(arena, &out, str8_first(file_content, i + 1));
Str8List_add(arena, &out, str8_lit("_RH("));
file_content = str8_skip(file_content, i+2);
i = -1;
if (t == HAND_PERSPECTIVE || t == HAND_ROTATE) {
printf("\t[%u]: ", Line);
if (t == HAND_PERSPECTIVE) {
printf("HMM_Perspective_RH()");
} else {
printf("HMM_Rotate_RH()");
}
printf(" now takes Radians. Wrapping Degrees with HMM_AngleDeg()\n");
u64 end_arg = str8_char_location(file_content, ',');
if (end_arg != LCF_STRING_NO_MATCH) {
Str8List_add(arena, &out, str8_lit("HMM_AngleDeg("));
Str8List_add(arena, &out, str8_first(file_content, end_arg));
Str8List_add(arena, &out, str8_lit(")"));
file_content = str8_skip(file_content, end_arg);
}
}
}
}
} else {
MatchProgress[t] = 0;
}
}
}
}
Str8List_add(arena, &out, file_content);
return out;
}
void print_usage() {
printf("Updates C and C++ source code to use Handmade Math version 2.\n");
#ifdef _WIN32
printf("Usage: update_hmm.exe <filename> [<filename>...]\n");
#else
printf("Usage: update_hmm <filename> [<filename>...]\n");
#endif
}
int main(int argc, char* argv[]) {
Arena *tempa = Arena_create(GB(1));
if (argc == 1) {
print_usage();
return 1;
}
s32 argi = 1;
str8 arg = str8_from_cstring(argv[argi]);
if (arg.len == 2 && (arg.str[1] == 'h' || arg.str[1] == '?')) {
print_usage();
return 0;
}
for (; argi < argc; argi++) {
arg = str8_from_cstring(argv[argi]);
str8 file_content = stdio_load_entire_file(tempa, arg);
if (str8_is_empty(file_content)) {
printf("X - Invalid file name: %.*s\n\n", str8_PRINTF_ARGS(arg));
continue;
}
printf("O - Updating file: %.*s -------------------\n", str8_PRINTF_ARGS(arg));
Str8List result = update_file_content(tempa, file_content);
printf("\n");
stdio_write_file(arg, result);
Arena_reset_all(tempa);
}
return 0;
}

166
update/update_hmm.py Executable file
View File

@@ -0,0 +1,166 @@
#!/usr/bin/env python3
import argparse
import os
import re
typeReplacements = [
('hmm_', 'HMM_'),
('vec', 'Vec'),
('mat', 'Mat'),
('quaternion', 'Quaternion'),
('bool', 'Bool'),
('.InternalElementsSSE', '.SSE'),
]
funcReplacements = [
('HMM_', 'HMM_'),
('Vec', 'V'),
('Mat', 'M'),
('Quaternion', 'Q'),
('Equals', 'Eq'),
('Subtract', 'Sub'),
('Multiply', 'Mul'),
('Divide', 'Div'),
('Inverse', 'Inv'),
('RSquareRoot', 'InvSqrt'),
('SquareRoot', 'Sqrt'),
('Squared', 'Sqr'),
('Length', 'Len'),
('Slerp', 'SLerp'),
('By', ''),
('LinearCombineSSE', 'LinearCombineV4M4'),
('Transpose', 'TransposeM4'),
('Fast', ''), # TODO(port): emit warning, lower precision
('Normalize', 'Norm'),
('ToRadians', 'ToRad')
]
handedFuncs = [
'Perspective',
'Rotate',
'Orthographic',
'LookAt',
'FromAxisAngle',
'ToQuaternion',
]
projectionFuncs = [
'Perspective',
'Orthographic',
]
numFiles = 0
numWarnings = 0
def printWarning(msg):
global numWarnings
numWarnings += 1
print('WARNING: {}'.format(msg))
def updateFile(filename):
global numFiles
print('Updating: {}'.format(filename))
numFiles += 1
result = ''
with open(filename, 'r', newline='') as f:
for lineNo, line in enumerate(f):
updatedLine = line
def printLineWarning(msg):
printWarning(' Line {}: {}'.format(lineNo + 1, msg))
def replaceName(m):
name = m.group()
if name.startswith('hmm_'):
# do type replacements
for before, after in typeReplacements:
if before not in name:
continue
name = name.replace(before, after)
else:
# do func replacements
for before, after in funcReplacements:
if before not in name:
continue
name = name.replace(before, after)
if after == 'LinearCombineV4M4':
printLineWarning('HMM_LinearCombineSSE is now HMM_LinearCombineV4M4, and will now use a fallback method when SSE is not available. You no longer need to check for the availability of SSE.')
if after == 'V' or after == 'M':
# uppercase the modifier, if any
name = re.sub(
r'[VM]\d[ivfd]?',
lambda m: m.group().upper(),
name
)
# and also nuke the integer constructors
vecIntMatch = re.search(r'(V\d)I', name)
if vecIntMatch:
name = name.replace(vecIntMatch.group(), vecIntMatch.group(1))
# add handedness / NDC modifiers
if not any(x in name for x in ['RH', 'LH', 'NO', 'ZO']):
for handedFunc in handedFuncs:
suffixed = handedFunc + '_RH'
if handedFunc in projectionFuncs:
suffixed += '_NO'
name = name.replace(handedFunc, suffixed)
return name
def wrapDegrees(m):
name = m.group('name')
arg = m.group('arg')
if '(' in arg:
# all bets are off, don't wrap the argument
printLineWarning('{} now takes radians, but we were unable to automatically wrap the first argument with HMM_AngleDeg().'.format(name))
return m.group()
return '{}(HMM_AngleDeg({}),'.format(name, arg)
updatedLine = re.sub(r'(hmm_|HMM_)\w+', replaceName, updatedLine)
updatedLine = re.sub(r'(?P<name>HMM_Perspective_RH_NO|HMM_Rotate_RH)\((?P<arg>.*?),', wrapDegrees, updatedLine)
result += updatedLine
with open(filename, 'w', newline='') as f:
f.write(result)
parser = argparse.ArgumentParser(
prog = 'update_hmm',
description = 'Updates C and C++ source code to use Handmade Math 2.0.',
)
parser.add_argument(
'filename', nargs='+',
help='A file or directory to update to HMM 2.0. If a directory, all files with extensions from --exts will be processed.',
)
parser.add_argument(
'--exts', nargs='+', default=['.c', '.cpp', '.h', '.hpp'],
help='File extensions to run the script on, when targeting a directory. Default: .c, .cpp, .h, .hpp.',
metavar='.foo',
)
args = parser.parse_args()
for path in args.filename:
filenames = []
if os.path.isfile(path):
filenames = [path]
else:
for root, dirs, files in os.walk(path):
for file in files:
if file == 'HandmadeMath.h':
printWarning('HandmadeMath.h will not be replaced by this script.')
elif file.endswith(tuple(args.exts)):
filenames.append(os.path.join(root, file))
for filename in filenames:
try:
updateFile(filename)
except UnicodeDecodeError:
pass
print('Updated {} files with {} warnings.'.format(numFiles, numWarnings))

View File

@@ -1,12 +0,0 @@
@REM Batch script to run update_hmm.exe on all your code files.
@REM Example:
@REM "update_hmm_all.bat Code\Project\" -> Recursively update all files/folders in .\Code\Project\
for /r %1 %%v in (*.c) do update_hmm.exe "%%v"
for /r %1 %%v in (*.h) do update_hmm.exe "%%v"
for /r %1 %%v in (*.cpp) do update_hmm.exe "%%v"
for /r %1 %%v in (*.hpp) do update_hmm.exe "%%v"
@REM @REM Uncomment for sokol-samples
@REM for /r %1 %%v in (*.glsl) do update_hmm.exe "%%v"
@REM for /r %1 %%v in (*.hlsl) do update_hmm.exe "%%v"

View File

@@ -1,12 +0,0 @@
# Bash script to run update_hmm on all your code files.
# Example:
# "update_hmm_all Code/Project/" -> Recursively update all files/folders in ./Code/Project/
echo $1
for file in "$1"/*.{c,h,cpp,hpp} "$1"/**/*.{c,h,cpp,hpp} ; do
./update_hmm "$file"
done
# # Uncomment for sokol-samples
# for file in "$1"/*.{glsl,hlsl} "$1"/**/*.{glsl,hlsl} ; do
# ./update_hmm "$file"
# done