#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 modelview;
+ 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;
_group_t* g = (_group_t*)mem_alloc(sizeof(_group_t));
g->triangles = NULL;
- g->modelview = MAT_IDENTITY;
+ 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, 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)
+ 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
}
- list_reverse(&g->triangles);
+
+#if FIND_NORMALS == 2
+ map_vnorm_call(m, _find_normals_average);
+ rbtree_destroy(m);
+#endif
fclose(file);
*/
static void _group_destroy(_group_t* g)
{
+ IF_RENDER_PROGRESS(mem_free(g->name));
list_destroy(g->triangles);
mem_free(g);
}
*/
static int _group_add_translate(_group_t* g, FILE* file)
{
- double tx, ty;
- if (fscanf(file, " %lf %lf", &tx, &ty) != 2) {
+ 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->modelview = mat_mult(g->modelview, MAT_TRANSLATE((scal_t)tx, (scal_t)ty, S(1.0)));
+ g->model = mat_mult(g->model, MAT_TRANSLATE((scal_t)tx, (scal_t)ty, (scal_t)tz));
return 0;
}
*/
static int _group_add_rotate(_group_t* g, FILE* file)
{
- double theta;
- if (fscanf(file, " %lf", &theta) != 1) {
+ 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->modelview = mat_mult(g->modelview, MAT_ROTATE_Z((scal_t)theta));
+ g->model = mat_mult(g->model, MAT_ROTATE((scal_t)theta, (scal_t)ax, (scal_t)ay, (scal_t)az));
return 0;
}
*/
static int _group_add_scale(_group_t* g, FILE* file)
{
- double sx, sy;
- if (fscanf(file, " %lf %lf", &sx, &sy) != 2) {
+ 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->modelview = mat_mult(g->modelview, MAT_SCALE((scal_t)sx, (scal_t)sy, S(1.0)));
+ g->model = mat_mult(g->model, MAT_SCALE((scal_t)sx, (scal_t)sy, (scal_t)sz));
return 0;
}
struct scene
{
list_t* groups;
- int w;
- int h;
+ list_t* lights;
+ int w, h;
+ mat_t view;
mat_t projection;
+ vec_t eye;
};
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");
+ if (file == NULL) {
+ fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
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) {
+ 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 file header.\n");
return NULL;
}
+ scene_t* s = (scene_t*)mem_alloc(sizeof(scene_t));
+ s->groups = NULL;
s->w = w;
s->h = h;
- s->projection = MAT_ORTHO((scal_t)minX, (scal_t)maxX, (scal_t)minY, (scal_t)maxY);
+ 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);
char grp_filename[4096];
_group_t* g = NULL;
#define _ASSERT_G \
if (g == NULL) { \
- fprintf(stderr, "Unexpected line before group is loaded.\n"); \
+ fprintf(stderr, "Unexpected line before group is specified.\n"); \
goto fail; \
}
fprintf(stderr, "Unknown identifier: %c\n", type);
}
}
- list_reverse(&s->groups);
#undef _ASSERT_G
}
-pixmap_t* scene_render(scene_t* s)
+raster_t* scene_render(scene_t* s)
{
- pixmap_t* pix = pixmap_alloc(s->w, s->h, COLOR_BLACK);
- pixmap_projection(pix, &s->projection);
+#if RENDER_TIMER
+ timer_start();
+#endif
+
+ raster_t* p = raster_alloc(s->w, s->h, COLOR_BLACK);
+ raster_view(p, &s->view);
+ raster_projection(p, &s->projection);
+ raster_eye(p, s->eye);
+
+ 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))));
+
+#if RENDER_PROGRESS
+#define PROGRESS_FMT "\033[80D\033[2K %s\t %9d / %d"
+ int tri;
+ printf("render scene:\n");
+#endif
for (list_t* gi = s->groups; gi; gi = gi->link) {
_group_t* g = (_group_t*)gi->val;
- pixmap_modelview(pix, &g->modelview);
+ raster_model(p, &g->model);
+ IF_RENDER_PROGRESS(tri = 0);
for (list_t* ti = g->triangles; ti; ti = ti->link) {
- pixmap_draw_tri(pix, (tri_t*)ti->val);
+#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
}
+ IF_RENDER_PROGRESS(printf("render complete!\n"));
+
+#if RENDER_TIMER
+ long dt = timer_stop();
+ printf("time\t%.3fms\n", (float)dt / 1000.0f);
+#endif
- return pix;
+ return p;
}