Add model loading from OBJ and wireframe rendering

This commit is contained in:
Abdelrahman Said 2024-07-20 19:41:21 +01:00
parent f88b74912b
commit e266879a4f
2 changed files with 143 additions and 0 deletions

105
src/obj.c Normal file
View File

@ -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 <stdio.h>
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);
}

38
src/obj.h Normal file
View File

@ -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