refactor triangle group into a separate class
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Mon, 20 Feb 2012 06:06:14 +0000 (23:06 -0700)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Mon, 20 Feb 2012 06:06:14 +0000 (23:06 -0700)
Makefile
animate.lua
common.h
config.h
model.c [new file with mode: 0644]
model.h [new file with mode: 0644]
raster.c
raster.h
scene.c
scene.u3d

index aaac3cd..4a5844d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@ CFLAGS  = -std=c99 -O2 -g -pg
 CPPFLAGS= -MMD $(IUSE:%=-D%)
 LDLIBS  = -lm
 
-SRCS    = main.c common.c list.c raster.c rbtree.c scene.c
+SRCS    = main.c common.c list.c model.c raster.c rbtree.c scene.c
 OBJS   = $(SRCS:%.c=%.o)
 DEPS   = $(OBJS:%.o=%.d)
 
index 9917cb1..fb4ca82 100755 (executable)
@@ -20,21 +20,18 @@ local frames = 360
 -- Define the code to calculate where the camera is, in world coordinates.
 local eye = function(frame)
     -- just rotate around the center of the scene on the XZ plane
-    local center    = {x = 0, y = 1, z = 0}
+    local center    = vec_new(0, 1, 0)
     local distance  = 4
     local start     = math.pi
     local t = start + 2 * math.pi * frame / frames
-    local v = {
-        x = math.cos(t),
-        y = 0,
-        z = math.sin(t),
-    }
+    local v = vec_new(math.cos(t), 0, math.sin(t))
     return vec_add(vec_scale(v, distance), center)
 end
 
 -- Define the code to calculate where the focal point of the scene is.
 local look = function(frame)
-    return {x = 2, y = 1, z = 2}    -- keep focused on the buddha
+    -- keep the camera focused on the buddha
+    return vec_new(2, 1, 2)
 end
 
 -- Define the actual objects of the scene that will be rendered, in the
@@ -77,12 +74,16 @@ local jobs = 6
 local fmt   = string.format
 local write = function(...) io.write(fmt(...)) end
 
+function vec_new(x, y, z)
+    return {x = x, y = y, z = z}
+end
+
 function vec_add(a, b)
-    return {x = a.x + b.x, y = a.y + b.y, z = a.z + b.z}
+    return vec_new(a.x + b.x, a.y + b.y, a.z + b.z)
 end
 
 function vec_scale(v, s)
-    return {x = v.x * s, y = v.y * s, z = v.z * s}
+    return vec_new(v.x * s, v.y * s, v.z * s)
 end
 
 function render(i)
index 172b8af..537e135 100644 (file)
--- a/common.h
+++ b/common.h
@@ -246,10 +246,11 @@ void ltrim(char *str);
  * Trim white space off of both sides of a string.
  */
 INLINE_MAYBE
-void trim(char *str)
+char* trim(char *str)
 {
     rtrim(str);
     ltrim(str);
+    return str;
 }
 
 
index a1d7fb9..5930439 100644 (file)
--- a/config.h
+++ b/config.h
 #endif
 
 /*
- * PRE_NORMALS
- * If enabled, normals are pre-computed while the triangles are loading.
- * Otherwise, the normals are computed during rasterization.  The behavior of
- * this option is effected by its precise value:
- *   1 Normals are computed per-face, according to the right-hand rule and
- *     counter-clockwise winding of the verticies.
- *   2 Normals are computed per-vertex; the normal for a vertex that is shared
- *     between two or more faces will be the average of the normals of the
- *     faces.  There is a performance penalty for this setting.
- *   3 Same as 2, but the normals will be cached so that they will not need to
- *     be computed the next time the mesh is loaded.
+ * CACHE_GEOMETRY
+ * If enabled, models will be saved to a quick-loading binary format after
+ * they are loaded.  Or, if a cached version of a model is discovered when a
+ * model is requested to be loaded, it is loaded from the cache instead.  This
+ * provides a decent speed improvement in cases where the model would
+ * otherwise be loaded from some kind of text format which would load slowly.
  */
