X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Frasterize;a=blobdiff_plain;f=raster.c;fp=raster.c;h=23b1d69d19f5f0f855ec843ff4532ee379ed6fa7;hp=0000000000000000000000000000000000000000;hb=c875478cdd823c7df8fdc859941bd9e5948c9315;hpb=82087d9bb9e28c2375008bde4453f6c019419697 diff --git a/raster.c b/raster.c new file mode 100644 index 0000000..23b1d69 --- /dev/null +++ b/raster.c @@ -0,0 +1,396 @@ + +/* + * CS5600 University of Utah + * Charles McGarvey + * mcgarvey@eng.utah.edu + */ + +#include +#include +#include +#include + +#include "list.h" +#include "raster.h" + + +struct raster +{ + color_t* pixels; + scal_t* zbuf; + int w, h; + int left, right, bottom, top; + mat_t model; + mat_t view; + mat_t projection; + mat_t modelviewprojection; + mat_t viewport; + bool dirty; +#if LIGHTING + list_t* lights; + color_t ambient; + vec_t eye; +#endif +#if RASTER_STATS + unsigned total; + unsigned clipped; + unsigned culled; +#define IF_RASTER_STATS(X) X +#else +#define IF_RASTER_STATS(X) +#endif +}; + + +raster_t* raster_alloc(int width, int height, color_t fill) +{ + assert(0 < width && 0 < height && "zero-dimension raster not allowed"); + size_t size = width * height; + + raster_t* p = (raster_t*)mem_alloc(sizeof(raster_t)); + p->pixels = (color_t*)mem_alloc(sizeof(color_t) * size); + p->w = width; + p->h = height; + raster_clear(p, fill); + raster_viewport(p, 0, 0, width, height); + p->model = p->view = p->projection = MAT_IDENTITY; + p->dirty = false; + +#if LIGHTING + p->ambient = color_new(S(0.05), S(0.05), S(0.05), S(1.0)); + p->lights = NULL; +#endif + /*= light_new(COLOR_WHITE, vec_new(S(-2.0), S(4.0), S(0.0)));*/ + p->zbuf = (scal_t*)mem_alloc(sizeof(scal_t) * size); + for (size_t i = 0; i < size; ++i) { + p->zbuf[i] = S(1.0); + } + + return p; +} + +void raster_destroy(raster_t* p) +{ + mem_free(p->pixels); + mem_free(p->zbuf); + mem_free(p); +} + + +void raster_printstats(raster_t* p) +{ +#if RASTER_STATS + unsigned drawn = p->total - p->clipped - p->culled; + float percent = 100.0f * (float)drawn / (float)p->total; + printf("culled\t%u\n" + "clipped\t%u\n" + "drawn\t%u (%6.2f%%)\n" + "total\t%u\n", p->culled, p->clipped, drawn, percent, p->total); +#endif +} + + +void raster_clear(raster_t* p, color_t fill) +{ + size_t size = p->w * p->h; + for (int i = 0; i < size; ++i) { + p->pixels[i] = fill; + } +#if RASTER_STATS + p->total = 0; + p->clipped = 0; + p->culled = 0; +#endif +} + + +void raster_viewport(raster_t* p, int x, int y, int width, int height) +{ + p->left = x; + p->right = x + width; + p->bottom = y; + p->top = y + height; + p->viewport = MAT_VIEWPORT(x, y, width, height); +} + +void raster_model(raster_t* p, const mat_t* transform) +{ + p->model = *transform; + p->dirty = true; +} + +void raster_view(raster_t* p, const mat_t* transform) +{ + p->view = *transform; + p->dirty = true; +} + +void raster_projection(raster_t* p, const mat_t* transform) +{ + p->projection = *transform; + p->dirty = true; +} + + +void raster_eye(raster_t* p, vec_t eye) +{ +#if LIGHTING + p->eye = eye; +#endif +} + +void raster_light(raster_t* p, light_t light) +{ +#if LIGHTING + light_t* l = (light_t*)mem_alloc(sizeof(light_t)); + memcpy(l, &light, sizeof(light_t)); + list_push2(&p->lights, l, mem_free); +#endif +} + + +#define _CHECK_WRITE(X) if ((X) <= 0) goto fail + +int raster_export_ppm(const raster_t* p, const char* filename) +{ + FILE* file = fopen(filename, "w"); + if (file == NULL) { +fail: fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno)); + return -1; + } + + _CHECK_WRITE(fprintf(file, "P3\n%u %u\n255\n", p->w, p->h)); + for (int y = (int)p->h - 1; y >= 0; --y) { + for (int x = 0; x < p->w; ++x) { + rgbachan_t r, g, b; + color_split(p->pixels[y * p->w + x], &r, &g, &b, NULL); + _CHECK_WRITE(fprintf(file, "%hhu %hhu %hhu\n", r, g, b)); + } + } + + fclose(file); + return 0; +} + +int raster_export_bmp(const raster_t* p, const char* filename) +{ + /* + * This function was adapted from sample code provided with the assignment + * instructions. + */ + FILE* file = fopen(filename, "wb"); + if (file == NULL) { +fail: fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno)); + return -1; + } + + uint16_t magicNumber = 0x4D42; + uint16_t reserved0 = 0;//0x4D41; + uint16_t reserved1 = 0;//0x5454; + uint32_t dataOffset = 54; + uint32_t infoHeaderSize = 40; + uint32_t width = p->w; + uint32_t height = p->h; + uint16_t colorPlanes = 1; + uint16_t bitsPerPixel = 32; + uint32_t compression = 0; + uint32_t dataSize = width * height * bitsPerPixel / 8; + uint32_t horizontalResolution = 2835; + uint32_t verticalResolution = 2835; + uint32_t paletteColorCount = 0; + uint32_t importantPaletteColorCount = 0; + uint32_t fileSize = 54 + dataSize; + + /* + * Check the return values to avoid loud warnings. + */ + _CHECK_WRITE(fwrite(&magicNumber, sizeof(magicNumber), 1, file)); + _CHECK_WRITE(fwrite(&fileSize, sizeof(fileSize), 1, file)); + _CHECK_WRITE(fwrite(&reserved0, sizeof(reserved0), 1, file)); + _CHECK_WRITE(fwrite(&reserved1, sizeof(reserved1), 1, file)); + _CHECK_WRITE(fwrite(&dataOffset, sizeof(dataOffset), 1, file)); + _CHECK_WRITE(fwrite(&infoHeaderSize, sizeof(infoHeaderSize), 1, file)); + _CHECK_WRITE(fwrite(&width, sizeof(width), 1, file)); + _CHECK_WRITE(fwrite(&height, sizeof(height), 1, file)); + _CHECK_WRITE(fwrite(&colorPlanes, sizeof(colorPlanes), 1, file)); + _CHECK_WRITE(fwrite(&bitsPerPixel, sizeof(bitsPerPixel), 1, file)); + _CHECK_WRITE(fwrite(&compression, sizeof(compression), 1, file)); + _CHECK_WRITE(fwrite(&dataSize, sizeof(dataSize), 1, file)); + _CHECK_WRITE(fwrite(&horizontalResolution, sizeof(horizontalResolution), 1, file)); + _CHECK_WRITE(fwrite(&verticalResolution, sizeof(verticalResolution), 1, file)); + _CHECK_WRITE(fwrite(&paletteColorCount, sizeof(paletteColorCount), 1, file)); + _CHECK_WRITE(fwrite(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file)); + + size_t size = width * height; + for (int i = 0; i < size; ++i) + { + rgbachan_t a, r, g, b; + color_split(p->pixels[i], &r, &g, &b, &a); + uint32_t argb = PACK(argb, 3, a); + argb = PACK(argb, 2, r); + argb = PACK(argb, 1, g); + argb = PACK(argb, 0, b); + _CHECK_WRITE(fwrite(&argb, sizeof(argb), 1, file)); + } + + fclose(file); + return 0; +} + +#undef _CHECK_WRITE + + +/* + * See if the triangle is at all visible in the viewport. Also, minimize the + * rectangle around the area that includes the triangle. + */ +INLINE_MAYBE +bool _try_clip(tri_t t, int* left, int* right, int* bottom, int* top) +{ +#if CLIPPING + aabb_t box = tri_aabb(t); + if (box.min.z < S(-1.0) || S(1.0) < box.max.z) { + return false; + } + *left = imax((int)scal_floor(box.min.x), *left); + *right = imin((int)scal_ceil(box.max.x), *right); + if (*right <= *left) { + return false; + } + *bottom = imax((int)scal_floor(box.min.y), *bottom); + *top = imin((int)scal_ceil(box.max.y), *top); + if (*top <= *bottom) { + return false; + } +#endif // CLIPPING + return true; +} + +/* + * See whether or not we need to draw based on the orientation of the + * triangle. + */ +INLINE_MAYBE +bool _try_cull_backface(tri_t t) +{ +#if BACKFACE_CULLING + vec_t n = tri_normal(t); + if (n.z < S(0.0)) { + return false; + } +#endif + return true; +} + +/* + * Determine what color is associated with the given vertex. + */ +INLINE_MAYBE +color_t _get_vertex_color(raster_t* p, vert_t vert) +{ +#if LIGHTING + color_t color = COLOR_BLACK; + for (list_t* i = p->lights; i; i = i->link) { + light_t light = *(light_t*)i->val; + vec_t mpos = vert.v; + vec_t lpos = light.position; + vec_t vpos = p->eye; + vec_t n = vert.n; + vec_t l = vec_normalize(vec_sub(lpos, mpos)); + vec_t r = vec_sub(vec_scale(n, S(2.0) * vec_dot(n, l)), l); + vec_t v = vec_normalize(vec_sub(vpos, mpos)); + + scal_t kd = scal_max(vec_dot(l, n), S(0.0)); + color_t Id = color_new( + light.color.r * vert.c.r * kd, + light.color.g * vert.c.g * kd, + light.color.b * vert.c.b * kd, + S(1.0) + ); + scal_t ks = scal_pow(scal_max(vec_dot(r, v), S(0.0)), S(64.0)); + color_t Is = color_new( + light.color.r * COLOR_WHITE.r * ks, + light.color.g * COLOR_WHITE.g * ks, + light.color.b * COLOR_WHITE.b * ks, + S(1.0) + ); + + color = color_add2(color, Id, Is); + } + color_t Ia = p->ambient; + return color_clamp(color_add(color, Ia)); +#else + return vert.c; +#endif // LIGHTING +} + +void raster_draw_tri(raster_t* p, const tri_t* triangle) +{ + IF_RASTER_STATS(++p->total); + tri_t t = *triangle; + + // need to recalculate the model-view-projection matrix if any one of its + // composing matrices have been changed + if (p->dirty) { + p->modelviewprojection = mat_mult(p->view, p->model); + p->modelviewprojection = mat_mult(p->projection, p->modelviewprojection); + p->dirty = false; + } + t = tri_transform(t, p->modelviewprojection); + t = tri_homodiv(t); + + if (!_try_cull_backface(t)) { + IF_RASTER_STATS(++p->culled); + return; + } + + t = tri_transform(t, p->viewport); + + int left = p->left; + int right = p->right; + int bottom = p->bottom; + int top = p->top; + + if (!_try_clip(t, &left, &right, &bottom, &top)) { + IF_RASTER_STATS(++p->clipped); + return; + } + + tri_t temp = tri_transform(*triangle, p->model); +#if LIGHTING && (!FIND_NORMALS || (!SMOOTH_COLOR && FIND_NORMALS == 2)) + temp.a.n = temp.b.n = temp.c.n = vec_normalize(tri_normal(temp)); +#endif +#if !SMOOTH_COLOR + temp.a.c = tri_color(temp); +#endif + color_t color1 = _get_vertex_color(p, temp.a); +#if SMOOTH_COLOR + color_t color2 = _get_vertex_color(p, temp.b); + color_t color3 = _get_vertex_color(p, temp.c); +#endif + + for (int y = bottom; y < top; ++y) { + for (int x = left; x < right; ++x) { + vec_t v = vec_new((scal_t)x, (scal_t)y, S(0.0)); + scal_t b[3]; + if (tri_barycentric(t, b, v)) { +#if DEPTH_TEST + v.z = tri_z(t, b); + scal_t* n = p->zbuf + y * p->w + x; + if (S(-1.0) < v.z && v.z < *n) { +#endif + color_t* c = p->pixels + y * p->w + x; +#if SMOOTH_COLOR + *c = color_interp2(color1, color2, color3, b); +#else + *c = color1; +#endif +#if DEPTH_TEST + *n = v.z; + } +#endif + } + } + } +} +