add scene lighting constructs; real stdin 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 #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 /*= light_new(COLOR_WHITE, vec_new(S(-2.0), S(4.0), S(0.0)));*/
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 /*
262 * See if the triangle is at all visible in the viewport. Also, minimize the
263 * rectangle around the area that includes the triangle.
264 */
265 INLINE_MAYBE
266 bool _try_clip(tri_t t, int* left, int* right, int* bottom, int* top)
267 {
268 #if CLIPPING
269 aabb_t box = tri_aabb(t);
270 if (box.min.z < S(-1.0) || S(1.0) < box.max.z) {
271 return false;
272 }
273 *left = imax((int)scal_floor(box.min.x), *left);
274 *right = imin((int)scal_ceil(box.max.x), *right);
275 if (*right <= *left) {
276 return false;
277 }
278 *bottom = imax((int)scal_floor(box.min.y), *bottom);
279 *top = imin((int)scal_ceil(box.max.y), *top);
280 if (*top <= *bottom) {
281 return false;
282 }
283 #endif // CLIPPING
284 return true;
285 }
286
287 /*
288 * See whether or not we need to draw based on the orientation of the
289 * triangle.
290 */
291 INLINE_MAYBE
292 bool _try_cull_backface(tri_t t)
293 {
294 #if BACKFACE_CULLING
295 vec_t n = tri_normal(t);
296 if (n.z < S(0.0)) {
297 return false;
298 }
299 #endif
300 return true;
301 }
302
303 /*
304 * Determine what color is associated with the given vertex.
305 */
306 INLINE_MAYBE
307 color_t _get_vertex_color(raster_t* p, vert_t vert)
308 {
309 #if LIGHTING
310 color_t color = COLOR_BLACK;
311 for (list_t* i = p->lights; i; i = i->link) {
312 light_t light = *(light_t*)i->val;
313 vec_t mpos = vert.v;
314 vec_t lpos = light.v;
315 vec_t vpos = p->eye;
316 vec_t n = vert.n;
317 vec_t l = vec_normalize(vec_sub(lpos, mpos));
318 vec_t r = vec_sub(vec_scale(n, S(2.0) * vec_dot(n, l)), l);
319 vec_t v = vec_normalize(vec_sub(vpos, mpos));
320
321 scal_t kd = scal_max(vec_dot(l, n), S(0.0));
322 color_t Id = color_scale2(light.d, vert.c, kd);
323 scal_t ks = scal_pow(scal_max(vec_dot(r, v), S(0.0)), p->shininess);
324 color_t Is = color_scale2(light.s, p->specular, ks);
325
326 color = color_add2(color, Id, Is);
327 }
328 color_t Ia = color_mult(p->ambient, vert.c);
329 return color_clamp(color_add(color, Ia));
330 #else
331 return vert.c;
332 #endif // LIGHTING
333 }
334
335 void raster_draw_tri(raster_t* p, const tri_t* triangle)
336 {
337 IF_RASTER_STATS(++p->total);
338 tri_t t = *triangle;
339
340 // need to recalculate the model-view-projection matrix if any one of its
341 // composing matrices have been changed
342 if (p->dirty) {
343 p->modelviewprojection = mat_mult(p->view, p->model);
344 p->modelviewprojection = mat_mult(p->projection, p->modelviewprojection);
345 p->dirty = false;
346 }
347 t = tri_transform(t, p->modelviewprojection);
348 t = tri_homodiv(t);
349
350 if (!_try_cull_backface(t)) {
351 IF_RASTER_STATS(++p->culled);
352 return;
353 }
354
355 t = tri_transform(t, p->viewport);
356
357 int left = p->left;
358 int right = p->right;
359 int bottom = p->bottom;
360 int top = p->top;
361
362 if (!_try_clip(t, &left, &right, &bottom, &top)) {
363 IF_RASTER_STATS(++p->clipped);
364 return;
365 }
366
367 tri_t temp = tri_transform(*triangle, p->model);
368 #if LIGHTING && (!PRE_NORMALS || (!SMOOTH_COLOR && PRE_NORMALS >= 2))
369 temp.a.n = temp.b.n = temp.c.n = vec_normalize(tri_normal(temp));
370 #endif
371 #if !SMOOTH_COLOR
372 temp.a.c = tri_color(temp);
373 #endif
374 color_t color1 = _get_vertex_color(p, temp.a);
375 #if SMOOTH_COLOR
376 color_t color2 = _get_vertex_color(p, temp.b);
377 color_t color3 = _get_vertex_color(p, temp.c);
378 #endif
379
380 for (int y = bottom; y < top; ++y) {
381 for (int x = left; x < right; ++x) {
382 vec_t v = vec_new((scal_t)x, (scal_t)y, S(0.0));
383 scal_t b[3];
384 if (tri_barycentric(t, b, v)) {
385 #if DEPTH_TEST
386 v.z = tri_z(t, b);
387 scal_t* n = p->zbuf + y * p->w + x;
388 if (S(-1.0) < v.z && v.z < *n) {
389 #endif
390 color_t* c = p->pixels + y * p->w + x;
391 #if SMOOTH_COLOR
392 *c = color_interp2(color1, color2, color3, b);
393 #else
394 *c = color1;
395 #endif
396 #if DEPTH_TEST
397 *n = v.z;
398 }
399 #endif
400 }
401 }
402 }
403 }
404
This page took 0.06629 seconds and 4 git commands to generate.