-#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 model;
- color_t specular;
- scal_t shininess;
- 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)
-{
- 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 _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;
- }
-
- _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
-
-
-/*
- * Destroy a group.
- */
-static void _group_destroy(_group_t* g)
-{
- mem_free(g->name);
- list_destroy(&g->triangles);
- mem_free(g);
-}
-
-/*
- * 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
-
- FILE* file = fopen(filename, "r");
- if (file == NULL) {
- fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
- _group_destroy(g);
- return NULL;
- }
-
-#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),
- 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);
-
- if (g->triangles == NULL) {
- fprintf(stderr, "No triangles read from %s\n", filename);
- _group_destroy(g);
- return NULL;
- }
-
- return g;
-}
-
-
-/*
- * Set the colors of the triangles in the group as defined in a file.
- */
-static int _group_set_colors(_group_t* g, FILE* file)
-{
- double r1, g1, b1, r2, g2, b2, r3, g3, b3;
- if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
- &r1, &g1, &b1, &r2, &g2, &b2, &r3, &g3, &b3) != 9) {
- fprintf(stderr, "Cannot read color values from scene.\n");
- return -1;
- }
-
- for (list_t* i = g->triangles; 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));
- t->c.c = color_new((colorchan_t)r3, (colorchan_t)g3, (colorchan_t)b3, S(1.0));
- }
- return 0;
-}
-
-/*
- * Concat a translation matrix to the transformation as defined in a file.
- */
-static int _group_add_translate(_group_t* g, 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));
- return 0;
-}
-
-/*
- * Concat a rotation matrix to the transformation as defined in a file.
- */
-static int _group_add_rotate(_group_t* g, 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));
- return 0;
-}
-
-/*
- * Concat a scale matrix to the transformation as defined in a file.
- */
-static int _group_add_scale(_group_t* g, 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));
- return 0;
-}
-
-/*
- * Set the specular highlight and shininess factor for the group.
- */
-static int _group_set_material(_group_t* g, 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;
- return 0;
-}