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