diff --git a/Makefile b/Makefile index 536d870..b2c0ac5 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,8 @@ CC=clang++ WARNINGS_ON=-Weverything WARNINGS_OFF=-Wno-missing-braces -Wno-gnu-anonymous-struct -Wno-old-style-cast\ -Wno-zero-as-null-pointer-constant -Wno-nested-anon-types\ - -Wno-padded -Wno-exit-time-destructors -Wno-global-constructors + -Wno-padded -Wno-exit-time-destructors -Wno-global-constructors\ + -Wno-c++98-compat CFLAGS=$(D) $(O) -std=c++11 $(WARNINGS_ON) $(WARNINGS_OFF) -I$(INCLUDE_DIR) LIBS=-lSDL2 diff --git a/include/engine.h b/include/engine.h index 5e49eb8..bb9d214 100644 --- a/include/engine.h +++ b/include/engine.h @@ -26,7 +26,8 @@ enum Engine_Input // STRUCTURES struct Engine_Buffer { - uint32_t *pixels; + uint32_t *buffer; + float *zbuffer; int width; int height; }; diff --git a/include/geometry.h b/include/geometry.h index b847a03..bc18e03 100644 --- a/include/geometry.h +++ b/include/geometry.h @@ -77,10 +77,11 @@ struct Mesh // PUBLIC FUNCTIONS void FillTriangle( - uint32_t *buffer, int width, uint32_t color, - float x0, float y0, - float x1, float y1, - float x2, float y2); + 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); #define GEOMETRY_H diff --git a/include/matrix.h b/include/matrix.h index 633ae19..62b53d9 100644 --- a/include/matrix.h +++ b/include/matrix.h @@ -74,6 +74,13 @@ inline Point operator*(Point v, Matrix m) return result; } +inline Point &operator*=(Point &v, Matrix m) +{ + v = v * m; + + return v; +} + #define MATRIX_H #endif diff --git a/include/point.h b/include/point.h index 92cdfba..0c51f86 100644 --- a/include/point.h +++ b/include/point.h @@ -36,6 +36,14 @@ inline Point operator/(Point v, float f) return result; } +// v /= f +inline Point &operator/=(Point &v, float f) +{ + v = v / f; + + return v; +} + // v1 - v2 inline Vector operator-(Point v1, Point v2) { diff --git a/src/engine.cpp b/src/engine.cpp index 4d551cc..409647f 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -8,7 +8,7 @@ #include "transform.h" #include "util.h" #include "vec.h" -#include +#include // GLOBALS @@ -50,6 +50,8 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input) { CheckInputs(input); + 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( @@ -62,8 +64,7 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input) for (size_t v = 0; v < mesh.verts.size(); ++v) { - mesh.vertsTransformed[v] = - mesh.verts[v] * tScale * tRotate * tTranslate; + mesh.vertsTransformed[v] = mesh.verts[v] * tScale * tRotate * tTranslate; } @@ -99,25 +100,17 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input) // World space to camera (view) space Matrix tView = Transform_View(camera.position, camera.rotation); - // Camera space to perspective Matrix tPersp = Transform_Perspective( camera.xZoom, camera.yZoom, camera.nearClip, camera.farClip); - for (size_t v = 0; v < mesh.verts.size(); ++v) - { - mesh.vertsTransformed[v] = mesh.vertsTransformed[v] * tView * tPersp; - - mesh.vertsTransformed[v] = - mesh.vertsTransformed[v] / mesh.vertsTransformed[v].w; - } - // Perspective to screen Matrix tScreen = Transform_Screen(camera.xScale, camera.yScale); for (size_t v = 0; v < mesh.verts.size(); ++v) { - mesh.vertsTransformed[v] = mesh.vertsTransformed[v] * tScreen; + mesh.vertsTransformed[v] *= tView * tPersp * tScreen; + mesh.vertsTransformed[v] /= mesh.vertsTransformed[v].w; } @@ -129,10 +122,11 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input) unsigned int v2 = mesh.culledFaces[f].vertIndex[2]; FillTriangle( - buffer.pixels, buffer.width, mesh.faces[f].color.u32, - mesh.vertsTransformed[v0].x, mesh.vertsTransformed[v0].y, - mesh.vertsTransformed[v1].x, mesh.vertsTransformed[v1].y, - mesh.vertsTransformed[v2].x, mesh.vertsTransformed[v2].y); + 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); } } diff --git a/src/geometry.cpp b/src/geometry.cpp index 82ce269..1291cd9 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -12,24 +12,27 @@ // PRIVATE PROTOTYPES static void FillTriangleFlatBottom( - uint32_t *buffer, int width, uint32_t color, - float x0, float y0, - float x1, float y1, - float x2, float y2); + 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, uint32_t color, - float x0, float y0, - float x1, float y1, - float x2, float y2); + 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, uint32_t color, - float x0, float y0, - float x1, float y1, - float x2, float y2) + 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)) @@ -44,31 +47,34 @@ void FillTriangle( { 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, color, x0, y0, x1, y1, x2, y2); + 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, color, x0, y0, x1, y1, x2, y2); + FillTriangleFlatBottom(buffer, width, zbuffer, color, x0, y0, z0, x1, y1, z1, x2, y2, z2); } // Other - decompose into two triangles @@ -78,27 +84,30 @@ void FillTriangle( // 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, color, x0, y0, x1, y1, x3, y3); - FillTriangleFlatTop(buffer, width, color, x1, y1, x3, y3, x2, y2); + 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, uint32_t color, - float x0, float y0, - float x1, float y1, - float x2, float y2) + 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(x1, x2, 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); @@ -108,35 +117,62 @@ static void FillTriangleFlatBottom( 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) { - DrawPixel(buffer, width, color, x, y); + 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, uint32_t color, - float x0, float y0, - float x1, float y1, - float x2, float y2) + 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); @@ -146,19 +182,43 @@ static void FillTriangleFlatTop( 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) { - DrawPixel(buffer, width, color, x, y); + 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; } } diff --git a/src/main.cpp b/src/main.cpp index b8945c9..3a465d9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,7 +29,8 @@ int main(int argc, char *argv[]) if (result == PLATFORM_OK) { Engine_Buffer buffer = {}; - buffer.pixels = (uint32_t*)platform.surface->pixels; + buffer.buffer = (uint32_t*)platform.surface->pixels; + buffer.zbuffer = (float*)calloc((size_t)(platform.surface->w * platform.surface->h), sizeof(float)); buffer.width = platform.surface->w; buffer.height = platform.surface->h;