add support for 3d scenes, depth testing, lighting
[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 FIND_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 /*
50 * Calculate an averaged normal from a list of triangles that share a common
51 * vertex.
52 */
53 static void _find_normals_average(const vec_t* v, list_t** l)
54 {
55 // first, compute the average normal
56 vec_t n = VEC_ZERO;
57 for (list_t* i = *l; i; i = i->link) {
58 tri_t* t = (tri_t*)i->val;
59 n = vec_add(n, tri_normal(*t));
60 }
61 n = vec_normalize(n);
62
63 // set the normal on each triangle's vertex that is shared
64 while (*l) {
65 tri_t* t = (tri_t*)(*l)->val;
66 if (vec_isequal(*v, t->a.v)) {
67 t->a.n = n;
68 }
69 else if (vec_isequal(*v, t->b.v)) {
70 t->b.n = n;
71 }
72 else if (vec_isequal(*v, t->c.v)) {
73 t->c.n = n;
74 }
75 list_pop(l);
76 }
77 }
78
79 #endif // FIND_NORMALS
80
81
82 /*
83 * A group of triangles and a transformation.
84 */
85 struct _group
86 {
87 list_t* triangles;
88 mat_t model;
89 #if RENDER_PROGRESS
90 char* name;
91 int count;
92 #define IF_RENDER_PROGRESS(X) X
93 #else
94 #define IF_RENDER_PROGRESS(X)
95 #endif
96 };
97 typedef struct _group _group_t;
98
99 /*
100 * Allocate a group by reading raw triangle coordinates from a file.
101 */
102 static _group_t* _group_alloc(const char* filename)
103 {
104 FILE* file = fopen(filename, "r");
105 if (file == NULL) {
106 fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
107 return NULL;
108 }
109
110 _group_t* g = (_group_t*)mem_alloc(sizeof(_group_t));
111 g->triangles = NULL;
112 g->model = MAT_IDENTITY;
113 #if RENDER_PROGRESS
114 g->name = mem_strdup(filename);
115 g->count = 0;
116 #endif
117
118 #if FIND_NORMALS == 2
119 map_t* m = map_vnorm_alloc();
120 #endif
121
122 double x1, y1, z1, x2, y2, z2, x3, y3, z3;
123 while (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
124 &x1, &y1, &z1, &x2, &y2, &z2, &x3, &y3, &z3) == 9) {
125 tri_t* t = tri_alloc(
126 vert_new2((scal_t)x1, (scal_t)y1, (scal_t)z1),
127 vert_new2((scal_t)x2, (scal_t)y2, (scal_t)z2),
128 vert_new2((scal_t)x3, (scal_t)y3, (scal_t)z3)
129 );
130 list_push2(&g->triangles, t, mem_free);
131 IF_RENDER_PROGRESS(++g->count);
132
133 #if FIND_NORMALS == 1
134 vec_t n = vec_normalize(tri_normal(*t));
135 t->a.n = n;
136 t->b.n = n;
137 t->c.n = n;
138 #elif FIND_NORMALS == 2
139 _find_normals_add_triangle(m, t);
140 #endif
141 }
142
143 #if FIND_NORMALS == 2
144 map_vnorm_call(m, _find_normals_average);
145 rbtree_destroy(m);
146 #endif
147
148 fclose(file);
149
150 if (g->triangles == NULL) {
151 fprintf(stderr, "No triangles coordinates read from %s\n", filename);
152 mem_free(g);
153 return NULL;
154 }
155
156 return g;
157 }
158
159 /*
160 * Destroy a group.
161 */
162 static void _group_destroy(_group_t* g)
163 {
164 IF_RENDER_PROGRESS(mem_free(g->name));
165 list_destroy(g->triangles);
166 mem_free(g);
167 }
168
169
170 /*
171 * Set the colors of the triangles in the group as defined in a file.
172 */
173 static int _group_set_colors(_group_t* g, FILE* file)
174 {
175 double r1, g1, b1, r2, g2, b2, r3, g3, b3;
176 if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
177 &r1, &g1, &b1, &r2, &g2, &b2, &r3, &g3, &b3) != 9) {
178 fprintf(stderr, "Cannot read color values from scene file.\n");
179 return -1;
180 }
181
182 for (list_t* i = g->triangles; i; i = i->link) {
183 tri_t* t = (tri_t*)i->val;
184 t->a.c = color_new((colorchan_t)r1, (colorchan_t)g1, (colorchan_t)b1, S(1.0));
185 t->b.c = color_new((colorchan_t)r2, (colorchan_t)g2, (colorchan_t)b2, S(1.0));
186 t->c.c = color_new((colorchan_t)r3, (colorchan_t)g3, (colorchan_t)b3, S(1.0));
187 }
188 return 0;
189 }
190
191 /*
192 * Concat a translation matrix to the transformation as defined in a file.
193 */
194 static int _group_add_translate(_group_t* g, FILE* file)
195 {
196 double tx, ty, tz;
197 if (fscanf(file, " %lf %lf %lf", &tx, &ty, &tz) != 3) {
198 fprintf(stderr, "Cannot read translate coordinates from scene file.\n");
199 return -1;
200 }
201 g->model = mat_mult(g->model, MAT_TRANSLATE((scal_t)tx, (scal_t)ty, (scal_t)tz));
202 return 0;
203 }
204
205 /*
206 * Concat a rotation matrix to the transformation as defined in a file.
207 */
208 static int _group_add_rotate(_group_t* g, FILE* file)
209 {
210 double theta, ax, ay, az;
211 if (fscanf(file, " %lf %lf %lf %lf", &theta, &ax, &ay, &az) != 4) {
212 fprintf(stderr, "Cannot read rotation angle from scene file.\n");
213 return -1;
214 }
215 g->model = mat_mult(g->model, MAT_ROTATE((scal_t)theta, (scal_t)ax, (scal_t)ay, (scal_t)az));
216 return 0;
217 }
218
219 /*
220 * Concat a scale matrix to the transformation as defined in a file.
221 */
222 static int _group_add_scale(_group_t* g, FILE* file)
223 {
224 double sx, sy, sz;
225 if (fscanf(file, " %lf %lf %lf", &sx, &sy, &sz) != 3) {
226 fprintf(stderr, "Cannot read scale factors from scene file.\n");
227 return -1;
228 }
229 g->model = mat_mult(g->model, MAT_SCALE((scal_t)sx, (scal_t)sy, (scal_t)sz));
230 return 0;
231 }
232
233
234 struct scene
235 {
236 list_t* groups;
237 list_t* lights;
238 int w, h;
239 mat_t view;
240 mat_t projection;
241 vec_t eye;
242 };
243
244 scene_t* scene_alloc(const char* filename)
245 {
246 FILE* file = fopen(filename, "r");
247 if (file == NULL) {
248 fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
249 return NULL;
250 }
251
252 int w, h;
253 double eyeX, eyeY, eyeZ, spotX, spotY, spotZ, upX, upY, upZ;
254 double fovy, aspect, near, far;
255 if (fscanf(file, "U3 %d %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
256 &w, &h,
257 &eyeX, &eyeY, &eyeZ, &spotX, &spotY, &spotZ, &upX, &upY, &upZ,
258 &fovy, &aspect, &near, &far) != 15) {
259 fprintf(stderr, "Cannot read scene file header.\n");
260 return NULL;
261 }
262
263 scene_t* s = (scene_t*)mem_alloc(sizeof(scene_t));
264 s->groups = NULL;
265 s->w = w;
266 s->h = h;
267 s->view = MAT_LOOKAT(vec_new( (scal_t)eyeX, (scal_t)eyeY, (scal_t)eyeZ),
268 vec_new((scal_t)spotX, (scal_t)spotY, (scal_t)spotZ),
269 vec_new( (scal_t)upX, (scal_t)upY, (scal_t)upZ));
270 s->eye = vec_new(eyeX, eyeY, eyeZ);
271 s->projection = MAT_PERSPECTIVE((scal_t)fovy, (scal_t)aspect, (scal_t)near, (scal_t)far);
272
273 char grp_filename[4096];
274 _group_t* g = NULL;
275
276 #define _ASSERT_G \
277 if (g == NULL) { \
278 fprintf(stderr, "Unexpected line before group is specified.\n"); \
279 goto fail; \
280 }
281
282 char type;
283 while (fscanf(file, " %c", &type) == 1) {
284 switch (type) {
285 case 'g':
286 if (fgets(grp_filename, 4096, file) == NULL) {
287 fprintf(stderr, "Cannot read raw triangle filename.\n");
288 }
289 trim(grp_filename);
290 g = _group_alloc(grp_filename);
291 if (g == NULL) {
292 goto fail;
293 }
294 list_push2(&s->groups, g, DTOR(_group_destroy));
295 break;
296
297 case 'c':
298 _ASSERT_G;
299 if (_group_set_colors(g, file) != 0) {
300 goto fail;
301 }
302 break;
303
304 case 't':
305 _ASSERT_G;
306 if (_group_add_translate(g, file) != 0) {
307 goto fail;
308 }
309 break;
310
311 case 'r':
312 if (_group_add_rotate(g, file) != 0) {
313 goto fail;
314 }
315 break;
316
317 case 's':
318 _ASSERT_G;
319 if (_group_add_scale(g, file) != 0) {
320 goto fail;
321 }
322 break;
323
324 default:
325 fprintf(stderr, "Unknown identifier: %c\n", type);
326 }
327 }
328
329 #undef _ASSERT_G
330
331 fclose(file);
332 return s;
333
334 fail:
335 scene_destroy(s);
336 return NULL;
337 }
338
339 void scene_destroy(scene_t* s)
340 {
341 list_destroy(s->groups);
342 mem_free(s);
343 }
344
345
346 raster_t* scene_render(scene_t* s)
347 {
348 #if RENDER_TIMER
349 timer_start();
350 #endif
351
352 raster_t* p = raster_alloc(s->w, s->h, COLOR_BLACK);
353 raster_view(p, &s->view);
354 raster_projection(p, &s->projection);
355 raster_eye(p, s->eye);
356
357 raster_light(p, light_new(COLOR_WHITE, vec_new(S(5.0), S(3.0), S(6.0))));
358 raster_light(p, light_new(COLOR_MAGENTA, vec_new(S(-2.0), S(2.0), S(-2.0))));
359
360 #if RENDER_PROGRESS
361 #define PROGRESS_FMT "\033[80D\033[2K %s\t %9d / %d"
362 int tri;
363 printf("render scene:\n");
364 #endif
365
366 for (list_t* gi = s->groups; gi; gi = gi->link) {
367 _group_t* g = (_group_t*)gi->val;
368 raster_model(p, &g->model);
369 IF_RENDER_PROGRESS(tri = 0);
370 for (list_t* ti = g->triangles; ti; ti = ti->link) {
371 #if RENDER_PROGRESS
372 if (++tri % 100 == 0) {
373 printf(PROGRESS_FMT, g->name, tri, g->count);
374 fflush(stdout);
375 }
376 #endif
377 raster_draw_tri(p, (tri_t*)ti->val);
378 }
379 #if RENDER_PROGRESS
380 printf(PROGRESS_FMT"\n", g->name, tri, g->count);
381 #endif
382 }
383 IF_RENDER_PROGRESS(printf("render complete!\n"));
384
385 #if RENDER_TIMER
386 long dt = timer_stop();
387 printf("time\t%.3fms\n", (float)dt / 1000.0f);
388 #endif
389
390 return p;
391 }
392
This page took 0.063478 seconds and 4 git commands to generate.