add support for 3d scenes, depth testing, lighting
[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 #endif
34 #if RASTER_STATS
35 unsigned total;
36 unsigned clipped;
37 unsigned culled;
38 #define IF_RASTER_STATS(X) X
39 #else
40 #define IF_RASTER_STATS(X)
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
59 #if LIGHTING
60 p->ambient = color_new(S(0.05), S(0.05), S(0.05), S(1.0));
61 p->lights = NULL;
62 #endif
63 /*= light_new(COLOR_WHITE, vec_new(S(-2.0), S(4.0), S(0.0)));*/
64 p->zbuf = (scal_t*)mem_alloc(sizeof(scal_t) * size);
65 for (size_t i = 0; i < size; ++i) {
66 p->zbuf[i] = S(1.0);
67 }
68
69 return p;
70 }
71
72 void raster_destroy(raster_t* p)
73 {
74 mem_free(p->pixels);
75 mem_free(p->zbuf);
76 mem_free(p);
77 }
78
79
80 void raster_printstats(raster_t* p)
81 {
82 #if RASTER_STATS
83 unsigned drawn = p->total - p->clipped - p->culled;
84 float percent = 100.0f * (float)drawn / (float)p->total;
85 printf("culled\t%u\n"
86 "clipped\t%u\n"
87 "drawn\t%u (%6.2f%%)\n"
88 "total\t%u\n", p->culled, p->clipped, drawn, percent, p->total);
89 #endif
90 }
91
92
93 void raster_clear(raster_t* p, color_t fill)
94 {
95 size_t size = p->w * p->h;
96 for (int i = 0; i < size; ++i) {
97 p->pixels[i] = fill;
98 }
99 #if RASTER_STATS
100 p->total = 0;
101 p->clipped = 0;
102 p->culled = 0;
103 #endif
104 }
105
106
107 void raster_viewport(raster_t* p, int x, int y, int width, int height)
108 {
109 p->left = x;
110 p->right = x + width;
111 p->bottom = y;
112 p->top = y + height;
113 p->viewport = MAT_VIEWPORT(x, y, width, height);
114 }
115
116 void raster_model(raster_t* p, const mat_t* transform)
117 {
118 p->model = *transform;
119 p->dirty = true;
120 }
121
122 void raster_view(raster_t* p, const mat_t* transform)
123 {
124 p->view = *transform;
125 p->dirty = true;
126 }
127
128 void raster_projection(raster_t* p, const mat_t* transform)
129 {
130 p->projection = *transform;
131 p->dirty = true;
132 }
133
134
135 void raster_eye(raster_t* p, vec_t eye)
136 {
137 #if LIGHTING
138 p->eye = eye;
139 #endif
140 }
141
142 void raster_light(raster_t* p, light_t light)
143 {
144 #if LIGHTING
145 light_t* l = (light_t*)mem_alloc(sizeof(light_t));
146 memcpy(l, &light, sizeof(light_t));
147 list_push2(&p->lights, l, mem_free);
148 #endif
149 }
150
151
152 #define _CHECK_WRITE(X) if ((X) <= 0) goto fail
153
154 int raster_export_ppm(const raster_t* p, const char* filename)
155 {
156 FILE* file = fopen(filename, "w");
157 if (file == NULL) {
158 fail: fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno));
159 return -1;
160 }
161
162 _CHECK_WRITE(fprintf(file, "P3\n%u %u\n255\n", p->w, p->h));
163 for (int y = (int)p->h - 1; y >= 0; --y) {
164 for (int x = 0; x < p->w; ++x) {
165 rgbachan_t r, g, b;
166 color_split(p->pixels[y * p->w + x], &r, &g, &b, NULL);
167 _CHECK_WRITE(fprintf(file, "%hhu %hhu %hhu\n", r, g, b));
168 }
169 }
170
171 fclose(file);
172 return 0;
173 }
174
175 int raster_export_bmp(const raster_t* p, const char* filename)
176 {
177 /*
178 * This function was adapted from sample code provided with the assignment
179 * instructions.
180 */
181 FILE* file = fopen(filename, "wb");
182 if (file == NULL) {
183 fail: fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno));
184 return -1;
185 }
186
187 uint16_t magicNumber = 0x4D42;
188 uint16_t reserved0 = 0;//0x4D41;
189 uint16_t reserved1 = 0;//0x5454;
190 uint32_t dataOffset = 54;
191 uint32_t infoHeaderSize = 40;
192 uint32_t width = p->w;
193 uint32_t height = p->h;
194 uint16_t colorPlanes = 1;
195 uint16_t bitsPerPixel = 32;
196 uint32_t compression = 0;
197 uint32_t dataSize = width * height * bitsPerPixel / 8;
198 uint32_t horizontalResolution = 2835;
199 uint32_t verticalResolution = 2835;
200 uint32_t paletteColorCount = 0;
201 uint32_t importantPaletteColorCount = 0;
202 uint32_t fileSize = 54 + dataSize;
203
204 /*
205 * Check the return values to avoid loud warnings.
206 */
207 _CHECK_WRITE(fwrite(&magicNumber, sizeof(magicNumber), 1, file));
208 _CHECK_WRITE(fwrite(&fileSize, sizeof(fileSize), 1, file));
209 _CHECK_WRITE(fwrite(&reserved0, sizeof(reserved0), 1, file));
210 _CHECK_WRITE(fwrite(&reserved1, sizeof(reserved1), 1, file));
211 _CHECK_WRITE(fwrite(&dataOffset, sizeof(dataOffset), 1, file));
212 _CHECK_WRITE(fwrite(&infoHeaderSize, sizeof(infoHeaderSize), 1, file));
213 _CHECK_WRITE(fwrite(&width, sizeof(width), 1, file));
214 _CHECK_WRITE(fwrite(&height, sizeof(height), 1, file));
215 _CHECK_WRITE(fwrite(&colorPlanes, sizeof(colorPlanes), 1, file));
216 _CHECK_WRITE(fwrite(&bitsPerPixel, sizeof(bitsPerPixel), 1, file));
217 _CHECK_WRITE(fwrite(&compression, sizeof(compression), 1, file));
218 _CHECK_WRITE(fwrite(&dataSize, sizeof(dataSize), 1, file));
219 _CHECK_WRITE(fwrite(&horizontalResolution, sizeof(horizontalResolution), 1, file));
220 _CHECK_WRITE(fwrite(&verticalResolution, sizeof(verticalResolution), 1, file));
221 _CHECK_WRITE(fwrite(&paletteColorCount, sizeof(paletteColorCount), 1, file));
222 _CHECK_WRITE(fwrite(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file));
223
224 size_t size = width * height;
225 for (int i = 0; i < size; ++i)
226 {
227 rgbachan_t a, r, g, b;
228 color_split(p->pixels[i], &r, &g, &b, &a);
229 uint32_t argb = PACK(argb, 3, a);
230 argb = PACK(argb, 2, r);
231 argb = PACK(argb, 1, g);
232 argb = PACK(argb, 0, b);
233 _CHECK_WRITE(fwrite(&argb, sizeof(argb), 1, file));
234 }
235
236 fclose(file);
237 return 0;
238 }
239
240 #undef _CHECK_WRITE
241
242
243 /*
244 * See if the triangle is at all visible in the viewport. Also, minimize the
245 * rectangle around the area that includes the triangle.
246 */
247 INLINE_MAYBE
248 bool _try_clip(tri_t t, int* left, int* right, int* bottom, int* top)
249 {
250 #if CLIPPING
251 aabb_t box = tri_aabb(t);
252 if (box.min.z < S(-1.0) || S(1.0) < box.max.z) {
253 return false;
254 }
255 *left = imax((int)scal_floor(box.min.x), *left);
256 *right = imin((int)scal_ceil(box.max.x), *right);
257 if (*right <= *left) {
258 return false;
259 }
260 *bottom = imax((int)scal_floor(box.min.y), *bottom);
261 *top = imin((int)scal_ceil(box.max.y), *top);
262 if (*top <= *bottom) {
263 return false;
264 }
265 #endif // CLIPPING
266 return true;
267 }
268
269 /*
270 * See whether or not we need to draw based on the orientation of the
271 * triangle.
272 */
273 INLINE_MAYBE
274 bool _try_cull_backface(tri_t t)
275 {
276 #if BACKFACE_CULLING
277 vec_t n = tri_normal(t);
278 if (n.z < S(0.0)) {
279 return false;
280 }
281 #endif
282 return true;
283 }
284
285 /*
286 * Determine what color is associated with the given vertex.
287 */
288 INLINE_MAYBE
289 color_t _get_vertex_color(raster_t* p, vert_t vert)
290 {
291 #if LIGHTING
292 color_t color = COLOR_BLACK;
293 for (list_t* i = p->lights; i; i = i->link) {
294 light_t light = *(light_t*)i->val;
295 vec_t mpos = vert.v;
296 vec_t lpos = light.position;
297 vec_t vpos = p->eye;
298 vec_t n = vert.n;
299 vec_t l = vec_normalize(vec_sub(lpos, mpos));
300 vec_t r = vec_sub(vec_scale(n, S(2.0) * vec_dot(n, l)), l);
301 vec_t v = vec_normalize(vec_sub(vpos, mpos));
302
303 scal_t kd = scal_max(vec_dot(l, n), S(0.0));
304 color_t Id = color_new(
305 light.color.r * vert.c.r * kd,
306 light.color.g * vert.c.g * kd,
307 light.color.b * vert.c.b * kd,
308 S(1.0)
309 );
310 scal_t ks = scal_pow(scal_max(vec_dot(r, v), S(0.0)), S(64.0));
311 color_t Is = color_new(
312 light.color.r * COLOR_WHITE.r * ks,
313 light.color.g * COLOR_WHITE.g * ks,
314 light.color.b * COLOR_WHITE.b * ks,
315 S(1.0)
316 );
317
318 color = color_add2(color, Id, Is);
319 }
320 color_t Ia = p->ambient;
321 return color_clamp(color_add(color, Ia));
322 #else
323 return vert.c;
324 #endif // LIGHTING
325 }
326
327 void raster_draw_tri(raster_t* p, const tri_t* triangle)
328 {
329 IF_RASTER_STATS(++p->total);
330 tri_t t = *triangle;
331
332 // need to recalculate the model-view-projection matrix if any one of its
333 // composing matrices have been changed
334 if (p->dirty) {
335 p->modelviewprojection = mat_mult(p->view, p->model);
336 p->modelviewprojection = mat_mult(p->projection, p->modelviewprojection);
337 p->dirty = false;
338 }
339 t = tri_transform(t, p->modelviewprojection);
340 t = tri_homodiv(t);
341
342 if (!_try_cull_backface(t)) {
343 IF_RASTER_STATS(++p->culled);
344 return;
345 }
346
347 t = tri_transform(t, p->viewport);
348
349 int left = p->left;
350 int right = p->right;
351 int bottom = p->bottom;
352 int top = p->top;
353
354 if (!_try_clip(t, &left, &right, &bottom, &top)) {
355 IF_RASTER_STATS(++p->clipped);
356 return;
357 }
358
359 tri_t temp = tri_transform(*triangle, p->model);
360 #if LIGHTING && (!FIND_NORMALS || (!SMOOTH_COLOR && FIND_NORMALS == 2))
361 temp.a.n = temp.b.n = temp.c.n = vec_normalize(tri_normal(temp));
362 #endif
363 #if !SMOOTH_COLOR
364 temp.a.c = tri_color(temp);
365 #endif
366 color_t color1 = _get_vertex_color(p, temp.a);
367 #if SMOOTH_COLOR
368 color_t color2 = _get_vertex_color(p, temp.b);
369 color_t color3 = _get_vertex_color(p, temp.c);
370 #endif
371
372 for (int y = bottom; y < top; ++y) {
373 for (int x = left; x < right; ++x) {
374 vec_t v = vec_new((scal_t)x, (scal_t)y, S(0.0));
375 scal_t b[3];
376 if (tri_barycentric(t, b, v)) {
377 #if DEPTH_TEST
378 v.z = tri_z(t, b);
379 scal_t* n = p->zbuf + y * p->w + x;
380 if (S(-1.0) < v.z && v.z < *n) {
381 #endif
382 color_t* c = p->pixels + y * p->w + x;
383 #if SMOOTH_COLOR
384 *c = color_interp2(color1, color2, color3, b);
385 #else
386 *c = color1;
387 #endif
388 #if DEPTH_TEST
389 *n = v.z;
390 }
391 #endif
392 }
393 }
394 }
395 }
396
This page took 0.065247 seconds and 4 git commands to generate.