math: add 4x4 matrix inverse

This commit is contained in:
Zack Buhman 2025-04-24 01:02:45 -05:00
parent 8c03d19520
commit 0156721a6b
9 changed files with 766 additions and 64 deletions

View File

@ -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<q3bsp_vertex_t *>(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<uint32_t *>(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<uint8_t *>(&_binary_pk_maps_20kdm2_bsp_start);
q3bsp_header_t * header = reinterpret_cast<q3bsp_header_t *>(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<uint8_t *>(&_binary_pk_maps_20kdm2_bsp_start);
q3bsp_header_t * header = reinterpret_cast<q3bsp_header_t *>(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<uint8_t *>(&_binary_pk_maps_20kdm2_bsp_start);
q3bsp_header_t * header = reinterpret_cast<q3bsp_header_t *>(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<uint8_t *>(&_binary_pk_maps_20kdm2_bsp_start);
q3bsp_header_t * header = reinterpret_cast<q3bsp_header_t *>(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<float>(data[0].analog_coordinate_axis[2] - 0x80) / 127.f;
const float y_ = static_cast<float>(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();

View File

@ -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 <typename T>
inline constexpr typename mat<2, 2, T>::row_type &
mat<2, 2, T>::operator[](int i)
{
return value[i];
}
template <typename T>
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<typename T>
@ -103,3 +106,9 @@ inline constexpr mat<2, 2, T> transpose(mat<2, 2, T> const& m)
m[0][1], m[1][1]
);
}
template<typename T>
inline constexpr float determinant(mat<2, 2, T> const& a)
{
return a[0][0] * a[1][1] - a[0][1] * a[1][0];
}

View File

@ -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 <typename T>
inline constexpr typename mat<3, 3, T>::row_type &
mat<3, 3, T>::operator[](int i)
{
return value[i];
}
template <typename T>
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<typename T>
@ -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<typename T>
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<typename T>
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<typename T>
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<typename T>
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;
}

View File

@ -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 <typename T>
inline constexpr typename mat<4, 4, T>::row_type &
mat<4, 4, T>::operator[](int i)
{
return value[i];
}
template <typename T>
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<typename T>
@ -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<typename T>
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<typename T>
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<typename T>
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<typename T>
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<typename T>
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<typename T>
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);
}

View File

@ -11,6 +11,7 @@ template <typename T>
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 <typename T>
inline constexpr T & vec<2, T>::operator[](int i)
{
return val[i];
}
template <typename T>
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 <typename T>

View File

@ -11,6 +11,7 @@ template <typename T>
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 <typename T>
inline constexpr T & vec<3, T>::operator[](int i)
{
return val[i];
}
template <typename T>
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 <typename T>

View File

@ -11,6 +11,7 @@ template <typename T>
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 <typename T>
inline constexpr T & vec<4, T>::operator[](int i)
{
return val[i];
}
template <typename T>
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 <typename T>

27
test/runner.hpp Normal file
View File

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

499
test/test_matrices.cpp Normal file
View File

@ -0,0 +1,499 @@
#include <stdbool.h>
#include <stdio.h>
#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<float>();
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<float>();
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<float>();
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)