add scene lighting constructs; real stdin support
[chaz/rasterize] / scene.c
1
2 /*
3 * CS5600 University of Utah
4 * Charles McGarvey
5 * mcgarvey@eng.utah.edu
6 */
7
8 #include <errno.h>
9 #include <stdio.h>
10 #include <string.h>
11
12 #include "common.h"
13 #include "mat.h"
14 #include "list.h"
15 #include "scene.h"
16 #include "tri.h"
17
18
19 #if PRE_NORMALS >= 2
20
21 #include "map.h"
22 DECLARE_AND_DEFINE_MAP_TYPE3(vec_t, list_t*, vnorm, vec_compare(*a, *b));
23
24
25 /*
26 * Associate a triangle with one of its vertices.
27 */
28 static void _find_normals_add_vertex(map_t* m, vec_t v, tri_t* t)
29 {
30 list_t** l = map_vnorm_search(m, v);
31 if (l == NULL) {
32 map_vnorm_data_t* d = map_vnorm_insert(m, v, NULL);
33 l = &d->val;
34 }
35 list_push(l, t);
36 }
37
38 /*
39 * Associate a triangle with all of its vertices.
40 */
41 static void _find_normals_add_triangle(map_t* m, tri_t* t)
42 {
43 _find_normals_add_vertex(m, t->a.v, t);
44 _find_normals_add_vertex(m, t->b.v, t);
45 _find_normals_add_vertex(m, t->c.v, t);
46 }
47
48 /*
49 * Calculate an averaged normal from a list of triangles that share a common
50 * vertex.
51 */
52 static void _find_normals_average(const vec_t* v, list_t** l)
53 {
54 // first, compute the average normal
55 vec_t n = VEC_ZERO;
56 for (list_t* i = *l; i; i = i->link) {
57 tri_t* t = (tri_t*)i->val;
58 n = vec_add(n, tri_normal(*t));
59 }
60 n = vec_normalize(n);
61
62 // set the normal on each triangle's vertex that is shared
63 while (*l) {
64 tri_t* t = (tri_t*)(*l)->val;
65 if (vec_isequal(*v, t->a.v)) {
66 t->a.n = n;
67 }
68 else if (vec_isequal(*v, t->b.v)) {
69 t->b.n = n;
70 }
71 else if (vec_isequal(*v, t->c.v)) {
72 t->c.n = n;
73 }
74 list_pop(l);
75 }
76 }
77
78 #endif // PRE_NORMALS
79
80
81 /*
82 * A group of triangles and a transformation.
83 */
84 struct _group
85 {
86 list_t* triangles;
87 mat_t model;
88 color_t specular;
89 scal_t shininess;
90 char* name;
91 int count;
92 };
93 typedef struct _group _group_t;
94
95
96 #define _CHECK_IO(X) if ((X) <= 0) goto fail
97
98 /*
99 * Try to read the triangle geometry from the cache file.
100 */
101 static int _group_try_read_cache(const char* filename, list_t** l)
102 {
103 char* cachename = mem_strcat(".", filename);
104 FILE* file = fopen(cachename, "rb");
105 if (file == NULL) {
106 goto fail;
107 }
108
109 int count = 0;
110 _CHECK_IO(fread(&count, sizeof(count), 1, file));
111
112 float x1, y1, z1, x2, y2, z2, x3, y3, z3;
113 for (int i = 0; i < count; ++i) {
114 _CHECK_IO(fread(&x1, sizeof(float), 1, file));
115 _CHECK_IO(fread(&y1, sizeof(float), 1, file));
116 _CHECK_IO(fread(&z1, sizeof(float), 1, file));
117 _CHECK_IO(fread(&x2, sizeof(float), 1, file));
118 _CHECK_IO(fread(&y2, sizeof(float), 1, file));
119 _CHECK_IO(fread(&z2, sizeof(float), 1, file));
120 _CHECK_IO(fread(&x3, sizeof(float), 1, file));
121 _CHECK_IO(fread(&y3, sizeof(float), 1, file));
122 _CHECK_IO(fread(&z3, sizeof(float), 1, file));
123 tri_t* t = tri_alloc(
124 vert_new2((scal_t)x1, (scal_t)y1, (scal_t)z1),
125 vert_new2((scal_t)x2, (scal_t)y2, (scal_t)z2),
126 vert_new2((scal_t)x3, (scal_t)y3, (scal_t)z3)
127 );
128 list_push2(l, t, mem_free);
129 _CHECK_IO(fread(&x1, sizeof(float), 1, file));
130 _CHECK_IO(fread(&y1, sizeof(float), 1, file));
131 _CHECK_IO(fread(&z1, sizeof(float), 1, file));
132 _CHECK_IO(fread(&x2, sizeof(float), 1, file));
133 _CHECK_IO(fread(&y2, sizeof(float), 1, file));
134 _CHECK_IO(fread(&z2, sizeof(float), 1, file));
135 _CHECK_IO(fread(&x3, sizeof(float), 1, file));
136 _CHECK_IO(fread(&y3, sizeof(float), 1, file));
137 _CHECK_IO(fread(&z3, sizeof(float), 1, file));
138 t->a.n = vec_new((scal_t)x1, (scal_t)y1, (scal_t)z1);
139 t->b.n = vec_new((scal_t)x2, (scal_t)y2, (scal_t)z2);
140 t->c.n = vec_new((scal_t)x3, (scal_t)y3, (scal_t)z3);
141 }
142
143 fail:
144 mem_free(cachename);
145 if (file != NULL) {
146 fclose(file);
147 }
148
149 return count;
150 }
151
152 /*
153 * Write the triangle data to the cache.
154 */
155 static void _group_write_cache(const char* filename, int count, list_t* l)
156 {
157 char* cachename = mem_strcat(".", filename);
158 FILE* file = fopen(cachename, "wb");
159 if (file == NULL) {
160 fprintf(stderr, "Cannot write %s: %s\n", cachename, strerror(errno));
161 goto fail;
162 }
163
164 _CHECK_IO(fwrite(&count, sizeof(count), 1, file));
165 for (list_t* i = l; i; i = i->link) {
166 tri_t* t = (tri_t*)i->val;
167 _CHECK_IO(fwrite(&t->a.v.x, sizeof(float), 1, file));
168 _CHECK_IO(fwrite(&t->a.v.y, sizeof(float), 1, file));
169 _CHECK_IO(fwrite(&t->a.v.z, sizeof(float), 1, file));
170 _CHECK_IO(fwrite(&t->b.v.x, sizeof(float), 1, file));
171 _CHECK_IO(fwrite(&t->b.v.y, sizeof(float), 1, file));
172 _CHECK_IO(fwrite(&t->b.v.z, sizeof(float), 1, file));
173 _CHECK_IO(fwrite(&t->c.v.x, sizeof(float), 1, file));
174 _CHECK_IO(fwrite(&t->c.v.y, sizeof(float), 1, file));
175 _CHECK_IO(fwrite(&t->c.v.z, sizeof(float), 1, file));
176 _CHECK_IO(fwrite(&t->a.n.x, sizeof(float), 1, file));
177 _CHECK_IO(fwrite(&t->a.n.y, sizeof(float), 1, file));
178 _CHECK_IO(fwrite(&t->a.n.z, sizeof(float), 1, file));
179 _CHECK_IO(fwrite(&t->b.n.x, sizeof(float), 1, file));
180 _CHECK_IO(fwrite(&t->b.n.y, sizeof(float), 1, file));
181 _CHECK_IO(fwrite(&t->b.n.z, sizeof(float), 1, file));
182 _CHECK_IO(fwrite(&t->c.n.x, sizeof(float), 1, file));
183 _CHECK_IO(fwrite(&t->c.n.y, sizeof(float), 1, file));
184 _CHECK_IO(fwrite(&t->c.n.z, sizeof(float), 1, file));
185 }
186
187 fail:
188 mem_free(cachename);
189 if (file != NULL) {
190 fclose(file);
191 }
192 }
193
194 #undef _CHECK_IO
195
196
197 /*
198 * Destroy a group.
199 */
200 static void _group_destroy(_group_t* g)
201 {
202 mem_free(g->name);
203 list_destroy(&g->triangles);
204 mem_free(g);
205 }
206
207 /*
208 * Allocate a group by reading raw triangle coordinates from a file.
209 */
210 static _group_t* _group_alloc(const char* filename)
211 {
212 _group_t* g = (_group_t*)mem_alloc(sizeof(_group_t));
213 g->triangles = NULL;
214 g->model = MAT_IDENTITY;
215 g->name = mem_strdup(filename);
216 g->count = 0;
217 g->specular = COLOR_WHITE;
218 g->shininess = S(64.0);
219
220 #if PRE_NORMALS == 3
221 int r = _group_try_read_cache(filename, &g->triangles);
222 if (0 < r) {
223 g->count = r;
224 return g;
225 }
226 #endif
227
228 FILE* file = fopen(filename, "r");
229 if (file == NULL) {
230 fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
231 _group_destroy(g);
232 return NULL;
233 }
234
235 #if PRE_NORMALS >= 2
236 map_t* m = map_vnorm_alloc();
237 #endif
238
239 double x1, y1, z1, x2, y2, z2, x3, y3, z3;
240 while (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
241 &x1, &y1, &z1, &x2, &y2, &z2, &x3, &y3, &z3) == 9) {
242 tri_t* t = tri_alloc(
243 vert_new2((scal_t)x1, (scal_t)y1, (scal_t)z1),
244 vert_new2((scal_t)x2, (scal_t)y2, (scal_t)z2),
245 vert_new2((scal_t)x3, (scal_t)y3, (scal_t)z3)
246 );
247 list_push2(&g->triangles, t, mem_free);
248 ++g->count;
249
250 #if PRE_NORMALS == 1
251 vec_t n = vec_normalize(tri_normal(*t));
252 t->a.n = n;
253 t->b.n = n;
254 t->c.n = n;
255 #elif PRE_NORMALS >= 2
256 _find_normals_add_triangle(m, t);
257 #endif
258 }
259
260 #if PRE_NORMALS >= 2
261 map_vnorm_call(m, _find_normals_average);
262 rbtree_destroy(m);
263 #if PRE_NORMALS == 3
264 list_reverse(&g->triangles);
265 _group_write_cache(filename, g->count, g->triangles);
266 #endif
267 #endif
268
269 fclose(file);
270
271 if (g->triangles == NULL) {
272 fprintf(stderr, "No triangles read from %s\n", filename);
273 _group_destroy(g);
274 return NULL;
275 }
276
277 return g;
278 }
279
280
281 /*
282 * Set the colors of the triangles in the group as defined in a file.
283 */
284 static int _group_set_colors(_group_t* g, FILE* file)
285 {
286 double r1, g1, b1, r2, g2, b2, r3, g3, b3;
287 if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
288 &r1, &g1, &b1, &r2, &g2, &b2, &r3, &g3, &b3) != 9) {
289 fprintf(stderr, "Cannot read color values from scene.\n");
290 return -1;
291 }
292
293 for (list_t* i = g->triangles; i; i = i->link) {
294 tri_t* t = (tri_t*)i->val;
295 t->a.c = color_new((colorchan_t)r1, (colorchan_t)g1, (colorchan_t)b1, S(1.0));
296 t->b.c = color_new((colorchan_t)r2, (colorchan_t)g2, (colorchan_t)b2, S(1.0));
297 t->c.c = color_new((colorchan_t)r3, (colorchan_t)g3, (colorchan_t)b3, S(1.0));
298 }
299 return 0;
300 }
301
302 /*
303 * Concat a translation matrix to the transformation as defined in a file.
304 */
305 static int _group_add_translate(_group_t* g, FILE* file)
306 {
307 double tx, ty, tz;
308 if (fscanf(file, " %lf %lf %lf", &tx, &ty, &tz) != 3) {
309 fprintf(stderr, "Cannot read translate coordinates from scene.\n");
310 return -1;
311 }
312 g->model = mat_mult(g->model, MAT_TRANSLATE((scal_t)tx, (scal_t)ty, (scal_t)tz));
313 return 0;
314 }
315
316 /*
317 * Concat a rotation matrix to the transformation as defined in a file.
318 */
319 static int _group_add_rotate(_group_t* g, FILE* file)
320 {
321 double theta, ax, ay, az;
322 if (fscanf(file, " %lf %lf %lf %lf", &theta, &ax, &ay, &az) != 4) {
323 fprintf(stderr, "Cannot read rotation angle from scene.\n");
324 return -1;
325 }
326 g->model = mat_mult(g->model, MAT_ROTATE((scal_t)theta, (scal_t)ax, (scal_t)ay, (scal_t)az));
327 return 0;
328 }
329
330 /*
331 * Concat a scale matrix to the transformation as defined in a file.
332 */
333 static int _group_add_scale(_group_t* g, FILE* file)
334 {
335 double sx, sy, sz;
336 if (fscanf(file, " %lf %lf %lf", &sx, &sy, &sz) != 3) {
337 fprintf(stderr, "Cannot read scale factors from scene.\n");
338 return -1;
339 }
340 g->model = mat_mult(g->model, MAT_SCALE((scal_t)sx, (scal_t)sy, (scal_t)sz));
341 return 0;
342 }
343
344 /*
345 * Set the specular highlight and shininess factor for the group.
346 */
347 static int _group_set_material(_group_t* g, FILE* file)
348 {
349 double sr, sg, sb, shininess;
350 if (fscanf(file, " %lf %lf %lf %lf", &sr, &sg, &sb, &shininess) != 4) {
351 fprintf(stderr, "Cannot read material information from scene.\n");
352 return -1;
353 }
354 g->specular = color_new((scal_t)sr, (scal_t)sg, (scal_t)sb, S(1.0));
355 g->shininess = (scal_t)shininess;
356 return 0;
357 }
358
359
360 struct scene
361 {
362 list_t* groups;
363 int w, h;
364 mat_t view;
365 mat_t projection;
366 vec_t eye;
367 list_t* lights;
368 color_t ambient;
369 };
370
371
372 /*
373 * Set the ambient light properties of a scene.
374 */
375 static int _scene_set_ambient(scene_t* s, FILE* file)
376 {
377 double r, g, b;
378 if (fscanf(file, " %lf %lf %lf", &r, &g, &b) != 3) {
379 fprintf(stderr, "Cannot read ambient light from scene.\n");
380 return -1;
381 }
382 s->ambient = color_new((scal_t)r, (scal_t)g, (scal_t)b, S(1.0));
383 return 0;
384 }
385
386 static int _scene_add_light(scene_t* s, FILE* file)
387 {
388 double lx, ly, lz, dr, dg, db, sr, sg, sb;
389 if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
390 &lx, &ly, &lz, &dr, &dg, &db, &sr, &sg, &sb) != 9) {
391 fprintf(stderr, "Cannot read light values from scene.\n");
392 return -1;
393 }
394 light_t* l = light_alloc(
395 vec_new(lx, ly, lz),
396 color_new(dr, dg, db, S(1.0)),
397 color_new(sr, sg, sb, S(1.0))
398 );
399 list_push2(&s->lights, l, mem_free);
400 return 0;
401 }
402
403
404 scene_t* scene_alloc(FILE* file)
405 {
406 int w, h;
407 double eyeX, eyeY, eyeZ, spotX, spotY, spotZ, upX, upY, upZ;
408 double fovy, aspect, near, far;
409 if (fscanf(file, "U3 %d %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
410 &w, &h,
411 &eyeX, &eyeY, &eyeZ, &spotX, &spotY, &spotZ, &upX, &upY, &upZ,
412 &fovy, &aspect, &near, &far) != 15) {
413 fprintf(stderr, "Cannot read scene header.\n");
414 return NULL;
415 }
416
417 scene_t* s = (scene_t*)mem_alloc(sizeof(scene_t));
418 s->groups = NULL;
419 s->w = w;
420 s->h = h;
421 s->view = MAT_LOOKAT(vec_new( (scal_t)eyeX, (scal_t)eyeY, (scal_t)eyeZ),
422 vec_new((scal_t)spotX, (scal_t)spotY, (scal_t)spotZ),
423 vec_new( (scal_t)upX, (scal_t)upY, (scal_t)upZ));
424 s->eye = vec_new(eyeX, eyeY, eyeZ);
425 s->projection = MAT_PERSPECTIVE((scal_t)fovy, (scal_t)aspect, (scal_t)near, (scal_t)far);
426 s->lights = NULL;
427 s->ambient = color_new(S(0.05), S(0.05), S(0.05), S(1.0));
428
429 char grp_filename[4096];
430 _group_t* g = NULL;
431
432 #define _ASSERT_G \
433 if (g == NULL) { \
434 fprintf(stderr, "Unexpected line before group is specified.\n"); \
435 goto fail; \
436 }
437
438 char type;
439 while (fscanf(file, " %c", &type) == 1) {
440 switch (type) {
441 case 'g':
442 if (fgets(grp_filename, 4096, file) == NULL) {
443 fprintf(stderr, "Cannot read raw triangle filename.\n");
444 }
445 trim(grp_filename);
446 g = _group_alloc(grp_filename);
447 if (g == NULL) {
448 goto fail;
449 }
450 list_push2(&s->groups, g, DTOR(_group_destroy));
451 break;
452
453 case 'c':
454 _ASSERT_G;
455 if (_group_set_colors(g, file) != 0) {
456 goto fail;
457 }
458 break;
459
460 case 't':
461 _ASSERT_G;
462 if (_group_add_translate(g, file) != 0) {
463 goto fail;
464 }
465 break;
466
467 case 'r':
468 if (_group_add_rotate(g, file) != 0) {
469 goto fail;
470 }
471 break;
472
473 case 's':
474 _ASSERT_G;
475 if (_group_add_scale(g, file) != 0) {
476 goto fail;
477 }
478 break;
479
480 case 'A':
481 if (_scene_set_ambient(s, file) != 0) {
482 goto fail;
483 }
484 break;
485
486 case 'L':
487 if (_scene_add_light(s, file) != 0) {
488 goto fail;
489 }
490 break;
491
492 case 'M':
493 _ASSERT_G;
494 if (_group_set_material(g, file) != 0) {
495 goto fail;
496 }
497 break;
498
499 case 'X':
500 goto done;
501
502 default:
503 fprintf(stderr, "Unknown identifier: %c\n", type);
504 }
505 }
506 #undef _ASSERT_G
507
508 done:
509 return s;
510
511 fail:
512 scene_destroy(s);
513 return NULL;
514 }
515
516 void scene_destroy(scene_t* s)
517 {
518 list_destroy(&s->groups);
519 list_destroy(&s->lights);
520 mem_free(s);
521 }
522
523
524 raster_t* scene_render(scene_t* s)
525 {
526 #if VERBOSITY >= 3
527 timer_start();
528 #endif
529
530 raster_t* p = raster_alloc(s->w, s->h, COLOR_BLACK);
531 raster_view(p, &s->view);
532 raster_projection(p, &s->projection);
533 raster_eye(p, s->eye);
534 raster_ambient(p, s->ambient);
535
536 for (list_t* i = s->lights; i; i = i->link) {
537 raster_light(p, *(light_t*)i->val);
538 }
539
540 #if VERBOSITY >= 4
541 #define PROGRESS_FMT "\033[80D\033[2K %s\t %9d / %d"
542 int tri;
543 printf("render scene:\n");
544 #endif
545
546 for (list_t* gi = s->groups; gi; gi = gi->link) {
547 _group_t* g = (_group_t*)gi->val;
548 raster_model(p, &g->model);
549 raster_material(p, g->specular, g->shininess);
550 IF_RENDER_PROGRESS(tri = 0);
551 for (list_t* ti = g->triangles; ti; ti = ti->link) {
552 #if VERBOSITY >= 4
553 if (++tri % 100 == 0) {
554 printf(PROGRESS_FMT, g->name, tri, g->count);
555 fflush(stdout);
556 }
557 #endif
558 raster_draw_tri(p, (tri_t*)ti->val);
559 }
560 #if VERBOSITY >= 4
561 printf(PROGRESS_FMT"\n", g->name, tri, g->count);
562 #endif
563 }
564 IF_RENDER_PROGRESS(printf("render complete!\n"));
565
566 #if VERBOSITY >= 3
567 long dt = timer_stop();
568 printf("time\t%.3fms\n", (float)dt / 1000.0f);
569 #endif
570
571 return p;
572 }
573
This page took 0.082191 seconds and 4 git commands to generate.