]> Dogcows Code - chaz/rasterize/blob - raster.c
make animate script work for modern luas
[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 const model_t* current;
30 #if LIGHTING
31 list_t* lights;
32 color_t ambient;
33 vec_t eye;
34 color_t specular;
35 scal_t shininess;
36 #endif
37 #if VERBOSITY >= 2
38 unsigned total;
39 unsigned clipped;
40 unsigned culled;
41 #endif
42 };
43
44
45 raster_t* raster_alloc(int width, int height, color_t fill)
46 {
47 assert(0 < width && 0 < height && "zero-dimension raster not allowed");
48 size_t size = width * height;
49
50 raster_t* p = (raster_t*)mem_alloc(sizeof(raster_t));
51 p->pixels = (color_t*)mem_alloc(sizeof(color_t) * size);
52 p->w = width;
53 p->h = height;
54 raster_clear(p, fill);
55 raster_viewport(p, 0, 0, width, height);
56 p->model = p->view = p->projection = MAT_IDENTITY;
57 p->dirty = false;
58 p->current = NULL;
59
60 #if LIGHTING
61 p->ambient = color_new(S(0.2), S(0.2), S(0.2), S(1.0));
62 p->lights = NULL;
63 p->specular = COLOR_WHITE;
64 p->shininess = S(1.0);
65 #endif
66
67 p->zbuf = (scal_t*)mem_alloc(sizeof(scal_t) * size);
68 for (size_t i = 0; i < size; ++i) {
69 p->zbuf[i] = S(1.0);
70 }
71
72 return p;
73 }
74
75 void raster_destroy(raster_t* p)
76 {
77 mem_free(p->pixels);
78 mem_free(p->zbuf);
79 #if LIGHTING
80 list_destroy(&p->lights);
81 #endif
82 mem_free(p);
83 }
84
85
86 void raster_printstats(raster_t* p)
87 {
88 #if VERBOSITY >= 2
89 unsigned drawn = p->total - p->clipped - p->culled;
90 float percent = 100.0f * (float)drawn / (float)p->total;
91 printf("culled\t%u\n"
92 "clipped\t%u\n"
93 "drawn\t%u (%6.2f%%)\n"
94 "total\t%u\n", p->culled, p->clipped, drawn, percent, p->total);
95 #endif
96 }
97
98
99 color_t raster_color(const raster_t* p, vec_t pt)
100 {
101 int u = (int)((scal_t)p->w * pt.x);
102 int v = (int)((scal_t)p->h * pt.y);
103 return p->pixels[p->w * (p->h - v - 1) + u];
104 }
105
106
107 void raster_clear(raster_t* p, color_t fill)
108 {
109 size_t size = p->w * p->h;
110 for (int i = 0; i < size; ++i) {
111 p->pixels[i] = fill;
112 }
113 #if VERBOSITY >= 2
114 p->total = 0;
115 p->clipped = 0;
116 p->culled = 0;
117 #endif
118 }
119
120
121 void raster_viewport(raster_t* p, int x, int y, int width, int height)
122 {
123 p->left = x;
124 p->right = x + width;
125 p->bottom = y;
126 p->top = y + height;
127 p->viewport = MAT_VIEWPORT(x, y, width, height);
128 }
129
130 void raster_model(raster_t* p, const mat_t* transform)
131 {
132 p->model = *transform;
133 p->dirty = true;
134 }
135
136 void raster_view(raster_t* p, const mat_t* transform)
137 {
138 p->view = *transform;
139 p->dirty = true;
140 }
141
142 void raster_projection(raster_t* p, const mat_t* transform)
143 {
144 p->projection = *transform;
145 p->dirty = true;
146 }
147
148
149 void raster_eye(raster_t* p, vec_t eye)
150 {
151 #if LIGHTING
152 p->eye = eye;
153 #endif
154 }
155
156 void raster_ambient(raster_t* p, color_t ambient)
157 {
158 #if LIGHTING
159 p->ambient = ambient;
160 #endif
161 }
162
163 void raster_light(raster_t* p, light_t light)
164 {
165 #if LIGHTING
166 light_t* l = light_copy(light);
167 list_push2(&p->lights, l, mem_free);
168 #endif
169 }
170
171 void raster_material(raster_t* p, color_t specular, scal_t shininess)
172 {
173 #if LIGHTING
174 p->specular = specular;
175 p->shininess = shininess;
176 #endif
177 }
178
179
180 #define _DO_OR_DIE(X) if ((X) <= 0) goto fail
181
182 int raster_export_ppm(const raster_t* p, const char* filename)
183 {
184 FILE* file = fopen(filename, "w");
185 if (file == NULL) {
186 fail: fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno));
187 return -1;
188 }
189
190 _DO_OR_DIE(fprintf(file, "P3\n%u %u\n255\n", p->w, p->h));
191 for (int y = (int)p->h - 1; y >= 0; --y) {
192 for (int x = 0; x < p->w; ++x) {
193 rgbachan_t r, g, b;
194 color_split(p->pixels[y * p->w + x], &r, &g, &b, NULL);
195 _DO_OR_DIE(fprintf(file, "%hhu %hhu %hhu\n", r, g, b));
196 }
197 }
198
199 fclose(file);
200 return 0;
201 }
202
203 int raster_export_bmp(const raster_t* p, const char* filename)
204 {
205 /*
206 * This function was adapted from sample code provided with the assignment
207 * instructions.
208 */
209 FILE* file = fopen(filename, "wb");
210 if (file == NULL) {
211 fail: fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno));
212 return -1;
213 }
214
215 uint16_t magicNumber = 0x4D42;
216 uint16_t reserved0 = 0;//0x4D41;
217 uint16_t reserved1 = 0;//0x5454;
218 uint32_t dataOffset = 54;
219 uint32_t infoHeaderSize = 40;
220 uint32_t width = p->w;
221 uint32_t height = p->h;
222 uint16_t colorPlanes = 1;
223 uint16_t bitsPerPixel = 32;
224 uint32_t compression = 0;
225 uint32_t dataSize = width * height * bitsPerPixel / 8;
226 uint32_t horizontalResolution = 2835;
227 uint32_t verticalResolution = 2835;
228 uint32_t paletteColorCount = 0;
229 uint32_t importantPaletteColorCount = 0;
230 uint32_t fileSize = 54 + dataSize;
231
232 /*
233 * Check the return values to avoid loud warnings.
234 */
235 _DO_OR_DIE(fwrite(&magicNumber, sizeof(magicNumber), 1, file));
236 _DO_OR_DIE(fwrite(&fileSize, sizeof(fileSize), 1, file));
237 _DO_OR_DIE(fwrite(&reserved0, sizeof(reserved0), 1, file));
238 _DO_OR_DIE(fwrite(&reserved1, sizeof(reserved1), 1, file));
239 _DO_OR_DIE(fwrite(&dataOffset, sizeof(dataOffset), 1, file));
240 _DO_OR_DIE(fwrite(&infoHeaderSize, sizeof(infoHeaderSize), 1, file));
241 _DO_OR_DIE(fwrite(&width, sizeof(width), 1, file));
242 _DO_OR_DIE(fwrite(&height, sizeof(height), 1, file));
243 _DO_OR_DIE(fwrite(&colorPlanes, sizeof(colorPlanes), 1, file));
244 _DO_OR_DIE(fwrite(&bitsPerPixel, sizeof(bitsPerPixel), 1, file));
245 _DO_OR_DIE(fwrite(&compression, sizeof(compression), 1, file));
246 _DO_OR_DIE(fwrite(&dataSize, sizeof(dataSize), 1, file));
247 _DO_OR_DIE(fwrite(&horizontalResolution, sizeof(horizontalResolution), 1, file));
248 _DO_OR_DIE(fwrite(&verticalResolution, sizeof(verticalResolution), 1, file));
249 _DO_OR_DIE(fwrite(&paletteColorCount, sizeof(paletteColorCount), 1, file));
250 _DO_OR_DIE(fwrite(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file));
251
252 size_t size = width * height;
253 for (int i = 0; i < size; ++i)
254 {
255 rgbachan_t a, r, g, b;
256 color_split(p->pixels[i], &r, &g, &b, &a);
257 uint32_t argb = PACK(argb, 3, a);
258 argb = PACK(argb, 2, r);
259 argb = PACK(argb, 1, g);
260 argb = PACK(argb, 0, b);
261 _DO_OR_DIE(fwrite(&argb, sizeof(argb), 1, file));
262 }
263
264 fclose(file);
265 return 0;
266 }
267
268
269 raster_t* raster_import(const char* filename)
270 {
271 int type = 0;
272 char* ext = strrchr(filename, '.');
273 if (ext == NULL) {
274 goto fail;
275 }
276 ++ext;
277
278 if (strcmp(ext, "bmp") == 0) {
279 return raster_import_bmp(filename);
280 }
281 if (strcmp(ext, "ppm") == 0) {
282 return raster_import_ppm(filename);
283 }
284
285 fail:
286 fprintf(stderr, "Unknown file type: %s", filename);
287 return NULL;
288 }
289
290 raster_t* raster_import_ppm(const char* filename)
291 {
292 FILE* file = fopen(filename, "r");
293 if (file == NULL) {
294 fprintf(stderr, "Cannot read from %s: %s\n", filename, strerror(errno));
295 return NULL;
296 }
297
298 int w, h;
299 if (fscanf(file, "P3 %d %d 255 ", &w, &h) != 2) {
300 fprintf(stderr, "Cannot read header from %s: %s\n", filename, strerror(errno));
301 return NULL;
302 }
303
304 raster_t* p = raster_alloc(w, h, COLOR_WHITE);
305
306 for (int y = h - 1; y >= 0; --y) {
307 for (int x = 0; x < w; ++x) {
308 uint16_t r, g, b;
309 /* mingw32 does not like %hhu conversion type */
310 if (fscanf(file, "%hu %hu %hu ", &r, &g, &b) != 3) {
311 fprintf(stderr, "Failed reading color values from %s: %s\n", filename, strerror(errno));
312 return NULL;
313 }
314 rgba_t rgba = PACK(rgba, 3, (uint8_t)r);
315 rgba = PACK(rgba, 2, (uint8_t)g);
316 rgba = PACK(rgba, 1, (uint8_t)b);
317 rgba = PACK(rgba, 0, 255);
318 p->pixels[y * w + x] = color_from_rgba(rgba);
319 }
320 }
321
322 fclose(file);
323 return p;
324
325 fail:
326 raster_destroy(p);
327 fclose(file);
328 fprintf(stderr, "Unexpected file format in %s: %s\n", filename, strerror(errno));
329 return NULL;
330 }
331
332 raster_t* raster_import_bmp(const char* filename)
333 {
334 FILE* file = fopen(filename, "rb");
335 if (file == NULL) {
336 fprintf(stderr, "Cannot read from %s: %s\n", filename, strerror(errno));
337 return NULL;
338 }
339
340 uint16_t magicNumber;
341 uint16_t reserved0;//0x4D41;
342 uint16_t reserved1;//0x5454;
343 uint32_t dataOffset;
344 uint32_t infoHeaderSize;
345 uint32_t width;
346 uint32_t height;
347 uint16_t colorPlanes;
348 uint16_t bitsPerPixel;
349 uint32_t compression;
350 uint32_t dataSize;
351 uint32_t horizontalResolution;
352 uint32_t verticalResolution;
353 uint32_t paletteColorCount;
354 uint32_t importantPaletteColorCount;
355 uint32_t fileSize;
356
357 raster_t* p = NULL;
358
359 _DO_OR_DIE(fread(&magicNumber, sizeof(magicNumber), 1, file));
360 _DO_OR_DIE(fread(&fileSize, sizeof(fileSize), 1, file));
361 _DO_OR_DIE(fread(&reserved0, sizeof(reserved0), 1, file));
362 _DO_OR_DIE(fread(&reserved1, sizeof(reserved1), 1, file));
363 _DO_OR_DIE(fread(&dataOffset, sizeof(dataOffset), 1, file));
364 _DO_OR_DIE(fread(&infoHeaderSize, sizeof(infoHeaderSize), 1, file));
365 _DO_OR_DIE(fread(&width, sizeof(width), 1, file));
366 _DO_OR_DIE(fread(&height, sizeof(height), 1, file));
367 _DO_OR_DIE(fread(&colorPlanes, sizeof(colorPlanes), 1, file));
368 _DO_OR_DIE(fread(&bitsPerPixel, sizeof(bitsPerPixel), 1, file));
369 _DO_OR_DIE(fread(&compression, sizeof(compression), 1, file));
370 _DO_OR_DIE(fread(&dataSize, sizeof(dataSize), 1, file));
371 _DO_OR_DIE(fread(&horizontalResolution, sizeof(horizontalResolution), 1, file));
372 _DO_OR_DIE(fread(&verticalResolution, sizeof(verticalResolution), 1, file));
373 _DO_OR_DIE(fread(&paletteColorCount, sizeof(paletteColorCount), 1, file));
374 _DO_OR_DIE(fread(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file));
375
376 p = raster_alloc((int)width, (int)height, COLOR_WHITE);
377
378 size_t size = width * height;
379 for (int i = 0; i < size; ++i)
380 {
381 uint32_t argb;
382 _DO_OR_DIE(fread(&argb, sizeof(argb), 1, file));
383 rgba_t rgba = PACK(rgba, 3, UNPACK(argb, 2));
384 rgba = PACK(rgba, 2, UNPACK(argb, 1));
385 rgba = PACK(rgba, 1, UNPACK(argb, 0));
386 rgba = PACK(rgba, 0, UNPACK(argb, 3));
387 p->pixels[i] = color_from_rgba(rgba);
388 }
389
390 fclose(file);
391 return p;
392
393 fail:
394 if (p) {
395 raster_destroy(p);
396 }
397 fclose(file);
398 fprintf(stderr, "Unexpected file format in %s: %s\n", filename, strerror(errno));
399 return NULL;
400 }
401
402 #undef _DO_OR_DIE
403
404
405 void raster_draw_model(raster_t* p, const model_t* model)
406 {
407 #if VERBOSITY >= 4
408 #define PROGRESS_FMT "\033[80D\033[2K %s\t %9d / %d"
409 int tri;
410 #endif
411
412 model_transformation(model, &p->model);
413 p->dirty = true;
414 raster_material(p, model_specular(model), model_shininess(model));
415 p->current = model;
416 IF_RENDER_PROGRESS(tri = 0);
417 for (const list_t* ti = model_geometry(model); ti; ti = ti->link) {
418 #if VERBOSITY >= 4
419 if (++tri % 100 == 0) {
420 printf(PROGRESS_FMT, model_name(model), tri, model_size(model));
421 fflush(stdout);
422 }
423 #endif
424 raster_draw_tri(p, (tri_t*)ti->val);
425 }
426 #if VERBOSITY >= 4
427 printf(PROGRESS_FMT"\n", model_name(model), tri, model_size(model));
428 #endif
429 }
430
431
432 /*
433 * See if the triangle is at all visible in the viewport. Also, minimize the
434 * rectangle around the area that includes the triangle.
435 */
436 INLINE_MAYBE
437 bool _try_clip(tri_t t, int* left, int* right, int* bottom, int* top)
438 {
439 #if CLIPPING
440 aabb_t box = tri_aabb(t);
441 if (box.min.z < S(-1.0) || S(1.0) < box.max.z) {
442 return false;
443 }
444 *left = imax((int)scal_floor(box.min.x), *left);
445 *right = imin((int)scal_ceil(box.max.x), *right);
446 if (*right <= *left) {
447 return false;
448 }
449 *bottom = imax((int)scal_floor(box.min.y), *bottom);
450 *top = imin((int)scal_ceil(box.max.y), *top);
451 if (*top <= *bottom) {
452 return false;
453 }
454 #endif // CLIPPING
455 return true;
456 }
457
458 /*
459 * See whether or not we need to draw based on the orientation of the
460 * triangle.
461 */
462 INLINE_MAYBE
463 bool _try_cull_backface(tri_t t)
464 {
465 #if BACKFACE_CULLING
466 vec_t n = tri_normal(t);
467 if (n.z < S(0.0)) {
468 return false;
469 }
470 #endif
471 return true;
472 }
473
474 /*
475 * Determine what color is associated with the given vertex.
476 */
477 INLINE_MAYBE
478 color_t _do_phong_lighting(raster_t* p, vert_t vert)
479 {
480 #if TEXTURING
481 vert.c = color_mult(vert.c, model_tcolor(p->current, vert.t));
482 #endif
483 #if LIGHTING
484 color_t color = COLOR_BLACK;
485 color.a = vert.c.a;
486 for (list_t* i = p->lights; i; i = i->link) {
487 light_t light = *(light_t*)i->val;
488 vec_t mpos = vert.v;
489 vec_t lpos = light.v;
490 vec_t vpos = p->eye;
491 vec_t n = vert.n;
492 vec_t l = vec_normalize(vec_sub(lpos, mpos));
493 vec_t r = vec_normalize(vec_sub(vec_scale(n, S(2.0) * vec_dot(n, l)), l));
494 vec_t v = vec_normalize(vec_sub(vpos, mpos));
495
496 scal_t kd = scal_max(vec_dot(l, n), S(0.0));
497 color_t Id = color_scale2(light.d, vert.c, kd);
498 scal_t ks = scal_pow(scal_max(vec_dot(r, v), S(0.0)), p->shininess);
499 color_t Is = color_scale2(light.s, p->specular, ks);
500
501 color = color_add2(color, Id, Is);
502 }
503 color_t Ia = color_mult(p->ambient, vert.c);
504 return color_clamp(color_add(color, Ia));
505 #else
506 return vert.c;
507 #endif // LIGHTING
508 }
509
510 void raster_draw_tri(raster_t* p, const tri_t* triangle)
511 {
512 IF_RASTER_STATS(++p->total);
513 tri_t t = *triangle;
514
515 // need to recalculate the model-view-projection matrix if any one of its
516 // composing matrices have been changed
517 if (p->dirty) {
518 p->modelviewprojection = mat_mult(p->view, p->model);
519 p->modelviewprojection = mat_mult(p->projection, p->modelviewprojection);
520 p->dirty = false;
521 }
522
523 t = tri_transform(t, p->modelviewprojection);
524
525 // save w-values for texture mapping perspective correction
526 scal_t w1 = t.a.v.w;
527 scal_t w2 = t.b.v.w;
528 scal_t w3 = t.c.v.w;
529
530 t = tri_homodiv(t);
531
532 if (!_try_cull_backface(t)) {
533 IF_RASTER_STATS(++p->culled);
534 return;
535 }
536
537 t = tri_transform(t, p->viewport);
538
539 int left = p->left;
540 int right = p->right;
541 int bottom = p->bottom;
542 int top = p->top;
543
544 if (!_try_clip(t, &left, &right, &bottom, &top)) {
545 IF_RASTER_STATS(++p->clipped);
546 return;
547 }
548
549 #if LIGHTING >= 1
550 tri_t tl = tri_transform(*triangle, p->model);
551 tl.a.t.w = w1;
552 tl.b.t.w = w2;
553 tl.c.t.w = w3;
554 #endif
555
556 #if LIGHTING == 1
557 vert_t tv = vert_new(tri_midpoint(tl));
558 tv.n = vec_normalize(tri_normal(tl));
559 tv.c = tri_color(tl);
560 color_t color = _do_phong_lighting(p, tv);
561 #elif LIGHTING == 2 && SMOOTH_COLOR
562 color_t color1 = _do_phong_lighting(p, tl.a);
563 color_t color2 = _do_phong_lighting(p, tl.b);
564 color_t color3 = _do_phong_lighting(p, tl.c);
565 #elif LIGHTING == 2 && !SMOOTH_COLOR
566 color_t c = tri_color(t);
567 tl.a.c = tl.b.c = tl.c.c = c;
568 color_t color1 = _do_phong_lighting(p, tl.a);
569 color_t color2 = _do_phong_lighting(p, tl.b);
570 color_t color3 = _do_phong_lighting(p, tl.c);
571 #elif !LIGHTING && SMOOTH_COLOR
572 color_t color1 = t.a.c;
573 color_t color2 = t.b.c;
574 color_t color3 = t.c.c;
575 #else
576 color_t color = tri_color(t);
577 #endif
578
579 for (int y = bottom; y < top; ++y) {
580 for (int x = left; x < right; ++x) {
581 vec_t v = vec_new((scal_t)x, (scal_t)y, S(0.0));
582 scal_t b[3];
583 if (tri_barycentric(t, b, v)) {
584 #if DEPTH_TEST
585 v.z = tri_z(t, b);
586 scal_t* n = p->zbuf + y * p->w + x;
587 if (S(-1.0) < v.z && v.z < *n) {
588 #endif
589 color_t* c = p->pixels + y * p->w + x;
590 color_t newC;
591
592 #if LIGHTING == 2 || (!LIGHTING && SMOOTH_COLOR)
593 newC = color_interp2(color1, color2, color3, b);
594 #elif LIGHTING == 3 && SMOOTH_COLOR
595 newC = _do_phong_lighting(p, tri_interp(tl, b));
596 #elif LIGHTING == 3 && !SMOOTH_COLOR
597 vert_t d = vert_new(tri_point(t, b));
598 d.c = tri_color(t);
599 d.n = tri_normal2(t, b);
600 newC = _do_phong_lighting(p, d);
601 #else
602 newC = color;
603 #endif
604
605 #if BLENDING
606 *c = color_blend(*c, newC);
607 #else
608 *c = newC;
609 #endif
610
611 #if DEPTH_TEST
612 *n = v.z;
613 }
614 #endif
615 }
616 }
617 }
618 }
619
This page took 0.058942 seconds and 4 git commands to generate.