#include "tri.h"
-#if FIND_NORMALS == 2
+#if PRE_NORMALS >= 2
#include "map.h"
DECLARE_AND_DEFINE_MAP_TYPE3(vec_t, list_t*, vnorm, vec_compare(*a, *b));
_find_normals_add_vertex(m, t->c.v, t);
}
-
/*
* Calculate an averaged normal from a list of triangles that share a common
* vertex.
}
}
-#endif // FIND_NORMALS
+#endif // PRE_NORMALS
/*
{
list_t* triangles;
mat_t model;
-#if RENDER_PROGRESS
+ color_t specular;
+ scal_t shininess;
char* name;
int count;
-#define IF_RENDER_PROGRESS(X) X
-#else
-#define IF_RENDER_PROGRESS(X)
-#endif
};
typedef struct _group _group_t;
+
+#define _CHECK_IO(X) if ((X) <= 0) goto fail
+
/*
- * Allocate a group by reading raw triangle coordinates from a file.
+ * Try to read the triangle geometry from the cache file.
*/
-static _group_t* _group_alloc(const char* filename)
+static int _group_try_read_cache(const char* filename, list_t** l)
{
- FILE* file = fopen(filename, "r");
+ char* cachename = mem_strcat(".", filename);
+ FILE* file = fopen(cachename, "rb");
if (file == NULL) {
- fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
- return NULL;
+ goto fail;
+ }
+
+ 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)
+ );
+ 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;
-#if RENDER_PROGRESS
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
-#if FIND_NORMALS == 2
+ 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
vert_new2((scal_t)x3, (scal_t)y3, (scal_t)z3)
);
list_push2(&g->triangles, t, mem_free);
- IF_RENDER_PROGRESS(++g->count);
+ ++g->count;
-#if FIND_NORMALS == 1
+#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 FIND_NORMALS == 2
+#elif PRE_NORMALS >= 2
_find_normals_add_triangle(m, t);
#endif
}
-#if FIND_NORMALS == 2
+#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 coordinates read from %s\n", filename);
- mem_free(g);
+ fprintf(stderr, "No triangles read from %s\n", filename);
+ _group_destroy(g);
return NULL;
}
return g;
}
-/*
- * Destroy a group.
- */
-static void _group_destroy(_group_t* g)
-{
- IF_RENDER_PROGRESS(mem_free(g->name));
- list_destroy(g->triangles);
- mem_free(g);
-}
-
/*
* Set the colors of the triangles in the group as defined in a 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");
+ fprintf(stderr, "Cannot read color values from scene.\n");
return -1;
}
{
double tx, ty, tz;
if (fscanf(file, " %lf %lf %lf", &tx, &ty, &tz) != 3) {
- fprintf(stderr, "Cannot read translate coordinates from scene file.\n");
+ 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));
{
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");
+ 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));
{
double sx, sy, sz;
if (fscanf(file, " %lf %lf %lf", &sx, &sy, &sz) != 3) {
- fprintf(stderr, "Cannot read scale factors from scene file.\n");
+ 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;
+}
+
struct scene
{
list_t* groups;
- list_t* lights;
int w, h;
mat_t view;
mat_t projection;
vec_t eye;
+ list_t* lights;
+ color_t ambient;
};
-scene_t* scene_alloc(const char* filename)
+
+/*
+ * Set the ambient light properties of a scene.
+ */
+static int _scene_set_ambient(scene_t* s, FILE* file)
{
- FILE* file = fopen(filename, "r");
- if (file == NULL) {
- fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
- return NULL;
+ double r, g, b;
+ if (fscanf(file, " %lf %lf %lf", &r, &g, &b) != 3) {
+ fprintf(stderr, "Cannot read ambient light from scene.\n");
+ return -1;
}
+ s->ambient = color_new((scal_t)r, (scal_t)g, (scal_t)b, S(1.0));
+ return 0;
+}
+
+static int _scene_add_light(scene_t* s, FILE* file)
+{
+ double lx, ly, lz, dr, dg, db, sr, sg, sb;
+ if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
+ &lx, &ly, &lz, &dr, &dg, &db, &sr, &sg, &sb) != 9) {
+ fprintf(stderr, "Cannot read light values from scene.\n");
+ return -1;
+ }
+ light_t* l = light_alloc(
+ vec_new(lx, ly, lz),
+ color_new(dr, dg, db, S(1.0)),
+ color_new(sr, sg, sb, S(1.0))
+ );
+ list_push2(&s->lights, l, mem_free);
+ return 0;
+}
+
+scene_t* scene_alloc(FILE* file)
+{
int w, h;
double eyeX, eyeY, eyeZ, spotX, spotY, spotZ, upX, upY, upZ;
double fovy, aspect, near, far;
&w, &h,
&eyeX, &eyeY, &eyeZ, &spotX, &spotY, &spotZ, &upX, &upY, &upZ,
&fovy, &aspect, &near, &far) != 15) {
- fprintf(stderr, "Cannot read scene file header.\n");
+ fprintf(stderr, "Cannot read scene header.\n");
return NULL;
}
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;
}
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
- fclose(file);
+done:
return s;
fail:
void scene_destroy(scene_t* s)
{
- list_destroy(s->groups);
+ list_destroy(&s->groups);
+ list_destroy(&s->lights);
mem_free(s);
}
raster_t* scene_render(scene_t* s)
{
-#if RENDER_TIMER
+#if VERBOSITY >= 3
timer_start();
#endif
raster_view(p, &s->view);
raster_projection(p, &s->projection);
raster_eye(p, s->eye);
+ raster_ambient(p, s->ambient);
- 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))));
+ for (list_t* i = s->lights; i; i = i->link) {
+ raster_light(p, *(light_t*)i->val);
+ }
-#if RENDER_PROGRESS
+#if VERBOSITY >= 4
#define PROGRESS_FMT "\033[80D\033[2K %s\t %9d / %d"
int tri;
printf("render scene:\n");
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 RENDER_PROGRESS
+#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 RENDER_PROGRESS
+#if VERBOSITY >= 4
printf(PROGRESS_FMT"\n", g->name, tri, g->count);
#endif
}
IF_RENDER_PROGRESS(printf("render complete!\n"));
-#if RENDER_TIMER
+#if VERBOSITY >= 3
long dt = timer_stop();
printf("time\t%.3fms\n", (float)dt / 1000.0f);
#endif