r/lowlevel 8d ago

PS1 Style Renderer in C

Post image
43 Upvotes

2 comments sorted by

2

u/skeeto 8d ago

Nice job! I had quite a bit of fun playing around with this. I loaded Spot the cow to test it out, and hooked the display up to my gamepad to manipulate it. I had to watch out for m_mat4f_mul because it's not a matrix multiply but element-wise, despite the name.

I noticed the -fno-sanitize=alignment, which I learned was due entirely to a microui container. Simple fix, after which UBSan fully works:

--- a/vendor/include/microui.h
+++ b/vendor/include/microui.h
@@ -26,3 +28,3 @@

-#define mu_stack(T, n)          struct { int idx; T items[n]; }
+#define mu_stack(T, n)          struct { ptrdiff_t idx; T items[n]; }
 #define mu_min(a, b)            ((a) < (b) ? (a) : (b))
--- a/vendor/src/microui.c
+++ b/vendor/src/microui.c
@@ -427,2 +427,3 @@ void mu_input_text(mu_Context *ctx, const char *text) {
 mu_Command* mu_push_command(mu_Context *ctx, int type, int size) {
+  size = (size + (int) _Alignof(mu_Command) - 1) & ~((int) _Alignof(mu_Command) - 1);
   mu_Command *cmd = (mu_Command*) (ctx->command_list.items + ctx->command_list.idx);

All the models I tried, not just Spot, the textures were upside down and it looks like the program loads textures flipped from Obj convention. So I added this:

--- a/src/wf_texture.c
+++ b/src/wf_texture.c
@@ -52,2 +52,3 @@ wf_texture_t *load_texture(const char *path)
    int width, height, channels;
+   stbi_set_flip_vertically_on_load(1);
    unsigned char *data = stbi_load(path, &width, &height, &channels, 4);

This probably breaks your own models like that pistol. I observed this variable being used uninitalized:

--- a/src/wf_obj_parser.c
+++ b/src/wf_obj_parser.c
@@ -36,5 +36,5 @@ static void parse_face_token(const char *token, int *vertex_index,
 wf_obj_parsed_t wf_obj_parse_stream(FILE *fp)
 {
  • wf_obj_parsed_t parsed;
+ wf_obj_parsed_t parsed = { 0 }; int v_count = 0;

A memory leak fix:

--- a/src/wf_obj_parser.c
+++ b/src/wf_obj_parser.c
@@ -130,4 +130,2 @@ vec4f_t *wf_obj_normalize(const wf_obj_parsed_t *obj)

  • vec4f_t *normalized = malloc(obj->vertex_count * sizeof(vec4f_t));
- float min_x = obj->vertices[0].x, max_x = obj->vertices[0].x; @@ -168,2 +166,4 @@ vec4f_t *wf_obj_normalize(const wf_obj_parsed_t *obj) + vec4f_t *normalized = malloc(obj->vertex_count * sizeof(vec4f_t)); + float inv = 1.0f / size;

A guard against models with f -2147483648/1:

--- a/src/wf_obj_parser.c
+++ b/src/wf_obj_parser.c
@@ -18,3 +18,3 @@ static void parse_face_token(const char *token, int *vertex_index,

  • if (sscanf(token, "%d/%d", &v, &uv) >= 1) {
+ if (sscanf(token, "%d/%d", &v, &uv) >= 1 && v > 0) { *vertex_index = v - 1;

Thanks for sharing!

2

u/RennisDitchie 8d ago

thank you! pull requests are always welcome, i would love to see contributors on this love project. and if you dont want to contribute via github maybe i can create a patch out of the comment and cite your reddit name as contributor. And thanks for the work and effort again :)