#include "color.h" #include "geometry.h" #include "util.h" // MACROS #define DrawPixel(buffer, width, color, x, y)\ {\ buffer[width * y + x] = color;\ } // PRIVATE PROTOTYPES static void FillTriangleFlatBottom( uint32_t *buffer, int width, float *zbuffer, uint32_t color, float x0, float y0, float z0, float x1, float y1, float z1, float x2, float y2, float z2); static void FillTriangleFlatTop( uint32_t *buffer, int width, float *zbuffer, uint32_t color, float x0, float y0, float z0, float x1, float y1, float z1, float x2, float y2, float z2); // PUBLIC FUNCTIONS void FillTriangle( uint32_t *buffer, int width, float *zbuffer, uint32_t color, float x0, float y0, float z0, float x1, float y1, float z1, float x2, float y2, float z2) { // Horizontal or vertical if ((FLOAT_EQUAL(x0,x1) && FLOAT_EQUAL(x1, x2)) || (FLOAT_EQUAL(y0, y1) && FLOAT_EQUAL(y1, y2))) { return; } // Sort in ascending Y order float temp; if (y0 > y1) { SWAP(x0, x1, temp); SWAP(y0, y1, temp); SWAP(z0, z1, temp); } if (y0 > y2) { SWAP(x0, x2, temp); SWAP(y0, y2, temp); SWAP(z0, z2, temp); } if (y1 > y2) { SWAP(x1, x2, temp); SWAP(y1, y2, temp); SWAP(z1, z2, temp); } // Flat top triangle if (FLOAT_EQUAL(y0, y1)) { FillTriangleFlatTop(buffer, width, zbuffer, color, x0, y0, z0, x1, y1, z1, x2, y2, z2); } // Flat bottom triangle else if (FLOAT_EQUAL(y1, y2)) { FillTriangleFlatBottom(buffer, width, zbuffer, color, x0, y0, z0, x1, y1, z1, x2, y2, z2); } // Other - decompose into two triangles else { // Calculate x coordinate at point of decomposition using intercept theorem // New y is the same as y1 float x3 = x0 + (((y1 - y0) / (y2 - y0)) * (x2 - x0)); float y3 = y1; float z3 = z1; FillTriangleFlatBottom(buffer, width, zbuffer, color, x0, y0, z0, x1, y1, z1, x3, y3, z3); FillTriangleFlatTop(buffer, width, zbuffer, color, x1, y1, z1, x3, y3, z3, x2, y2, z2); } } // PRIVATE FUNCTIONS static void FillTriangleFlatBottom( uint32_t *buffer, int width, float *zbuffer, uint32_t color, float x0, float y0, float z0, float x1, float y1, float z1, float x2, float y2, float z2) { if (x2 < x1) { float temp; SWAP(x2, x1, temp); SWAP(y2, y1, temp); SWAP(z2, z1, temp); } // Top-left fill convention float dxdy_left = (x1 - x0) / (y1 - y0); float dxdy_right = (x2 - x0) / (y2 - y0); int yStart = (int)ceilf(y0); int yEnd = (int)ceilf(y2) - 1; float xStart = x0 + dxdy_left * (yStart - y0); float xEnd = x0 + dxdy_right * (yStart - y0); // Inverse z-buffering float z0Inv = 1.0f / z0; float z1Inv = 1.0f / z1; float z2Inv = 1.0f / z2; float dzdy_left = (z1Inv - z0Inv) / (y1 - y0); float dzdy_right = (z2Inv - z0Inv) / (y2 - y0); float zStart = z0Inv; float zEnd = z0Inv; // Fill for (int y = yStart; y <= yEnd; ++y) { float dzdx = (zEnd - zStart) / (xEnd - xStart); float z = zStart; int xStartInt = (int)xStart; int xEndInt = (int)xEnd; for (int x = xStartInt; x <= xEndInt; ++x) { int pixel = (y * width) + x; if (z > zbuffer[pixel]) { DrawPixel(buffer, width, color, x, y); zbuffer[pixel] = z; } z += dzdx; } xStart += dxdy_left; xEnd += dxdy_right; zStart += dzdy_left; zEnd += dzdy_right; } } static void FillTriangleFlatTop( uint32_t *buffer, int width, float *zbuffer, uint32_t color, float x0, float y0, float z0, float x1, float y1, float z1, float x2, float y2, float z2) { if (x1 < x0) { float temp; SWAP(x1, x0, temp); SWAP(y1, y0, temp); SWAP(z1, z0, temp); } // Top-left fill convention float dxdy_left = (x2 - x0) / (y2 - y0); float dxdy_right = (x2 - x1) / (y2 - y1); int yStart = (int)ceilf(y0); int yEnd = (int)ceilf(y2) - 1; float xStart = x0 + dxdy_left * (yStart - y0); float xEnd = x1 + dxdy_right * (yStart - y0); // Inverse z-buffering float z0Inv = 1.0f / z0; float z1Inv = 1.0f / z1; float z2Inv = 1.0f / z2; float dzdy_left = (z2Inv - z0Inv) / (y2 - y0); float dzdy_right = (z2Inv - z1Inv) / (y2 - y1); float zStart = z0Inv; float zEnd = z1Inv; // Fill for (int y = yStart; y <= yEnd; ++y) { float dzdx = (zEnd - zStart) / (xEnd - xStart); float z = zStart; int xStartInt = (int)xStart; int xEndInt = (int)xEnd; for (int x = xStartInt; x <= xEndInt; ++x) { int pixel = (y * width) + x; if (z > zbuffer[pixel]) { DrawPixel(buffer, width, color, x, y); zbuffer[pixel] = z; } z += dzdx; } xStart += dxdy_left; xEnd += dxdy_right; zStart += dzdy_left; zEnd += dzdy_right; } }