diff --git a/src/main.c b/src/main.c index d72ae92..1696294 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,9 @@ +#include "aliases.h" #include "img.h" #include "mem_arena.h" #include "mem_utils.h" #include "obj.h" +#include "vec.h" #include #include #include @@ -10,6 +12,11 @@ #define SIZE 1200 #define RESOURCE(NAME) "resources/" NAME +V3f g_eye = {0.2f, 0.1f, 0.75f}; +V3f g_target = {0}; +V3f g_up = {0.0f, 1.0f, 0.0f}; +M4x4f g_cam_matrix = mat4x4_identity; + enum { TINY_EXIT_SUCCESS, TINY_EXIT_ARENA_INIT_FAILED, @@ -17,6 +24,10 @@ enum { TINY_EXIT_MODEL_LOAD_FAILED, }; +internal M4x4f get_projection_matrix(ProjectionType projection_type); +internal V3f main_shader_vertex(const V3f *vertex, M4x4f *model_view, + M4x4f *projection, const Render *render); + int main(void) { Arena *arena = NULL; if (!wapp_mem_arena_init(&arena, 10ul * 1024ul * 1024ul * 1024ul, @@ -38,12 +49,45 @@ int main(void) { return TINY_EXIT_MODEL_LOAD_FAILED; } + M4x4f model_view = lookat(g_eye, g_target, g_up); + M4x4f perspective_projection = + get_projection_matrix(PROJECTION_TYPE_PERSPECTIVE); + + Shader main_shader = { + .vertex = main_shader_vertex, + .model_view = &model_view, + .projection = &perspective_projection, + }; + clear_buffer(&(render.img), &bg); - render_model(&obj, &render, teal, RENDER_TYPE_SHADED, COLOUR_TYPE_FIXED, - PROJECTION_TYPE_PERSPECTIVE); + render_model(&obj, &render, &main_shader, RENDER_TYPE_SHADED, teal); save_image(&(render.img), "result.pam"); wapp_mem_arena_destroy(&arena); return TINY_EXIT_SUCCESS; } + +internal M4x4f get_projection_matrix(ProjectionType projection_type) { + if (projection_type == PROJECTION_TYPE_PERSPECTIVE) { + // Calculate projection matrix + V3f cam = V3(V3f, f32, g_target.x, g_target.y, g_target.z, g_eye.x, g_eye.y, + g_eye.z); + normalise_v3(cam); + f32 coeff = -1.0f / magnitude_v3(cam) * 0.5f; + return projection(coeff); + } + + return mat4x4_identity; +} + +internal V3f main_shader_vertex(const V3f *vertex, M4x4f *model_view, + M4x4f *projection, const Render *render) { + V4f vh = {.x = vertex->x, .y = vertex->y, .z = vertex->z, .w = 1.0f}; + vh = mat4x4_mul_vec4((*projection), mat4x4_mul_vec4((*model_view), vh)); + vh.y = 0.0 - vh.y; + vh = mat4x4_mul_vec4( + viewport(vh.x, vh.y, render->img.width, render->img.height), vh); + + return project_vec4(vh); +} diff --git a/src/obj.c b/src/obj.c index a77a4d8..0094a9e 100644 --- a/src/obj.c +++ b/src/obj.c @@ -5,6 +5,7 @@ #include "pam.h" #include "typed_list.h" #include "utils.h" +#include "vec.h" #include #include #include @@ -12,84 +13,6 @@ #include #define TRIANGLE_VERTICES 3 -#define DEPTH_MAX 255 - -#define V2(T, ELEM_T, X0, Y0, X1, Y1) \ - ((T){(ELEM_T)X1 - (ELEM_T)X0, (ELEM_T)Y1 - (ELEM_T)Y0}) -#define V3(T, ELEM_T, X0, Y0, Z0, X1, Y1, Z1) \ - ((T){(ELEM_T)X1 - (ELEM_T)X0, (ELEM_T)Y1 - (ELEM_T)Y0, \ - (ELEM_T)Z1 - (ELEM_T)Z0}) -#define dot_v2(V1, V2) ((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y) -#define dot_v3(V1, V2) \ - ((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y + (f32)V1.z * (f32)V2.z) -#define magnitude_v3(V) (sqrtf(dot_v3(V, V))) -#define normalise_v3(V) \ - do { \ - f32 magnitude = magnitude_v3(V); \ - V.x /= magnitude; \ - V.y /= magnitude; \ - V.z /= magnitude; \ - } while (0) -#define cross_product(V1, V2) \ - ((V3f){ \ - .x = V1.y * V2.z - V1.z * V2.y, \ - .y = V1.z * V2.x - V1.x * V2.z, \ - .z = V1.x * V2.y - V1.y * V2.x, \ - }) -#define mat4x4_identity \ - ((M4x4f){ \ - .row0 = {1.0f, 0.0f, 0.0f, 0.0f}, \ - .row1 = {0.0f, 1.0f, 0.0f, 0.0f}, \ - .row2 = {0.0f, 0.0f, 1.0f, 0.0f}, \ - .row3 = {0.0f, 0.0f, 0.0f, 1.0f}, \ - }) -#define mat4x4_mul(MAT1, MAT2) \ - ((M4x4f){ \ - .row0.x = MAT1.row0.x * MAT2.row0.x + MAT1.row0.y * MAT2.row1.x + \ - MAT1.row0.z * MAT2.row2.x + MAT1.row0.w * MAT2.row3.x, \ - .row0.y = MAT1.row0.x * MAT2.row0.y + MAT1.row0.y * MAT2.row1.y + \ - MAT1.row0.z * MAT2.row2.y + MAT1.row0.w * MAT2.row3.y, \ - .row0.z = MAT1.row0.x * MAT2.row0.z + MAT1.row0.y * MAT2.row1.z + \ - MAT1.row0.z * MAT2.row2.z + MAT1.row0.w * MAT2.row3.z, \ - .row0.w = MAT1.row0.x * MAT2.row0.w + MAT1.row0.y * MAT2.row1.w + \ - MAT1.row0.z * MAT2.row2.w + MAT1.row0.w * MAT2.row3.w, \ - .row1.x = MAT1.row1.x * MAT2.row0.x + MAT1.row1.y * MAT2.row1.x + \ - MAT1.row1.z * MAT2.row2.x + MAT1.row1.w * MAT2.row3.x, \ - .row1.y = MAT1.row1.x * MAT2.row0.y + MAT1.row1.y * MAT2.row1.y + \ - MAT1.row1.z * MAT2.row2.y + MAT1.row1.w * MAT2.row3.y, \ - .row1.z = MAT1.row1.x * MAT2.row0.z + MAT1.row1.y * MAT2.row1.z + \ - MAT1.row1.z * MAT2.row2.z + MAT1.row1.w * MAT2.row3.z, \ - .row1.w = MAT1.row1.x * MAT2.row0.w + MAT1.row1.y * MAT2.row1.w + \ - MAT1.row1.z * MAT2.row2.w + MAT1.row1.w * MAT2.row3.w, \ - .row2.x = MAT1.row2.x * MAT2.row0.x + MAT1.row2.y * MAT2.row1.x + \ - MAT1.row2.z * MAT2.row2.x + MAT1.row2.w * MAT2.row3.x, \ - .row2.y = MAT1.row2.x * MAT2.row0.y + MAT1.row2.y * MAT2.row1.y + \ - MAT1.row2.z * MAT2.row2.y + MAT1.row2.w * MAT2.row3.y, \ - .row2.z = MAT1.row2.x * MAT2.row0.z + MAT1.row2.y * MAT2.row1.z + \ - MAT1.row2.z * MAT2.row2.z + MAT1.row2.w * MAT2.row3.z, \ - .row2.w = MAT1.row2.x * MAT2.row0.w + MAT1.row2.y * MAT2.row1.w + \ - MAT1.row2.z * MAT2.row2.w + MAT1.row2.w * MAT2.row3.w, \ - .row3.x = MAT1.row3.x * MAT2.row0.x + MAT1.row3.y * MAT2.row1.x + \ - MAT1.row3.z * MAT2.row2.x + MAT1.row3.w * MAT2.row3.x, \ - .row3.y = MAT1.row3.x * MAT2.row0.y + MAT1.row3.y * MAT2.row1.y + \ - MAT1.row3.z * MAT2.row2.y + MAT1.row3.w * MAT2.row3.y, \ - .row3.z = MAT1.row3.x * MAT2.row0.z + MAT1.row3.y * MAT2.row1.z + \ - MAT1.row3.z * MAT2.row2.z + MAT1.row3.w * MAT2.row3.z, \ - .row3.w = MAT1.row3.x * MAT2.row0.w + MAT1.row3.y * MAT2.row1.w + \ - MAT1.row3.z * MAT2.row2.w + MAT1.row3.w * MAT2.row3.w, \ - }) -#define mat4x4_mul_vec4(MAT, V) \ - ((V4f){ \ - .x = MAT.row0.x * V.x + MAT.row0.y * V.y + MAT.row0.z * V.z + \ - MAT.row0.w * V.w, \ - .y = MAT.row1.x * V.x + MAT.row1.y * V.y + MAT.row1.z * V.z + \ - MAT.row1.w * V.w, \ - .z = MAT.row2.x * V.x + MAT.row2.y * V.y + MAT.row2.z * V.z + \ - MAT.row2.w * V.w, \ - .w = MAT.row3.x * V.x + MAT.row3.y * V.y + MAT.row3.z * V.z + \ - MAT.row3.w * V.w, \ - }) -#define project_vec4(V) ((V3f){.x = V.x / V.w, .y = V.y / V.w, .z = V.z / V.w}) typedef struct triangle_bbox TriangleBBox; struct triangle_bbox { @@ -100,15 +23,12 @@ struct triangle_bbox { }; internal void render_triangle(const Triangle *triangle, const Model *model, - Render *render, Colour colour, RenderType type, - ProjectionType projection, M4x4f mv); + const Shader *shader, Render *render, + RenderType render_type, Colour colour); internal void fill_triangle(Render *render, V3f vertices[TRIANGLE_VERTICES], V3f normals[TRIANGLE_VERTICES], V2f coordinates[TRIANGLE_VERTICES], Colour colour, Image *texture, RenderType type); -internal M4x4f lookat(V3f eye, V3f target, V3f up); -internal M4x4f viewport(f32 x, f32 y, u64 w, u64 h); -internal M4x4f projection(f32 coeff); internal TriangleBBox get_triangle_bbox(const Image *img, V3f vertices[TRIANGLE_VERTICES]); internal V3f get_barycentric_coords(f32 d00, f32 d01, f32 d11, f32 denom, @@ -117,10 +37,6 @@ internal V3f get_barycentric_coords(f32 d00, f32 d01, f32 d11, f32 denom, internal V3f get_viewport_vertex(const V3f *vertex, const Image *img); V3f g_light_dir = {0.0f, 0.0f, 1.0f}; -V3f g_eye = {0.2f, 0.1f, 0.75f}; -V3f g_target = {0}; -V3f g_up = {0.0f, 1.0f, 0.0f}; -M4x4f g_cam_matrix = mat4x4_identity; Model load_obj_file(Arena *arena, const char *filename, const char *texture) { if (!arena) { @@ -215,35 +131,18 @@ bool init_render(Arena *arena, Render *render, u64 width, u64 height) { return true; } -void render_model(const Model *model, Render *render, Colour colour, - RenderType type, ColourType colour_type, - ProjectionType projection_type) { +void render_model(const Model *model, Render *render, const Shader *shader, + RenderType render_type, Colour colour) { Triangle triangle; - M4x4f model_view = lookat(g_eye, g_target, g_up); - - // Calculate projection matrix - V3f cam = V3(V3f, f32, g_target.x, g_target.y, g_target.z, g_eye.x, g_eye.y, - g_eye.z); - normalise_v3(cam); - f32 coeff = -1.0f / magnitude_v3(cam) * 0.5f; - g_cam_matrix = projection(coeff); - for (u64 i = 0; i < model->triangles->count; ++i) { triangle = list_get(model->triangles, i); - if (colour_type == COLOUR_TYPE_RANDOM) { - colour = (Colour){.r = rand() % UINT8_MAX, - .g = rand() % UINT8_MAX, - .b = rand() % UINT8_MAX, - .a = 255}; - } - render_triangle(&triangle, model, render, colour, type, projection_type, - model_view); + render_triangle(&triangle, model, shader, render, render_type, colour); } } internal void render_triangle(const Triangle *triangle, const Model *model, - Render *render, Colour colour, RenderType type, - ProjectionType projection_type, M4x4f mv) { + const Shader *shader, Render *render, + RenderType render_type, Colour colour) { Image *img = &(render->img); V3f vertices[TRIANGLE_VERTICES] = { list_get(model->vertices, triangle->p0), @@ -261,51 +160,24 @@ internal void render_triangle(const Triangle *triangle, const Model *model, list_get(model->texture_coordinates, triangle->tx2), }; - // Camera for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { - V4f vertex; - vertex = (V4f){ - .x = vertices[i].x, - .y = vertices[i].y, - .z = vertices[i].z, - .w = 1.0f, - }; - vertex = mat4x4_mul_vec4(mv, vertex); - vertices[i] = project_vec4(vertex); + vertices[i] = shader->vertex(&vertices[i], shader->model_view, + shader->projection, render); } - // Basic perspective projection - if (projection_type == PROJECTION_TYPE_PERSPECTIVE) { - V4f vertex; - - for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { - vertex = (V4f){ - .x = vertices[i].x, - .y = vertices[i].y, - .z = vertices[i].z, - .w = 1.0f, - }; - vertex = mat4x4_mul_vec4(g_cam_matrix, vertex); - vertices[i] = project_vec4(vertex); - } - } - - if (type == RENDER_TYPE_WIREFRAME) { + if (render_type == RENDER_TYPE_WIREFRAME) { V3f v0, v1; u64 x0, y0, x1, y1; - V3f vp0, vp1; for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { v0 = vertices[i]; v1 = vertices[(i + 1) % TRIANGLE_VERTICES]; - vp0 = get_viewport_vertex(&v0, img); - vp1 = get_viewport_vertex(&v1, img); - - draw_line(img, (u64)vp0.x, (u64)vp0.y, (u64)vp1.x, (u64)vp1.y, colour); + draw_line(img, (u64)v0.x, (u64)v0.y, (u64)v1.x, (u64)v1.y, colour); } - } else if (type == RENDER_TYPE_FILLED || type == RENDER_TYPE_SHADED) { + } else if (render_type == RENDER_TYPE_FILLED || + render_type == RENDER_TYPE_SHADED) { fill_triangle(render, vertices, normals, coordinates, colour, - model->texture, type); + model->texture, render_type); } } @@ -317,9 +189,9 @@ internal void fill_triangle(Render *render, V3f vertices[TRIANGLE_VERTICES], Depth *depth = &(render->depth); TriangleBBox bbox = get_triangle_bbox(img, vertices); - V3f v0 = get_viewport_vertex(&vertices[0], img); - V3f v1 = get_viewport_vertex(&vertices[1], img); - V3f v2 = get_viewport_vertex(&vertices[2], img); + V3f v0 = vertices[0]; + V3f v1 = vertices[1]; + V3f v2 = vertices[2]; V2i ab = V2(V2i, i64, v0.x, v0.y, v1.x, v1.y); V2i ac = V2(V2i, i64, v0.x, v0.y, v2.x, v2.y); @@ -388,77 +260,18 @@ internal void fill_triangle(Render *render, V3f vertices[TRIANGLE_VERTICES], } } -internal M4x4f lookat(V3f eye, V3f target, V3f up) { - V3f z = V3(V3f, f32, target.x, target.y, target.z, eye.x, eye.y, eye.z); - normalise_v3(z); - V3f x = cross_product(up, z); - normalise_v3(x); - V3f y = cross_product(z, x); - normalise_v3(y); - - M4x4f rotation = mat4x4_identity; - rotation.row0.x = x.x; - rotation.row0.y = x.y; - rotation.row0.z = x.z; - rotation.row1.x = y.x; - rotation.row1.y = y.y; - rotation.row1.z = y.z; - rotation.row2.x = z.x; - rotation.row2.y = z.y; - rotation.row2.z = z.z; - - M4x4f translation = mat4x4_identity; - translation.row0.w = -(eye.x); - translation.row1.w = -(eye.y); - translation.row2.w = -(eye.z); - - return mat4x4_mul(rotation, translation); -} - -internal M4x4f viewport(f32 x, f32 y, u64 w, u64 h) { - M4x4f output = mat4x4_identity; - - f32 half_width = (f32)w * 0.5f; - f32 half_height = (f32)h * 0.5f; - f32 half_depth = (f32)DEPTH_MAX * 0.5f; - - output.row0.x = half_width; - output.row0.w = x + half_width; - output.row1.y = half_height; - output.row1.w = y + half_height; - output.row2.z = output.row2.w = half_depth; - - return output; -} - -internal M4x4f projection(f32 coeff) { - // clang-format off - return (M4x4f){ - .row0 = {1.0f, 0.0f, 0.0f, 0.0f}, - .row1 = {0.0f, 1.0f, 0.0f, 0.0f}, - .row2 = {0.0f, 0.0f, 1.0f, 0.0f}, - .row3 = {0.0f, 0.0f, coeff, 1.0f}, - }; - // clang-format on -} - internal TriangleBBox get_triangle_bbox(const Image *img, V3f vertices[TRIANGLE_VERTICES]) { f32 x0 = min(vertices[0].x, min(vertices[1].x, vertices[2].x)); f32 x1 = max(vertices[0].x, max(vertices[1].x, vertices[2].x)); - // NOTE (Abdelrahman): Because y is flipped, we use max for the minimum and - // min for the maximum - f32 y0 = max(vertices[0].y, max(vertices[1].y, vertices[2].y)); - f32 y1 = min(vertices[0].y, min(vertices[1].y, vertices[2].y)); - - V3f minimum = get_viewport_vertex(&(V3f){x0, y0, 0.0f}, img); - V3f maximum = get_viewport_vertex(&(V3f){x1, y1, 0.0f}, img); + f32 y0 = min(vertices[0].y, min(vertices[1].y, vertices[2].y)); + f32 y1 = max(vertices[0].y, max(vertices[1].y, vertices[2].y)); return (TriangleBBox){ - .x0 = minimum.x, - .y0 = minimum.y, - .x1 = maximum.x, - .y1 = maximum.y, + .x0 = x0, + .y0 = y0, + .x1 = x1, + .y1 = y1, }; } diff --git a/src/obj.h b/src/obj.h index d47580b..03ee5a0 100644 --- a/src/obj.h +++ b/src/obj.h @@ -5,6 +5,7 @@ #include "img.h" #include "mem_arena.h" #include "typed_list.h" +#include "vec.h" #define INVALID_MODEL ((Model){0}) #define IS_INVALID_MODEL(m) (m.vertices == NULL || m.triangles == NULL) @@ -22,67 +23,6 @@ struct triangle { u64 tx2; }; -typedef struct i64x2 V2i; -struct i64x2 { - i64 x; - i64 y; -}; - -typedef struct u64x2 V2u; -struct u64x2 { - u64 x; - u64 y; -}; - -typedef struct f32x2 V2f; -struct f32x2 { - union { - f32 x; - f32 u; - }; - union { - f32 y; - f32 v; - }; -}; - -typedef struct f32x3 V3f; -struct f32x3 { - f32 x; - f32 y; - f32 z; -}; - -typedef struct f32x4 V4f; -struct f32x4 { - f32 x; - f32 y; - f32 z; - f32 w; -}; - -typedef struct u64x3 V3u; -struct u64x3 { - u64 x; - u64 y; - u64 z; -}; - -typedef struct f32_3x3 M3x3f; -struct f32_3x3 { - V3f row0; - V3f row1; - V3f row2; -}; - -typedef struct f32_4x4 M4x4f; -struct f32_4x4 { - V4f row0; - V4f row1; - V4f row2; - V4f row3; -}; - typedef enum { RENDER_TYPE_WIREFRAME, RENDER_TYPE_FILLED, @@ -91,13 +31,6 @@ typedef enum { COUNT_RENDER_TYPES, } RenderType; -typedef enum { - COLOUR_TYPE_FIXED, - COLOUR_TYPE_RANDOM, - - COUNT_COLOUR_TYPE, -} ColourType; - typedef enum { PROJECTION_TYPE_ORTHOGRAPHIC, PROJECTION_TYPE_PERSPECTIVE, @@ -105,8 +38,6 @@ typedef enum { COUNT_PROJECTION_TYPE, } ProjectionType; -MAKE_LIST_TYPE(V3f); -MAKE_LIST_TYPE(V2f); MAKE_LIST_TYPE(Triangle); typedef struct model Model; @@ -124,10 +55,21 @@ struct render { Depth depth; }; +typedef V3f(VertexShader)(const V3f *vertex, M4x4f *model_view, + M4x4f *projection, const Render *render); +typedef bool(FragmentShader)(Colour colour); + +typedef struct shader Shader; +struct shader { + VertexShader *vertex; + FragmentShader *fragment; + M4x4f *model_view; + M4x4f *projection; +}; + Model load_obj_file(Arena *arena, const char *filename, const char *texture); bool init_render(Arena *arena, Render *render, u64 width, u64 height); -void render_model(const Model *model, Render *render, Colour colour, - RenderType type, ColourType colour_type, - ProjectionType projection); +void render_model(const Model *model, Render *render, const Shader *shader, + RenderType render_type, Colour colour); #endif // OBJ_H diff --git a/src/vec.c b/src/vec.c new file mode 100644 index 0000000..de689bc --- /dev/null +++ b/src/vec.c @@ -0,0 +1,57 @@ +#include "vec.h" + +#define DEPTH_MAX 255 + +M4x4f lookat(V3f eye, V3f target, V3f up) { + V3f z = V3(V3f, f32, target.x, target.y, target.z, eye.x, eye.y, eye.z); + normalise_v3(z); + V3f x = cross_product(up, z); + normalise_v3(x); + V3f y = cross_product(z, x); + normalise_v3(y); + + M4x4f rotation = mat4x4_identity; + rotation.row0.x = x.x; + rotation.row0.y = x.y; + rotation.row0.z = x.z; + rotation.row1.x = y.x; + rotation.row1.y = y.y; + rotation.row1.z = y.z; + rotation.row2.x = z.x; + rotation.row2.y = z.y; + rotation.row2.z = z.z; + + M4x4f translation = mat4x4_identity; + translation.row0.w = -(eye.x); + translation.row1.w = -(eye.y); + translation.row2.w = -(eye.z); + + return mat4x4_mul(rotation, translation); +} + +M4x4f projection(f32 coeff) { + // clang-format off + return (M4x4f){ + .row0 = {1.0f, 0.0f, 0.0f, 0.0f}, + .row1 = {0.0f, 1.0f, 0.0f, 0.0f}, + .row2 = {0.0f, 0.0f, 1.0f, 0.0f}, + .row3 = {0.0f, 0.0f, coeff, 1.0f}, + }; + // clang-format on +} + +M4x4f viewport(f32 x, f32 y, u64 w, u64 h) { + M4x4f output = mat4x4_identity; + + f32 half_width = (f32)w * 0.5f; + f32 half_height = (f32)h * 0.5f; + f32 half_depth = (f32)DEPTH_MAX * 0.5f; + + output.row0.x = half_width; + output.row0.w = x + half_width; + output.row1.y = half_height; + output.row1.w = y + half_height; + output.row2.z = output.row2.w = half_depth; + + return output; +} diff --git a/src/vec.h b/src/vec.h new file mode 100644 index 0000000..f6a56e7 --- /dev/null +++ b/src/vec.h @@ -0,0 +1,163 @@ +#ifndef VEC_H +#define VEC_H + +#include "aliases.h" +#include "typed_list.h" +#include + +typedef struct i64x2 V2i; +struct i64x2 { + i64 x; + i64 y; +}; + +typedef struct u64x2 V2u; +struct u64x2 { + u64 x; + u64 y; +}; + +typedef struct f32x2 V2f; +struct f32x2 { + union { + f32 x; + f32 u; + }; + union { + f32 y; + f32 v; + }; +}; + +typedef struct f32x3 V3f; +struct f32x3 { + f32 x; + f32 y; + f32 z; +}; + +typedef struct f32x4 V4f; +struct f32x4 { + f32 x; + f32 y; + f32 z; + f32 w; +}; + +typedef struct u64x3 V3u; +struct u64x3 { + u64 x; + u64 y; + u64 z; +}; + +typedef struct f32_3x3 M3x3f; +struct f32_3x3 { + V3f row0; + V3f row1; + V3f row2; +}; + +typedef struct f32_4x4 M4x4f; +struct f32_4x4 { + V4f row0; + V4f row1; + V4f row2; + V4f row3; +}; + +MAKE_LIST_TYPE(V3f); +MAKE_LIST_TYPE(V2f); + +#define V2(T, ELEM_T, X0, Y0, X1, Y1) \ + ((T){(ELEM_T)X1 - (ELEM_T)X0, (ELEM_T)Y1 - (ELEM_T)Y0}) + +#define V3(T, ELEM_T, X0, Y0, Z0, X1, Y1, Z1) \ + ((T){(ELEM_T)X1 - (ELEM_T)X0, (ELEM_T)Y1 - (ELEM_T)Y0, \ + (ELEM_T)Z1 - (ELEM_T)Z0}) + +#define dot_v2(V1, V2) ((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y) + +#define dot_v3(V1, V2) \ + ((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y + (f32)V1.z * (f32)V2.z) + +#define magnitude_v3(V) (sqrtf(dot_v3(V, V))) + +#define normalise_v3(V) \ + do { \ + f32 magnitude = magnitude_v3(V); \ + V.x /= magnitude; \ + V.y /= magnitude; \ + V.z /= magnitude; \ + } while (0) + +#define cross_product(V1, V2) \ + ((V3f){ \ + .x = V1.y * V2.z - V1.z * V2.y, \ + .y = V1.z * V2.x - V1.x * V2.z, \ + .z = V1.x * V2.y - V1.y * V2.x, \ + }) + +#define mat4x4_identity \ + ((M4x4f){ \ + .row0 = {1.0f, 0.0f, 0.0f, 0.0f}, \ + .row1 = {0.0f, 1.0f, 0.0f, 0.0f}, \ + .row2 = {0.0f, 0.0f, 1.0f, 0.0f}, \ + .row3 = {0.0f, 0.0f, 0.0f, 1.0f}, \ + }) + +#define mat4x4_mul(MAT1, MAT2) \ + ((M4x4f){ \ + .row0.x = MAT1.row0.x * MAT2.row0.x + MAT1.row0.y * MAT2.row1.x + \ + MAT1.row0.z * MAT2.row2.x + MAT1.row0.w * MAT2.row3.x, \ + .row0.y = MAT1.row0.x * MAT2.row0.y + MAT1.row0.y * MAT2.row1.y + \ + MAT1.row0.z * MAT2.row2.y + MAT1.row0.w * MAT2.row3.y, \ + .row0.z = MAT1.row0.x * MAT2.row0.z + MAT1.row0.y * MAT2.row1.z + \ + MAT1.row0.z * MAT2.row2.z + MAT1.row0.w * MAT2.row3.z, \ + .row0.w = MAT1.row0.x * MAT2.row0.w + MAT1.row0.y * MAT2.row1.w + \ + MAT1.row0.z * MAT2.row2.w + MAT1.row0.w * MAT2.row3.w, \ + .row1.x = MAT1.row1.x * MAT2.row0.x + MAT1.row1.y * MAT2.row1.x + \ + MAT1.row1.z * MAT2.row2.x + MAT1.row1.w * MAT2.row3.x, \ + .row1.y = MAT1.row1.x * MAT2.row0.y + MAT1.row1.y * MAT2.row1.y + \ + MAT1.row1.z * MAT2.row2.y + MAT1.row1.w * MAT2.row3.y, \ + .row1.z = MAT1.row1.x * MAT2.row0.z + MAT1.row1.y * MAT2.row1.z + \ + MAT1.row1.z * MAT2.row2.z + MAT1.row1.w * MAT2.row3.z, \ + .row1.w = MAT1.row1.x * MAT2.row0.w + MAT1.row1.y * MAT2.row1.w + \ + MAT1.row1.z * MAT2.row2.w + MAT1.row1.w * MAT2.row3.w, \ + .row2.x = MAT1.row2.x * MAT2.row0.x + MAT1.row2.y * MAT2.row1.x + \ + MAT1.row2.z * MAT2.row2.x + MAT1.row2.w * MAT2.row3.x, \ + .row2.y = MAT1.row2.x * MAT2.row0.y + MAT1.row2.y * MAT2.row1.y + \ + MAT1.row2.z * MAT2.row2.y + MAT1.row2.w * MAT2.row3.y, \ + .row2.z = MAT1.row2.x * MAT2.row0.z + MAT1.row2.y * MAT2.row1.z + \ + MAT1.row2.z * MAT2.row2.z + MAT1.row2.w * MAT2.row3.z, \ + .row2.w = MAT1.row2.x * MAT2.row0.w + MAT1.row2.y * MAT2.row1.w + \ + MAT1.row2.z * MAT2.row2.w + MAT1.row2.w * MAT2.row3.w, \ + .row3.x = MAT1.row3.x * MAT2.row0.x + MAT1.row3.y * MAT2.row1.x + \ + MAT1.row3.z * MAT2.row2.x + MAT1.row3.w * MAT2.row3.x, \ + .row3.y = MAT1.row3.x * MAT2.row0.y + MAT1.row3.y * MAT2.row1.y + \ + MAT1.row3.z * MAT2.row2.y + MAT1.row3.w * MAT2.row3.y, \ + .row3.z = MAT1.row3.x * MAT2.row0.z + MAT1.row3.y * MAT2.row1.z + \ + MAT1.row3.z * MAT2.row2.z + MAT1.row3.w * MAT2.row3.z, \ + .row3.w = MAT1.row3.x * MAT2.row0.w + MAT1.row3.y * MAT2.row1.w + \ + MAT1.row3.z * MAT2.row2.w + MAT1.row3.w * MAT2.row3.w, \ + }) + +#define mat4x4_mul_vec4(MAT, V) \ + ((V4f){ \ + .x = MAT.row0.x * V.x + MAT.row0.y * V.y + MAT.row0.z * V.z + \ + MAT.row0.w * V.w, \ + .y = MAT.row1.x * V.x + MAT.row1.y * V.y + MAT.row1.z * V.z + \ + MAT.row1.w * V.w, \ + .z = MAT.row2.x * V.x + MAT.row2.y * V.y + MAT.row2.z * V.z + \ + MAT.row2.w * V.w, \ + .w = MAT.row3.x * V.x + MAT.row3.y * V.y + MAT.row3.z * V.z + \ + MAT.row3.w * V.w, \ + }) + +#define project_vec4(V) ((V3f){.x = V.x / V.w, .y = V.y / V.w, .z = V.z / V.w}) + +M4x4f lookat(V3f eye, V3f target, V3f up); +M4x4f projection(f32 coeff); +M4x4f viewport(f32 x, f32 y, u64 w, u64 h); + +#endif // VEC_H