]> Dogcows Code - chaz/rasterize/blob - raster.c
add texture mapping with perspective correction
[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 rgbachan_t r, g, b;
309 if (fscanf(file, "%hhu %hhu %hhu ", &r, &g, &b) != 3) {
310 fprintf(stderr, "Failed reading color values from %s: %s\n", filename, strerror(errno));
311 return NULL;
312 }
313 rgba_t rgba = PACK(rgba, 3, r);
314 rgba = PACK(rgba, 2, g);
315 rgba = PACK(rgba, 1, b);
316 rgba = PACK(rgba, 0, 255);
317 p->pixels[y * w + x] = color_from_rgba(rgba);
318 }
319 }
320
321 fclose(file);
322 return p;
323
324 fail:
325 raster_destroy(p);
326 fclose(file);
327 fprintf(stderr, "Unexpected file format in %s: %s\n", filename, strerror(errno));
328 return NULL;
329 }
330
331 raster_t* raster_import_bmp(const char* filename)
332 {
333 FILE* file = fopen(filename, "rb");
334 if (file == NULL) {
335 fprintf(stderr, "Cannot read from %s: %s\n", filename, strerror(errno));
336 return NULL;
337 }
338
339 uint16_t magicNumber;
340 uint16_t reserved0;//0x4D41;
341 uint16_t reserved1;//0x5454;
342 uint32_t dataOffset;
343 uint32_t infoHeaderSize;
344 uint32_t width;
345 uint32_t height;
346 uint16_t colorPlanes;
347 uint16_t bitsPerPixel;
348 uint32_t compression;
349 uint32_t dataSize;
350 uint32_t horizontalResolution;
351 uint32_t verticalResolution;
352 uint32_t paletteColorCount;
353 uint32_t importantPaletteColorCount;
354 uint32_t fileSize;
355
356 raster_t* p = NULL;
357
358 _DO_OR_DIE(fread(&magicNumber, sizeof(magicNumber), 1, file));
359 _DO_OR_DIE(fread(&fileSize, sizeof(fileSize), 1, file));
360 _DO_OR_DIE(fread(&reserved0, sizeof(reserved0), 1, file));
361 _DO_OR_DIE(fread(&reserved1, sizeof(reserved1), 1, file));
362 _DO_OR_DIE(fread(&dataOffset, sizeof(dataOffset), 1, file));
363 _DO_OR_DIE(fread(&infoHeaderSize, sizeof(infoHeaderSize), 1, file));
364 _DO_OR_DIE(fread(&width, sizeof(width), 1, file));
365 _DO_OR_DIE(fread(&height, sizeof(height), 1, file));
366 _DO_OR_DIE(fread(&colorPlanes, sizeof(colorPlanes), 1, file));
367 _DO_OR_DIE(fread(&bitsPerPixel, sizeof(bitsPerPixel), 1, file));
368 _DO_OR_DIE(fread(&compression, sizeof(compression), 1, file));
369 _DO_OR_DIE(fread(&dataSize, sizeof(dataSize), 1, file));
370 _DO_OR_DIE(fread(&horizontalResolution, sizeof(horizontalResolution), 1, file));
371 _DO_OR_DIE(fread(&verticalResolution, sizeof(verticalResolution), 1, file));
372 _DO_OR_DIE(fread(&paletteColorCount, sizeof(paletteColorCount), 1, file));
373 _DO_OR_DIE(fread(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file));
374
375 p = raster_alloc((int)width, (int)height, COLOR_WHITE);
376
377 size_t size = width * height;
378 for (int i = 0; i < size; ++i)
379 {
380 uint32_t argb;
381 _DO_OR_DIE(fread(&argb, sizeof(argb), 1, file));
382 rgba_t rgba = PACK(rgba, 3, UNPACK(argb, 2));
383 rgba = PACK(rgba, 2, UNPACK(argb, 1));
384 rgba = PACK(rgba, 1, UNPACK(argb, 0));
385 rgba = PACK(rgba, 0, UNPACK(argb, 3));
386 p->pixels[i] = color_from_rgba(rgba);
387 }
388
389 fclose(file);
390 return p;
391
392 fail:
393 if (p) {
394 raster_destroy(p);
395 }
396 fclose(file);
397 fprintf(stderr, "Unexpected file format in %s: %s\n", filename, strerror(errno));
398 return NULL;
399 }
400
401 #undef _DO_OR_DIE
402
403
404 void raster_draw_model(raster_t* p, const model_t* model)
405 {
406 #if VERBOSITY >= 4
407 #define PROGRESS_FMT "\033[80D\033[2K %s\t %9d / %d"
408 int tri;
409 #endif
410
411 model_transformation(model, &p->model);
412 p->dirty = true;
413 raster_material(p, model_specular(model), model_shininess(model));
414 p->current = model;
415 IF_RENDER_PROGRESS(tri = 0);
416 for (const list_t* ti = model_geometry(model); ti; ti = ti->link) {
417 #if VERBOSITY >= 4
418 if (++tri % 100 == 0) {
419 printf(PROGRESS_FMT, model_name(model), tri, model_size(model));
420 fflush(stdout);
421 }
422 #endif
423 raster_draw_tri(p, (tri_t*)ti->val);
424 }
425 #if VERBOSITY >= 4
426 printf(PROGRESS_FMT"\n", model_name(model), tri, model_size(model));
427 #endif
428 }
429
430
431 /*
432 * See if the triangle is at all visible in the viewport. Also, minimize the
433 * rectangle around the area that includes the triangle.
434 */
435 INLINE_MAYBE
436 bool _try_clip(tri_t t, int* left, int* right, int* bottom, int* top)
437 {
438 #if CLIPPING
439 aabb_t box = tri_aabb(t);
440 if (box.min.z < S(-1.0) || S(1.0) < box.max.z) {
441 return false;
442 }
443 *left = imax((int)scal_floor(box.min.x), *left);
444 *right = imin((int)scal_ceil(box.max.x), *right);
445 if (*right <= *left) {
446 return false;
447 }
448 *bottom = imax((int)scal_floor(box.min.y), *bottom);
449 *top = imin((int)scal_ceil(box.max.y), *top);
450 if (*top <= *bottom) {
451 return false;
452 }
453 #endif // CLIPPING
454 return true;
455 }
456
457 /*
458 * See whether or not we need to draw based on the orientation of the
459 * triangle.
460 */
461 INLINE_MAYBE
462 bool _try_cull_backface(tri_t t)
463 {
464 #if BACKFACE_CULLING
465 vec_t n = tri_normal(t);
466 if (n.z < S(0.0)) {
467 return false;
468 }
469 #endif
470 return true;
471 }
472
473 /*
474 * Determine what color is associated with the given vertex.
475 */
476 INLINE_MAYBE
477 color_t _do_phong_lighting(raster_t* p, vert_t vert)
478 {
479 #if TEXTURING
480 vert.c = color_mult(vert.c, model_tcolor(p->current, vert.t));
481 #endif
482 #if LIGHTING
483 color_t color = COLOR_BLACK;
484 color.a = vert.c.a;
485 for (list_t* i = p->lights; i; i = i->link) {
486 light_t light = *(light_t*)i->val;
487 vec_t mpos = vert.v;
488 vec_t lpos = light.v;
489 vec_t vpos = p->eye;
490 vec_t n = vert.n;
491 vec_t l = vec_normalize(vec_sub(lpos, mpos));
492 vec_t r = vec_normalize(vec_sub(vec_scale(n, S(2.0) * vec_dot(n, l)), l));
493 vec_t v = vec_normalize(vec_sub(vpos, mpos));
494
495 scal_t kd = scal_max(vec_dot(l, n), S(0.0));
496 color_t Id = color_scale2(light.d, vert.c, kd);
497 scal_t ks = scal_pow(scal_max(vec_dot(r, v), S(0.0)), p->shininess);
498 color_t Is = color_scale2(light.s, p->specular, ks);
499
500 color = color_add2(color, Id, Is);
501 }
502 color_t Ia = color_mult(p->ambient, vert.c);
503 return color_clamp(color_add(color, Ia));
504 #else
505 return vert.c;
506 #endif // LIGHTING
507 }
508
509 void raster_draw_tri(raster_t* p, const tri_t* triangle)
510 {
511 IF_RASTER_STATS(++p->total);
512 tri_t t = *triangle;
513
514 // need to recalculate the model-view-projection matrix if any one of its
515 // composing matrices have been changed
516 if (p->dirty) {
517 p->modelviewprojection = mat_mult(p->view, p->model);
518 p->modelviewprojection = mat_mult(p->projection, p->modelviewprojection);
519 p->dirty = false;
520 }
521
522 t = tri_transform(t, p->modelviewprojection);
523
524 // save w-values for texture mapping perspective correction
525 scal_t w1 = t.a.v.w;
526 scal_t w2 = t.b.v.w;
527 scal_t w3 = t.c.v.w;
528
529 t = tri_homodiv(t);
530
531 if (!_try_cull_backface(t)) {
532 IF_RASTER_STATS(++p->culled);
533 return;
534 }
535
536 t = tri_transform(t, p->viewport);
537
538 int left = p->left;
539 int right = p->right;
540 int bottom = p->bottom;
541 int top = p->top;
542
543 if (!_try_clip(t, &left, &right, &bottom, &top)) {
544 IF_RASTER_STATS(++p->clipped);
545 return;
546 }
547
548 #if LIGHTING >= 1
549 tri_t tl = tri_transform(*triangle, p->model);
550 tl.a.t.w = w1;
551 tl.b.t.w = w2;
552 tl.c.t.w = w3;
553 #endif
554
555 #if LIGHTING == 1
556 vert_t tv = vert_new(tri_midpoint(tl));
557 tv.n = vec_normalize(tri_normal(tl));
558 tv.c = tri_color(tl);
559 color_t color = _do_phong_lighting(p, tv);
560 #elif LIGHTING == 2 && SMOOTH_COLOR
561 color_t color1 = _do_phong_lighting(p, tl.a);
562 color_t color2 = _do_phong_lighting(p, tl.b);
563 color_t color3 = _do_phong_lighting(p, tl.c);
564 #elif LIGHTING == 2 && !SMOOTH_COLOR
565 color_t c = tri_color(t);
566 tl.a.c = tl.b.c = tl.c.c = c;
567 color_t color1 = _do_phong_lighting(p, tl.a);
568 color_t color2 = _do_phong_lighting(p, tl.b);
569 color_t color3 = _do_phong_lighting(p, tl.c);
570 #elif !LIGHTING && SMOOTH_COLOR
571 color_t color1 = t.a.c;
572 color_t color2 = t.b.c;
573 color_t color3 = t.c.c;
574 #else
575 color_t color = tri_color(t);
576 #endif
577
578 for (int y = bottom; y < top; ++y) {
579 for (int x = left; x < right; ++x) {
580 vec_t v = vec_new((scal_t)x, (scal_t)y, S(0.0));
581 scal_t b[3];
582 if (tri_barycentric(t, b, v)) {
583 #if DEPTH_TEST
584 v.z = tri_z(t, b);
585 scal_t* n = p->zbuf + y * p->w + x;
586 if (S(-1.0) < v.z && v.z < *n) {
587 #endif
588 color_t* c = p->pixels + y * p->w + x;
589 color_t newC;
590
591 #if LIGHTING == 2 || (!LIGHTING && SMOOTH_COLOR)
592 newC = color_interp2(color1, color2, color3, b);
593 #elif LIGHTING == 3 && SMOOTH_COLOR
594 newC = _do_phong_lighting(p, tri_interp(tl, b));
595 #elif LIGHTING == 3 && !SMOOTH_COLOR
596 vert_t d = vert_new(tri_point(t, b));
597 d.c = tri_color(t);
598 d.n = tri_normal2(t, b);
599 newC = _do_phong_lighting(p, d);
600 #else
601 newC = color;
602 #endif
603
604 #if BLENDING
605 *c = color_blend(*c, newC);
606 #else
607 *c = newC;
608 #endif
609
610 #if DEPTH_TEST
611 *n = v.z;
612 }
613 #endif
614 }
615 }
616 }
617 }
618
This page took 0.056287 seconds and 5 git commands to generate.