diff --git a/compile b/compile index e6bf2a4..23050bf 100755 --- a/compile +++ b/compile @@ -3,9 +3,11 @@ CC=clang WAPP_INCLUDE="$(find intern/wapp/src -type d | xargs -I{} echo -n "-I{} ")" WAPP_SRC="$(find intern/wapp/src -type f -name "*.c" | xargs -I{} echo -n "{} ")" -CFLAGS="-g -Isrc $WAPP_INCLUDE" +TINYRENDER_INCLUDE="$(find src -type d | xargs -I{} echo -n "-I{} ")" +TINYRENDER_SRC="$(find src -type f -name "*.c" | xargs -I{} echo -n "{} ")" +CFLAGS="-g $TINYRENDER_INCLUDE $WAPP_INCLUDE" LIBS="-lm" -SRC="src/*.c $WAPP_SRC" +SRC="$TINYRENDER_SRC $WAPP_SRC" OUT=tiny (set -x ; $CC $CFLAGS $LIBS $SRC -o $OUT) diff --git a/src/img.c b/src/img/img.c similarity index 82% rename from src/img.c rename to src/img/img.c index 12dec1e..51a63f1 100644 --- a/src/img.c +++ b/src/img/img.c @@ -7,7 +7,7 @@ #include #include -internal u64 calculate_pixel_index(Buffer *buffer, u64 x, u64 y, u64 base_size); +internal i64 calculate_pixel_index(Buffer *buffer, u64 x, u64 y, u64 base_size); bool _init_buffer(Arena *arena, Buffer *buffer, u64 base_size) { if (!arena || !buffer || buffer->width == 0 || buffer->height == 0) { @@ -21,12 +21,20 @@ bool _init_buffer(Arena *arena, Buffer *buffer, u64 base_size) { } u8 *_get_pixel(Buffer *buffer, u64 x, u64 y, u64 base_size) { - u64 idx = calculate_pixel_index(buffer, x, y, base_size); + i64 idx = calculate_pixel_index(buffer, x, y, base_size); + if (idx == -1) { + idx = 0; + } + return ((u8 *)(buffer->buf)) + idx; } void _set_pixel(Buffer *buffer, u64 x, u64 y, void *value, u64 base_size) { - u64 idx = calculate_pixel_index(buffer, x, y, base_size); + i64 idx = calculate_pixel_index(buffer, x, y, base_size); + if (idx == -1) { + return; + } + memcpy(((u8 *)(buffer->buf)) + idx, value, base_size); return; } @@ -75,7 +83,11 @@ void save_image(const Image *img, const char *filename) { write_p7_image(img->width, img->height, (u8 *)(img->buf), filename); } -internal u64 calculate_pixel_index(Buffer *buffer, u64 x, u64 y, +internal i64 calculate_pixel_index(Buffer *buffer, u64 x, u64 y, u64 base_size) { + if (x < 0 || y < 0 || x >= buffer->width || y >= buffer->height) { + return -1; + } + return (y * buffer->width + x) * base_size; } diff --git a/src/img.h b/src/img/img.h similarity index 100% rename from src/img.h rename to src/img/img.h diff --git a/src/typed_list.c b/src/list/typed_list.c similarity index 100% rename from src/typed_list.c rename to src/list/typed_list.c diff --git a/src/typed_list.h b/src/list/typed_list.h similarity index 100% rename from src/typed_list.h rename to src/list/typed_list.h diff --git a/src/main.c b/src/main.c index d72ae92..f0895e6 100644 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,9 @@ #include "mem_arena.h" #include "mem_utils.h" #include "obj.h" +#include "shader.h" +#include "shaders.h" +#include #include #include #include @@ -10,6 +13,13 @@ #define SIZE 1200 #define RESOURCE(NAME) "resources/" NAME +extern ShaderID perspective_lit_textured_id; +extern ShaderID perspective_lit_coloured_id; +extern ShaderID perspective_albedo_id; +extern ShaderID orthographic_lit_textured_id; +extern ShaderID orthographic_lit_coloured_id; +extern ShaderID orthographic_albedo_id; + enum { TINY_EXIT_SUCCESS, TINY_EXIT_ARENA_INIT_FAILED, @@ -38,9 +48,11 @@ int main(void) { return TINY_EXIT_MODEL_LOAD_FAILED; } + load_shaders(); + clear_buffer(&(render.img), &bg); - render_model(&obj, &render, teal, RENDER_TYPE_SHADED, COLOUR_TYPE_FIXED, - PROJECTION_TYPE_PERSPECTIVE); + render_model(&obj, &render, perspective_lit_textured_id, RENDER_TYPE_SHADED, + teal); save_image(&(render.img), "result.pam"); wapp_mem_arena_destroy(&arena); diff --git a/src/obj.c b/src/obj.c deleted file mode 100644 index a77a4d8..0000000 --- a/src/obj.c +++ /dev/null @@ -1,486 +0,0 @@ -#include "obj.h" -#include "aliases.h" -#include "img.h" -#include "mem_arena.h" -#include "pam.h" -#include "typed_list.h" -#include "utils.h" -#include -#include -#include -#include -#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 { - u64 x0; - u64 y0; - u64 x1; - u64 y1; -}; - -internal void render_triangle(const Triangle *triangle, const Model *model, - Render *render, Colour colour, RenderType type, - ProjectionType projection, M4x4f mv); -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, - const V2i *ab, const V2i *ac, - const V2i *ap); -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) { - return INVALID_MODEL; - } - - FILE *fp = fopen(filename, "r"); - if (!fp) { - return INVALID_MODEL; - } - - Model model = (Model){ - .vertices = list_create(V3f, arena), - .normals = list_create(V3f, arena), - .texture_coordinates = list_create(V2f, arena), - .triangles = list_create(Triangle, arena), - }; - if (!(model.vertices) || !(model.normals) || !(model.texture_coordinates) || - !(model.triangles)) { - return INVALID_MODEL; - } - - char line[8192]; - char identifier[8]; - V3f vertex; - V3f normal; - V2f coord; - Triangle triangle; - f32 vx, vy, vz; - f32 nx, ny, nz; - f32 u, v; - u64 fp0, fp1, fp2; - u64 vn0, vn1, vn2; - u64 tx0, tx1, tx2; - while (fgets(line, 8191, fp) != NULL) { - sscanf(line, "%s", identifier); - if (strncmp(identifier, "v", 8) == 0) { - sscanf(line + 2, "%f %f %f", &vx, &vy, &vz); - vertex.x = vx; - vertex.y = vy; - vertex.z = vz; - list_append(V3f, arena, model.vertices, vertex); - } else if (strncmp(identifier, "vn", 8) == 0) { - sscanf(line + 2, "%f %f %f", &nx, &ny, &nz); - normal.x = nx; - normal.y = ny; - normal.z = nz; - list_append(V3f, arena, model.normals, normal); - } else if (strncmp(identifier, "vt", 8) == 0) { - sscanf(line + 2, "%f %f", &u, &v); - coord.u = u; - coord.v = v; - list_append(V2f, arena, model.texture_coordinates, coord); - } else if (strncmp(identifier, "f", 8) == 0) { - sscanf(line + 2, "%lu/%lu/%lu %lu/%lu/%lu %lu/%lu/%lu", &fp0, &tx0, &vn0, - &fp1, &tx1, &vn1, &fp2, &tx2, &vn2); - // OBJ indices start from 1 - triangle.p0 = fp0 - 1; - triangle.p1 = fp1 - 1; - triangle.p2 = fp2 - 1; - triangle.n0 = vn0 - 1; - triangle.n1 = vn1 - 1; - triangle.n2 = vn2 - 1; - triangle.tx0 = tx0 - 1; - triangle.tx1 = tx1 - 1; - triangle.tx2 = tx2 - 1; - list_append(Triangle, arena, model.triangles, triangle); - } - } - - if (texture) { - model.texture = load_p6_image(arena, texture); - } - - return model; -} - -bool init_render(Arena *arena, Render *render, u64 width, u64 height) { - render->img = (Image){.width = width, .height = height}; - if (!init_buffer(arena, &(render->img))) { - return false; - } - - render->depth = (Depth){.width = width, .height = height}; - if (!init_buffer(arena, &(render->depth))) { - return false; - } - - f32 inf = -INFINITY; - clear_buffer(&(render->depth), &inf); - - return true; -} - -void render_model(const Model *model, Render *render, Colour colour, - RenderType type, ColourType colour_type, - ProjectionType projection_type) { - 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); - } -} - -internal void render_triangle(const Triangle *triangle, const Model *model, - Render *render, Colour colour, RenderType type, - ProjectionType projection_type, M4x4f mv) { - Image *img = &(render->img); - V3f vertices[TRIANGLE_VERTICES] = { - list_get(model->vertices, triangle->p0), - list_get(model->vertices, triangle->p1), - list_get(model->vertices, triangle->p2), - }; - V3f normals[TRIANGLE_VERTICES] = { - list_get(model->normals, triangle->n0), - list_get(model->normals, triangle->n1), - list_get(model->normals, triangle->n2), - }; - V2f coordinates[TRIANGLE_VERTICES] = { - list_get(model->texture_coordinates, triangle->tx0), - list_get(model->texture_coordinates, triangle->tx1), - 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); - } - - // 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) { - 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); - } - } else if (type == RENDER_TYPE_FILLED || type == RENDER_TYPE_SHADED) { - fill_triangle(render, vertices, normals, coordinates, colour, - model->texture, type); - } -} - -internal void fill_triangle(Render *render, V3f vertices[TRIANGLE_VERTICES], - V3f normals[TRIANGLE_VERTICES], - V2f coordinates[TRIANGLE_VERTICES], Colour colour, - Image *texture, RenderType type) { - Image *img = &(render->img); - 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); - - 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); - f32 d00 = dot_v2(ab, ab); - f32 d01 = dot_v2(ab, ac); - f32 d11 = dot_v2(ac, ac); - f32 denom = d00 * d11 - d01 * d01; - V2i ap; - V3f coords; - f32 z; - f32 zbuf; - f32 nx, ny, nz; - V3f normal; - f32 tx_u, tx_v; - u64 tx_x, tx_y; - - f32 intensity = 1.0f; - - for (u64 y = bbox.y0; y <= bbox.y1; ++y) { - for (u64 x = bbox.x0; x <= bbox.x1; ++x) { - ap = V2(V2i, i64, v0.x, v0.y, x, y); - coords = get_barycentric_coords(d00, d01, d11, denom, &ab, &ac, &ap); - if (coords.x < 0.0f || coords.y < 0.0f || coords.x + coords.y > 1.0f) { - continue; - } - - z = 0.0f; - z += v0.z * coords.x + v1.z * coords.y + v2.z * coords.z; - zbuf = get_pixel(f32, &(render->depth), x, y); - - if (z > zbuf) { - if (type == RENDER_TYPE_SHADED) { - nx = normals[0].x * coords.x + normals[1].x * coords.y + - normals[2].x * coords.z; - ny = normals[0].y * coords.x + normals[1].y * coords.y + - normals[2].y * coords.z; - nz = normals[0].z * coords.x + normals[1].z * coords.y + - normals[2].z * coords.z; - normal = (V3f){nx, ny, nz}; - intensity = dot_v3(normal, g_light_dir); - } - - if (intensity < 0.0f) { - intensity = 0.01f; - } - - if (texture) { - tx_u = coordinates[0].u * coords.x + coordinates[1].u * coords.y + - coordinates[2].u * coords.z; - tx_v = coordinates[0].v * coords.x + coordinates[1].v * coords.y + - coordinates[2].v * coords.z; - tx_x = tx_u * texture->width; - tx_y = (1.0f - tx_v) * texture->height; - - colour = get_pixel(Colour, texture, tx_x, tx_y); - } - - colour.r *= intensity; - colour.g *= intensity; - colour.b *= intensity; - - set_pixel(depth, x, y, &z); - set_pixel(img, x, y, &colour); - } - } - } -} - -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); - - return (TriangleBBox){ - .x0 = minimum.x, - .y0 = minimum.y, - .x1 = maximum.x, - .y1 = maximum.y, - }; -} - -internal V3f get_barycentric_coords(f32 d00, f32 d01, f32 d11, f32 denom, - const V2i *ab, const V2i *ac, - const V2i *ap) { - if (denom == 0.0f) { - return (V3f){-INFINITY, -INFINITY, -INFINITY}; - } - - f32 d20 = dot_v2((*ap), (*ab)); - f32 d21 = dot_v2((*ap), (*ac)); - - f32 v = (d11 * d20 - d01 * d21) / denom; - f32 w = (d00 * d21 - d01 * d20) / denom; - f32 u = 1.0f - v - w; - - return (V3f){v, w, u}; -} - -internal V3f get_viewport_vertex(const V3f *vertex, const Image *img) { - V4f vh = {.x = vertex->x, .y = 0.0f - vertex->y, .z = vertex->z, .w = 1.0f}; - vh = mat4x4_mul_vec4(viewport(vh.x, vh.y, img->width, img->height), vh); - return project_vec4(vh); -} diff --git a/src/obj/obj.c b/src/obj/obj.c new file mode 100644 index 0000000..862f7b9 --- /dev/null +++ b/src/obj/obj.c @@ -0,0 +1,294 @@ +#include "obj.h" +#include "aliases.h" +#include "img.h" +#include "mem_arena.h" +#include "pam.h" +#include "shader.h" +#include "typed_list.h" +#include "utils.h" +#include "vec.h" +#include +#include +#include +#include +#include + +#define TRIANGLE_VERTICES 3 + +typedef struct triangle_bbox TriangleBBox; +struct triangle_bbox { + u64 x0; + u64 y0; + u64 x1; + u64 y1; +}; + +internal void render_triangle(const Triangle *triangle, const Model *model, + ShaderID shader, Render *render, + RenderType render_type, Colour colour); +internal void fill_triangle(Render *render, ShaderID shader, + V3f vertices[TRIANGLE_VERTICES], + V3f normals[TRIANGLE_VERTICES], + V2f coordinates[TRIANGLE_VERTICES], Colour colour, + Image *texture, RenderType type); +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, + const V2i *ab, const V2i *ac, + const V2i *ap); +internal V3f get_viewport_vertex(const V3f *vertex, const Image *img); + +Model load_obj_file(Arena *arena, const char *filename, const char *texture) { + if (!arena) { + return INVALID_MODEL; + } + + FILE *fp = fopen(filename, "r"); + if (!fp) { + return INVALID_MODEL; + } + + Model model = (Model){ + .vertices = list_create(V3f, arena), + .normals = list_create(V3f, arena), + .texture_coordinates = list_create(V2f, arena), + .triangles = list_create(Triangle, arena), + }; + if (!(model.vertices) || !(model.normals) || !(model.texture_coordinates) || + !(model.triangles)) { + return INVALID_MODEL; + } + + char line[8192]; + char identifier[8]; + V3f vertex; + V3f normal; + V2f coord; + Triangle triangle; + f32 vx, vy, vz; + f32 nx, ny, nz; + f32 u, v; + u64 fp0, fp1, fp2; + u64 vn0, vn1, vn2; + u64 tx0, tx1, tx2; + while (fgets(line, 8191, fp) != NULL) { + sscanf(line, "%s", identifier); + if (strncmp(identifier, "v", 8) == 0) { + sscanf(line + 2, "%f %f %f", &vx, &vy, &vz); + vertex.x = vx; + vertex.y = vy; + vertex.z = vz; + list_append(V3f, arena, model.vertices, vertex); + } else if (strncmp(identifier, "vn", 8) == 0) { + sscanf(line + 2, "%f %f %f", &nx, &ny, &nz); + normal.x = nx; + normal.y = ny; + normal.z = nz; + list_append(V3f, arena, model.normals, normal); + } else if (strncmp(identifier, "vt", 8) == 0) { + sscanf(line + 2, "%f %f", &u, &v); + coord.u = u; + coord.v = v; + list_append(V2f, arena, model.texture_coordinates, coord); + } else if (strncmp(identifier, "f", 8) == 0) { + sscanf(line + 2, "%lu/%lu/%lu %lu/%lu/%lu %lu/%lu/%lu", &fp0, &tx0, &vn0, + &fp1, &tx1, &vn1, &fp2, &tx2, &vn2); + // OBJ indices start from 1 + triangle.p0 = fp0 - 1; + triangle.p1 = fp1 - 1; + triangle.p2 = fp2 - 1; + triangle.n0 = vn0 - 1; + triangle.n1 = vn1 - 1; + triangle.n2 = vn2 - 1; + triangle.tx0 = tx0 - 1; + triangle.tx1 = tx1 - 1; + triangle.tx2 = tx2 - 1; + list_append(Triangle, arena, model.triangles, triangle); + } + } + + if (texture) { + model.texture = load_p6_image(arena, texture); + } + + return model; +} + +bool init_render(Arena *arena, Render *render, u64 width, u64 height) { + render->img = (Image){.width = width, .height = height}; + if (!init_buffer(arena, &(render->img))) { + return false; + } + + render->depth = (Depth){.width = width, .height = height}; + if (!init_buffer(arena, &(render->depth))) { + return false; + } + + f32 inf = -INFINITY; + clear_buffer(&(render->depth), &inf); + + return true; +} + +void render_model(const Model *model, Render *render, ShaderID shader, + RenderType render_type, Colour colour) { + Triangle triangle; + for (u64 i = 0; i < model->triangles->count; ++i) { + triangle = list_get(model->triangles, i); + render_triangle(&triangle, model, shader, render, render_type, colour); + } +} + +internal void render_triangle(const Triangle *triangle, const Model *model, + ShaderID shader, Render *render, + RenderType render_type, Colour colour) { + Image *img = &(render->img); + V3f vertices[TRIANGLE_VERTICES] = { + list_get(model->vertices, triangle->p0), + list_get(model->vertices, triangle->p1), + list_get(model->vertices, triangle->p2), + }; + V3f normals[TRIANGLE_VERTICES] = { + list_get(model->normals, triangle->n0), + list_get(model->normals, triangle->n1), + list_get(model->normals, triangle->n2), + }; + V2f coordinates[TRIANGLE_VERTICES] = { + list_get(model->texture_coordinates, triangle->tx0), + list_get(model->texture_coordinates, triangle->tx1), + list_get(model->texture_coordinates, triangle->tx2), + }; + + for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { + vertices[i] = + run_vertex_shader(shader, &vertices[i], (Buffer *)&render->img); + } + + if (render_type == RENDER_TYPE_WIREFRAME) { + V3f v0, v1; + u64 x0, y0, x1, y1; + for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { + v0 = vertices[i]; + v1 = vertices[(i + 1) % TRIANGLE_VERTICES]; + + draw_line(img, (u64)v0.x, (u64)v0.y, (u64)v1.x, (u64)v1.y, colour); + } + } else if (render_type == RENDER_TYPE_FILLED || + render_type == RENDER_TYPE_SHADED) { + fill_triangle(render, shader, vertices, normals, coordinates, colour, + model->texture, render_type); + } +} + +internal void fill_triangle(Render *render, ShaderID shader, + V3f vertices[TRIANGLE_VERTICES], + V3f normals[TRIANGLE_VERTICES], + V2f coordinates[TRIANGLE_VERTICES], Colour colour, + Image *texture, RenderType type) { + Image *img = &(render->img); + Depth *depth = &(render->depth); + TriangleBBox bbox = get_triangle_bbox(img, vertices); + + 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); + f32 d00 = dot_v2(ab, ab); + f32 d01 = dot_v2(ab, ac); + f32 d11 = dot_v2(ac, ac); + f32 denom = d00 * d11 - d01 * d01; + V2i ap; + V3f coords; + f32 z; + f32 zbuf; + f32 nx, ny, nz; + V3f normal; + f32 tx_u, tx_v; + u64 tx_x, tx_y; + V2f tex_coords; + FragmentResult result; + + f32 intensity = 1.0f; + + for (u64 y = bbox.y0; y <= bbox.y1; ++y) { + for (u64 x = bbox.x0; x <= bbox.x1; ++x) { + ap = V2(V2i, i64, v0.x, v0.y, x, y); + coords = get_barycentric_coords(d00, d01, d11, denom, &ab, &ac, &ap); + if (coords.x < 0.0f || coords.y < 0.0f || coords.x + coords.y > 1.0f) { + continue; + } + + z = 0.0f; + z += v0.z * coords.x + v1.z * coords.y + v2.z * coords.z; + zbuf = get_pixel(f32, &(render->depth), x, y); + + if (z <= zbuf) { + continue; + } + + nx = normals[0].x * coords.x + normals[1].x * coords.y + + normals[2].x * coords.z; + ny = normals[0].y * coords.x + normals[1].y * coords.y + + normals[2].y * coords.z; + nz = normals[0].z * coords.x + normals[1].z * coords.y + + normals[2].z * coords.z; + normal = (V3f){nx, ny, nz}; + + tx_u = coordinates[0].u * coords.x + coordinates[1].u * coords.y + + coordinates[2].u * coords.z; + tx_v = coordinates[0].v * coords.x + coordinates[1].v * coords.y + + coordinates[2].v * coords.z; + tex_coords = (V2f){tx_u, tx_v}; + + result = + run_fragment_shader(shader, normal, tex_coords, &colour, texture); + if (DISCARD_FRAGMENT(result)) { + continue; + } + + set_pixel(depth, x, y, &z); + set_pixel(img, x, y, &result.colour); + } + } +} + +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)); + 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 = x0, + .y0 = y0, + .x1 = x1, + .y1 = y1, + }; +} + +internal V3f get_barycentric_coords(f32 d00, f32 d01, f32 d11, f32 denom, + const V2i *ab, const V2i *ac, + const V2i *ap) { + if (denom == 0.0f) { + return (V3f){-INFINITY, -INFINITY, -INFINITY}; + } + + f32 d20 = dot_v2((*ap), (*ab)); + f32 d21 = dot_v2((*ap), (*ac)); + + f32 v = (d11 * d20 - d01 * d21) / denom; + f32 w = (d00 * d21 - d01 * d20) / denom; + f32 u = 1.0f - v - w; + + return (V3f){v, w, u}; +} + +internal V3f get_viewport_vertex(const V3f *vertex, const Image *img) { + V4f vh = {.x = vertex->x, .y = 0.0f - vertex->y, .z = vertex->z, .w = 1.0f}; + vh = mat4x4_mul_vec4(viewport(vh.x, vh.y, img->width, img->height), vh); + return project_vec4(vh); +} diff --git a/src/obj.h b/src/obj/obj.h similarity index 52% rename from src/obj.h rename to src/obj/obj.h index d47580b..23f8c94 100644 --- a/src/obj.h +++ b/src/obj/obj.h @@ -4,6 +4,7 @@ #include "aliases.h" #include "img.h" #include "mem_arena.h" +#include "shader.h" #include "typed_list.h" #define INVALID_MODEL ((Model){0}) @@ -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; @@ -126,8 +57,7 @@ struct render { 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, ShaderID shader, + RenderType render_type, Colour colour); #endif // OBJ_H diff --git a/src/pam.c b/src/pam/pam.c similarity index 100% rename from src/pam.c rename to src/pam/pam.c diff --git a/src/pam.h b/src/pam/pam.h similarity index 100% rename from src/pam.h rename to src/pam/pam.h diff --git a/src/shader/shader.c b/src/shader/shader.c new file mode 100644 index 0000000..312220f --- /dev/null +++ b/src/shader/shader.c @@ -0,0 +1,62 @@ +#include "shader.h" +#include "aliases.h" +#include "vec.h" + +#define MAX_SHADER_COUNT 2048 + +typedef struct shader_repo ShaderRepo; +struct shader_repo { + void *shaders[MAX_SHADER_COUNT]; + VertexShader *vertex_funcs[MAX_SHADER_COUNT]; + FragmentShader *fragment_funcs[MAX_SHADER_COUNT]; + u64 count; +}; + +internal ShaderRepo g_repository = {0}; + +ShaderID create_shader(void *shader, VertexShader *vertex, + FragmentShader *fragment) { + if (g_repository.count + 1 >= MAX_SHADER_COUNT) { + return INVALID_SHADER; + } + + ++(g_repository.count); + g_repository.shaders[g_repository.count] = shader; + g_repository.vertex_funcs[g_repository.count] = vertex; + g_repository.fragment_funcs[g_repository.count] = fragment; + + return (ShaderID){g_repository.count}; +} + +V3f run_vertex_shader(ShaderID shader, const V3f *vertex, + const Buffer *out_buf) { + if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !vertex || + !out_buf) { + return (V3f){0}; + } + + void *shader_obj = g_repository.shaders[shader.id]; + VertexShader *vertex_func = g_repository.vertex_funcs[shader.id]; + + if (!shader_obj || !vertex_func) { + return (V3f){0}; + } + + return vertex_func(shader_obj, vertex, out_buf); +} + +FragmentResult run_fragment_shader(ShaderID shader, V3f normal, V2f tex_coords, + const Colour *colour, const Image *texture) { + if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !colour) { + return DISCARDED_FRAGMENT; + } + + void *shader_obj = g_repository.shaders[shader.id]; + FragmentShader *fragment_func = g_repository.fragment_funcs[shader.id]; + + if (!shader_obj || !fragment_func) { + return DISCARDED_FRAGMENT; + } + + return fragment_func(shader_obj, normal, tex_coords, colour, texture); +} diff --git a/src/shader/shader.h b/src/shader/shader.h new file mode 100644 index 0000000..ca73f49 --- /dev/null +++ b/src/shader/shader.h @@ -0,0 +1,37 @@ +#ifndef SHADER_H +#define SHADER_H + +#include "aliases.h" +#include "img.h" +#include "vec.h" + +typedef struct shader_id ShaderID; +struct shader_id { + u64 id; +}; + +typedef struct fragment_result FragmentResult; +struct fragment_result { + Colour colour; + bool discard; +}; + +#define INVALID_SHADER ((ShaderID){0}) +#define IS_INVALID_SHADER(ID) (ID.id == 0) +#define DISCARDED_FRAGMENT ((FragmentResult){.discard = true}) +#define DISCARD_FRAGMENT(RESULT) (RESULT.discard) + +typedef V3f(VertexShader)(void *shader, const V3f *vertex, + const Buffer *out_buf); +typedef FragmentResult(FragmentShader)(void *shader, V3f normal, V2f tex_coords, + const Colour *colour, + const Image *texture); + +ShaderID create_shader(void *shader, VertexShader *vertex, + FragmentShader *fragment); +V3f run_vertex_shader(ShaderID shader, const V3f *vertex, + const Buffer *out_buf); +FragmentResult run_fragment_shader(ShaderID shader, V3f normal, V2f tex_coords, + const Colour *colour, const Image *texture); + +#endif // SHADER_H diff --git a/src/shader/shaders.c b/src/shader/shaders.c new file mode 100644 index 0000000..34b7c9c --- /dev/null +++ b/src/shader/shaders.c @@ -0,0 +1,148 @@ +#include "img.h" +#include "obj.h" +#include "shader.h" +#include "utils.h" +#include "vec.h" + +typedef struct shader Shader; +struct shader { + M4x4f model_view; + M4x4f projection; +}; + +Shader perspective = {0}; +Shader orthographic = {0}; + +ShaderID perspective_lit_textured_id = {0}; +ShaderID perspective_lit_coloured_id = {0}; +ShaderID perspective_albedo_id = {0}; +ShaderID orthographic_lit_textured_id = {0}; +ShaderID orthographic_lit_coloured_id = {0}; +ShaderID orthographic_albedo_id = {0}; + +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; + +internal V3f general_shader_vertex(void *shader, const V3f *vertex, + const Buffer *out_buf); +internal FragmentResult lit_textured_shader_fragment(void *shader, V3f normal, + V2f tex_coords, + const Colour *colour, + const Image *texture); +internal FragmentResult lit_coloured_shader_fragment(void *shader, V3f normal, + V2f tex_coords, + const Colour *colour, + const Image *texture); +internal FragmentResult albedo_shader_fragment(void *shader, V3f normal, + V2f tex_coords, + const Colour *colour, + const Image *texture); +internal M4x4f get_projection_matrix(ProjectionType projection_type); +internal f32 get_intensity(const V3f *normal); + +void load_shaders(void) { + M4x4f model_view = lookat(g_eye, g_target, g_up); + M4x4f orthographic_projection = + get_projection_matrix(PROJECTION_TYPE_ORTHOGRAPHIC); + M4x4f perspective_projection = + get_projection_matrix(PROJECTION_TYPE_PERSPECTIVE); + + perspective.model_view = orthographic.model_view = model_view; + perspective.projection = perspective_projection; + orthographic.projection = orthographic_projection; + + perspective_lit_textured_id = create_shader( + &perspective, general_shader_vertex, lit_textured_shader_fragment); + perspective_lit_coloured_id = create_shader( + &perspective, general_shader_vertex, lit_coloured_shader_fragment); + perspective_albedo_id = create_shader(&perspective, general_shader_vertex, + albedo_shader_fragment); + orthographic_lit_textured_id = create_shader( + &orthographic, general_shader_vertex, lit_textured_shader_fragment); + orthographic_lit_coloured_id = create_shader( + &orthographic, general_shader_vertex, lit_coloured_shader_fragment); + orthographic_albedo_id = create_shader(&orthographic, general_shader_vertex, + albedo_shader_fragment); +} + +internal V3f general_shader_vertex(void *shader, const V3f *vertex, + const Buffer *out_buf) { + Shader *shader_ptr = (Shader *)shader; + + V4f vh = {.x = vertex->x, .y = vertex->y, .z = vertex->z, .w = 1.0f}; + vh = mat4x4_mul_vec4(shader_ptr->projection, + mat4x4_mul_vec4(shader_ptr->model_view, vh)); + vh.y = 0.0 - vh.y; + vh = mat4x4_mul_vec4(viewport(vh.x, vh.y, out_buf->width, out_buf->height), + vh); + + return project_vec4(vh); +} + +internal FragmentResult lit_textured_shader_fragment(void *shader, V3f normal, + V2f tex_coords, + const Colour *colour, + const Image *texture) { + if (!texture) { + return DISCARDED_FRAGMENT; + } + + f32 intensity = get_intensity(&normal); + + u64 tx_x = tex_coords.u * texture->width; + u64 tx_y = (1.0f - tex_coords.v) * texture->height; + + Colour output = get_pixel(Colour, texture, tx_x, tx_y); + + output.r *= intensity; + output.g *= intensity; + output.b *= intensity; + + return (FragmentResult){.colour = output}; +} + +internal FragmentResult lit_coloured_shader_fragment(void *shader, V3f normal, + V2f tex_coords, + const Colour *colour, + const Image *texture) { + f32 intensity = get_intensity(&normal); + Colour output = *colour; + + output.r *= intensity; + output.g *= intensity; + output.b *= intensity; + + return (FragmentResult){.colour = output}; +} + +internal FragmentResult albedo_shader_fragment(void *shader, V3f normal, + V2f tex_coords, + const Colour *colour, + const Image *texture) { + return (FragmentResult){.colour = *colour}; +} + +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 f32 get_intensity(const V3f *normal) { + f32 intensity = dot_v3((*normal), g_light_dir); + if (intensity < 0.0f) { + intensity = 0.01f; + } + + return intensity; +} diff --git a/src/shader/shaders.h b/src/shader/shaders.h new file mode 100644 index 0000000..2022243 --- /dev/null +++ b/src/shader/shaders.h @@ -0,0 +1,6 @@ +#ifndef SHADERS_H +#define SHADERS_H + +void load_shaders(void); + +#endif // SHADERS_H diff --git a/src/utils.c b/src/utils/utils.c similarity index 100% rename from src/utils.c rename to src/utils/utils.c diff --git a/src/utils.h b/src/utils/utils.h similarity index 100% rename from src/utils.h rename to src/utils/utils.h diff --git a/src/vec/vec.c b/src/vec/vec.c new file mode 100644 index 0000000..de689bc --- /dev/null +++ b/src/vec/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/vec.h b/src/vec/vec.h new file mode 100644 index 0000000..f6a56e7 --- /dev/null +++ b/src/vec/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