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
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)

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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;