*/
#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include "common.h"
-#include "mat.h"
#include "list.h"
+#include "model.h"
+#include "mat.h"
#include "scene.h"
-#include "tri.h"
-
-
-#if FIND_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 // FIND_NORMALS
-
-
-/*
- * A group of triangles and a transformation.
- */
-struct _group
-{
- list_t* triangles;
- mat_t model;
-#if RENDER_PROGRESS
- char* name;
- int count;
-#define IF_RENDER_PROGRESS(X) X
-#else
-#define IF_RENDER_PROGRESS(X)
-#endif
-};
-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->model = MAT_IDENTITY;
-#if RENDER_PROGRESS
- g->name = mem_strdup(filename);
- g->count = 0;
-#endif
-
-#if FIND_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);
- IF_RENDER_PROGRESS(++g->count);
-
-#if FIND_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
- _find_normals_add_triangle(m, t);
-#endif
- }
-
-#if FIND_NORMALS == 2
- map_vnorm_call(m, _find_normals_average);
- rbtree_destroy(m);
-#endif
-
- 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)
-{
- 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.
- */
-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, tz;
- if (fscanf(file, " %lf %lf %lf", &tx, &ty, &tz) != 3) {
- fprintf(stderr, "Cannot read translate coordinates from scene file.\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 file.\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 file.\n");
- return -1;
- }
- g->model = mat_mult(g->model, MAT_SCALE((scal_t)sx, (scal_t)sy, (scal_t)sz));
- return 0;
-}
+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);
struct scene
{
- list_t* groups;
- list_t* lights;
+ list_t* models;
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)
-{
- FILE* file = fopen(filename, "r");
- if (file == NULL) {
- fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
- return NULL;
- }
+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",
+ double aR, aG, aB;
+ if (fscanf(file, "U4 %d %d %lf %lf %lf %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");
+ &fovy, &aspect, &near, &far,
+ &aR, &aG, &aB) != 18) {
+ fprintf(stderr, "Cannot read scene header.\n");
return NULL;
}
scene_t* s = (scene_t*)mem_alloc(sizeof(scene_t));
- s->groups = NULL;
+ 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)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((scal_t)aR, (scal_t)aG, (scal_t)aB, S(1.0));
- char grp_filename[4096];
- _group_t* g = NULL;
+ char filename[4096];
+ model_t* m = NULL;
#define _ASSERT_G \
-if (g == NULL) { \
+if (m == NULL) { \
fprintf(stderr, "Unexpected line before group is specified.\n"); \
goto fail; \
}
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");
+ if (fgets(filename, 4096, file) == NULL) {
+ fprintf(stderr, "Cannot read model filename.\n");
}
- trim(grp_filename);
- g = _group_alloc(grp_filename);
- if (g == NULL) {
+ m = model_alloc(trim(filename));
+ if (m == NULL) {
goto fail;
}
- list_push2(&s->groups, g, DTOR(_group_destroy));
+ list_push2(&s->models, m, DTOR(model_destroy));
break;
case 'c':
_ASSERT_G;
- if (_group_set_colors(g, file) != 0) {
+ if (_model_set_colors(m, file) != 0) {
goto fail;
}
break;
case 't':
_ASSERT_G;
- if (_group_add_translate(g, file) != 0) {
+ if (_model_add_translate(m, file) != 0) {
goto fail;
}
break;
case 'r':
- if (_group_add_rotate(g, file) != 0) {
+ if (_model_add_rotate(m, file) != 0) {
goto fail;
}
break;
case 's':
_ASSERT_G;
- if (_group_add_scale(g, file) != 0) {
+ if (_model_add_scale(m, file) != 0) {
+ goto fail;
+ }
+ break;
+
+ case 'l':
+ if (_scene_add_light(s, file) != 0) {
+ goto fail;
+ }
+ break;
+
+ case 'p':
+ _ASSERT_G;
+ if (_model_set_material(m, file) != 0) {
goto fail;
}
break;
+ case 'X':
+ goto done;
+
default:
fprintf(stderr, "Unknown identifier: %c\n", type);
}
}
-
#undef _ASSERT_G
- fclose(file);
+done:
+ list_reverse(&s->models);
return s;
fail:
void scene_destroy(scene_t* s)
{
- list_destroy(s->groups);
+ 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 _model_set_colors(model_t* m, FILE* file)
+{
+ double a, r, g, b;
+ if (fscanf(file, " %lf %lf %lf %lf", &a, &r, &g, &b) != 4) {
+ fprintf(stderr, "Cannot read color values from scene.\n");
+ return -1;
+ }
+
+ 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)r, (colorchan_t)g, (colorchan_t)b, (colorchan_t)a);
+ t->b.c = color_new((colorchan_t)r, (colorchan_t)g, (colorchan_t)b, (colorchan_t)a);
+ t->c.c = color_new((colorchan_t)r, (colorchan_t)g, (colorchan_t)b, (colorchan_t)a);
+ }
+ return 0;
+}
+
+/*
+ * Concat a translation matrix to the transformation as defined in a 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;
+ }
+ 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 _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;
+ }
+ 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 _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;
+ }
+ 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 _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;
+ }
+ model_material(m, color_new((scal_t)sr, (scal_t)sg, (scal_t)sb, S(1.0)), (scal_t)shininess);
+ return 0;
+}
+
+/*
+ * Add a light to the scene.
+ */
+static int _scene_add_light(scene_t* s, FILE* file)
+{
+ double lx, ly, lz, r, g, b;
+ if (fscanf(file, " %lf %lf %lf %lf %lf %lf",
+ &lx, &ly, &lz, &r, &g, &b) != 6) {
+ fprintf(stderr, "Cannot read light values from scene.\n");
+ return -1;
+ }
+ light_t* l = light_alloc(
+ vec_new(lx, ly, lz),
+ color_new((scal_t)r, (scal_t)g, (scal_t)b, S(1.0)),
+ color_new((scal_t)r, (scal_t)g, (scal_t)b, S(1.0))
+ );
+ list_push2(&s->lights, l, mem_free);
+ return 0;
+}
+
+
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
-#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);
- IF_RENDER_PROGRESS(tri = 0);
- for (list_t* ti = g->triangles; ti; ti = ti->link) {
-#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
+ 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 RENDER_TIMER
+#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;