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