diff --git a/example/q3bsp.cpp b/example/q3bsp.cpp index 88c8aa9..9a199cd 100644 --- a/example/q3bsp.cpp +++ b/example/q3bsp.cpp @@ -36,6 +36,8 @@ #include "math/vec2.hpp" #include "math/vec3.hpp" #include "math/vec4.hpp" +#include "math/mat2x2.hpp" +#include "math/mat3x3.hpp" #include "math/mat4x4.hpp" #include "math/geometry.hpp" @@ -133,7 +135,7 @@ struct position_normal { static position_normal vertex_cache[16384]; -static inline vec3 normal_transform(mat4x4& trans, vec3 normal) +static inline vec3 normal_transform(const mat4x4& trans, vec3 normal) { vec4 n = trans * (vec4){normal.x, normal.y, normal.z, 0.f}; // no translation component return {n.x, n.y, n.z}; @@ -239,7 +241,7 @@ void global_texture(ta_parameter_writer& writer, int ix) texture_control_word); } -void transform_vertices(uint8_t * buf, int length, mat4x4& trans) +void transform_vertices(uint8_t * buf, int length, const mat4x4& trans) { q3bsp_vertex_t * vert = reinterpret_cast(buf); @@ -440,7 +442,7 @@ void transfer_faces(ta_parameter_writer& writer) } } -void transfer_icosphere(ta_parameter_writer& writer, mat4x4& screen_trans) +void transfer_icosphere(ta_parameter_writer& writer, const mat4x4& screen_trans) { const struct model * model = &icosphere_model; const struct object * object = model->object[0]; @@ -522,7 +524,7 @@ static inline void render_quad(ta_parameter_writer& writer, base_color); } -void render_bounding_box(ta_parameter_writer& writer, mat4x4& trans, vec3 max, vec3 min, uint32_t color) +void render_bounding_box(ta_parameter_writer& writer, const mat4x4& trans, vec3 max, vec3 min, uint32_t color) { vec3 a = max; vec3 b = min; @@ -640,6 +642,30 @@ void render_sphere_position(ta_parameter_writer& writer) para_control::list_type::opaque); } +void render_zero_position(ta_parameter_writer& writer, const mat4x4& screen_trans_inv) +{ + char __attribute__((aligned(4))) s[64] = "zero: "; + for (uint32_t i = 2; i < ((sizeof (s)) - 8) / 4; i++) + reinterpret_cast(s)[i] = 0x20202020; + + vec3 zero = {0, 0, 0}; + vec3 pos = screen_trans_inv * zero; + + int offset = 8; + int row = 6; + offset += format_float(&s[offset], pos[0], 7); + offset += format_float(&s[offset], pos[1], 7); + offset += format_float(&s[offset], pos[2], 7); + + font_bitmap::transform_string(writer, + 8, 16, // texture + 8, 16, // glyph + 16 + 2 * 8, // position x + 16 + row * 16, // position y + s, offset, + para_control::list_type::opaque); +} + static int root_ix = 0; void render_ix(ta_parameter_writer& writer, int row, char * s, int ix) @@ -705,14 +731,14 @@ void render_leaf_ix(ta_parameter_writer& writer) } } -void render_bounding_box_mm(ta_parameter_writer& writer, mat4x4& trans, int mins[3], int maxs[3], uint32_t color) +void render_bounding_box_mm(ta_parameter_writer& writer, const mat4x4& trans, int mins[3], int maxs[3], uint32_t color) { vec3 max = {(float)maxs[0], (float)maxs[1], (float)maxs[2]}; vec3 min = {(float)mins[0], (float)mins[1], (float)mins[2]}; render_bounding_box(writer, trans, max, min, color); } -void render_bounding_boxes(ta_parameter_writer& writer, mat4x4& trans) +void render_bounding_boxes(ta_parameter_writer& writer, const mat4x4& trans) { uint8_t * buf = reinterpret_cast(&_binary_pk_maps_20kdm2_bsp_start); q3bsp_header_t * header = reinterpret_cast(buf); @@ -760,7 +786,7 @@ bool vec3_in_bb(vec3 v, int mins[3], int maxs[3]) v.z <= maxs[2]; } -void render_leaf_faces(ta_parameter_writer& writer, mat4x4& trans, q3bsp_leaf_t * leaf) +void render_leaf_faces(ta_parameter_writer& writer, const mat4x4& trans, q3bsp_leaf_t * leaf) { uint8_t * buf = reinterpret_cast(&_binary_pk_maps_20kdm2_bsp_start); q3bsp_header_t * header = reinterpret_cast(buf); @@ -784,7 +810,7 @@ void render_leaf_faces(ta_parameter_writer& writer, mat4x4& trans, q3bsp_leaf_t } } -void render_sphere_bounding_box(ta_parameter_writer& writer, mat4x4& trans) +void render_sphere_bounding_box(ta_parameter_writer& writer, const mat4x4& trans) { uint8_t * buf = reinterpret_cast(&_binary_pk_maps_20kdm2_bsp_start); q3bsp_header_t * header = reinterpret_cast(buf); @@ -871,12 +897,12 @@ void render_sphere_bounding_box(ta_parameter_writer& writer, mat4x4& trans) } } -void transfer_scene(ta_parameter_writer& writer, const mat4x4& screen_trans) +void transfer_scene(ta_parameter_writer& writer, const mat4x4& screen_trans, const mat4x4& screen_trans_inv) { uint8_t * buf = reinterpret_cast(&_binary_pk_maps_20kdm2_bsp_start); q3bsp_header_t * header = reinterpret_cast(buf); - mat4x4 trans = screen_trans; + const mat4x4 trans = screen_trans; q3bsp_direntry * ve = &header->direntries[LUMP_VERTEXES]; transform_vertices(&buf[ve->offset], ve->length, trans); @@ -884,9 +910,10 @@ void transfer_scene(ta_parameter_writer& writer, const mat4x4& screen_trans) //transfer_faces(writer); transfer_icosphere(writer, trans); - render_matrix(writer, trans); + render_matrix(writer, screen_trans_inv); render_leaf_ix(writer); render_sphere_position(writer); + render_zero_position(writer, screen_trans_inv); render_sphere_bounding_box(writer, trans); @@ -989,7 +1016,7 @@ void transfer_textures() static bool push = false; -mat4x4 update_analog(mat4x4& screen) +mat4x4 update_analog(const mat4x4& screen) { const float x_ = static_cast(data[0].analog_coordinate_axis[2] - 0x80) / 127.f; const float y_ = static_cast(data[0].analog_coordinate_axis[3] - 0x80) / 127.f; @@ -1168,6 +1195,8 @@ int main() trans = update_analog(trans); + mat4x4 trans_inv = inverse(trans); + ta_polygon_converter_init2(texture_memory_alloc.isp_tsp_parameters[ta].start, texture_memory_alloc.isp_tsp_parameters[ta].end, texture_memory_alloc.object_list[ta].start, @@ -1178,7 +1207,7 @@ int main() tile_height); writer.offset = 0; - transfer_scene(writer, trans); + transfer_scene(writer, trans, trans_inv); ta_polygon_converter_writeback(writer.buf, writer.offset); ta_polygon_converter_transfer(writer.buf, writer.offset); ta_wait_translucent_list(); diff --git a/math/mat2x2.hpp b/math/mat2x2.hpp index 008ca2b..da5f33c 100644 --- a/math/mat2x2.hpp +++ b/math/mat2x2.hpp @@ -30,6 +30,9 @@ public: inline static constexpr int length() { return 4; } + inline constexpr typename mat<2, 2, T>::row_type & + operator[](int i); + inline constexpr typename mat<2, 2, T>::row_type const & operator[](int i) const; @@ -54,18 +57,18 @@ inline constexpr mat<2, 2, T>::mat std::move(row_type(a10, a11))} { } +template +inline constexpr typename mat<2, 2, T>::row_type & +mat<2, 2, T>::operator[](int i) +{ + return value[i]; +} + template inline constexpr typename mat<2, 2, T>::row_type const & mat<2, 2, T>::operator[](int i) const { - switch (i) - { - default: [[fallthrough]]; - case 0: - return value[0]; - case 1: - return value[1]; - } + return value[i]; } template @@ -103,3 +106,9 @@ inline constexpr mat<2, 2, T> transpose(mat<2, 2, T> const& m) m[0][1], m[1][1] ); } + +template +inline constexpr float determinant(mat<2, 2, T> const& a) +{ + return a[0][0] * a[1][1] - a[0][1] * a[1][0]; +} diff --git a/math/mat3x3.hpp b/math/mat3x3.hpp index 438d4e4..bd1462f 100644 --- a/math/mat3x3.hpp +++ b/math/mat3x3.hpp @@ -31,6 +31,9 @@ public: inline static constexpr int length() { return 3; } + inline constexpr typename mat<3, 3, T>::row_type & + operator[](int i); + inline constexpr typename mat<3, 3, T>::row_type const & operator[](int i) const; @@ -58,20 +61,18 @@ inline constexpr mat<3, 3, T>::mat std::move(row_type(a20, a21, a22))} { } +template +inline constexpr typename mat<3, 3, T>::row_type & +mat<3, 3, T>::operator[](int i) +{ + return value[i]; +} + template inline constexpr typename mat<3, 3, T>::row_type const & mat<3, 3, T>::operator[](int i) const { - switch (i) - { - default: [[fallthrough]]; - case 0: - return value[0]; - case 1: - return value[1]; - case 2: - return value[2]; - } + return value[i]; } template @@ -135,3 +136,51 @@ inline constexpr mat<3, 3, T> transpose(mat<3, 3, T> const& m) m[0][2], m[1][2], m[2][2] ); } + +template +inline constexpr mat<2, 2, T> submatrix(mat<3, 3, T> const& a, int r, int c) +{ + mat<2, 2, T> b; + int row2 = 0; + for (int row3 = 0; row3 < 3; row3++) { + if (row3 == r) continue; + int col2 = 0; + for (int col3 = 0; col3 < 3; col3++) { + if (col3 == c) continue; + b[row2][col2] = a[row3][col3]; + col2++; + } + row2++; + } + return b; +} + +template +inline constexpr float minor(mat<3, 3, T> const& a, int r, int c) +{ + mat<2, 2, T> s = submatrix(a, r, c); + float ret = determinant(s); + return ret; +} + +template +inline constexpr float cofactor(mat<3, 3, T> const& a, int r, int c) +{ + float m = minor(a, r, c); + if ((r + c) & 1) + return -m; + else + return m; +} + +template +inline constexpr float determinant(mat<3, 3, T> const& a) +{ + float f0 = cofactor(a, 0, 0); + float f1 = cofactor(a, 0, 1); + float f2 = cofactor(a, 0, 2); + return + a[0][0] * f0 + + a[0][1] * f1 + + a[0][2] * f2; +} diff --git a/math/mat4x4.hpp b/math/mat4x4.hpp index d65c2c2..3008ef9 100644 --- a/math/mat4x4.hpp +++ b/math/mat4x4.hpp @@ -32,6 +32,9 @@ public: inline static constexpr int length() { return 4; } + inline constexpr typename mat<4, 4, T>::row_type & + operator[](int i); + inline constexpr typename mat<4, 4, T>::row_type const & operator[](int i) const; @@ -62,22 +65,19 @@ inline constexpr mat<4, 4, T>::mat std::move(row_type(a30, a31, a32, a33))} { } + +template +inline constexpr typename mat<4, 4, T>::row_type & +mat<4, 4, T>::operator[](int i) +{ + return value[i]; +} + template inline constexpr typename mat<4, 4, T>::row_type const & mat<4, 4, T>::operator[](int i) const { - switch (i) - { - default: [[fallthrough]]; - case 0: - return value[0]; - case 1: - return value[1]; - case 2: - return value[2]; - case 3: - return value[3]; - } + return value[i]; } template @@ -164,3 +164,86 @@ inline constexpr mat<4, 4, T> transpose(mat<4, 4, T> const& m) m[0][3], m[1][3], m[2][3], m[3][3] ); } + +template +inline constexpr mat<3, 3, T> submatrix(mat<4, 4, T> const& a, int r, int c) +{ + mat<3, 3, T> b; + int row3 = 0; + for (int row4 = 0; row4 < 4; row4++) { + if (row4 == r) continue; + int col3 = 0; + for (int col4 = 0; col4 < 4; col4++) { + if (col4 == c) continue; + b[row3][col3] = a[row4][col4]; + col3++; + } + row3++; + } + return b; +} + +template +inline constexpr float minor(mat<4, 4, T> const& a, int r, int c) +{ + mat<3, 3, T> s = submatrix(a, r, c); + float ret = determinant(s); + return ret; +} + +template +inline constexpr float cofactor(mat<4, 4, T> const& a, int r, int c) +{ + float m = minor(a, r, c); + if ((r + c) & 1) + return -m; + else + return m; +} + +template +inline constexpr float determinant(mat<4, 4, T> const& a) +{ + float f0 = cofactor(a, 0, 0); + float f1 = cofactor(a, 0, 1); + float f2 = cofactor(a, 0, 2); + float f3 = cofactor(a, 0, 3); + return + a[0][0] * f0 + + a[0][1] * f1 + + a[0][2] * f2 + + a[0][3] * f3; +} + +template +inline constexpr mat<4, 4, T> inverse(mat<4, 4, T> const& a) +{ + mat<4, 4, T> m; + float idet = 1.0f / determinant(a); + m[0][0] = cofactor(a, 0, 0) * idet; + m[1][0] = cofactor(a, 0, 1) * idet; + m[2][0] = cofactor(a, 0, 2) * idet; + m[3][0] = cofactor(a, 0, 3) * idet; + m[0][1] = cofactor(a, 1, 0) * idet; + m[1][1] = cofactor(a, 1, 1) * idet; + m[2][1] = cofactor(a, 1, 2) * idet; + m[3][1] = cofactor(a, 1, 3) * idet; + m[0][2] = cofactor(a, 2, 0) * idet; + m[1][2] = cofactor(a, 2, 1) * idet; + m[2][2] = cofactor(a, 2, 2) * idet; + m[3][2] = cofactor(a, 2, 3) * idet; + m[0][3] = cofactor(a, 3, 0) * idet; + m[1][3] = cofactor(a, 3, 1) * idet; + m[2][3] = cofactor(a, 3, 2) * idet; + m[3][3] = cofactor(a, 3, 3) * idet; + return m; +} + +template +inline constexpr mat<4, 4, T> identity() +{ + return mat<4, 4, T>(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); +} diff --git a/math/vec2.hpp b/math/vec2.hpp index f89f179..a7f6ea7 100644 --- a/math/vec2.hpp +++ b/math/vec2.hpp @@ -11,6 +11,7 @@ template struct vec<2, T> { union { + T val[2]; struct { T x, y; }; struct { T u, v; }; }; @@ -20,6 +21,7 @@ struct vec<2, T> inline constexpr vec(T _x, T _y); constexpr inline vec<2, T> operator-() const; + inline constexpr T & operator[](int i); inline constexpr T const& operator[](int i) const; inline constexpr vec<2, T>& operator=(vec<2, T> const& v); inline constexpr vec<2, T>& operator+=(vec<2, T> const& v); @@ -47,15 +49,16 @@ constexpr inline vec<2, T> vec<2, T>::operator-() const return vec<2, T>(-x, -y); } +template +inline constexpr T & vec<2, T>::operator[](int i) +{ + return val[i]; +} + template inline constexpr T const& vec<2, T>::operator[](int i) const { - switch(i) - { - default: [[fallthrough]]; - case 0: return x; - case 1: return y; - } + return val[i]; } template diff --git a/math/vec3.hpp b/math/vec3.hpp index 5adae6b..f4868df 100644 --- a/math/vec3.hpp +++ b/math/vec3.hpp @@ -11,6 +11,7 @@ template struct vec<3, T> { union { + T val[3]; struct { T x, y, z; }; struct { T r, g, b; }; }; @@ -20,6 +21,7 @@ struct vec<3, T> inline constexpr vec(T _x, T _y, T _z); constexpr inline vec<3, T> operator-() const; + inline constexpr T & operator[](int i); inline constexpr T const& operator[](int i) const; inline constexpr vec<3, T>& operator=(vec<3, T> const& v); inline constexpr vec<3, T>& operator+=(vec<3, T> const& v); @@ -47,16 +49,16 @@ constexpr inline vec<3, T> vec<3, T>::operator-() const return vec<3, T>(-x, -y, -z); } +template +inline constexpr T & vec<3, T>::operator[](int i) +{ + return val[i]; +} + template inline constexpr T const& vec<3, T>::operator[](int i) const { - switch(i) - { - default: [[fallthrough]]; - case 0: return x; - case 1: return y; - case 2: return z; - } + return val[i]; } template diff --git a/math/vec4.hpp b/math/vec4.hpp index 9f2ec88..7375e7c 100644 --- a/math/vec4.hpp +++ b/math/vec4.hpp @@ -11,6 +11,7 @@ template struct vec<4, T> { union { + T val[4]; struct { T x, y, z, w; }; struct { T a, r, g, b; }; }; @@ -21,6 +22,7 @@ struct vec<4, T> inline constexpr vec(const vec<3, T>& v); constexpr inline vec<4, T> operator-() const; + inline constexpr T & operator[](int i); inline constexpr T const& operator[](int i) const; inline constexpr vec<4, T>& operator=(vec<4, T> const& v); inline constexpr vec<4, T>& operator+=(vec<4, T> const& v); @@ -53,17 +55,16 @@ constexpr inline vec<4, T> vec<4, T>::operator-() const return vec<4, T>(-x, -y, -z, -w); } +template +inline constexpr T & vec<4, T>::operator[](int i) +{ + return val[i]; +} + template inline constexpr T const& vec<4, T>::operator[](int i) const { - switch(i) - { - default: [[fallthrough]]; - case 0: return x; - case 1: return y; - case 2: return z; - case 3: return w; - } + return val[i]; } template diff --git a/test/runner.hpp b/test/runner.hpp new file mode 100644 index 0000000..88630b1 --- /dev/null +++ b/test/runner.hpp @@ -0,0 +1,27 @@ +#pragma once + +typedef bool (*test_t)(const char ** scenario); + +#define ANSI_RED "\x1b[31m" +#define ANSI_GREEN "\x1b[32m" +#define ANSI_RESET "\x1b[0m" + +#define RUNNER(tests) \ + int main() \ + { \ + int fail_count = 0; \ + for (int i = 0; i < (sizeof (tests)) / (sizeof (test_t)); i++) { \ + const char * scenario = NULL; \ + bool result = tests[i](&scenario); \ + const char * result_s = result ? "ok" : ANSI_RED "fail" ANSI_RESET; \ + fail_count += !result; \ + fprintf(stderr, "%s: %s\n", scenario, result_s); \ + } \ + if (fail_count == 0) { \ + fprintf(stderr, ANSI_GREEN "failed tests: %d\n\n" ANSI_RESET, fail_count); \ + } else { \ + fprintf(stderr, ANSI_RED "failed tests: %d\n\n" ANSI_RESET, fail_count); \ + } \ + \ + return !(fail_count == 0); \ + } diff --git a/test/test_matrices.cpp b/test/test_matrices.cpp new file mode 100644 index 0000000..c15a6ec --- /dev/null +++ b/test/test_matrices.cpp @@ -0,0 +1,499 @@ +#include +#include + +#include "runner.hpp" + +#include "math/vec2.hpp" +#include "math/vec3.hpp" +#include "math/vec4.hpp" +#include "math/mat2x2.hpp" +#include "math/mat3x3.hpp" +#include "math/mat4x4.hpp" + +using vec2 = vec<2, float>; +using vec3 = vec<3, float>; +using vec4 = vec<4, float>; +using mat2x2 = mat<2, 2, float>; +using mat3x3 = mat<3, 3, float>; +using mat4x4 = mat<4, 4, float>; + +static const float epsilon = 0.00001; + +#define fabsf __builtin_fabsf + +inline static bool float_equal(float a, float b) +{ + return fabsf(a - b) < epsilon; +} + +inline static bool vec4_equal(vec4 a, vec4 b) +{ + return + float_equal(a.x, b.x) && + float_equal(a.y, b.y) && + float_equal(a.z, b.z) && + float_equal(a.w, b.w); +} + +inline static bool mat2x2_equal(mat2x2 a, + mat2x2 b) +{ + return + float_equal(a[0][0], b[0][0]) && + float_equal(a[0][1], b[0][1]) && + float_equal(a[1][0], b[1][0]) && + float_equal(a[1][1], b[1][1]); +} + +inline static bool mat3x3_equal(mat3x3 a, + mat3x3 b) +{ + return + float_equal(a[0][0], b[0][0]) && + float_equal(a[0][1], b[0][1]) && + float_equal(a[0][2], b[0][2]) && + float_equal(a[1][0], b[1][0]) && + float_equal(a[1][1], b[1][1]) && + float_equal(a[1][2], b[1][2]) && + float_equal(a[2][0], b[2][0]) && + float_equal(a[2][1], b[2][1]) && + float_equal(a[2][2], b[2][2]); +} + +inline static bool mat4x4_equal(mat4x4 a, + mat4x4 b) +{ + return + float_equal(a[0][0], b[0][0]) && + float_equal(a[0][1], b[0][1]) && + float_equal(a[0][2], b[0][2]) && + float_equal(a[0][3], b[0][3]) && + float_equal(a[1][0], b[1][0]) && + float_equal(a[1][1], b[1][1]) && + float_equal(a[1][2], b[1][2]) && + float_equal(a[1][3], b[1][3]) && + float_equal(a[2][0], b[2][0]) && + float_equal(a[2][1], b[2][1]) && + float_equal(a[2][2], b[2][2]) && + float_equal(a[2][3], b[2][3]) && + float_equal(a[3][0], b[3][0]) && + float_equal(a[3][1], b[3][1]) && + float_equal(a[3][2], b[3][2]) && + float_equal(a[3][3], b[3][3]); +} + +inline static bool mat4x4_is_invertible(mat4x4 a) +{ + return !float_equal(determinant(a), 0.f); +} + +static bool matrices_test_0(const char ** scenario) +{ + *scenario = "A 4x4 matrix ought to be representable"; + mat4x4 m = mat4x4( 1.0f, 2.0f, 3.0f, 4.0f, + 5.5f, 6.5f, 7.5f, 8.5f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.5f, 14.5f, 15.5f, 16.5f); + return + m[0][0] == 1.0f && + m[0][3] == 4.0f && + m[1][0] == 5.5f && + m[1][2] == 7.5f && + m[2][2] == 11.0f && + m[3][0] == 13.5f && + m[3][2] == 15.5f; +} + +static bool matrices_test_1(const char ** scenario) +{ + *scenario = "A 2x2 matrix ought to be representable"; + mat2x2 m = mat2x2(-3.0f, 5.0f, + 1.0f, -2.0f); + return + m[0][0] == -3.0f && + m[0][1] == 5.0f && + m[1][0] == 1.0f && + m[1][1] == -2.0f; +} + +static bool matrices_test_2(const char ** scenario) +{ + *scenario = "A 3x3 matrix ought to be representable"; + mat3x3 m = mat3x3(-3.0f, 5.0f, 0.0f, + 1.0f, -2.0f, -7.0f, + 0.0f, 1.0f, 1.0f); + return + m[0][0] == -3.0f && + m[1][1] == -2.0f && + m[2][2] == 1.0f; +} + +static bool matrices_test_3(const char ** scenario) +{ + *scenario = "Matrix equality with identical matrices"; + mat4x4 a = mat4x4(1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 8.0f, 7.0f, 6.0f, + 5.0f, 4.0f, 3.0f, 2.0f); + + mat4x4 b = mat4x4(1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 8.0f, 7.0f, 6.0f, + 5.0f, 4.0f, 3.0f, 2.0f); + + return mat4x4_equal(a, b); +} + +static bool matrices_test_4(const char ** scenario) +{ + *scenario = "Matrix equality with different matrices"; + + mat4x4 a = mat4x4(1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 8.0f, 7.0f, 6.0f, + 5.0f, 4.0f, 3.0f, 2.0f); + + mat4x4 b = mat4x4(2.0f, 3.0f, 4.0f, 5.0f, + 6.0f, 7.0f, 8.0f, 9.0f, + 8.0f, 7.0f, 6.0f, 5.0f, + 4.0f, 3.0f, 2.0f, 1.0f); + + return !mat4x4_equal(a, b); +} + +static bool matrices_test_5(const char ** scenario) +{ + *scenario = "Multiplying two matrices"; + + mat4x4 a = mat4x4(1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 8.0f, 7.0f, 6.0f, + 5.0f, 4.0f, 3.0f, 2.0f); + + mat4x4 b = mat4x4(-2.0f, 1.0f, 2.0f, 3.0f, + 3.0f, 2.0f, 1.0f, -1.0f, + 4.0f, 3.0f, 6.0f, 5.0f, + 1.0f, 2.0f, 7.0f, 8.0f); + + mat4x4 m1 = a * b; + mat4x4 m2 = mat4x4(20.0f, 22.0f, 50.0f, 48.0f, + 44.0f, 54.0f, 114.0f, 108.0f, + 40.0f, 58.0f, 110.0f, 102.0f, + 16.0f, 26.0f, 46.0f, 42.0f); + return mat4x4_equal(m1, m2); +} + +static bool matrices_test_6(const char ** scenario) +{ + *scenario = "A matrix multiplied by a vec4"; + + mat4x4 a = mat4x4(1.0f, 2.0f, 3.0f, 4.0f, + 2.0f, 4.0f, 4.0f, 2.0f, + 8.0f, 6.0f, 4.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + vec4 b = vec4(1.0f, 2.0f, 3.0f, 1.0f); + + vec4 c = a * b; + return vec4_equal(c, vec4(18, 24, 33, 1)); +} + +static bool matrices_test_7(const char ** scenario) +{ + *scenario = "Multiplying a matrix by the identity matrix"; + + mat4x4 a = mat4x4(0.0f, 1.0f, 2.0f, 4.0f, + 1.0f, 2.0f, 4.0f, 8.0f, + 2.0f, 4.0f, 8.0f, 16.0f, + 4.0f, 8.0f, 16.0f, 32.0f); + + mat4x4 id = identity(); + mat4x4 c = a * id; + return mat4x4_equal(a, c); +} + +static bool matrices_test_8(const char ** scenario) +{ + *scenario = "Multiplying a the identity matrix by a vec4"; + + vec4 a = vec4(1.0f, 2.0f, 3.0f, 4.0f); + + mat4x4 id = identity(); + vec4 c = id * a; + return vec4_equal(a, c); +} + +static bool matrices_test_9(const char ** scenario) +{ + *scenario = "Transposing a matrix"; + + mat4x4 a = mat4x4(0.0f, 9.0f, 3.0f, 0.0f, + 9.0f, 8.0f, 0.0f, 8.0f, + 1.0f, 8.0f, 5.0f, 3.0f, + 0.0f, 0.0f, 5.0f, 8.0f); + + mat4x4 b = mat4x4(0.0f, 9.0f, 1.0f, 0.0f, + 9.0f, 8.0f, 8.0f, 0.0f, + 3.0f, 0.0f, 5.0f, 5.0f, + 0.0f, 8.0f, 3.0f, 8.0f); + + mat4x4 c = transpose(a); + + return mat4x4_equal(c, b); +} + +static bool matrices_test_10(const char ** scenario) +{ + *scenario = "Transposing the identity matrix"; + + mat4x4 a = identity(); + + mat4x4 c = transpose(a); + + return mat4x4_equal(a, c); +} + +static bool matrices_test_11(const char ** scenario) +{ + *scenario = "Calculating the determinant of a 2x2 matrix"; + + mat2x2 a = mat2x2( 1.0f, 5.0f, + -3.0f, 2.0f); + + return float_equal(determinant(a), 17); +} + +static bool matrices_test_12(const char ** scenario) +{ + *scenario = "A submatrix of a 3x3 matrix is a 2x2 matrix"; + + mat3x3 a = mat3x3( 1.0f, 5.0f, 0.0f, + -3.0f, 2.0f, 7.0f, + 0.0f, 6.0f, -3.0f); + + mat2x2 b = submatrix(a, 0, 2); + mat2x2 c = mat2x2(-3.0f, 2.0f, + 0.0f, 6.0f); + + return mat2x2_equal(b, c); +} + +static bool matrices_test_13(const char ** scenario) +{ + *scenario = "A submatrix of a 4x4 matrix is a 3x3 matrix"; + + mat4x4 a = mat4x4(-6.0f, 1.0f, 1.0f, 6.0f, + -8.0f, 5.0f, 8.0f, 6.0f, + -1.0f, 0.0f, 8.0f, 2.0f, + -7.0f, 1.0f, -1.0f, 1.0f); + + mat3x3 b = submatrix(a, 2, 1); + mat3x3 c = mat3x3(-6.0f, 1.0f, 6.0f, + -8.0f, 8.0f, 6.0f, + -7.0f, -1.0f, 1.0f); + + return mat3x3_equal(b, c); +} + +static bool matrices_test_14(const char ** scenario) +{ + *scenario = "Calculating a minor of a 3x3 matrix"; + + mat3x3 a = mat3x3(3, 5, 0, + 2, -1, -7, + 6, -1, 5); + mat2x2 b = submatrix(a, 1, 0); + + return + float_equal(determinant(b), 25.0f) && + float_equal(minor(a, 1, 0), 25.0f); +} + +static bool matrices_test_15(const char ** scenario) +{ + *scenario = "Calculating a cofactor of a 3x3 matrix"; + + mat3x3 a = mat3x3(3, 5, 0, + 2, -1, -7, + 6, -1, 5); + + return + float_equal(minor(a, 0, 0), -12.0f) && + float_equal(cofactor(a, 0, 0), -12.0f) && + float_equal(minor(a, 1, 0), 25.0f) && + float_equal(cofactor(a, 1, 0), -25.0f) && + float_equal(cofactor(a, 2, 1), 21.0f); + +} + +static bool matrices_test_16(const char ** scenario) +{ + *scenario = "Calculating the determinant of a 3x3 matrix"; + + mat3x3 a = mat3x3( 1.0f, 2.0f, 6.0f, + -5.0f, 8.0f, -4.0f, + 2.0f, 6.0f, 4.0f); + + return + float_equal(cofactor(a, 0, 0), 56.0f) && + float_equal(cofactor(a, 0, 1), 12.0f) && + float_equal(cofactor(a, 0, 2), -46.0f) && + float_equal(determinant(a), -196.0f); +} + +static bool matrices_test_17(const char ** scenario) +{ + *scenario = "Calculating the determinant of a 4x4 matrix"; + + mat4x4 a = mat4x4(-2.0f, -8.0f, 3.0f, 5.0f, + -3.0f, 1.0f, 7.0f, 3.0f, + 1.0f, 2.0f, -9.0f, 6.0f, + -6.0f, 7.0f, 7.0f, -9.0f); + + return + float_equal(cofactor(a, 0, 0), 690.0f) && + float_equal(cofactor(a, 0, 1), 447.0f) && + float_equal(cofactor(a, 0, 2), 210.0f) && + float_equal(cofactor(a, 0, 3), 51.0f) && + float_equal(determinant(a), -4071.0f); +} + +static bool matrices_test_18(const char ** scenario) +{ + *scenario = "Testing an invertible matrix for invertibility"; + + mat4x4 a = mat4x4(6.0f, 4.0f, 4.0f, 4.0f, + 5.0f, 5.0f, 7.0f, 6.0f, + 4.0f, -9.0f, 3.0f, -7.0f, + 9.0f, 1.0f, 7.0f, -6.0f); + + return + float_equal(determinant(a), -2120.0f) && + mat4x4_is_invertible(a); +} + +static bool matrices_test_19(const char ** scenario) +{ + *scenario = "Testing a noninvertible matrix for invertibility"; + + mat4x4 a = mat4x4(-4.0f, 2.0f, -2.0f, -3.0f, + 9.0f, 6.0f, 2.0f, 6.0f, + 0.0f, -5.0f, 1.0f, -5.0f, + 0.0f, 0.0f, 0.0f, 0.0f); + + return + float_equal(determinant(a), 0.0f) && + !mat4x4_is_invertible(a); +} + +static bool matrices_test_20(const char ** scenario) +{ + *scenario = "Calculating the inverse of a matrix"; + + mat4x4 a = mat4x4(-5.0, 2.0, 6.0, -8.0, + 1.0, -5.0, 1.0, 8.0, + 7.0, 7.0, -6.0, -7.0, + 1.0, -3.0, 7.0, 4.0); + mat4x4 b = inverse(a); + + mat4x4 c = mat4x4( 0.218045f, 0.451128f, 0.240602f, -0.045113f, + -0.808271f, -1.456767f, -0.443609f, 0.520677f, + -0.078947f, -0.223684f, -0.052632f, 0.197368f, + -0.522564f, -0.813910f, -0.300752f, 0.306391f); + + return + float_equal(determinant(a), 532.0f) && + float_equal(cofactor(a, 2, 3), -160.0f) && + float_equal(b[3][2], -160.0f/532.0f) && + float_equal(cofactor(a, 3, 2), 105.0f) && + float_equal(b[2][3], 105.0f/532.0f) && + mat4x4_equal(b, c); +} + +static bool matrices_test_21(const char ** scenario) +{ + *scenario = "Calculating the inverse of a second matrix"; + + mat4x4 a = mat4x4( 8.0f, -5.0f, 9.0f, 2.0f, + 7.0f, 5.0f, 6.0f, 1.0f, + -6.0f, 0.0f, 9.0f, 6.0f, + -3.0f, 0.0f, -9.0f, -4.0f); + + mat4x4 b = inverse(a); + + mat4x4 c = mat4x4(-0.15385f, -0.15385f, -0.28205f, -0.53846f, + -0.07692f, 0.12308f, 0.02564f, 0.03077f, + 0.35897f, 0.35897f, 0.43590f, 0.92308f, + -0.69231f, -0.69231f, -0.76923f, -1.92308f); + + return mat4x4_equal(b, c); +} + +static bool matrices_test_22(const char ** scenario) +{ + *scenario = "Calculating the inverse of a third matrix"; + + mat4x4 a = mat4x4( 9.0f, 3.0f, 0.0f, 9.0f, + -5.0f, -2.0f, -6.0f, -3.0f, + -4.0f, 9.0f, 6.0f, 4.0f, + -7.0f, 6.0f, 6.0f, 2.0f); + + mat4x4 b = inverse(a); + + mat4x4 c = mat4x4(-0.04074f, -0.07778f, 0.14444f, -0.22222f, + -0.07778f, 0.03333f, 0.36667f, -0.33333f, + -0.02901f, -0.14630f, -0.10926f, 0.12963f, + 0.17778f, 0.06667f, -0.26667f, 0.33333f); + + return mat4x4_equal(b, c); +} + +static bool matrices_test_23(const char ** scenario) +{ + *scenario = "Multiplying a product by its inverse"; + + mat4x4 a = mat4x4( 3.0f, -9.0f, 7.0f, 3.0f, + 3.0f, -8.0f, 2.0f, -9.0f, + -4.0f, 4.0f, 4.0f, 1.0f, + -6.0f, 5.0f, -1.0f, 1.0f); + + mat4x4 b = mat4x4( 8.0f, 2.0f, 2.0f, 2.0f, + 3.0f, -1.0f, 7.0f, 0.0f, + 7.0f, 0.0f, 5.0f, 4.0f, + 6.0f, -2.0f, 0.0f, 5.0f); + + mat4x4 c = a * b; + + mat4x4 b_i = inverse(b); + mat4x4 d = c * b_i; + + return mat4x4_equal(d, a); +} + +test_t matrices_tests[] = { + matrices_test_0, + matrices_test_1, + matrices_test_2, + matrices_test_3, + matrices_test_4, + matrices_test_5, + matrices_test_6, + matrices_test_7, + matrices_test_8, + matrices_test_9, + matrices_test_10, + matrices_test_11, + matrices_test_12, + matrices_test_13, + matrices_test_14, + matrices_test_15, + matrices_test_16, + matrices_test_17, + matrices_test_18, + matrices_test_19, + matrices_test_20, + matrices_test_21, + matrices_test_22, + matrices_test_23, +}; + +RUNNER(matrices_tests)