-#if PRE_NORMALS
-#define IF_PRE_NORMALS(X) X
+#if CACHE_GEOMETRY
+#define IF_CACHE_GEOMETRY(X) X
 #else
-#define IF_PRE_NORMALS(X)
+#define IF_CACHE_GEOMETRY(X)
+#endif
+
+/*
+ * CALC_NORMALS
+ * If enabled, per-vertex normals are calculated for models that don't come
+ * with pre-computed normals.  This occurs at loading time.  The normals are
+ * calculated by averaging the true normals of all the faces shared by each
+ * vertex.  Otherwise, only per-face normals are calculated for such models,
+ * also at loading time.
+ */
+#if CALC_NORMALS
+#define IF_CALC_NORMALS(X) X
+#else
+#define IF_CALC_NORMALS(X)
 #endif
 
 /*
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
+
+
diff --git a/model.h b/model.h
new file mode 100644 (file)
index 0000000..23cc3aa
--- /dev/null
+++ b/model.h
@@ -0,0 +1,90 @@
+
+/*
+ * CS5600 University of Utah
+ * Charles McGarvey
+ * mcgarvey@eng.utah.edu
+ */
+
+#ifndef _MODEL_H_
+#define _MODEL_H_
+
+#include "color.h"
+#include "common.h"
+#include "list.h"
+#include "mat.h"
+
+
+#define MODEL_TYPE_RAW (1)
+#define MODEL_TYPE_OBJ (2)
+
+
+/*
+ * A model is a group of geometry and its attributes.
+ */
+typedef struct model model_t;
+
+
+/*
+ * Allocate and load a model from a file.  The file format of the model will
+ * be inferred from the filename extension.
+ */
+model_t* model_alloc(const char* filename);
+
+/*
+ * Allocate and load a model from a file.  You must explicitly pass the file
+ * format of the model data.
+ */
+model_t* model_alloc2(const char* filename, int type);
+
+/*
+ * Destroy a model, freeing up its memory.
+ */
+void model_destroy(model_t* m);
+
+
+/*
+ * Get the model's geometry as a list of triangles.
+ */
+const list_t* model_geometry(const model_t* m);
+
+/*
+ * Get the number of triangles that make up the model.
+ */
+int model_size(const model_t* m);
+
+/*
+ * Get a string representation for the model (i.e. a filename).
+ */
+const char* model_name(const model_t* m);
+
+/*
+ * Get the color of the specular light property of the model.
+ */
+color_t model_specular(const model_t* m);
+
+/*
+ * Get the level of shininess of a model for use in lighting calculations.
+ */
+scal_t model_shininess(const model_t* m);
+
+/*
+ * Get the current transformation of the model.  This can be changed by a call
+ * to model_transform.
+ */
+void model_transformation(const model_t* m, mat_t* transform);
+
+
+/*
+ * Post-multiply a transformation matrix to the internal matrix representing
+ * the model's location and orientation.
+ */
+void model_transform(model_t* m, const mat_t* transform);
+
+/*
+ * Set the material attributes of the model.
+ */
+void model_material(model_t* m, color_t specular, scal_t shininess);
+
+
+#endif // _MODEL_H_
+
index 766e6ea..2be94da 100644 (file)
--- a/raster.c
+++ b/raster.c
@@ -258,6 +258,32 @@ fail:   fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno));
 #undef _CHECK_WRITE
 
 
