Browse Source

Add viewobj

rexim 2 years ago
parent
commit
51e501a814
1 changed files with 282 additions and 0 deletions
  1. 282 0
      viewobj.c

+ 282 - 0
viewobj.c

@@ -0,0 +1,282 @@
+#include <stdio.h>
+#include <errno.h>
+#include <float.h>
+#include <limits.h>
+
+#define OLIVEC_IMPLEMENTATION
+#include "olive.c"
+
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "stb_image_write.h"
+
+#define ARENA_IMPLEMENTATION
+#include "arena.h"
+
+#define SV_IMPLEMENTATION
+#include "sv.h"
+
+static Arena default_arena = {0};
+static Arena *context_arena = &default_arena;
+
+static void *context_alloc(size_t size)
+{
+    assert(context_arena);
+    return arena_alloc(context_arena, size);
+}
+
+static void *context_realloc(void *oldp, size_t oldsz, size_t newsz)
+{
+    if (newsz <= oldsz) return oldp;
+    return memcpy(context_alloc(newsz), oldp, oldsz);
+}
+
+#define FACTOR 120
+#define WIDTH (16*FACTOR)
+#define HEIGHT (9*FACTOR)
+
+uint32_t pixels[WIDTH*HEIGHT] = {0};
+float zbuffer[WIDTH*HEIGHT] = {0};
+
+#define return_defer(value) do { result = (value); goto defer; } while (0)
+typedef int Errno;
+
+Errno read_entire_file(const char *file_path, char **buffer, size_t *buffer_size)
+{
+    Errno result = 0;
+    FILE *f = NULL;
+
+    f = fopen(file_path, "rb");
+    if (f == NULL) return_defer(errno);
+
+    if (fseek(f, 0, SEEK_END) < 0) return_defer(errno);
+    long m = ftell(f);
+    if (m < 0) return_defer(errno);
+    if (fseek(f, 0, SEEK_SET) < 0) return_defer(errno);
+
+    *buffer_size = m;
+    *buffer = context_alloc(*buffer_size);
+
+    fread(*buffer, *buffer_size, 1, f);
+    if (ferror(f)) return_defer(errno);
+
+defer:
+    if (f) fclose(f);
+    return result;
+}
+
+typedef struct {
+    float x, y;
+} Vector2;
+
+Vector2 make_vector2(float x, float y)
+{
+    Vector2 v2;
+    v2.x = x;
+    v2.y = y;
+    return v2;
+}
+
+typedef struct {
+    float x, y, z;
+} Vector3;
+
+Vector3 make_vector3(float x, float y, float z)
+{
+    Vector3 v3;
+    v3.x = x;
+    v3.y = y;
+    v3.z = z;
+    return v3;
+}
+
+Vector2 project_3d_2d(Vector3 v3)
+{
+    return make_vector2(v3.x / v3.z, v3.y / v3.z);
+}
+
+Vector2 project_2d_scr(Vector2 v2)
+{
+    return make_vector2((v2.x + 1)/2*WIDTH, (1 - (v2.y + 1)/2)*HEIGHT);
+}
+
+typedef struct {
+    Vector3 *items;
+    size_t capacity;
+    size_t count;
+} Vertices;
+
+typedef struct {
+    int a, b, c;
+} Face;
+
+Face make_face(int a, int b, int c)
+{
+    Face f = {
+        .a = a,
+        .b = b,
+        .c = c,
+    };
+    return f;
+}
+
+typedef struct {
+    Face *items;
+    size_t capacity;
+    size_t count;
+} Faces;
+
+#define DA_INIT_CAPACITY 8192
+#define DA_REALLOC context_realloc
+#define da_append(da, item)                                                 \
+    do {                                                                    \
+        if ((da)->count >= (da)->capacity) {                                \
+            size_t new_capacity = (da)->capacity*2;                         \
+            if (new_capacity == 0) {                                        \
+                new_capacity = DA_INIT_CAPACITY;                            \
+            }                                                               \
+                                                                            \
+            (da)->items = DA_REALLOC((da)->items,                           \
+                                     (da)->capacity*sizeof((da)->items[0]), \
+                                     new_capacity*sizeof((da)->items[0]));  \
+            (da)->capacity = new_capacity;                                  \
+        }                                                                   \
+                                                                            \
+        (da)->items[(da)->count++] = (item);                                \
+    } while (0)
+
+#define UNUSED(x) (void)(x)
+
+Vector3 remap_teapot(Vector3 v, float lx, float hx, float ly, float hy, float lz, float hz)
+{
+    v.z = (v.z - lz)/(hz - lz) + 1;
+    v.x = (v.x - lx)/(hx - lx)*2 - 1;
+    v.y = (v.y - ly)/(hy - ly)*2 - 1;
+    return v;
+}
+
+int main(void)
+{
+    int result = 0;
+
+    const char *obj_file_path = "teapot.obj";
+    char *buffer;
+    size_t buffer_size;
+    Errno err = read_entire_file(obj_file_path, &buffer, &buffer_size);
+    if (err != 0) {
+        fprintf(stderr, "ERROR: could not read file %s: %s\n", obj_file_path, strerror(errno));
+        return_defer(1);
+    }
+
+    String_View content = sv_from_parts(buffer, buffer_size);
+    Vertices vertices = {0};
+    Faces faces = {0};
+    float lx = FLT_MAX, hx = FLT_MIN;
+    float ly = FLT_MAX, hy = FLT_MIN;
+    float lz = FLT_MAX, hz = FLT_MIN;
+    int lf = INT_MAX, hf = INT_MIN;
+    for (size_t line_number = 0; content.count > 0; ++line_number) {
+        String_View line = sv_trim_left(sv_chop_by_delim(&content, '\n'));
+        if (line.count > 0) {
+            String_View kind = sv_chop_by_delim(&line, ' ');
+            if (sv_eq(kind, SV("v"))) {
+                char *endptr;
+
+                line = sv_trim_left(line);
+                float x = strtof(line.data, &endptr);
+                line.data = endptr;
+                if (lx > x) lx = x;
+                if (hx < x) hx = x;
+
+                line = sv_trim_left(line);
+                float y = strtof(line.data, &endptr);
+                line.data = endptr;
+                if (ly > y) ly = y;
+                if (hy < y) hy = y;
+
+                line = sv_trim_left(line);
+                float z = strtof(line.data, &endptr);
+                line.data = endptr;
+                if (lz > z) lz = z;
+                if (hz < z) hz = z;
+
+                da_append(&vertices, make_vector3(x, y, z));
+            } else if (sv_eq(kind, SV("f"))) {
+                char *endptr;
+
+                line = sv_trim_left(line);
+                int a = strtol(line.data, &endptr, 10);
+                line.data = endptr;
+                if (lf > a) lf = a;
+                if (hf < a) hf = a;
+
+                line = sv_trim_left(line);
+                int b = strtol(line.data, &endptr, 10);
+                line.data = endptr;
+                if (lf > b) lf = b;
+                if (hf < b) hf = b;
+
+                line = sv_trim_left(line);
+                int c = strtol(line.data, &endptr, 10);
+                line.data = endptr;
+                if (lf > c) lf = c;
+                if (hf < c) hf = c;
+
+                da_append(&faces, make_face(a, b, c));
+            } else {
+                fprintf(stderr, "%s:%zu: unknown kind of entry `"SV_Fmt"`\n", obj_file_path, line_number, SV_Arg(kind));
+                return_defer(1);
+            }
+        }
+    }
+    printf("Vertices: %zu (x: %f..%f, y: %f..%f, z: %f..%f)\n", vertices.count, lx, hx, ly, hy, lz, hz);
+    printf("Faces:    %zu (index: %d..%d)\n", faces.count, lf, hf);
+
+    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);
+    olivec_fill(oc, 0xFF202020);
+
+    for (size_t i = 0; i < faces.count; ++i) {
+        Face f = faces.items[i];
+        Vector3 v1 = remap_teapot(vertices.items[f.a - 1], lx, hx, ly, hy, lz, hz);
+        Vector3 v2 = remap_teapot(vertices.items[f.b - 1], lx, hx, ly, hy, lz, hz);
+        Vector3 v3 = remap_teapot(vertices.items[f.c - 1], lx, hx, ly, hy, lz, hz);
+        Vector2 p1 = project_2d_scr(project_3d_2d(v1));
+        Vector2 p2 = project_2d_scr(project_3d_2d(v2));
+        Vector2 p3 = project_2d_scr(project_3d_2d(v3));
+        Olivec_Tri tri = olivec_tri_new(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
+        int y1, y2;
+        if (olivec_tri_vert(&tri, HEIGHT, &y1, &y2)) {
+            for (int y = y1; y <= y2; ++y) {
+                int x1, x2;
+                if (olivec_tri_horz(&tri, WIDTH, y, &x1, &x2)) {
+                    for (int x = x1; x <= x2; ++x) {
+                        int u1, u2, det;
+                        olivec_tri_bary(&tri, x, y, &u1, &u2, &det);
+                        float z = 1/v1.z*u1/det + 1/v2.z*u2/det + 1/v3.z*(det - u1 - u2)/det;
+                        if (z > zbuffer[y*WIDTH + x]) {
+                            zbuffer[y*WIDTH + x] = z;
+                            OLIVEC_PIXEL(oc, x, y) = mix_colors3(0xFF1818FF, 0xFF18FF18, 0xFFFF1818, u1, u2, det - u1 - u2, det);
+
+                            z = 1.0f/z;
+                            if (z >= 1.0) {
+                                z -= 1.0;
+                                uint32_t v = z*255;
+                                if (v > 255) v = 255;
+                                olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), (v<<(3*8)));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    const char *file_path = "tri.png";
+    if (!stbi_write_png(file_path, WIDTH, HEIGHT, 4, pixels, sizeof(uint32_t)*WIDTH)) {
+        fprintf(stderr, "ERROR: could not write file %s\n", file_path);
+        return_defer(1);
+    }
+
+defer:
+    arena_free(&default_arena);
+    return result;
+}