From e15ee2392500bb82f3900a980f74a021459d0e1a Mon Sep 17 00:00:00 2001 From: Technical Fowl <159498671+TechnicalFowl@users.noreply.github.com> Date: Sat, 6 Dec 2025 15:08:51 -0800 Subject: [PATCH] Matrix math cleanup (#2620) * Cleanup inconsistent matrix math functions * Add more matrix unit tests --- lib/std/math/matrix.c3 | 20 +++++---- test/unit/stdlib/math/matrix.c3 | 76 +++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 9 deletions(-) diff --git a/lib/std/math/matrix.c3 b/lib/std/math/matrix.c3 index f73088642..e6089ee92 100644 --- a/lib/std/math/matrix.c3 +++ b/lib/std/math/matrix.c3 @@ -13,6 +13,8 @@ alias matrix4_ortho @builtin = matrix::ortho {double}; alias matrix4f_ortho @builtin = matrix::ortho {float}; alias matrix4_perspective @builtin = matrix::perspective {double}; alias matrix4f_perspective @builtin = matrix::perspective {float}; +alias matrix4_look_at @builtin = matrix::look_at {double}; +alias matrix4f_look_at @builtin = matrix::look_at {float}; alias MATRIX2_IDENTITY @builtin = matrix::IDENTITY2 {double}; alias MATRIX2F_IDENTITY @builtin = matrix::IDENTITY2 {float}; @@ -149,9 +151,9 @@ fn Matrix4x4 Matrix4x4.mul(Matrix4x4* self, Matrix4x4 b) @operator(*) }; } -fn Matrix2x2 Matrix2x2.component_mul(&self, Real s) => matrix_component_mul(self, s); -fn Matrix3x3 Matrix3x3.component_mul(&self, Real s) => matrix_component_mul(self, s); -fn Matrix4x4 Matrix4x4.component_mul(&self, Real s) => matrix_component_mul(self, s); +fn Matrix2x2 Matrix2x2.component_mul(&self, Real s) @operator(*) => matrix_component_mul(self, s); +fn Matrix3x3 Matrix3x3.component_mul(&self, Real s) @operator(*) => matrix_component_mul(self, s); +fn Matrix4x4 Matrix4x4.component_mul(&self, Real s) @operator(*) => matrix_component_mul(self, s); fn Matrix2x2 Matrix2x2.add(&self, Matrix2x2 mat2) @operator(+) => matrix_add(self, mat2); fn Matrix3x3 Matrix3x3.add(&self, Matrix3x3 mat2) @operator(+) => matrix_add(self, mat2); @@ -384,9 +386,9 @@ fn Matrix4x4 Matrix4x4.rotate_z(&self, Real r) fn Matrix4x4 Matrix4x4.rotate_y(&self, Real r) { return self.mul({ - math::cos(r), 0, -math::sin(r), 0, + math::cos(r), 0, math::sin(r), 0, 0, 1, 0, 0, - math::sin(r), 0, math::cos(r), 0, + -math::sin(r), 0, math::cos(r), 0, 0, 0, 0, 1, }); } @@ -432,10 +434,10 @@ fn Matrix4x4 ortho(Real left, Real right, Real top, Real bottom, Real near, Real Real height = top - bottom; Real depth = far - near; return { - 2 / width, 0, 0, 0, - 0, 2 / height, 0, 0, - 0, 0, -2 / depth, 0, - -(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1 + 2 / width, 0, 0, -(right + left) / width, + 0, 2 / height, 0, -(top + bottom) / height, + 0, 0, -2 / depth, -(far + near) / depth, + 0, 0, 0, 1 }; } diff --git a/test/unit/stdlib/math/matrix.c3 b/test/unit/stdlib/math/matrix.c3 index 3bc4ed771..f4d6996f4 100644 --- a/test/unit/stdlib/math/matrix.c3 +++ b/test/unit/stdlib/math/matrix.c3 @@ -13,6 +13,36 @@ fn void test_mat4_translate() assert(translated.m == mat.m); } +fn void test_mat4_translate_v() +{ + Matrix4 mat = MATRIX4_IDENTITY.translate({1.0, 2.0, 3.0}); + assert(math::round_to_decimals(mat * (double[<4>]){ 10.0, 11.0, 12.0, 1.0}, 4) == {11.0, 13.0, 15.0, 1.0}); +} + +fn void test_mat4_rotate() +{ + Matrix4 rot_x = MATRIX4_IDENTITY.rotate_x(math::deg_to_rad(90)); + assert(math::round_to_decimals(rot_x * (double[<4>]){ 0.0, 1.0, 0.0, 1.0}, 4) == {0.0, 0.0, 1.0, 1.0}); + Matrix4f rot_xf = MATRIX4F_IDENTITY.rotate_x((float) math::deg_to_rad(90)); + assert(math::round_to_decimals(rot_xf * (float[<4>]){ 0.0, 1.0, 0.0, 1.0}, 4) == {0.0, 0.0, 1.0, 1.0}); + + Matrix4 rot_y = MATRIX4_IDENTITY.rotate_y(math::deg_to_rad(90)); + assert(math::round_to_decimals(rot_y * (double[<4>]){ 1.0, 0.0, 0.0, 1.0}, 4) == {0.0, 0.0, -1.0, 1.0}); + Matrix4f rot_yf = MATRIX4F_IDENTITY.rotate_y((float) math::deg_to_rad(90)); + assert(math::round_to_decimals(rot_yf * (float[<4>]){ 1.0, 0.0, 0.0, 1.0}, 4) == {0.0, 0.0, -1.0, 1.0}); + + Matrix4 rot_z = MATRIX4_IDENTITY.rotate_z(math::deg_to_rad(90)); + assert(math::round_to_decimals(rot_z * (double[<4>]){ 0.0, 1.0, 0.0, 1.0}, 4) == {-1.0, 0.0, 0.0, 1.0}); + Matrix4f rot_zf = MATRIX4F_IDENTITY.rotate_z((float) math::deg_to_rad(90)); + assert(math::round_to_decimals(rot_zf * (float[<4>]){ 0.0, 1.0, 0.0, 1.0}, 4) == {-1.0, 0.0, 0.0, 1.0}); +} + +fn void test_mat4_scale() +{ + Matrix4 scale = MATRIX4_IDENTITY.scale({2.0, 3.0, 4.0}); + assert(math::round_to_decimals(scale * (double[<4>]){ 10.0, 11.0, 12.0, 1.0}, 4) == {20.0, 33.0, 48.0, 1.0}); +} + fn void test_mat4_mul() { Matrix4 mat = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 }; @@ -22,6 +52,7 @@ fn void test_mat4_mul() assert(calc.m == value.m); assert(mat * mat2 == value); } + fn void test_mat4_lookat() { Matrix4 result = { @@ -85,6 +116,51 @@ fn void test_mat4_perspective() assert(math::round_to_decimals((double[<16>])result.m, 4) == math::round_to_decimals((double[<16>])perspective.m, 4)); assert(math::round_to_decimals((float[<16>])result_f.m, 4) == math::round_to_decimals((float[<16>])perspective_f.m, 4)); + + // Test the corners of the view frustum + double top = math::tan(math::deg_to_rad(45) / 2); + double right = math::tan(math::deg_to_rad(45) / 2) * 1.3; + + assert(math::round_to_decimals(perspective * (double[<4>]) {-right * 0.1, -top * 0.1, -0.1, 1}, 4) == {-0.1, -0.1, -0.1, 0.1}); + assert(math::round_to_decimals(perspective * (double[<4>]) { right * 0.1, -top * 0.1, -0.1, 1}, 4) == { 0.1, -0.1, -0.1, 0.1}); + assert(math::round_to_decimals(perspective * (double[<4>]) {-right * 0.1, top * 0.1, -0.1, 1}, 4) == {-0.1, 0.1, -0.1, 0.1}); + assert(math::round_to_decimals(perspective * (double[<4>]) { right * 0.1, top * 0.1, -0.1, 1}, 4) == { 0.1, 0.1, -0.1, 0.1}); + assert(math::round_to_decimals(perspective * (double[<4>]) {-right * 1000, -top * 1000, -1000, 1}, 4) == {-1000, -1000, 1000, 1000}); + assert(math::round_to_decimals(perspective * (double[<4>]) { right * 1000, -top * 1000, -1000, 1}, 4) == { 1000, -1000, 1000, 1000}); + assert(math::round_to_decimals(perspective * (double[<4>]) {-right * 1000, top * 1000, -1000, 1}, 4) == {-1000, 1000, 1000, 1000}); + assert(math::round_to_decimals(perspective * (double[<4>]) { right * 1000, top * 1000, -1000, 1}, 4) == { 1000, 1000, 1000, 1000}); +} + +fn void test_mat4_ortho() +{ + Matrix4 result = { + 0.0025, 0, 0, -0.5, + 0, 0.005, 0, 0, + 0, 0, -0.05, -0.5, + 0, 0, 0, 1 + }; + Matrix4f result_f = { + 0.0025, 0, 0, -0.5, + 0, 0.005, 0, 0, + 0, 0, -0.05, -0.5, + 0, 0, 0, 1 + }; + + Matrix4 ortho = matrix4_ortho(-200, 600, 200, -200, -10, 30); + Matrix4f ortho_f = matrix4f_ortho(-200, 600, 200, -200, -10, 30); + + assert(math::round_to_decimals((double[<16>])result.m, 4) == math::round_to_decimals((double[<16>])ortho.m, 4)); + assert(math::round_to_decimals((double[<16>])result_f.m, 4) == math::round_to_decimals((double[<16>])ortho_f.m, 4)); + + // Test the corners of the view space + assert(math::round_to_decimals(ortho * (double[<4>]) {-200, -200, 10, 1}, 4) == {-1.0, -1.0, -1.0, 1.0}); + assert(math::round_to_decimals(ortho * (double[<4>]) { 600, -200, 10, 1}, 4) == { 1.0, -1.0, -1.0, 1.0}); + assert(math::round_to_decimals(ortho * (double[<4>]) {-200, 200, 10, 1}, 4) == {-1.0, 1.0, -1.0, 1.0}); + assert(math::round_to_decimals(ortho * (double[<4>]) { 600, 200, 10, 1}, 4) == { 1.0, 1.0, -1.0, 1.0}); + assert(math::round_to_decimals(ortho * (double[<4>]) {-200, -200, -30, 1}, 4) == {-1.0, -1.0, 1.0, 1.0}); + assert(math::round_to_decimals(ortho * (double[<4>]) { 600, -200, -30, 1}, 4) == { 1.0, -1.0, 1.0, 1.0}); + assert(math::round_to_decimals(ortho * (double[<4>]) {-200, 200, -30, 1}, 4) == {-1.0, 1.0, 1.0, 1.0}); + assert(math::round_to_decimals(ortho * (double[<4>]) { 600, 200, -30, 1}, 4) == { 1.0, 1.0, 1.0, 1.0}); } fn void test_mat3()