/* * CS5600 University of Utah * Charles McGarvey * mcgarvey@eng.utah.edu */ #include #include #include #include "common.h" #include "mat.h" #include "list.h" #include "scene.h" #include "tri.h" /* * A group of triangles and a transformation. */ struct _group { list_t* triangles; mat_t modelview; }; typedef struct _group _group_t; /* * Allocate a group by reading raw triangle coordinates from a file. */ static _group_t* _group_alloc(const char* filename) { 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; 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) ); list_push2(&g->triangles, t, mem_free); } list_reverse(&g->triangles); fclose(file); if (g->triangles == NULL) { fprintf(stderr, "No triangles coordinates read from %s\n", filename); mem_free(g); return NULL; } return g; } /* * Destroy a group. */ static void _group_destroy(_group_t* g) { list_destroy(g->triangles); mem_free(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 file.\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; if (fscanf(file, " %lf %lf", &tx, &ty) != 2) { 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))); 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; if (fscanf(file, " %lf", &theta) != 1) { fprintf(stderr, "Cannot read rotation angle from scene file.\n"); return -1; } g->modelview = mat_mult(g->modelview, MAT_ROTATE_Z((scal_t)theta)); 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; if (fscanf(file, " %lf %lf", &sx, &sy) != 2) { 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))); return 0; } struct scene { list_t* groups; int w; int h; mat_t projection; }; 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"); 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) { fprintf(stderr, "Cannot read scene file header.\n"); return NULL; } s->w = w; s->h = h; s->projection = MAT_ORTHO((scal_t)minX, (scal_t)maxX, (scal_t)minY, (scal_t)maxY); char grp_filename[4096]; _group_t* g = NULL; #define _ASSERT_G \ if (g == NULL) { \ fprintf(stderr, "Unexpected line before group is loaded.\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; default: fprintf(stderr, "Unknown identifier: %c\n", type); } } list_reverse(&s->groups); #undef _ASSERT_G fclose(file); return s; fail: scene_destroy(s); return NULL; } void scene_destroy(scene_t* s) { list_destroy(s->groups); mem_free(s); } pixmap_t* scene_render(scene_t* s) { pixmap_t* pix = pixmap_alloc(s->w, s->h, COLOR_BLACK); pixmap_projection(pix, &s->projection); for (list_t* gi = s->groups; gi; gi = gi->link) { _group_t* g = (_group_t*)gi->val; pixmap_modelview(pix, &g->modelview); for (list_t* ti = g->triangles; ti; ti = ti->link) { pixmap_draw_tri(pix, (tri_t*)ti->val); } } return pix; }