diff --git a/include/geometry.h b/include/geometry.h index bc18e03..cf42e63 100644 --- a/include/geometry.h +++ b/include/geometry.h @@ -1,6 +1,7 @@ #ifndef GEOMETRY_H #include "color.h" +#include "engine.h" #include "point.h" #include #include @@ -77,11 +78,8 @@ struct Mesh // 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); + Engine_Buffer &buffer, ColorU32 &color, + Point &p0, Point &p1, Point &p2); #define GEOMETRY_H diff --git a/src/engine.cpp b/src/engine.cpp index 409647f..6281e31 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -50,9 +50,12 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input) { CheckInputs(input); + + // Clear the z-buffer unsigned long bufferSize = (unsigned long)(buffer.width * buffer.height); memset(buffer.zbuffer, 0, bufferSize * sizeof(float)); + // Local space to world space Matrix tTranslate = Transform_Translate( mesh.position.x, mesh.position.y, mesh.position.z); @@ -122,11 +125,10 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input) unsigned int v2 = mesh.culledFaces[f].vertIndex[2]; FillTriangle( - buffer.buffer, buffer.width, - buffer.zbuffer, mesh.faces[f].color.u32, - mesh.vertsTransformed[v0].x, mesh.vertsTransformed[v0].y, mesh.vertsTransformed[v0].z, - mesh.vertsTransformed[v1].x, mesh.vertsTransformed[v1].y, mesh.vertsTransformed[v1].z, - mesh.vertsTransformed[v2].x, mesh.vertsTransformed[v2].y, mesh.vertsTransformed[v2].z); + buffer, mesh.faces[f].color, + mesh.vertsTransformed[v0], + mesh.vertsTransformed[v1], + mesh.vertsTransformed[v2]); } } diff --git a/src/geometry.cpp b/src/geometry.cpp index 1291cd9..c3c8955 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -1,6 +1,8 @@ #include "color.h" +#include "engine.h" #include "geometry.h" #include "util.h" +#include // MACROS @@ -10,215 +12,80 @@ } -// 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); +// STRUCTURES +struct BoundingBox +{ + BoundingBox(Point &v0, Point &v1, Point &v2) + { + yMin = MIN(v0.y, MIN(v1.y, v2.y)); + yMax = MAX(v0.y, MAX(v1.y, v2.y)); -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); + xMin = MIN(v0.x, MIN(v1.x, v2.x)); + xMax = MAX(v0.x, MAX(v1.x, v2.x)); + } + + float yMin, yMax; + float xMin, xMax; +}; + + +static void ComputeBarycenter(float *w, Point &v0, Point &v1, Point &v2, Point &p) +{ + float area = (v2.x - v0.x) * (v1.y - v0.y) - (v2.y - v0.y) * (v1.x - v0.x); + + float result[3]; + result[0] = (p.x - v1.x) * (v2.y - v1.y) - (p.y - v1.y) * (v2.x - v1.x); + result[1] = (p.x - v2.x) * (v0.y - v2.y) - (p.y - v2.y) * (v0.x - v2.x); + result[2] = (p.x - v0.x) * (v1.y - v0.y) - (p.y - v0.y) * (v1.x - v0.x); + + result[0] /= area; + result[1] /= area; + result[2] /= area; + + w[0] = result[0]; + w[1] = result[1]; + w[2] = result[2]; +} // 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) + Engine_Buffer &buffer, ColorU32 &color, + Point &v0, Point &v1, Point &v2) { - // Horizontal or vertical - if ((FLOAT_EQUAL(x0,x1) && FLOAT_EQUAL(x1, x2)) - || (FLOAT_EQUAL(y0, y1) && FLOAT_EQUAL(y1, y2))) + BoundingBox box(v0, v1, v2); + int yMin = (int)ceilf(box.yMin); + int yMax = (int)ceilf(box.yMax) - 1; + int xMin = (int)ceilf(box.xMin); + int xMax = (int)ceilf(box.xMax) - 1; + + for (int y = yMin; y <= yMax; ++y) { - 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) + for (int x = xMin; x <= xMax; ++x) { - int pixel = (y * width) + x; + Point p(x, y, 1.0f); - if (z > zbuffer[pixel]) + float barycenter[3]; + + ComputeBarycenter(barycenter, v0, v1, v2, p); + + if (barycenter[0] >= 0 && barycenter[1] >= 0 && barycenter[2] >= 0) { - DrawPixel(buffer, width, color, x, y); + float zInv = + (barycenter[0] * (1.0f/v0.z)) + + (barycenter[1] * (1.0f/v1.z)) + + (barycenter[2] * (1.0f/v2.z)); - zbuffer[pixel] = z; + int pixel = (y * buffer.width + x); + + if (zInv > buffer.zbuffer[pixel]) + { + DrawPixel(buffer.buffer, buffer.width, color.u32, x, y); + + buffer.zbuffer[pixel] = zInv; + } } - - 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; } }