#include "raster.h" int doBlend = 0; int doZ = 0; Canvas* makecanvas(int w, int h) { Canvas *canv; canv = malloc(sizeof(*canv) + w*h*(4+4)); canv->w = w; canv->h = h; canv->fb = ((u8*)canv + sizeof(*canv)); canv->zbuf = (u32*)(canv->fb + w*h*4); return canv; } Texture* maketexture(int w, int h) { Texture *t; t = malloc(sizeof(*t) + w*h*4); t->w = w; t->h = h; t->pixels = (u8*)t + sizeof(*t); t->wrap = 0x11; // wrap u and v return t; } void putpixel(Canvas *canvas, Point3 p, Color c) { if(p.x < 0 || p.x >= canvas->w || p.y < 0 || p.y >= canvas->h) return; u8 *px = &canvas->fb[(p.y*canvas->w + p.x)*4]; u32 *z = &canvas->zbuf[p.y*canvas->w + p.x]; if(doZ && *z > p.z) return; if(doBlend){ px[3] = (px[3]*(255-c.a) + c.r*c.a)/255; px[2] = (px[2]*(255-c.a) + c.g*c.a)/255; px[1] = (px[1]*(255-c.a) + c.b*c.a)/255; px[0] = (px[0]*(255-c.a) + c.a*c.a)/255; }else{ px[3] = c.r; px[2] = c.g; px[1] = c.b; px[0] = c.a; } *z = p.z; } void clearcanvas(Canvas *canvas) { memset(canvas->fb, 0, canvas->w*canvas->h*4); memset(canvas->zbuf, 0, canvas->w*canvas->h*4); } void drawRect(Canvas *canvas, Point3 p1, Point3 p2, Color c) { int x, y; for(y = p1.y; y <= p2.y; y++) for(x = p1.x; x <= p2.x; x++) putpixel(canvas, (Point3){x, y, 0}, c); } void drawLine(Canvas *canvas, Point3 p1, Point3 p2, Color c) { int dx, dy; int incx, incy; int e; int x, y; dx = abs(p2.x-p1.x); incx = p2.x > p1.x ? 1 : -1; dy = abs(p2.y-p1.y); incy = p2.y > p1.y ? 1 : -1; e = 0; if(dx == 0){ for(y = p1.y; y != p2.y; y += incy) putpixel(canvas, (Point3){p1.x, y, 0}, c); }else if(dx > dy){ y = p1.y; for(x = p1.x; x != p2.x; x += incx){ putpixel(canvas, (Point3){x, y, 0}, c); e += dy; if(2*e >= dx){ e -= dx; y += incy; } } }else{ x = p1.x; for(y = p1.y; y != p2.y; y += incy){ putpixel(canvas, (Point3){x, y, 0}, c); e += dx; if(2*e >= dy){ e -= dy; x += incx; } } } } void drawWireTri(Canvas *canvas, Point3 p1, Point3 p2, Point3 p3, Color c) { drawLine(canvas, p1, p2, c); drawLine(canvas, p2, p3, c); drawLine(canvas, p3, p1, c); } Color sampletex(Vector2 st, float q) { int u, v; Color c; u8 *cp; q = 1/q; st.s = st.s*q*curtex->w; st.t = st.t*q*curtex->h; // wrap or clamp if(curtex->wrap&0xF){ while(st.s < -0.000001f) st.s += curtex->h; while(st.s > curtex->w+0.000001f) st.s -= curtex->w; }else{ if(st.s < -0.000001f) st.s = 0.0f; if(st.s > curtex->w+0.000001f) st.s = curtex->w; } if(curtex->wrap&0xF0){ while(st.t < -0.000001f) st.t += curtex->w; while(st.t > curtex->h+0.000001f) st.t -= curtex->h; }else{ if(st.t < -0.000001f) st.t = 0.0f; if(st.t > curtex->h+0.000001f) st.t = curtex->h; } u = st.s; v = st.t; if(u == curtex->w) u--; if(v == curtex->h) v--; cp = &curtex->pixels[(v*curtex->w + u)*4]; c.r = cp[0]; c.g = cp[1]; c.b = cp[2]; c.a = cp[3]; return c; } Color modulate(Color c1, Color c2) { return (Color){(c1.r*c2.r)/255, (c1.g*c2.g)/255, (c1.b*c2.b)/255, (c1.a*c2.a)/255}; } Color clampcolor(Color c) { c.r >>= 14; c.g >>= 14; c.b >>= 14; c.a >>= 14; if(c.r < 0) c.r = 0; if(c.r > 255) c.r = 255; if(c.g < 0) c.g = 0; if(c.g > 255) c.g = 255; if(c.b < 0) c.b = 0; if(c.b > 255) c.b = 255; if(c.a < 0) c.a = 0; if(c.a > 255) c.a = 255; return c; } void drawTriangle(Canvas *canvas, Vertex p1, Vertex p2, Vertex p3) { Vertex *p[] = {&p1, &p2, &p3}; Vertex s, i, ierr; Vertex gx, gy; Vertex *tmp; int dx[3]; int dy[3]; int d; int x2, e2; int right; int a, b; float denom; Color c, texcol; #define SWAP(x,y) {tmp = (x); (x) = (y); (y) = tmp;} if(p[0]->v.y > p[1]->v.y) SWAP(p[0], p[1]); if(p[0]->v.y > p[2]->v.y) SWAP(p[0], p[2]); if(p[1]->v.y > p[2]->v.y) SWAP(p[1], p[2]); #undef SWAP dy[0] = p[2]->v.y - p[0]->v.y; dy[1] = p[1]->v.y - p[0]->v.y; dy[2] = p[2]->v.y - p[1]->v.y; dx[0] = p[2]->v.x - p[0]->v.x; dx[1] = p[1]->v.x - p[0]->v.x; dx[2] = p[2]->v.x - p[1]->v.x; // compare slopes to find out if the long edge is right or left right = dx[0]*dy[1] > dx[1]*dy[0]; // calculate gradients // x denom = (p[1]->v.x-p[2]->v.x)*(p[0]->v.y-p[2]->v.y) - (p[0]->v.x-p[2]->v.x)*(p[1]->v.y-p[2]->v.y); if(denom != 0.0f) denom = 1.0f/denom; #define NUM(prop,a) ((p[1]->prop-p[2]->prop)*(p[0]->v.a-p[2]->v.a) -\ (p[0]->prop-p[2]->prop)*(p[1]->v.a-p[2]->v.a)) gx.c.r = denom*NUM(c.r, y); gx.c.g = denom*NUM(c.g, y); gx.c.b = denom*NUM(c.b, y); gx.c.a = denom*NUM(c.a, y); gx.z = denom*NUM(z, y); gx.oneoverz = denom*NUM(oneoverz, y); gx.st.s = denom*NUM(st.s, y); gx.st.t = denom*NUM(st.t, y); // y denom = -denom; gy.c.r = denom*NUM(c.r, x); gy.c.g = denom*NUM(c.g, x); gy.c.b = denom*NUM(c.b, x); gy.c.a = denom*NUM(c.a, x); gy.z = denom*NUM(z, x); gy.oneoverz = denom*NUM(oneoverz, x); gy.st.s = denom*NUM(st.s, x); gy.st.t = denom*NUM(st.t, x); #undef NUM i = *p[0]; x2 = p[0]->v.x; ierr.v.x = 0; e2 = 0; // choose edges such that i.v.x <= x2 a = right; b = !right; // < does not draw bottom edge while(i.v.y < p[1]->v.y){ #define LOOPBODY \ s = i;\ for(s.v.x = i.v.x; s.v.x < x2; s.v.x++){\ c = clampcolor(s.c);\ texcol = sampletex(s.st, s.oneoverz);\ c = modulate(c, texcol);\ putpixel(canvas, (Point3){s.v.x, i.v.y, i.z}, c);\ s.c.r += gx.c.r;\ s.c.g += gx.c.g;\ s.c.b += gx.c.b;\ s.c.a += gx.c.a;\ s.z += gx.z;\ s.oneoverz += gx.oneoverz;\ s.st.s += gx.st.s;\ s.st.t += gx.st.t;\ }\ i.v.y++;\ i.c.r += gy.c.r;\ i.c.g += gy.c.g;\ i.c.b += gy.c.b;\ i.c.a += gy.c.a;\ i.z += gy.z;\ i.oneoverz += gy.oneoverz;\ i.st.s += gy.st.s;\ i.st.t += gy.st.t;\ ierr.v.x += dx[a];\ d = 0;\ while(ierr.v.x > 0){\ ierr.v.x -= dy[a];\ d++;\ }\ while(ierr.v.x <= -dy[a]){\ ierr.v.x += dy[a];\ d--;\ }\ i.v.x += d;\ i.c.r += d*gx.c.r;\ i.c.g += d*gx.c.g;\ i.c.b += d*gx.c.b;\ i.c.a += d*gx.c.a;\ i.z += d*gx.z;\ i.oneoverz += d*gx.oneoverz;\ i.st.s += d*gx.st.s;\ i.st.t += d*gx.st.t;\ e2 += dx[b];\ while(e2 > 0){\ e2 -= dy[b];\ x2++;\ }\ while(e2 <= -dy[b]){\ e2 += dy[b];\ x2--;\ } LOOPBODY } // if the above loop isn't run (dy == 0), i is still at p[0] if(right) i = *p[1]; else x2 = p[1]->v.x; // switch from edge 1 to edge 2 a <<= 1; b <<= 1; while(i.v.y < p[2]->v.y){ LOOPBODY #undef LOOPBODY } }