From d4918a514ecf5c501ed2fdfdf474b54f558cdcbe Mon Sep 17 00:00:00 2001 From: Logan Forman <35375036+dev-dwarf@users.noreply.github.com> Date: Thu, 26 Jan 2023 20:01:31 -0700 Subject: [PATCH] Drop Projection Matrix Config; Make explicit. (#154) * Add N0/Z0 projection Remove configuration macro * Update update_hmm.c Co-authored-by: Ben Visness --- HandmadeMath.h | 121 +++++++++++++++++++++++------------ test/categories/MatrixOps.h | 6 +- test/categories/Projection.h | 61 ++++++++++++------ update/update_hmm.c | 9 ++- 4 files changed, 130 insertions(+), 67 deletions(-) diff --git a/HandmadeMath.h b/HandmadeMath.h index 7fc3d9c..ab0d66c 100644 --- a/HandmadeMath.h +++ b/HandmadeMath.h @@ -26,20 +26,6 @@ ----------------------------------------------------------------------------- - 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" - - ----------------------------------------------------------------------------- - 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: @@ -1711,10 +1697,10 @@ 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_N0_RH, 1) +static inline HMM_Mat4 HMM_Orthographic_N0_RH(float Left, float Right, float Bottom, float Top, float Near, float Far) { - ASSERT_COVERED(HMM_Orthographic_RH); + ASSERT_COVERED(HMM_Orthographic_N0_RH); HMM_Mat4 Result = {0}; @@ -1725,23 +1711,49 @@ static inline HMM_Mat4 HMM_Orthographic_RH(float Left, float Right, float Bottom 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 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_Z0_RH, 1) +static inline HMM_Mat4 HMM_Orthographic_Z0_RH(float Left, float Right, float Bottom, float Top, float Near, float Far) { - ASSERT_COVERED(HMM_Orthographic_LH); + ASSERT_COVERED(HMM_Orthographic_Z0_RH); - 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[3][3] = 1.0f; + + Result.Elements[3][0] = (Left + Right) / (Left - Right); + Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top); + + Result.Elements[2][2] = 1.0f / (Near - Far); + Result.Elements[3][2] = (Near) / (Near - Far); + + return Result; +} + +COVERAGE(HMM_Orthographic_N0_LH, 1) +static inline HMM_Mat4 HMM_Orthographic_N0_LH(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + ASSERT_COVERED(HMM_Orthographic_N0_LH); + + HMM_Mat4 Result = HMM_Orthographic_N0_RH(Left, Right, Bottom, Top, Near, Far); + Result.Elements[2][2] = -Result.Elements[2][2]; + + return Result; +} + +COVERAGE(HMM_Orthographic_Z0_LH, 1) +static inline HMM_Mat4 HMM_Orthographic_Z0_LH(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + ASSERT_COVERED(HMM_Orthographic_Z0_LH); + + HMM_Mat4 Result = HMM_Orthographic_Z0_RH(Left, Right, Bottom, Top, Near, Far); Result.Elements[2][2] = -Result.Elements[2][2]; return Result; @@ -1765,43 +1777,68 @@ 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_N0_RH, 1) +static inline HMM_Mat4 HMM_Perspective_N0_RH(float FOV, float AspectRatio, float Near, float Far) { - ASSERT_COVERED(HMM_Perspective_RH); + ASSERT_COVERED(HMM_Perspective_N0_RH); 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_Z0_RH, 1) +static inline HMM_Mat4 HMM_Perspective_Z0_RH(float FOV, float AspectRatio, float Near, float Far) +{ + ASSERT_COVERED(HMM_Perspective_Z0_RH); + + 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, 1) -static inline HMM_Mat4 HMM_Perspective_LH(float FOV, float AspectRatio, float Near, float Far) +COVERAGE(HMM_Perspective_N0_LH, 1) +static inline HMM_Mat4 HMM_Perspective_N0_LH(float FOV, float AspectRatio, float Near, float Far) { - ASSERT_COVERED(HMM_Perspective_LH); + ASSERT_COVERED(HMM_Perspective_N0_LH); - HMM_Mat4 Result = HMM_Perspective_RH(FOV, AspectRatio, Near, Far); - Result.Elements[2][2] = -Result.Elements[2][2]; + HMM_Mat4 Result = HMM_Perspective_N0_RH(FOV, AspectRatio, Near, Far); + Result.Elements[2][2] = -Result.Elements[2][2]; Result.Elements[2][3] = -Result.Elements[2][3]; + + return Result; +} - return (Result); +COVERAGE(HMM_Perspective_Z0_LH, 1) +static inline HMM_Mat4 HMM_Perspective_Z0_LH(float FOV, float AspectRatio, float Near, float Far) +{ + ASSERT_COVERED(HMM_Perspective_Z0_LH); + + HMM_Mat4 Result = HMM_Perspective_Z0_RH(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, 1) diff --git a/test/categories/MatrixOps.h b/test/categories/MatrixOps.h index c1bc987..54c59a7 100644 --- a/test/categories/MatrixOps.h +++ b/test/categories/MatrixOps.h @@ -249,7 +249,7 @@ TEST(InvMatrix, InvGeneral) TEST(InvMatrix, Mat4Inverses) { { - HMM_Mat4 Matrix = HMM_Orthographic_RH(-160+100, 160+100, -90+200, 90+200, 10, 10000); + HMM_Mat4 Matrix = HMM_Orthographic_N0_RH(-160+100, 160+100, -90+200, 90+200, 10, 10000); HMM_Mat4 Expect = HMM_M4D(1.0f); HMM_Mat4 Inverse = HMM_InvOrthographic(Matrix); HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse); @@ -272,10 +272,10 @@ TEST(InvMatrix, Mat4Inverses) 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_FLOAT_EQ(Result.Elements[3][3], Expect.Elements[3][3]); } { - HMM_Mat4 Matrix = HMM_Perspective_RH(HMM_AngleDeg(120), 16.0/9.0, 10, 10000); + HMM_Mat4 Matrix = HMM_Perspective_N0_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); diff --git a/test/categories/Projection.h b/test/categories/Projection.h index 0682982..f87a676 100644 --- a/test/categories/Projection.h +++ b/test/categories/Projection.h @@ -3,47 +3,66 @@ TEST(Projection, Orthographic) { { - HMM_Mat4 projection = HMM_Orthographic_RH(-10.0f, 10.0f, -5.0f, 5.0f, 0.0f, -10.0f); - - HMM_Vec3 original = HMM_V3(5.0f, 5.0f, -5.0f); - HMM_Vec4 projected = HMM_MulM4V4(projection, HMM_V4V(original, 1)); + HMM_Mat4 projection = HMM_Orthographic_N0_RH(-10.0f, 10.0f, -5.0f, 5.0f, 1.0f, 10.0f); + HMM_Vec4 original = HMM_V4(5.0f, 5.0f, -1.0f, 1.0); + HMM_Vec4 projected = HMM_MulM4V4(projection, original); 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.Z, -1.0f); EXPECT_FLOAT_EQ(projected.W, 1.0f); + + /* Z0 */ + projection = HMM_Orthographic_Z0_RH(-10.0f, 10.0f, -5.0f, 5.0f, 1.0f, -10.0f); + projected = HMM_MulM4V4(projection, original); + EXPECT_FLOAT_EQ(projected.Z, 0.0f); } { - HMM_Mat4 projection = HMM_Orthographic_LH(-10.0f, 10.0f, -5.0f, 5.0f, 0.0f, 10.0f); - - HMM_Vec3 original = HMM_V3(5.0f, 5.0f, -5.0f); - HMM_Vec4 projected = HMM_MulM4V4(projection, HMM_V4V(original, 1)); - + HMM_Mat4 projection = HMM_Orthographic_N0_LH(-10.0f, 10.0f, -5.0f, 5.0f, 1.0f, -10.0f); + HMM_Vec4 original = HMM_V4(5.0f, 5.0f, 1.0f, 1.0); + HMM_Vec4 projected = HMM_MulM4V4(projection, original); + 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.Z, -1.0f); EXPECT_FLOAT_EQ(projected.W, 1.0f); + + /* Z0 */ + projection = HMM_Orthographic_Z0_LH(-10.0f, 10.0f, -5.0f, 5.0f, 1.0f, -10.0f); + projected = HMM_MulM4V4(projection, original); + EXPECT_FLOAT_EQ(projected.Z, 0.0f); } } TEST(Projection, Perspective) { { - 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)); + HMM_Mat4 projection = HMM_Perspective_N0_RH(HMM_AngleDeg(90.0f), 2.0f, 1.0f, 15.0f); + HMM_Vec4 original = HMM_V4(5.0f, 5.0f, -1.0f, 1.0f); + HMM_Vec4 projected = HMM_MulM4V4(projection, original); 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); + EXPECT_FLOAT_EQ(projected.Z, -1.0f); + EXPECT_FLOAT_EQ(projected.W, 1.0f); + + /* Z0 */ + projection = HMM_Perspective_Z0_RH(HMM_AngleDeg(90.0f), 2.0f, 1.0f, 15.0f); + projected = HMM_MulM4V4(projection, original); + EXPECT_FLOAT_EQ(projected.Z, 0.0f); } + { - 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)); + HMM_Mat4 projection = HMM_Perspective_N0_LH(HMM_AngleDeg(90.0f), 2.0f, 1.0f, 15.0f); + HMM_Vec4 original = HMM_V4(5.0f, 5.0f, 1.0f, 1.0f); + HMM_Vec4 projected = HMM_MulM4V4(projection, original); 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); + EXPECT_FLOAT_EQ(projected.Z, -1.0f); + EXPECT_FLOAT_EQ(projected.W, 1.0f); + + /* Z0 */ + projection = HMM_Perspective_Z0_LH(HMM_AngleDeg(90.0f), 2.0f, 1.0f, 15.0f); + projected = HMM_MulM4V4(projection, original); + EXPECT_FLOAT_EQ(projected.Z, 0.0f); } } diff --git a/update/update_hmm.c b/update/update_hmm.c index b01fdd0..493a2f6 100644 --- a/update/update_hmm.c +++ b/update/update_hmm.c @@ -482,8 +482,15 @@ Str8List update_file_content(Arena* arena, str8 file_content) { 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)); + + if (t == HAND_PERSPECTIVE || t == HAND_ORTHO) { + printf("\t[%u]: Appending _N0 for old default NDC.\n", Line, str8_PRINTF_ARGS(Find[t])); + Str8List_add(arena, &out, str8_lit("_N0")); + } + + printf("\t[%u]: Find: %.*s, Appending: _RH for old default handedness.\n", Line, str8_PRINTF_ARGS(Find[t])); + Str8List_add(arena, &out, str8_lit("_RH(")); file_content = str8_skip(file_content, i+2); i = -1;