From e266879a4f28e5a0c54abf45b3375f1d9a7f8eb7 Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sat, 20 Jul 2024 19:41:21 +0100 Subject: [PATCH] Add model loading from OBJ and wireframe rendering --- src/obj.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/obj.h | 38 ++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 src/obj.c create mode 100644 src/obj.h diff --git a/src/obj.c b/src/obj.c new file mode 100644 index 0000000..8b7c92c --- /dev/null +++ b/src/obj.c @@ -0,0 +1,105 @@ +#include "obj.h" +#include "aliases.h" +#include "img.h" +#include "mem_arena.h" +#include "typed_list.h" +#include "utils.h" +#include + +internal void render_wireframe_triangle(const Triangle *triangle, + const Model *model, Image *img, + Colour colour); +internal void get_image_coordinates(const Vertex *vertex, const Image *img, + u64 *x, u64 *y); +internal u64 ndc_to_image_coordinate(f32 value, u64 max); + +Model load_obj_file(Arena *arena, const char *filename) { + if (!arena) { + return NULL_MODEL; + } + + FILE *fp = fopen(filename, "r"); + if (!fp) { + return NULL_MODEL; + } + + Model model = (Model){ + .vertices = list_create(Vertex, arena), + .triangles = list_create(Triangle, arena), + }; + if (!(model.vertices) || !(model.triangles)) { + return NULL_MODEL; + } + + char line[8192]; + char identifier[8]; + Vertex vertex; + Triangle triangle; + f32 vx, vy, vz; + u64 fp0, fp1, fp2; + u64 ign_0_1, ign_0_2; + u64 ign_1_1, ign_1_2; + u64 ign_2_1, ign_2_2; + 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(Vertex, arena, model.vertices, vertex); + } else if (strncmp(identifier, "f", 8) == 0) { + sscanf(line + 2, "%lu/%lu/%lu %lu/%lu/%lu %lu/%lu/%lu", &fp0, &ign_0_1, + &ign_0_2, &fp1, &ign_1_1, &ign_1_2, &fp2, &ign_2_1, &ign_2_2); + // OBJ indices start from 1 + triangle.p0 = fp0 - 1; + triangle.p1 = fp1 - 1; + triangle.p2 = fp2 - 1; + list_append(Triangle, arena, model.triangles, triangle); + } + } + + return model; +} + +void render_wireframe_model(const Model *model, Image *img, Colour colour) { + Triangle triangle; + + for (u64 i = 0; i < model->triangles->count; ++i) { + triangle = list_get(model->triangles, i); + render_wireframe_triangle(&triangle, model, img, colour); + } +} + +internal void render_wireframe_triangle(const Triangle *triangle, + const Model *model, Image *img, + Colour colour) { + Vertex vertices[3] = { + list_get(model->vertices, triangle->p0), + list_get(model->vertices, triangle->p1), + list_get(model->vertices, triangle->p2), + }; + + Vertex v0, v1; + u64 x0, y0, x1, y1; + for (u64 i = 0; i < 3; ++i) { + v0 = vertices[i]; + v1 = vertices[(i + 1) % 3]; + + get_image_coordinates(&v0, img, &x0, &y0); + get_image_coordinates(&v1, img, &x1, &y1); + + draw_line(img, x0, y0, x1, y1, colour); + } +} + +internal void get_image_coordinates(const Vertex *vertex, const Image *img, + u64 *x, u64 *y) { + *x = ndc_to_image_coordinate(vertex->x, img->width); + *y = ndc_to_image_coordinate(0.0f - vertex->y, img->height); +} + +internal u64 ndc_to_image_coordinate(f32 value, u64 max) { + f32 result = (value + 1.0f) * max / 2.0f; + return clamp((u64)result, 0, max); +} diff --git a/src/obj.h b/src/obj.h new file mode 100644 index 0000000..80035f2 --- /dev/null +++ b/src/obj.h @@ -0,0 +1,38 @@ +#ifndef OBJ_H +#define OBJ_H + +#include "aliases.h" +#include "img.h" +#include "mem_arena.h" +#include "typed_list.h" + +#define NULL_MODEL ((Model){0}) +#define IS_NULL_MODEL(m) (m.vertices == NULL || m.triangles == NULL) + +typedef struct vertex Vertex; +struct vertex { + f32 x; + f32 y; + f32 z; +}; + +typedef struct triangle Triangle; +struct triangle { + u64 p0; + u64 p1; + u64 p2; +}; + +MAKE_LIST_TYPE(Vertex); +MAKE_LIST_TYPE(Triangle); + +typedef struct model Model; +struct model { + LIST_TYPE(Vertex) * vertices; + LIST_TYPE(Triangle) * triangles; +}; + +Model load_obj_file(Arena *arena, const char *filename); +void render_wireframe_model(const Model *model, Image *img, Colour colour); + +#endif // OBJ_H