1
0
Fork 0
2018-soft-3d-renderer/src/geometry.cpp

225 lines
5.3 KiB
C++

#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;
}
}