+void raster_draw_model(raster_t* p, const model_t* model)
+{
+#if VERBOSITY >= 4
+#define PROGRESS_FMT "\033[80D\033[2K  %s\t %9d / %d"
+    int tri;
+#endif
+
+    model_transformation(model, &p->model);
+    p->dirty = true;
+    raster_material(p, model_specular(model), model_shininess(model));
+    IF_RENDER_PROGRESS(tri = 0);
+    for (const list_t* ti = model_geometry(model); ti; ti = ti->link) {
+#if VERBOSITY >= 4
+        if (++tri % 100 == 0) {
+            printf(PROGRESS_FMT, model_name(model), tri, model_size(model));
+            fflush(stdout);
+        }
+#endif
+        raster_draw_tri(p, (tri_t*)ti->val);
+    }
+#if VERBOSITY >= 4
+    printf(PROGRESS_FMT"\n", model_name(model), tri, model_size(model));
+#endif
+}
+
+
 /*
  * See if the triangle is at all visible in the viewport.  Also, minimize the
  * rectangle around the area that includes the triangle.
@@ -366,9 +392,6 @@ void raster_draw_tri(raster_t* p, const tri_t* triangle)
 
 #if LIGHTING >= 1
     tri_t tl = tri_transform(*triangle, p->model);
-#if !PRE_NORMALS
-    tl.a.n = tl.b.n = tl.c.n = vec_normalize(tri_normal(tl));
-#endif
 #endif
 
 #if LIGHTING == 1
index 1d43132..53f7627 100644 (file)
--- a/raster.h
+++ b/raster.h
@@ -11,6 +11,7 @@
 #include "color.h"
 #include "common.h"
 #include "light.h"
+#include "model.h"
 #include "tri.h"
 
 
@@ -102,7 +103,12 @@ int raster_export_bmp(const raster_t* p, const char* filename);
 void raster_clear(raster_t* p, color_t fill);
 
 /*
- * Draw a smooth gradient triangle to the raster.
+ * Draw a model to the raster.
+ */
+void raster_draw_model(raster_t* p, const model_t* model);
+
+/*
+ * Draw a triangle to the raster.
  */
 void raster_draw_tri(raster_t* p, const tri_t* triangle);
 
diff --git a/scene.c b/scene.c
index 326091f..85f15e6 100644 (file)
--- a/scene.c
+++ b/scene.c
  */
 
 #include <errno.h>
-#include <stdio.h>
-#include <string.h>
 
-#include "common.h"
-#include "mat.h"
 #include "list.h"
+#include "model.h"
+#include "mat.h"
 #include "scene.h"
-#include "tri.h"
-
-
-#if PRE_NORMALS >= 2
-
-#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);
-}
+static int _model_set_colors(model_t* m, FILE* file);
+static int _model_add_translate(model_t* m, FILE* file);
+static int _model_add_rotate(model_t* m, FILE* file);
+static int _model_add_scale(model_t* m, FILE* file);
+static int _model_set_material(model_t* m, FILE* file);
+static int _scene_set_ambient(scene_t* s, FILE* file);
+static int _scene_add_light(scene_t* s, FILE* file);
 
-/*
- * 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 // PRE_NORMALS
-
-
-/*
- * A group of triangles and a transformation.
- */
-struct _group
+struct scene
 {
-    list_t*     triangles;
-    mat_t       model;
-    color_t     specular;
-    scal_t      shininess;
-    char*       name;
-    int         count;
+    list_t* models;
+    int     w, h;
+    mat_t   view;
+    mat_t   projection;
+    vec_t   eye;
+    list_t* lights;
+    color_t ambient;
 };
-typedef struct _group _group_t;
-
 
-#define _CHECK_IO(X) if ((X) <= 0) goto fail
 
-/*
- * Try to read the triangle geometry from the cache file.
- */
-static int _group_try_read_cache(const char* filename, list_t** l)
+scene_t* scene_alloc(FILE* file)
 {
-    int     count = 0;
-    char*   cachename  = mem_strcat(".", filename);
-    FILE*   file = fopen(cachename, "rb");
-    if (file == NULL) {
-        goto fail;
+    int w, h;
+    double eyeX, eyeY, eyeZ, spotX, spotY, spotZ, upX, upY, upZ;
+    double fovy, aspect, near, far;
+    if (fscanf(file, "U3 %d %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
+               &w, &h,
+               &eyeX, &eyeY, &eyeZ, &spotX, &spotY, &spotZ, &upX, &upY, &upZ,
+               &fovy, &aspect, &near, &far) != 15) {
+        fprintf(stderr, "Cannot read scene header.\n");
+        return NULL;
     }
 
-    _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);
-    }
+    scene_t* s = (scene_t*)mem_alloc(sizeof(scene_t));
+    s->models = NULL;
+    s->w = w;
+    s->h = h;
+    s->view = MAT_LOOKAT(vec_new( (scal_t)eyeX,  (scal_t)eyeY,  (scal_t)eyeZ),
+                         vec_new((scal_t)spotX, (scal_t)spotY, (scal_t)spotZ),
+                         vec_new(  (scal_t)upX,   (scal_t)upY,   (scal_t)upZ));
+    s->eye = vec_new(eyeX, eyeY, eyeZ);
+    s->projection = MAT_PERSPECTIVE((scal_t)fovy, (scal_t)aspect, (scal_t)near, (scal_t)far);
+    s->lights = NULL;
+    s->ambient = color_new(S(0.05), S(0.05), S(0.05), S(1.0));
 
