]> Dogcows Code - chaz/rasterize/blobdiff - model.c
refactor triangle group into a separate class
[chaz/rasterize] / model.c
diff --git a/model.c b/model.c
new file mode 100644 (file)
index 0000000..c136ae9
--- /dev/null
+++ b/model.c
@@ -0,0 +1,355 @@
+
+/*
+ * CS5600 University of Utah
+ * Charles McGarvey
+ * mcgarvey@eng.utah.edu
+ */
+
+#include <errno.h>
+
+#include "tri.h"
+#include "model.h"
+
+
+struct model
+{
+    list_t*     triangles;
+    mat_t       model;
+    color_t     specular;
+    scal_t      shininess;
+    char*       name;
+    int         count;
+};
+
+
+static int _model_read_raw(model_t* m, const char* filename);
+static int _model_read_obj(model_t* m, const char* filename);
+
+static int _model_try_read_cache(const char* filename, list_t** l);
+static void _model_write_cache(const char* filename, int count, list_t* l);
+    
+
+model_t* model_alloc(const char* filename)
+{
+    int     type = 0;
+    char*   ext = strrchr(filename, '.');
+    if (ext == NULL) {
+        goto fail;
+    }
+    ++ext;
+
+    if (strcmp(ext, "raw") == 0) {
+        return model_alloc2(filename, MODEL_TYPE_RAW);
+    }
+    if (strcmp(ext, "obj") == 0) {
+        return model_alloc2(filename, MODEL_TYPE_OBJ);
+    }
+
+fail:
+    fprintf(stderr, "Unknown file type: %s", filename);
+    return NULL;
+}
+
+model_t* model_alloc2(const char* filename, int type)
+{
+    model_t* m = (model_t*)mem_alloc(sizeof(model_t));
+    m->triangles = NULL;
+    m->model = MAT_IDENTITY;
+    m->specular = COLOR_WHITE;
+    m->shininess = S(64.0);
+    m->name = mem_strdup(filename);
+    m->count = 0;
+
+#if CACHE_GEOMETRY
+    int count = _model_try_read_cache(filename, &m->triangles);
+    if (0 < count) {
+        return m;
+    }
+#endif
+
+    int load;
+    switch (type) {
+    case MODEL_TYPE_RAW:
+        load = _model_read_raw(m, filename);
+        break;
+    case MODEL_TYPE_OBJ:
+        load = _model_read_obj(m, filename);
+        break;
+    }
+    if (load != 0) {
+        model_destroy(m);
+        return NULL;
+    }
+
+#if CACHE_GEOMETRY
+    _model_write_cache(filename, m->count, m->triangles);
+#endif
+
+    return m;
+}
+
+void model_destroy(model_t* m)
+{
+    list_destroy(&m->triangles);
+    mem_free(m->name);
+    mem_free(m);
+}
+
+
+const list_t* model_geometry(const model_t* m)
+{
+    return m->triangles;
+}
+
+int model_size(const model_t* m)
+{
+    return m->count;
+}
+
+const char* model_name(const model_t* m)
+{
+    return m->name;
+}
+
+color_t model_specular(const model_t* m)
+{
+    return m->specular;
+}
+
+scal_t model_shininess(const model_t* m)
+{
+    return m->shininess;
+}
+
+void model_transformation(const model_t* m, mat_t* transform)
+{
+    *transform = m->model;
+}
+
+
+void model_transform(model_t* m, const mat_t* transform)
+{
+    m->model = mat_mult(m->model, *transform);
+}
+
+void model_material(model_t* m, color_t specular, scal_t shininess)
+{
+    m->specular  = specular;
+    m->shininess = shininess;
+}
+
+
+#if CALC_NORMALS
+
+#include "map.h"
+DECLARE_AND_DEFINE_MAP_TYPE3(vec_t, list_t*, vnorm, vec_compare(*a, *b));
+
+
+/*
+ * Associate a triangle with one of its vertices.
+ */
+static void _find_normals_add_vertex(map_t* m, vec_t v, tri_t* t)
+{
+    list_t** l = map_vnorm_search(m, v);
+    if (l == NULL) {
+        map_vnorm_data_t* d = map_vnorm_insert(m, v, NULL);
+        l = &d->val;
+    }
+    list_push(l, t);
+}
+
+/*
+ * Associate a triangle with all of its vertices.
+ */
+static void _find_normals_add_triangle(map_t* m, tri_t* t)
+{
+    _find_normals_add_vertex(m, t->a.v, t);
+    _find_normals_add_vertex(m, t->b.v, t);
+    _find_normals_add_vertex(m, t->c.v, t);
+}
+
+/*
+ * Calculate an averaged normal from a list of triangles that share a common
+ * vertex.
+ */
+static void _find_normals_average(const vec_t* v, list_t** l)
+{
+    // first, compute the average normal
+    vec_t n = VEC_ZERO;
+    for (list_t* i = *l; i; i = i->link) {
+        tri_t* t = (tri_t*)i->val;
+        n = vec_add(n, tri_normal(*t));
+    }
+    n = vec_normalize(n);
+
+    // set the normal on each triangle's vertex that is shared
+    while (*l) {
+        tri_t* t = (tri_t*)(*l)->val;
+        if (vec_isequal(*v, t->a.v)) {
+            t->a.n = n;
+        }
+        else if (vec_isequal(*v, t->b.v)) {
+            t->b.n = n;
+        }
+        else if (vec_isequal(*v, t->c.v)) {
+            t->c.n = n;
+        }
+        list_pop(l);
+    }
+}
+
+#endif // CALC_NORMALS
+
+
+static int _model_read_raw(model_t* m, const char* filename)
+{
+    FILE* file = fopen(filename, "r");
+    if (file == NULL) {
+        fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
+        return -1;
+    }
+
+#if CALC_NORMALS
+    map_t* nlookup = map_vnorm_alloc();
+#endif
+
+    double x1, y1, z1, x2, y2, z2, x3, y3, z3;
+    while (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
+                  &x1, &y1, &z1, &x2, &y2, &z2, &x3, &y3, &z3) == 9) {
+        tri_t* t = tri_alloc(
+            vert_new2((scal_t)x1, (scal_t)y1, (scal_t)z1),
+            vert_new2((scal_t)x2, (scal_t)y2, (scal_t)z2),
+            vert_new2((scal_t)x3, (scal_t)y3, (scal_t)z3)
+        );
+        list_push2(&m->triangles, t, mem_free);
+        ++m->count;
+
+#if CALC_NORMALS
+        _find_normals_add_triangle(nlookup, t);
+#else
+        vec_t n = vec_normalize(tri_normal(*t));
+        t->a.n = n;
+        t->b.n = n;
+        t->c.n = n;
+#endif
+    }
+
+#if CALC_NORMALS
+    map_vnorm_call(nlookup, _find_normals_average);
+    rbtree_destroy(nlookup);
+#endif
+
+    fclose(file);
+    if (m->triangles == NULL) {
+        fprintf(stderr, "No triangles read from %s\n", filename);
+        return -1;
+    }
+    return 0;
+}
+
+static int _model_read_obj(model_t* m, const char* filename)
+{
+}
+
+
+#define _CHECK_IO(X) if ((X) <= 0) goto fail
+
+/*
+ * Try to read the triangle geometry from the cache file.
+ */
+static int _model_try_read_cache(const char* filename, list_t** l)
+{
+    int     count = 0;
+    char*   cachename  = mem_strcat(".", filename);
+    FILE*   file = fopen(cachename, "rb");
+    if (file == NULL) {
+        goto fail;
+    }
+
+    _CHECK_IO(fread(&count, sizeof(count), 1, file));
+
+    float x1, y1, z1, x2, y2, z2, x3, y3, z3;
+    for (int i = 0; i < count; ++i) {
+        _CHECK_IO(fread(&x1, sizeof(float), 1, file));
+        _CHECK_IO(fread(&y1, sizeof(float), 1, file));
+        _CHECK_IO(fread(&z1, sizeof(float), 1, file));
+        _CHECK_IO(fread(&x2, sizeof(float), 1, file));
+        _CHECK_IO(fread(&y2, sizeof(float), 1, file));
+        _CHECK_IO(fread(&z2, sizeof(float), 1, file));
+        _CHECK_IO(fread(&x3, sizeof(float), 1, file));
+        _CHECK_IO(fread(&y3, sizeof(float), 1, file));
+        _CHECK_IO(fread(&z3, sizeof(float), 1, file));
+        tri_t* t = tri_alloc(
+            vert_new2((scal_t)x1, (scal_t)y1, (scal_t)z1),
+            vert_new2((scal_t)x2, (scal_t)y2, (scal_t)z2),
+            vert_new2((scal_t)x3, (scal_t)y3, (scal_t)z3)
+        );
+        list_push2(l, t, mem_free);
+        _CHECK_IO(fread(&x1, sizeof(float), 1, file));
+        _CHECK_IO(fread(&y1, sizeof(float), 1, file));
+        _CHECK_IO(fread(&z1, sizeof(float), 1, file));
+        _CHECK_IO(fread(&x2, sizeof(float), 1, file));
+        _CHECK_IO(fread(&y2, sizeof(float), 1, file));
+        _CHECK_IO(fread(&z2, sizeof(float), 1, file));
+        _CHECK_IO(fread(&x3, sizeof(float), 1, file));
+        _CHECK_IO(fread(&y3, sizeof(float), 1, file));
+        _CHECK_IO(fread(&z3, sizeof(float), 1, file));
+        t->a.n = vec_new((scal_t)x1, (scal_t)y1, (scal_t)z1);
+        t->b.n = vec_new((scal_t)x2, (scal_t)y2, (scal_t)z2);
+        t->c.n = vec_new((scal_t)x3, (scal_t)y3, (scal_t)z3);
+    }
+
+fail:
+    mem_free(cachename);
+    if (file != NULL) {
+        fclose(file);
+    }
+
+    return count;
+}
+
+/*
+ * Write the triangle data to the cache.
+ */
+static void _model_write_cache(const char* filename, int count, list_t* l)
+{
+    char*   cachename  = mem_strcat(".", filename);
+    FILE*   file = fopen(cachename, "wb");
+    if (file == NULL) {
+        fprintf(stderr, "Cannot write %s: %s\n", cachename, strerror(errno));
+        goto fail;
+    }
+
+    _CHECK_IO(fwrite(&count, sizeof(count), 1, file));
+    for (list_t* i = l; i; i = i->link) {
+        tri_t* t = (tri_t*)i->val;
+        _CHECK_IO(fwrite(&t->a.v.x, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->a.v.y, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->a.v.z, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->b.v.x, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->b.v.y, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->b.v.z, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->c.v.x, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->c.v.y, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->c.v.z, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->a.n.x, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->a.n.y, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->a.n.z, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->b.n.x, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->b.n.y, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->b.n.z, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->c.n.x, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->c.n.y, sizeof(float), 1, file));
+        _CHECK_IO(fwrite(&t->c.n.z, sizeof(float), 1, file));
+    }
+
+fail:
+    mem_free(cachename);
+    if (file != NULL) {
+        fclose(file);
+    }
+}
+
+#undef _CHECK_IO
+
+
This page took 0.025915 seconds and 4 git commands to generate.