]> Dogcows Code - chaz/rasterize/blobdiff - scene.c
rotate script now more intuitive, maybe
[chaz/rasterize] / scene.c
diff --git a/scene.c b/scene.c
index 449dab661774e9770dd4ac18049740dc7be3ea97..3e0010fa399cb4c016a365e88cfbd700785c845d 100644 (file)
--- a/scene.c
+++ b/scene.c
 #include "tri.h"
 
 
+#if RENDER_PROGRESS
+#define IF_RENDER_PROGRESS(X) X
+#else
+#define IF_RENDER_PROGRESS(X)
+#endif
+
+#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);
+}
+
+/*
+ * 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
 {
-    list_t* triangles;
-    mat_t   modelview;
+    list_t*     triangles;
+    mat_t       model;
+    char*       name;
+    int         count;
 };
 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)
+{
+    char*   cachename  = mem_strcat(".", filename);
+    FILE*   file = fopen(cachename, "rb");
+    if (file == NULL) {
+        return 0;
+    }
+
+    int count = 0;
+    _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)
+        );
+        _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);
+
+        list_push2(l, t, mem_free);
+    }
+
+fail:
+    fclose(file);
+    mem_free(cachename);
+
+    return count;
+}
+
+/*
+ * 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));
+        return;
+    }
+
+    _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);
+}
+
+#undef _CHECK_IO
+
+
 /*
  * 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;
+
+#if PRE_NORMALS == 3
+    int r = _group_try_read_cache(filename, &g->triangles);
+    if (0 < r) {
+        g->count = r;
+        return g;
+    }
+#endif
+
     FILE* file = fopen(filename, "r");
     if (file == NULL) {
         fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
         return NULL;
     }
 
-    _group_t* g = (_group_t*)mem_alloc(sizeof(_group_t));
-    g->triangles = NULL;
-    g->modelview = MAT_IDENTITY;
+#if PRE_NORMALS >= 2
+    map_t*  m = 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, COLOR_WHITE),
-            vert_new2((scal_t)x2, (scal_t)y2, (scal_t)z2, COLOR_WHITE),
-            vert_new2((scal_t)x3, (scal_t)y3, (scal_t)z3, COLOR_WHITE)
+            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
     }
+
+#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);
 
@@ -69,6 +269,7 @@ static _group_t* _group_alloc(const char* filename)
  */
 static void _group_destroy(_group_t* g)
 {
+    mem_free(g->name);
     list_destroy(g->triangles);
     mem_free(g);
 }
