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>
This commit is contained in:
Logan Forman
2023-01-26 20:01:31 -07:00
committed by GitHub
parent 37aa3fa6a0
commit d4918a514e
4 changed files with 130 additions and 67 deletions

View File

@@ -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 Handmade Math ships with SSE (SIMD) implementations of several common
operations. To disable the use of SSE intrinsics, you must operations. To disable the use of SSE intrinsics, you must
define HANDMADE_MATH_NO_SSE before including this file: 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 * Common graphics transformations
*/ */
COVERAGE(HMM_Orthographic_RH, 1) COVERAGE(HMM_Orthographic_N0_RH, 1)
static inline HMM_Mat4 HMM_Orthographic_RH(float Left, float Right, float Bottom, float Top, float Near, float Far) 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}; 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][0] = (Left + Right) / (Left - Right);
Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top); 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[2][2] = 2.0f / (Near - Far);
Result.Elements[3][2] = (Far + Near) / (Near - Far); Result.Elements[3][2] = (Far + Near) / (Near - Far);
#endif
return Result; return Result;
} }
COVERAGE(HMM_Orthographic_LH, 1) COVERAGE(HMM_Orthographic_Z0_RH, 1)
static inline HMM_Mat4 HMM_Orthographic_LH(float Left, float Right, float Bottom, float Top, float Near, float Far) 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]; Result.Elements[2][2] = -Result.Elements[2][2];
return Result; return Result;
@@ -1765,43 +1777,68 @@ static inline HMM_Mat4 HMM_InvOrthographic(HMM_Mat4 OrthoMatrix)
return Result; return Result;
} }
COVERAGE(HMM_Perspective_RH, 1) COVERAGE(HMM_Perspective_N0_RH, 1)
static inline HMM_Mat4 HMM_Perspective_RH(float FOV, float AspectRatio, float Near, float Far) 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}; HMM_Mat4 Result = {0};
// See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml // See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
float Cotangent = 1.0f / HMM_TanF(FOV / 2.0f); float Cotangent = 1.0f / HMM_TanF(FOV / 2.0f);
Result.Elements[0][0] = Cotangent / AspectRatio; Result.Elements[0][0] = Cotangent / AspectRatio;
Result.Elements[1][1] = Cotangent; Result.Elements[1][1] = Cotangent;
Result.Elements[2][3] = -1.0f; 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[2][2] = (Near + Far) / (Near - Far);
Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far); Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far);
#endif
return Result; return Result;
} }
COVERAGE(HMM_Perspective_LH, 1) COVERAGE(HMM_Perspective_Z0_RH, 1)
static inline HMM_Mat4 HMM_Perspective_LH(float FOV, float AspectRatio, float Near, float Far) static inline HMM_Mat4 HMM_Perspective_Z0_RH(float FOV, float AspectRatio, float Near, float Far)
{ {
ASSERT_COVERED(HMM_Perspective_LH); ASSERT_COVERED(HMM_Perspective_Z0_RH);
HMM_Mat4 Result = HMM_Perspective_RH(FOV, AspectRatio, Near, Far); 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_N0_LH, 1)
static inline HMM_Mat4 HMM_Perspective_N0_LH(float FOV, float AspectRatio, float Near, float Far)
{
ASSERT_COVERED(HMM_Perspective_N0_LH);
HMM_Mat4 Result = HMM_Perspective_N0_RH(FOV, AspectRatio, Near, Far);
Result.Elements[2][2] = -Result.Elements[2][2]; Result.Elements[2][2] = -Result.Elements[2][2];
Result.Elements[2][3] = -Result.Elements[2][3]; 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) COVERAGE(HMM_InvPerspective, 1)

View File

@@ -249,7 +249,7 @@ TEST(InvMatrix, InvGeneral)
TEST(InvMatrix, Mat4Inverses) 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 Expect = HMM_M4D(1.0f);
HMM_Mat4 Inverse = HMM_InvOrthographic(Matrix); HMM_Mat4 Inverse = HMM_InvOrthographic(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse); HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);
@@ -275,7 +275,7 @@ TEST(InvMatrix, Mat4Inverses)
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 Expect = HMM_M4D(1.0f);
HMM_Mat4 Inverse = HMM_InvPerspective(Matrix); HMM_Mat4 Inverse = HMM_InvPerspective(Matrix);
HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse); HMM_Mat4 Result = HMM_MulM4(Matrix, Inverse);