-fail:
-    mem_free(cachename);
-    if (file != NULL) {
-        fclose(file);
-    }
+    char filename[4096];
+    model_t* m = NULL;
 
-    return count;
+#define _ASSERT_G  \
+if (m == NULL) { \
+    fprintf(stderr, "Unexpected line before group is specified.\n"); \
+    goto fail; \
 }
 
-/*
- * Write the triangle data to the cache.
- */
-static void _group_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;
-    }
+    char type;
+    while (fscanf(file, " %c", &type) == 1) {
+        switch (type) {
+            case 'g':
+                if (fgets(filename, 4096, file) == NULL) {
+                    fprintf(stderr, "Cannot read model filename.\n");
+                }
+                m = model_alloc(trim(filename));
+                if (m == NULL) {
+                    goto fail;
+                }
+                list_push2(&s->models, m, DTOR(model_destroy));
+                break;
 
-    _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));
-    }
+            case 'c':
+                _ASSERT_G;
+                if (_model_set_colors(m, file) != 0) {
+                    goto fail;
+                }
+                break;
 
-fail:
-    mem_free(cachename);
-    if (file != NULL) {
-        fclose(file);
-    }
-}
+            case 't':
+                _ASSERT_G;
+                if (_model_add_translate(m, file) != 0) {
+                    goto fail;
+                }
+                break;
 
-#undef _CHECK_IO
+            case 'r':
+                if (_model_add_rotate(m, file) != 0) {
+                    goto fail;
+                }
+                break;
 
+            case 's':
+                _ASSERT_G;
+                if (_model_add_scale(m, file) != 0) {
+                    goto fail;
+                }
+                break;
 
-/*
- * Destroy a group.
- */
-static void _group_destroy(_group_t* g)
-{
-    mem_free(g->name);
-    list_destroy(&g->triangles);
-    mem_free(g);
-}
+            case 'A':
+                if (_scene_set_ambient(s, file) != 0) {
+                    goto fail;
+                }
+                break;
 
-/*
- * Allocate a group by reading raw triangle coordinates from a file.
- */
-static _group_t* _group_alloc(const char* filename)
-{
-    _group_t* g = (_group_t*)mem_alloc(sizeof(_group_t));
-    g->triangles = NULL;
-    g->model = MAT_IDENTITY;
-    g->name = mem_strdup(filename);
-    g->count = 0;
-    g->specular = COLOR_WHITE;
-    g->shininess = S(64.0);
-
-#if PRE_NORMALS == 3
-    int r = _group_try_read_cache(filename, &g->triangles);
-    if (0 < r) {
-        g->count = r;
-        return g;
-    }
-#endif
+            case 'L':
+                if (_scene_add_light(s, file) != 0) {
+                    goto fail;
+                }
+                break;
 
-    FILE* file = fopen(filename, "r");
-    if (file == NULL) {
-        fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
-        _group_destroy(g);
-        return NULL;
-    }
+            case 'M':
+                _ASSERT_G;
+                if (_model_set_material(m, file) != 0) {
+                    goto fail;
+                }
+                break;
 
-#if PRE_NORMALS >= 2
-    map_t*  m = map_vnorm_alloc();
-#endif
+            case 'X':
+                goto done;
 
-    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(&g->triangles, t, mem_free);
-        ++g->count;
-
-#if PRE_NORMALS == 1
-        vec_t n = vec_normalize(tri_normal(*t));
-        t->a.n = n;
-        t->b.n = n;
-        t->c.n = n;
-#elif PRE_NORMALS >= 2
-        _find_normals_add_triangle(m, t);
-#endif
+            default:
+                fprintf(stderr, "Unknown identifier: %c\n", type);
+        }
     }
