add external supersampling to animate script
[chaz/rasterize] / raster.c
1
2 /*
3 * CS5600 University of Utah
4 * Charles McGarvey
5 * mcgarvey@eng.utah.edu
6 */
7
8 #include <assert.h>
9 #include <errno.h>
10 #include <stdio.h>
11 #include <string.h>
12
13 #include "list.h"
14 #include "raster.h"
15
16
17 struct raster
18 {
19 color_t* pixels;
20 scal_t* zbuf;
21 int w, h;
22 int left, right, bottom, top;
23 mat_t model;
24 mat_t view;
25 mat_t projection;
26 mat_t modelviewprojection;
27 mat_t viewport;
28 bool dirty;
29 #if LIGHTING
30 list_t* lights;
31 color_t ambient;
32 vec_t eye;
33 color_t specular;
34 scal_t shininess;
35 #endif
36 #if VERBOSITY >= 2
37 unsigned total;
38 unsigned clipped;
39 unsigned culled;
40 #endif
41 };
42
43
44 raster_t* raster_alloc(int width, int height, color_t fill)
45 {
46 assert(0 < width && 0 < height && "zero-dimension raster not allowed");
47 size_t size = width * height;
48
49 raster_t* p = (raster_t*)mem_alloc(sizeof(raster_t));
50 p->pixels = (color_t*)mem_alloc(sizeof(color_t) * size);
51 p->w = width;
52 p->h = height;
53 raster_clear(p, fill);
54 raster_viewport(p, 0, 0, width, height);
55 p->model = p->view = p->projection = MAT_IDENTITY;
56 p->dirty = false;
57
58 #if LIGHTING
59 p->ambient = color_new(S(0.2), S(0.2), S(0.2), S(1.0));
60 p->lights = NULL;
61 p->specular = COLOR_WHITE;
62 p->shininess = S(1.0);
63 #endif
64
65 p->zbuf = (scal_t*)mem_alloc(sizeof(scal_t) * size);
66 for (size_t i = 0; i < size; ++i) {
67 p->zbuf[i] = S(1.0);
68 }
69
70 return p;
71 }
72
73 void raster_destroy(raster_t* p)
74 {
75 mem_free(p->pixels);
76 mem_free(p->zbuf);
77 #if LIGHTING
78 list_destroy(&p->lights);
79 #endif
80 mem_free(p);
81 }
82
83
84 void raster_printstats(raster_t* p)
85 {
86 #if VERBOSITY >= 2
87 unsigned drawn = p->total - p->clipped - p->culled;
88 float percent = 100.0f * (float)drawn / (float)p->total;
89 printf("culled\t%u\n"
90 "clipped\t%u\n"
91 "drawn\t%u (%6.2f%%)\n"
92 "total\t%u\n", p->culled, p->clipped, drawn, percent, p->total);
93 #endif
94 }
95
96
97 void raster_clear(raster_t* p, color_t fill)
98 {
99 size_t size = p->w * p->h;
100 for (int i = 0; i < size; ++i) {
101 p->pixels[i] = fill;
102 }
103 #if VERBOSITY >= 2
104 p->total = 0;
105 p->clipped = 0;
106 p->culled = 0;
107 #endif
108 }
109
110
111 void raster_viewport(raster_t* p, int x, int y, int width, int height)
112 {
113 p->left = x;
114 p->right = x + width;
115 p->bottom = y;
116 p->top = y + height;
117 p->viewport = MAT_VIEWPORT(x, y, width, height);
118 }
119
120 void raster_model(raster_t* p, const mat_t* transform)
121 {
122 p->model = *transform;
123 p->dirty = true;
124 }
125
126 void raster_view(raster_t* p, const mat_t* transform)
127 {
128 p->view = *transform;
129 p->dirty = true;
130 }
131
132 void raster_projection(raster_t* p, const mat_t* transform)
133 {
134 p->projection = *transform;
135 p->dirty = true;
136 }
137
138
139 void raster_eye(raster_t* p, vec_t eye)
140 {
141 #if LIGHTING
142 p->eye = eye;
143 #endif
144 }
145
146 void raster_ambient(raster_t* p, color_t ambient)
147 {
148 #if LIGHTING
149 p->ambient = ambient;
150 #endif
151 }
152
153 void raster_light(raster_t* p, light_t light)
154 {
155 #if LIGHTING
156 light_t* l = light_copy(light);
157 list_push2(&p->lights, l, mem_free);
158 #endif
159 }
160
161 void raster_material(raster_t* p, color_t specular, scal_t shininess)
162 {
163 #if LIGHTING
164 p->specular = specular;
165 p->shininess = shininess;
166 #endif
167 }
168
169
170 #define _CHECK_WRITE(X) if ((X) <= 0) goto fail
171
172 int raster_export_ppm(const raster_t* p, const char* filename)
173 {
174 FILE* file = fopen(filename, "w");
175 if (file == NULL) {
176 fail: fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno));
177 return -1;
178 }
179
180 _CHECK_WRITE(fprintf(file, "P3\n%u %u\n255\n", p->w, p->h));
181 for (int y = (int)p->h - 1; y >= 0; --y) {
182 for (int x = 0; x < p->w; ++x) {
183 rgbachan_t r, g, b;
184 color_split(p->pixels[y * p->w + x], &r, &g, &b, NULL);
185 _CHECK_WRITE(fprintf(file, "%hhu %hhu %hhu\n", r, g, b));
186 }
187 }
188
189 fclose(file);
190 return 0;
191 }
192
193 int raster_export_bmp(const raster_t* p, const char* filename)
194 {
195 /*
196 * This function was adapted from sample code provided with the assignment
197 * instructions.
198 */
199 FILE* file = fopen(filename, "wb");
200 if (file == NULL) {
201 fail: fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno));
202 return -1;
203 }
204
205 uint16_t magicNumber = 0x4D42;
206 uint16_t reserved0 = 0;//0x4D41;
207 uint16_t reserved1 = 0;//0x5454;
208 uint32_t dataOffset = 54;
209 uint32_t infoHeaderSize = 40;
210 uint32_t width = p->w;
211 uint32_t height = p->h;
212 uint16_t colorPlanes = 1;
213 uint16_t bitsPerPixel = 32;
214 uint32_t compression = 0;
215 uint32_t dataSize = width * height * bitsPerPixel / 8;
216 uint32_t horizontalResolution = 2835;
217 uint32_t verticalResolution = 2835;
218 uint32_t paletteColorCount = 0;
219 uint32_t importantPaletteColorCount = 0;
220 uint32_t fileSize = 54 + dataSize;
221
222 /*
223 * Check the return values to avoid loud warnings.
224 */
225 _CHECK_WRITE(fwrite(&magicNumber, sizeof(magicNumber), 1, file));
226 _CHECK_WRITE(fwrite(&fileSize, sizeof(fileSize), 1, file));
227 _CHECK_WRITE(fwrite(&reserved0, sizeof(reserved0), 1, file));
228 _CHECK_WRITE(fwrite(&reserved1, sizeof(reserved1), 1, file));
229 _CHECK_WRITE(fwrite(&dataOffset, sizeof(dataOffset), 1, file));
230 _CHECK_WRITE(fwrite(&infoHeaderSize, sizeof(infoHeaderSize), 1, file));
231 _CHECK_WRITE(fwrite(&width, sizeof(width), 1, file));
232 _CHECK_WRITE(fwrite(&height, sizeof(height), 1, file));
233 _CHECK_WRITE(fwrite(&colorPlanes, sizeof(colorPlanes), 1, file));
234 _CHECK_WRITE(fwrite(&bitsPerPixel, sizeof(bitsPerPixel), 1, file));
235 _CHECK_WRITE(fwrite(&compression, sizeof(compression), 1, file));
236 _CHECK_WRITE(fwrite(&dataSize, sizeof(dataSize), 1, file));
237 _CHECK_WRITE(fwrite(&horizontalResolution, sizeof(horizontalResolution), 1, file));
238 _CHECK_WRITE(fwrite(&verticalResolution, sizeof(verticalResolution), 1, file));
239 _CHECK_WRITE(fwrite(&paletteColorCount, sizeof(paletteColorCount), 1, file));
240 _CHECK_WRITE(fwrite(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file));
241
242 size_t size = width * height;
243 for (int i = 0; i < size; ++i)
244 {
245 rgbachan_t a, r, g, b;
246 color_split(p->pixels[i], &r, &g, &b, &a);
247 uint32_t argb = PACK(argb, 3, a);
248 argb = PACK(argb, 2, r);
249 argb = PACK(argb, 1, g);
250 argb = PACK(argb, 0, b);
251 _CHECK_WRITE(fwrite(&argb, sizeof(argb), 1, file));
252 }
253
254 fclose(file);
255 return 0;
256 }
257
258 #undef _CHECK_WRITE
259
260
261 void raster_draw_model(raster_t* p, const model_t* model)
262 {
263 #if VERBOSITY >= 4
264 #define PROGRESS_FMT "\033[80D\033[2K %s\t %9d / %d"
265 int tri;
266 #endif
267
268 model_transformation(model, &p->model);
269 p->dirty = true;
270 raster_material(p, model_specular(model), model_shininess(model));
271 IF_RENDER_PROGRESS(tri = 0);
272 for (const list_t* ti = model_geometry(model); ti; ti = ti->link) {
273 #if VERBOSITY >= 4
274 if (++tri % 100 == 0) {
275 printf(PROGRESS_FMT, model_name(model), tri, model_size(model));
276 fflush(stdout);
277 }
278 #endif
279 raster_draw_tri(p, (tri_t*)ti->val);
280 }
281 #if VERBOSITY >= 4
282 printf(PROGRESS_FMT"\n", model_name(model), tri, model_size(model));
283 #endif
284 }
285
286
287 /*
288 * See if the triangle is at all visible in the viewport. Also, minimize the
289 * rectangle around the area that includes the triangle.
290 */
291 INLINE_MAYBE
292 bool _try_clip(tri_t t, int* left, int* right, int* bottom, int* top)
293 {
294 #if CLIPPING
295 aabb_t box = tri_aabb(t);
296 if (box.min.z < S(-1.0) || S(1.0) < box.max.z) {
297 return false;
298 }
299 *left = imax((int)scal_floor(box.min.x), *left);
300 *right = imin((int)scal_ceil(box.max.x), *right);
301 if (*right <= *left) {
302 return false;
303 }
304 *bottom = imax((int)scal_floor(box.min.y), *bottom);
305 *top = imin((int)scal_ceil(box.max.y), *top);
306 if (*top <= *bottom) {
307 return false;
308 }
309 #endif // CLIPPING
310 return true;
311 }
312
313 /*
314 * See whether or not we need to draw based on the orientation of the
315 * triangle.
316 */
317 INLINE_MAYBE
318 bool _try_cull_backface(tri_t t)
319 {
320 #if BACKFACE_CULLING
321 vec_t n = tri_normal(t);
322 if (n.z < S(0.0)) {
323 return false;
324 }
325 #endif
326 return true;
327 }
328
329 /*
330 * Determine what color is associated with the given vertex.
331 */
332 INLINE_MAYBE
333 color_t _do_phong_lighting(raster_t* p, vert_t vert)
334 {
335 #if LIGHTING
336 color_t color = COLOR_BLACK;
337 for (list_t* i = p->lights; i; i = i->link) {
338 light_t light = *(light_t*)i->val;
339 vec_t mpos = vert.v;
340 vec_t lpos = light.v;
341 vec_t vpos = p->eye;
342 vec_t n = vert.n;
343 vec_t l = vec_normalize(vec_sub(lpos, mpos));
344 vec_t r = vec_normalize(vec_sub(vec_scale(n, S(2.0) * vec_dot(n, l)), l));
345 vec_t v = vec_normalize(vec_sub(vpos, mpos));
346
347 scal_t kd = scal_max(vec_dot(l, n), S(0.0));
348 color_t Id = color_scale2(light.d, vert.c, kd);
349 scal_t ks = scal_pow(scal_max(vec_dot(r, v), S(0.0)), p->shininess);
350 color_t Is = color_scale2(light.s, p->specular, ks);
351
352 color = color_add2(color, Id, Is);
353 }
354 color_t Ia = color_mult(p->ambient, vert.c);
355 return color_clamp(color_add(color, Ia));
356 #else
357 return vert.c;
358 #endif // LIGHTING
359 }
360
361 void raster_draw_tri(raster_t* p, const tri_t* triangle)
362 {
363 IF_RASTER_STATS(++p->total);
364 tri_t t = *triangle;
365
366 // need to recalculate the model-view-projection matrix if any one of its
367 // composing matrices have been changed
368 if (p->dirty) {
369 p->modelviewprojection = mat_mult(p->view, p->model);
370 p->modelviewprojection = mat_mult(p->projection, p->modelviewprojection);
371 p->dirty = false;
372 }
373 t = tri_transform(t, p->modelviewprojection);
374 t = tri_homodiv(t);
375
376 if (!_try_cull_backface(t)) {
377 IF_RASTER_STATS(++p->culled);
378 return;
379 }
380
381 t = tri_transform(t, p->viewport);
382
383 int left = p->left;
384 int right = p->right;
385 int bottom = p->bottom;
386 int top = p->top;
387
388 if (!_try_clip(t, &left, &right, &bottom, &top)) {
389 IF_RASTER_STATS(++p->clipped);
390 return;
391 }
392
393 #if LIGHTING >= 1
394 tri_t tl = tri_transform(*triangle, p->model);
395 #endif
396
397 #if LIGHTING == 1
398 vert_t tv = vert_new(tri_midpoint(tl));
399 tv.n = vec_normalize(tri_normal(tl));
400 tv.c = tri_color(tl);
401 color_t color = _do_phong_lighting(p, tv);
402 #elif LIGHTING == 2 && SMOOTH_COLOR
403 color_t color1 = _do_phong_lighting(p, tl.a);
404 color_t color2 = _do_phong_lighting(p, tl.b);
405 color_t color3 = _do_phong_lighting(p, tl.c);
406 #elif LIGHTING == 2 && !SMOOTH_COLOR
407 color_t c = tri_color(t);
408 tl.a.c = tl.b.c = tl.c.c = c;
409 color_t color1 = _do_phong_lighting(p, tl.a);
410 color_t color2 = _do_phong_lighting(p, tl.b);
411 color_t color3 = _do_phong_lighting(p, tl.c);
412 #elif !LIGHTING && SMOOTH_COLOR
413 color_t color1 = t.a.c;
414 color_t color2 = t.b.c;
415 color_t color3 = t.c.c;
416 #else
417 color_t color = tri_color(t);
418 #endif
419
420 for (int y = bottom; y < top; ++y) {
421 for (int x = left; x < right; ++x) {
422 vec_t v = vec_new((scal_t)x, (scal_t)y, S(0.0));
423 scal_t b[3];
424 if (tri_barycentric(t, b, v)) {
425 #if DEPTH_TEST
426 v.z = tri_z(t, b);
427 scal_t* n = p->zbuf + y * p->w + x;
428 if (S(-1.0) < v.z && v.z < *n) {
429 #endif
430 color_t* c = p->pixels + y * p->w + x;
431
432 #if LIGHTING == 2 || (!LIGHTING && SMOOTH_COLOR)
433 *c = color_interp2(color1, color2, color3, b);
434 #elif LIGHTING == 3 && SMOOTH_COLOR
435 *c = _do_phong_lighting(p, tri_interp(tl, b));
436 #elif LIGHTING == 3 && !SMOOTH_COLOR
437 vert_t d = vert_new(tri_point(t, b));
438 d.c = tri_color(t);
439 d.n = tri_normal2(t, b);
440 *c = _do_phong_lighting(p, d);
441 #else
442 *c = color;
443 #endif
444
445 #if DEPTH_TEST
446 *n = v.z;
447 }
448 #endif
449 }
450 }
451 }
452 }
453
This page took 0.064905 seconds and 4 git commands to generate.