@@ -100,13 +301,12 @@ static int _group_set_colors(_group_t* g, FILE* file)
  */
 static int _group_add_translate(_group_t* g, FILE* file)
 {
-    double tx, ty;
-    if (fscanf(file, " %lf %lf", &tx, &ty) != 2) {
+    double tx, ty, tz;
+    if (fscanf(file, " %lf %lf %lf", &tx, &ty, &tz) != 3) {
         fprintf(stderr, "Cannot read translate coordinates from scene file.\n");
         return -1;
     }
-
-    g->modelview = mat_mult(g->modelview, MAT_TRANSLATE((scal_t)tx, (scal_t)ty, S(1.0)));
+    g->model = mat_mult(g->model, MAT_TRANSLATE((scal_t)tx, (scal_t)ty, (scal_t)tz));
     return 0;
 }
 
@@ -115,13 +315,12 @@ static int _group_add_translate(_group_t* g, FILE* file)
  */
 static int _group_add_rotate(_group_t* g, FILE* file)
 {
-    double theta;
-    if (fscanf(file, " %lf", &theta) != 1) {
+    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 file.\n");
         return -1;
     }
-
-    g->modelview = mat_mult(g->modelview, MAT_ROTATE_Z((scal_t)theta));
+    g->model = mat_mult(g->model, MAT_ROTATE((scal_t)theta, (scal_t)ax, (scal_t)ay, (scal_t)az));
     return 0;
 }
 
@@ -130,13 +329,12 @@ static int _group_add_rotate(_group_t* g, FILE* file)
  */
 static int _group_add_scale(_group_t* g, FILE* file)
 {
-    double sx, sy;
-    if (fscanf(file, " %lf %lf", &sx, &sy) != 2) {
+    double sx, sy, sz;
+    if (fscanf(file, " %lf %lf %lf", &sx, &sy, &sz) != 3) {
         fprintf(stderr, "Cannot read scale factors from scene file.\n");
         return -1;
     }
-
-    g->modelview = mat_mult(g->modelview, MAT_SCALE((scal_t)sx, (scal_t)sy, S(1.0)));
+    g->model = mat_mult(g->model, MAT_SCALE((scal_t)sx, (scal_t)sy, (scal_t)sz));
     return 0;
 }
 
@@ -144,36 +342,54 @@ static int _group_add_scale(_group_t* g, FILE* file)
 struct scene
 {
     list_t* groups;
-    int     w;
-    int     h;
+    list_t* lights;
+    int     w, h;
+    mat_t   view;
     mat_t   projection;
+    vec_t   eye;
 };
 
 scene_t* scene_alloc(const char* filename)
 {
-    scene_t* s = (scene_t*)mem_alloc(sizeof(scene_t));
-    s->groups = NULL;
-
-    FILE* file = fopen(filename, "r");
+    FILE* file;
+    if (strcmp(filename, "-") == 0) {
+        file = stdin;
+    }
+    else {
+        file = fopen(filename, "r");
+    }
+    if (file == NULL) {
+        fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
+        return NULL;
+    }
 
     int w, h;
-    double minX, minY, maxX, maxY;
-    if (fscanf(file, "U2 %d %d %lf %lf %lf %lf",
-               &w, &h, &minX, &minY, &maxX, &maxY) != 6) {
+    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 file header.\n");
         return NULL;
     }
 
+    scene_t* s = (scene_t*)mem_alloc(sizeof(scene_t));
+    s->groups = NULL;
     s->w = w;
     s->h = h;
-    s->projection = MAT_ORTHO((scal_t)minX, (scal_t)maxX, (scal_t)minY, (scal_t)maxY);
+    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);
 
     char grp_filename[4096];
     _group_t* g = NULL;
 
 #define _ASSERT_G  \
 if (g == NULL) { \
-    fprintf(stderr, "Unexpected line before group is loaded.\n"); \
+    fprintf(stderr, "Unexpected line before group is specified.\n"); \
     goto fail; \
 }
 
@@ -223,7 +439,6 @@ if (g == NULL) { \
                 fprintf(stderr, "Unknown identifier: %c\n", type);
         }
     }
-    list_reverse(&s->groups);
 
 #undef _ASSERT_G
 
@@ -242,19 +457,50 @@ void scene_destroy(scene_t* s)
 }
 
 
-pixmap_t* scene_render(scene_t* s)
+raster_t* scene_render(scene_t* s)
 {
-    pixmap_t* pix = pixmap_alloc(s->w, s->h, COLOR_BLACK);
-    pixmap_projection(pix, &s->projection);
+#if RENDER_TIMER
+    timer_start();
+#endif
+
+    raster_t* p = raster_alloc(s->w, s->h, COLOR_BLACK);
+    raster_view(p, &s->view);
+    raster_projection(p, &s->projection);
+    raster_eye(p, s->eye);
+
+    raster_light(p, light_new(COLOR_WHITE, vec_new(S(5.0), S(3.0), S(6.0))));
+    raster_light(p, light_new(COLOR_MAGENTA, vec_new(S(-2.0), S(2.0), S(-2.0))));
+
+#if RENDER_PROGRESS
+#define PROGRESS_FMT "\033[80D\033[2K  %s\t %9d / %d"
+    int tri;
+    printf("render scene:\n");
+#endif
 
     for (list_t* gi = s->groups; gi; gi = gi->link) {
         _group_t* g = (_group_t*)gi->val;
-        pixmap_modelview(pix, &g->modelview);
+        raster_model(p, &g->model);
+        IF_RENDER_PROGRESS(tri = 0);
         for (list_t* ti = g->triangles; ti; ti = ti->link) {
-            pixmap_draw_tri(pix, (tri_t*)ti->val);
+#if RENDER_PROGRESS
+            if (++tri % 100 == 0) {
+                printf(PROGRESS_FMT, g->name, tri, g->count);
+                fflush(stdout);
+            }
+#endif
+            raster_draw_tri(p, (tri_t*)ti->val);
         }
+#if RENDER_PROGRESS
+        printf(PROGRESS_FMT"\n", g->name, tri, g->count);
+#endif
     }
+    IF_RENDER_PROGRESS(printf("render complete!\n"));
+
+#if RENDER_TIMER
+    long dt = timer_stop();
+    printf("time\t%.3fms\n", (float)dt / 1000.0f);
+#endif
 
-    return pix;
+    return p;
 }
 
This page took 0.025703 seconds and 4 git commands to generate.