+#undef _ASSERT_G
 
-#if PRE_NORMALS >= 2
-    map_vnorm_call(m, _find_normals_average);
-    rbtree_destroy(m);
-#if PRE_NORMALS == 3
-    list_reverse(&g->triangles);
-    _group_write_cache(filename, g->count, g->triangles);
-#endif
-#endif
-
-    fclose(file);
+done:
+    return s;
 
-    if (g->triangles == NULL) {
-        fprintf(stderr, "No triangles read from %s\n", filename);
-        _group_destroy(g);
-        return NULL;
-    }
+fail:
+    scene_destroy(s);
+    return NULL;
+}
 
-    return g;
+void scene_destroy(scene_t* s)
+{
+    list_destroy(&s->models);
+    list_destroy(&s->lights);
+    mem_free(s);
 }
 
 
 /*
  * Set the colors of the triangles in the group as defined in a file.
  */
-static int _group_set_colors(_group_t* g, FILE* file)
+static int _model_set_colors(model_t* m, FILE* file)
 {
     double r1, g1, b1, r2, g2, b2, r3, g3, b3;
     if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
@@ -290,7 +165,7 @@ static int _group_set_colors(_group_t* g, FILE* file)
         return -1;
     }
 
-    for (list_t* i = g->triangles; i; i = i->link) {
+    for (const list_t* i = model_geometry(m); i; i = i->link) {
         tri_t* t = (tri_t*)i->val;
         t->a.c = color_new((colorchan_t)r1, (colorchan_t)g1, (colorchan_t)b1, S(1.0));
         t->b.c = color_new((colorchan_t)r2, (colorchan_t)g2, (colorchan_t)b2, S(1.0));
@@ -302,73 +177,62 @@ static int _group_set_colors(_group_t* g, FILE* file)
 /*
  * Concat a translation matrix to the transformation as defined in a file.
  */
-static int _group_add_translate(_group_t* g, FILE* file)
+static int _model_add_translate(model_t* m, FILE* file)
 {
     double tx, ty, tz;
     if (fscanf(file, " %lf %lf %lf", &tx, &ty, &tz) != 3) {
         fprintf(stderr, "Cannot read translate coordinates from scene.\n");
         return -1;
     }
-    g->model = mat_mult(g->model, MAT_TRANSLATE((scal_t)tx, (scal_t)ty, (scal_t)tz));
+    mat_t transform = MAT_TRANSLATE((scal_t)tx, (scal_t)ty, (scal_t)tz);
+    model_transform(m, &transform);
     return 0;
 }
 
 /*
  * Concat a rotation matrix to the transformation as defined in a file.
  */
-static int _group_add_rotate(_group_t* g, FILE* file)
+static int _model_add_rotate(model_t* m, FILE* file)
 {
     double theta, ax, ay, az;
     if (fscanf(file, " %lf %lf %lf %lf", &theta, &ax, &ay, &az) != 4) {
         fprintf(stderr, "Cannot read rotation angle from scene.\n");
         return -1;
     }
-    g->model = mat_mult(g->model, MAT_ROTATE((scal_t)theta, (scal_t)ax, (scal_t)ay, (scal_t)az));
+    mat_t transform = MAT_ROTATE((scal_t)theta, (scal_t)ax, (scal_t)ay, (scal_t)az);
+    model_transform(m, &transform);
     return 0;
 }
 
 /*
  * Concat a scale matrix to the transformation as defined in a file.
  */
-static int _group_add_scale(_group_t* g, FILE* file)
+static int _model_add_scale(model_t* m, FILE* file)
 {
     double sx, sy, sz;
     if (fscanf(file, " %lf %lf %lf", &sx, &sy, &sz) != 3) {
         fprintf(stderr, "Cannot read scale factors from scene.\n");
         return -1;
     }
-    g->model = mat_mult(g->model, MAT_SCALE((scal_t)sx, (scal_t)sy, (scal_t)sz));
+    mat_t transform = MAT_SCALE((scal_t)sx, (scal_t)sy, (scal_t)sz);
+    model_transform(m, &transform);
     return 0;
 }
 
 /*
  * Set the specular highlight and shininess factor for the group.
  */
-static int _group_set_material(_group_t* g, FILE* file)
+static int _model_set_material(model_t* m, FILE* file)
 {
     double sr, sg, sb, shininess;
     if (fscanf(file, " %lf %lf %lf %lf", &sr, &sg, &sb, &shininess) != 4) {
         fprintf(stderr, "Cannot read material information from scene.\n");
         return -1;
     }
-    g->specular = color_new((scal_t)sr, (scal_t)sg, (scal_t)sb, S(1.0));
-    g->shininess = (scal_t)shininess;
+    model_material(m, color_new((scal_t)sr, (scal_t)sg, (scal_t)sb, S(1.0)), (scal_t)shininess);
     return 0;
 }
 
-
-struct scene
-{
-    list_t* groups;
-    int     w, h;
-    mat_t   view;
-    mat_t   projection;
-    vec_t   eye;
-    list_t* lights;
-    color_t ambient;
-};
-
-
 /*
  * Set the ambient light properties of a scene.
  */
@@ -383,6 +247,9 @@ static int _scene_set_ambient(scene_t* s, FILE* file)
     return 0;
 }
 
+/*
+ * Add a light to the scene.
+ */
 static int _scene_add_light(scene_t* s, FILE* file)
 {
     double lx, ly, lz, dr, dg, db, sr, sg, sb;
@@ -401,126 +268,6 @@ static int _scene_add_light(scene_t* s, FILE* file)
 }
 
 
-scene_t* scene_alloc(FILE* file)
-{
-    int w, h;
-    double eyeX, eyeY, eyeZ, spotX, spotY, spotZ, upX, upY, upZ;
-    double fovy, aspect, near, far;
-    if (fscanf(file, "U3 %d %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
-               &w, &h,
-               &eyeX, &eyeY, &eyeZ, &spotX, &spotY, &spotZ, &upX, &upY, &upZ,
-               &fovy, &aspect, &near, &far) != 15) {
-        fprintf(stderr, "Cannot read scene header.\n");
-        return NULL;
-    }
-
-    scene_t* s = (scene_t*)mem_alloc(sizeof(scene_t));
-    s->groups = NULL;
-    s->w = w;
-    s->h = h;
-    s->view = MAT_LOOKAT(vec_new( (scal_t)eyeX,  (scal_t)eyeY,  (scal_t)eyeZ),
-                         vec_new((scal_t)spotX, (scal_t)spotY, (scal_t)spotZ),
-                         vec_new(  (scal_t)upX,   (scal_t)upY,   (scal_t)upZ));
-    s->eye = vec_new(eyeX, eyeY, eyeZ);
-    s->projection = MAT_PERSPECTIVE((scal_t)fovy, (scal_t)aspect, (scal_t)near, (scal_t)far);
-    s->lights = NULL;
-    s->ambient = color_new(S(0.05), S(0.05), S(0.05), S(1.0));
-
-    char grp_filename[4096];
-    _group_t* g = NULL;
-
-#define _ASSERT_G  \
-if (g == NULL) { \
-    fprintf(stderr, "Unexpected line before group is specified.\n"); \
-    goto fail; \
-}
-
-    char type;
-    while (fscanf(file, " %c", &type) == 1) {
-        switch (type) {
-            case 'g':
-                if (fgets(grp_filename, 4096, file) == NULL) {
-                    fprintf(stderr, "Cannot read raw triangle filename.\n");
-                }
-                trim(grp_filename);
-                g = _group_alloc(grp_filename);
-                if (g == NULL) {
-                    goto fail;
-                }
-                list_push2(&s->groups, g, DTOR(_group_destroy));
-                break;
-
-            case 'c':
-                _ASSERT_G;
-                if (_group_set_colors(g, file) != 0) {
-                    goto fail;
-                }
-                break;
-
-            case 't':
-                _ASSERT_G;
-                if (_group_add_translate(g, file) != 0) {
-                    goto fail;
-                }
-                break;
-
-            case 'r':
-                if (_group_add_rotate(g, file) != 0) {
-                    goto fail;
-                }
-                break;
-
-            case 's':
-                _ASSERT_G;
-                if (_group_add_scale(g, file) != 0) {
-                    goto fail;
-                }
-                break;
-
-            case 'A':
-                if (_scene_set_ambient(s, file) != 0) {
-                    goto fail;
-                }
-                break;
-
-            case 'L':
-                if (_scene_add_light(s, file) != 0) {
-                    goto fail;
-                }
-                break;
-
-            case 'M':
-                _ASSERT_G;
-                if (_group_set_material(g, file) != 0) {
-                    goto fail;
-                }
-                break;
-
-            case 'X':
-                goto done;
-
-            default:
-                fprintf(stderr, "Unknown identifier: %c\n", type);
-        }
-    }
-#undef _ASSERT_G
-
-done:
-    return s;
-
-fail:
-    scene_destroy(s);
-    return NULL;
-}
-
-void scene_destroy(scene_t* s)
-{
-    list_destroy(&s->groups);
-    list_destroy(&s->lights);
-    mem_free(s);
-}
-
-
 raster_t* scene_render(scene_t* s)
 {
 #if VERBOSITY >= 3
@@ -537,35 +284,18 @@ raster_t* scene_render(scene_t* s)
         raster_light(p, *(light_t*)i->val);
     }
 
-#if VERBOSITY >= 4
-#define PROGRESS_FMT "\033[80D\033[2K  %s\t %9d / %d"
-    int tri;
-    printf("render scene:\n");
+#if VERBOSITY >= 3
+    printf("rendering scene...\n");
 #endif
 
-    for (list_t* gi = s->groups; gi; gi = gi->link) {
-        _group_t* g = (_group_t*)gi->val;
-        raster_model(p, &g->model);
-        raster_material(p, g->specular, g->shininess);
-        IF_RENDER_PROGRESS(tri = 0);
-        for (list_t* ti = g->triangles; ti; ti = ti->link) {
-#if VERBOSITY >= 4
-            if (++tri % 100 == 0) {
-                printf(PROGRESS_FMT, g->name, tri, g->count);
-                fflush(stdout);
-            }
-#endif
-            raster_draw_tri(p, (tri_t*)ti->val);
-        }
-#if VERBOSITY >= 4
-        printf(PROGRESS_FMT"\n", g->name, tri, g->count);
-#endif
+    for (list_t* gi = s->models; gi; gi = gi->link) {
+        model_t* m = (model_t*)gi->val;
+        raster_draw_model(p, m);
     }
-    IF_RENDER_PROGRESS(printf("render complete!\n"));
 
 #if VERBOSITY >= 3
     long dt = timer_stop();
-    printf("time\t%.3fms\n", (float)dt / 1000.0f);
+    printf("render complete!\ntime\t%.3fms\n", (float)dt / 1000.0f);
 #endif
 
     return p;
index 1b9258d..0c27afc 100644 (file)
--- a/scene.u3d
+++ b/scene.u3d
@@ -4,6 +4,7 @@ U3
 0.0 0.0 0.0
 0.0 1.0 0.0
 1.57 1.0 0.1 1000.0
+L 0 99999 0 1 1 1 1 1 1
 g teapot.raw
 c 0.1 0.8 0.3 0.2 0.5 0.1 0.2 0.6 0.2
 t 0.0 0.0 0.0
This page took 0.067715 seconds and 4 git commands to generate.