View File

@@ -3,47 +3,66 @@
TEST(Projection, Orthographic) TEST(Projection, Orthographic)
{ {
{ {
HMM_Mat4 projection = HMM_Orthographic_RH(-10.0f, 10.0f, -5.0f, 5.0f, 0.0f, -10.0f); 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_Vec3 original = HMM_V3(5.0f, 5.0f, -5.0f); HMM_Vec4 projected = HMM_MulM4V4(projection, original);
HMM_Vec4 projected = HMM_MulM4V4(projection, HMM_V4V(original, 1));
EXPECT_FLOAT_EQ(projected.X, 0.5f); EXPECT_FLOAT_EQ(projected.X, 0.5f);
EXPECT_FLOAT_EQ(projected.Y, 1.0f); 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); 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_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_Vec3 original = HMM_V3(5.0f, 5.0f, -5.0f); HMM_Vec4 projected = HMM_MulM4V4(projection, original);
HMM_Vec4 projected = HMM_MulM4V4(projection, HMM_V4V(original, 1));
EXPECT_FLOAT_EQ(projected.X, 0.5f); EXPECT_FLOAT_EQ(projected.X, 0.5f);
EXPECT_FLOAT_EQ(projected.Y, 1.0f); 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); 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) TEST(Projection, Perspective)
{ {
{ {
HMM_Mat4 projection = HMM_Perspective_RH(HMM_AngleDeg(90.0f), 2.0f, 5.0f, 15.0f); HMM_Mat4 projection = HMM_Perspective_N0_RH(HMM_AngleDeg(90.0f), 2.0f, 1.0f, 15.0f);
HMM_Vec3 original = HMM_V3(5.0f, 5.0f, -15.0f); HMM_Vec4 original = HMM_V4(5.0f, 5.0f, -1.0f, 1.0f);
HMM_Vec4 projected = HMM_MulM4V4(projection, HMM_V4V(original, 1)); HMM_Vec4 projected = HMM_MulM4V4(projection, original);
EXPECT_FLOAT_EQ(projected.X, 2.5f); EXPECT_FLOAT_EQ(projected.X, 2.5f);
EXPECT_FLOAT_EQ(projected.Y, 5.0f); EXPECT_FLOAT_EQ(projected.Y, 5.0f);
EXPECT_FLOAT_EQ(projected.Z, 15.0f); EXPECT_FLOAT_EQ(projected.Z, -1.0f);
EXPECT_FLOAT_EQ(projected.W, 15.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_Mat4 projection = HMM_Perspective_N0_LH(HMM_AngleDeg(90.0f), 2.0f, 1.0f, 15.0f);
HMM_Vec3 original = HMM_V3(5.0f, 5.0f, 15.0f); HMM_Vec4 original = HMM_V4(5.0f, 5.0f, 1.0f, 1.0f);
HMM_Vec4 projected = HMM_MulM4V4(projection, HMM_V4V(original, 1)); HMM_Vec4 projected = HMM_MulM4V4(projection, original);
EXPECT_FLOAT_EQ(projected.X, 2.5f); EXPECT_FLOAT_EQ(projected.X, 2.5f);
EXPECT_FLOAT_EQ(projected.Y, 5.0f); EXPECT_FLOAT_EQ(projected.Y, 5.0f);
EXPECT_FLOAT_EQ(projected.Z, 15.0f); EXPECT_FLOAT_EQ(projected.Z, -1.0f);
EXPECT_FLOAT_EQ(projected.W, 15.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);
} }
} }

View File

@@ -482,8 +482,15 @@ Str8List update_file_content(Arena* arena, str8 file_content) {
chr8 check = file_content.str[i+1]; chr8 check = file_content.str[i+1];
if (check == '(') { 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_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(")); Str8List_add(arena, &out, str8_lit("_RH("));
file_content = str8_skip(file_content, i+2); file_content = str8_skip(file_content, i+2);
i = -1; i = -1;