/** * Playing around with matrices while studying * https://github.com/ssloy/tinyrenderer/wiki/Lesson-4:-Perspective-projection */ #include #include #include #include #include #include #include #include #define u64 uint64_t #define f32 float #define RADIANS(DEGREES) (DEGREES * M_PI / 180.0f) typedef struct vec2u V2u; struct vec2u { u64 x; u64 y; }; typedef struct vec3f V3f; struct vec3f { union { f32 x; f32 col0; }; union { f32 y; f32 col1; }; union { f32 z; f32 col2; }; }; typedef struct mat3x3 Mat3x3; struct mat3x3 { V3f row0; V3f row1; V3f row2; }; #define vec_add(v1, v2) ((V2u){.x = v1.x + v2.x, .y = v1.y + v2.y}) #define vec_sub(v1, v2) ((V2u){.x = v1.x - v2.x, .y = v1.y - v2.y}) #define vec_div_num(v, n) ((V2u){.x = v.x / n, .y = v.y / 2}) #define mat_mul_vec(mat, vec) \ ((V3f){.x = mat.row0.col0 * vec.x + mat.row0.col1 * vec.y + \ mat.row0.col2 * vec.z, \ .y = mat.row1.col0 * vec.x + mat.row1.col1 * vec.y + \ mat.row1.col2 * vec.z, \ .z = mat.row2.col0 * vec.x + mat.row2.col1 * vec.y + \ mat.row2.col2 * vec.z}) #define project_v3f(v) ((V2u){.x = (u64)(v.x / v.z), .y = (u64)(v.y / v.z)}) // clang-format off #define mat_mul(mat1, mat2) \ ((Mat3x3){ \ .row0 = {.col0 = mat1.row0.col0 * mat2.row0.col0 + mat1.row0.col1 * mat2.row1.col0 + mat1.row0.col2 * mat2.row2.col0, \ .col1 = mat1.row0.col0 * mat2.row0.col1 + mat1.row0.col1 * mat2.row1.col1 + mat1.row0.col2 * mat2.row2.col1, \ .col2 = mat1.row0.col0 * mat2.row0.col2 + mat1.row0.col1 * mat2.row1.col2 + mat1.row0.col2 * mat2.row2.col2}, \ .row1 = {.col0 = mat1.row1.col0 * mat2.row0.col0 + mat1.row1.col1 * mat2.row1.col0 + mat1.row1.col2 * mat2.row2.col0, \ .col1 = mat1.row1.col0 * mat2.row0.col1 + mat1.row1.col1 * mat2.row1.col1 + mat1.row1.col2 * mat2.row2.col1, \ .col2 = mat1.row1.col0 * mat2.row0.col2 + mat1.row1.col1 * mat2.row1.col2 + mat1.row1.col2 * mat2.row2.col2}, \ .row2 = {.col0 = mat1.row2.col0 * mat2.row0.col0 + mat1.row2.col1 * mat2.row1.col0 + mat1.row2.col2 * mat2.row2.col0, \ .col1 = mat1.row2.col0 * mat2.row0.col1 + mat1.row2.col1 * mat2.row1.col1 + mat1.row2.col2 * mat2.row2.col1, \ .col2 = mat1.row2.col0 * mat2.row0.col2 + mat1.row2.col1 * mat2.row1.col2 + mat1.row2.col2 * mat2.row2.col2}, \ }) // clang-format on #define MAT3x3(r0c0, r0c1, r0c2, r1c0, r1c1, r1c2, r2c0, r2c1, r2c2) \ ((Mat3x3){{r0c0, r0c1, r0c2}, {r1c0, r1c1, r1c2}, {r2c0, r2c1, r2c2}}) void draw_line(SDL_Renderer *renderer, const V2u *start, const V2u *end); int main(void) { SDL_Init(0); SDL_Window *wnd = SDL_CreateWindow("Matrices", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1200, 800, SDL_WINDOW_SHOWN); SDL_Renderer *renderer = SDL_CreateRenderer(wnd, -1, 0); f32 rotation = 0.0f; f32 radians = RADIANS(rotation); // clang-format off Mat3x3 pos_rot_mat, neg_rot_mat; Mat3x3 mat0 = MAT3x3(2.0f, -0.5f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 1.0f); Mat3x3 mat1 = MAT3x3( 0.5f, 0.0f, 0.0f, 0.25f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f); // clang-format on V3f v0p0 = {400.0f, 150.0f, 1.0f}; V3f v0p1 = {800.0f, 150.0f, 1.0f}; V3f v0p2 = {400.0f, 400.0f, 1.0f}; V3f v0p3 = {800.0f, 400.0f, 1.0f}; // clang-format off Mat3x3 mid_mat = MAT3x3(0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f); Mat3x3 neg_v0p0_xf = MAT3x3(1.0f, 0.0f, -(v0p0.x), 0.0f, 1.0f, -(v0p0.y), 0.0f, 0.0f, 1.0f); Mat3x3 pos_v0p0_xf = MAT3x3(1.0f, 0.0f, v0p0.x, 0.0f, 1.0f, v0p0.y, 0.0f, 0.0f, 1.0f); // clang-format on Mat3x3 v0_mid_mat = mat_mul(mat_mul(pos_v0p0_xf, mid_mat), neg_v0p0_xf); V3f v0_mid = mat_mul_vec(v0_mid_mat, v0p3); // clang-format off Mat3x3 neg_v0_mid_xf = MAT3x3(1.0f, 0.0f, -(v0_mid.x), 0.0f, 1.0f, -(v0_mid.y), 0.0f, 0.0f, 1.0f); Mat3x3 pos_v0_mid_xf = MAT3x3(1.0f, 0.0f, v0_mid.x, 0.0f, 1.0f, v0_mid.y, 0.0f, 0.0f, 1.0f); // clang-format on Mat3x3 v1_mat; V3f v1p0, v1p1, v1p2, v1p3; Mat3x3 v2_mat; V3f v2p0, v2p1, v2p2, v2p3; // Projection V2u v0_p0 = project_v3f(v0p0); V2u v0_p1 = project_v3f(v0p1); V2u v0_p2 = project_v3f(v0p2); V2u v0_p3 = project_v3f(v0p3); V2u v1_p0, v1_p1, v1_p2, v1_p3; V2u v2_p0, v2_p1, v2_p2, v2_p3; SDL_Event event; bool running = true; while (running) { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: running = false; break; } } rotation += 0.001f; radians = RADIANS(rotation); // clang-format off pos_rot_mat = MAT3x3( cosf(radians), -sinf(radians), 0.0f, sinf(radians), cosf(radians), 0.0f, 0.0f, 0.0f, 1.0f); neg_rot_mat = MAT3x3(cosf(-radians), -sinf(-radians), 0.0f, sinf(-radians), cosf(-radians), 0.0f, 0.0f, 0.0f, 1.0f); // clang-format on v1_mat = mat_mul(mat_mul(pos_v0_mid_xf, mat_mul(pos_rot_mat, mat0)), neg_v0_mid_xf); v1p0 = mat_mul_vec(v1_mat, v0p0); v1p1 = mat_mul_vec(v1_mat, v0p1); v1p2 = mat_mul_vec(v1_mat, v0p2); v1p3 = mat_mul_vec(v1_mat, v0p3); v2_mat = mat_mul(mat_mul(pos_v0_mid_xf, mat_mul(neg_rot_mat, mat1)), neg_v0_mid_xf); v2p0 = mat_mul_vec(v2_mat, v0p0); v2p1 = mat_mul_vec(v2_mat, v0p1); v2p2 = mat_mul_vec(v2_mat, v0p2); v2p3 = mat_mul_vec(v2_mat, v0p3); v1_p0 = project_v3f(v1p0); v1_p1 = project_v3f(v1p1); v1_p2 = project_v3f(v1p2); v1_p3 = project_v3f(v1p3); v2_p0 = project_v3f(v2p0); v2_p1 = project_v3f(v2p1); v2_p2 = project_v3f(v2p2); v2_p3 = project_v3f(v2p3); SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255); SDL_RenderClear(renderer); SDL_SetRenderDrawColor(renderer, 255, 64, 128, 255); draw_line(renderer, &v0_p0, &v0_p1); draw_line(renderer, &v0_p1, &v0_p3); draw_line(renderer, &v0_p3, &v0_p2); draw_line(renderer, &v0_p2, &v0_p0); SDL_SetRenderDrawColor(renderer, 64, 255, 128, 255); draw_line(renderer, &v1_p0, &v1_p1); draw_line(renderer, &v1_p1, &v1_p3); draw_line(renderer, &v1_p3, &v1_p2); draw_line(renderer, &v1_p2, &v1_p0); SDL_SetRenderDrawColor(renderer, 64, 128, 255, 255); draw_line(renderer, &v2_p0, &v2_p1); draw_line(renderer, &v2_p1, &v2_p3); draw_line(renderer, &v2_p3, &v2_p2); draw_line(renderer, &v2_p2, &v2_p0); SDL_RenderPresent(renderer); } return 0; } void draw_line(SDL_Renderer *renderer, const V2u *start, const V2u *end) { SDL_RenderDrawLine(renderer, start->x, start->y, end->x, end->y); }