From 143da66e7f625b7f195a115b9740a748ee003534 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Sun, 19 Feb 2012 23:06:14 -0700 Subject: [PATCH] refactor triangle group into a separate class --- Makefile | 2 +- animate.lua | 19 +- common.h | 3 +- config.h | 37 ++-- model.c | 355 ++++++++++++++++++++++++++++++++++ model.h | 90 +++++++++ raster.c | 29 ++- raster.h | 8 +- scene.c | 540 +++++++++++++--------------------------------------- scene.u3d | 1 + 10 files changed, 650 insertions(+), 434 deletions(-) create mode 100644 model.c create mode 100644 model.h diff --git a/Makefile b/Makefile index aaac3cd..4a5844d 100644 --- 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) diff --git a/animate.lua b/animate.lua index 9917cb1..fb4ca82 100755 --- a/animate.lua +++ b/animate.lua @@ -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) diff --git a/common.h b/common.h index 172b8af..537e135 100644 --- 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; } diff --git a/config.h b/config.h index a1d7fb9..5930439 100644 --- a/config.h +++ b/config.h @@ -135,22 +135,31 @@ #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 index 0000000..c136ae9 --- /dev/null +++ b/model.c @@ -0,0 +1,355 @@ + +/* + * CS5600 University of Utah + * Charles McGarvey + * mcgarvey@eng.utah.edu + */ + +#include + +#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 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_ + diff --git a/raster.c b/raster.c index 766e6ea..2be94da 100644 --- 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 diff --git a/raster.h b/raster.h index 1d43132..53f7627 100644 --- 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 --- a/scene.c +++ b/scene.c @@ -6,282 +6,157 @@ */ #include -#include -#include -#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; diff --git a/scene.u3d b/scene.u3d index 1b9258d..0c27afc 100644 --- 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 -- 2.45.2