Improve triangle rasteriser
This commit is contained in:
parent
d9ce1e1079
commit
bdb4b89bbf
@ -4,8 +4,20 @@
|
|||||||
#define DQN_IMPLEMENTATION
|
#define DQN_IMPLEMENTATION
|
||||||
#include "dqn.h"
|
#include "dqn.h"
|
||||||
|
|
||||||
#include <math.h>
|
FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer,
|
||||||
FILE_SCOPE void DR_DrawLine(PlatformRenderBuffer *const renderBuffer, DqnV2i a,
|
const i32 x, const i32 y, DqnV3 color)
|
||||||
|
{
|
||||||
|
if (!renderBuffer) return;
|
||||||
|
if (x < 0 || x > renderBuffer->width - 1) return;
|
||||||
|
if (y < 0 || y > renderBuffer->height - 1) return;
|
||||||
|
|
||||||
|
u32 *const bitmapPtr = (u32 *)renderBuffer->memory;
|
||||||
|
const u32 pitchInU32 = (renderBuffer->width * renderBuffer->bytesPerPixel) / 4;
|
||||||
|
u32 pixel = ((i32)color.r << 16) | ((i32)color.g << 8) | ((i32)color.b << 0);
|
||||||
|
bitmapPtr[x + (y * pitchInU32)] = pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE_SCOPE void DrawLine(PlatformRenderBuffer *const renderBuffer, DqnV2i a,
|
||||||
DqnV2i b, DqnV3 color)
|
DqnV2i b, DqnV3 color)
|
||||||
{
|
{
|
||||||
if (!renderBuffer) return;
|
if (!renderBuffer) return;
|
||||||
@ -50,14 +62,10 @@ FILE_SCOPE void DR_DrawLine(PlatformRenderBuffer *const renderBuffer, DqnV2i a,
|
|||||||
plotY = &newY;
|
plotY = &newY;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 pitchInU32 =
|
|
||||||
(renderBuffer->width * renderBuffer->bytesPerPixel) / 4;
|
|
||||||
u32 *const bitmapPtr = (u32 *)renderBuffer->memory;
|
|
||||||
u32 pixel = ((i32)color.r << 16) | ((i32)color.g << 8) | ((i32)color.b << 0);
|
|
||||||
for (i32 iterateX = 0; iterateX < numIterations; iterateX++)
|
for (i32 iterateX = 0; iterateX < numIterations; iterateX++)
|
||||||
{
|
{
|
||||||
newX = a.x + iterateX;
|
newX = a.x + iterateX;
|
||||||
bitmapPtr[*plotX + (*plotY * pitchInU32)] = pixel;
|
SetPixel(renderBuffer, *plotX, *plotY, color);
|
||||||
|
|
||||||
distAccumulator += distFromPixelOrigin;
|
distAccumulator += distFromPixelOrigin;
|
||||||
if (distAccumulator > run)
|
if (distAccumulator > run)
|
||||||
@ -68,26 +76,140 @@ FILE_SCOPE void DR_DrawLine(PlatformRenderBuffer *const renderBuffer, DqnV2i a,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE_SCOPE void DR_DrawTriangle(PlatformRenderBuffer *const renderBuffer,
|
FILE_SCOPE void DrawTriangle(PlatformRenderBuffer *const renderBuffer,
|
||||||
DqnV2 p1, DqnV2 p2, DqnV2 p3, DqnV3 color)
|
const DqnV2 p1, const DqnV2 p2, const DqnV2 p3,
|
||||||
|
const DqnV3 color)
|
||||||
{
|
{
|
||||||
DR_DrawLine(renderBuffer, p1, p2, color);
|
DqnV2i max = DqnV2i_2f(DQN_MAX(DQN_MAX(p1.x, p2.x), p3.x),
|
||||||
DR_DrawLine(renderBuffer, p2, p3, color);
|
DQN_MAX(DQN_MAX(p1.y, p2.y), p3.y));
|
||||||
DR_DrawLine(renderBuffer, p3, p1, color);
|
DqnV2i min = DqnV2i_2f(DQN_MIN(DQN_MIN(p1.x, p2.x), p3.x),
|
||||||
|
DQN_MIN(DQN_MIN(p1.y, p2.y), p3.y));
|
||||||
|
|
||||||
// NOTE(doyle): This is just an desc sort using bubble sort on 3 elements
|
min.x = DQN_MAX(min.x, 0);
|
||||||
if (p1.y < p2.y) DQN_SWAP(DqnV2i, p1, p2);
|
min.y = DQN_MAX(min.y, 0);
|
||||||
if (p2.y < p3.y) DQN_SWAP(DqnV2i, p1, p3);
|
|
||||||
if (p1.y < p2.y) DQN_SWAP(DqnV2i, p2, p3);
|
|
||||||
|
|
||||||
i32 y1i = (i32)(p1.y + 0.5f);
|
max.x = DQN_MIN(max.x, renderBuffer->width - 1);
|
||||||
i32 y2i = (i32)(p2.y + 0.5f);
|
max.y = DQN_MIN(max.y, renderBuffer->height - 1);
|
||||||
i32 y3i = (i32)(p3.y + 0.5f);
|
|
||||||
|
|
||||||
if (y1i == y3i) return; // Zero height triangle
|
DrawLine(renderBuffer, DqnV2i_2i(min.x, min.y), DqnV2i_2i(min.x, max.y), color);
|
||||||
|
DrawLine(renderBuffer, DqnV2i_2i(min.x, max.y), DqnV2i_2i(max.x, max.y), color);
|
||||||
|
DrawLine(renderBuffer, DqnV2i_2i(max.x, max.y), DqnV2i_2i(max.x, min.y), color);
|
||||||
|
DrawLine(renderBuffer, DqnV2i_2i(max.x, min.y), DqnV2i_2i(min.x, min.y), color);
|
||||||
|
|
||||||
|
DqnV2 a = p1;
|
||||||
|
DqnV2 b = p2;
|
||||||
|
DqnV2 c = p3;
|
||||||
|
|
||||||
|
f32 area2Times = ((b.x - a.x) * (b.y + a.y)) + ((c.x - b.x) * (c.y + b.y)) +
|
||||||
|
((a.x - c.x) * (a.y + c.y));
|
||||||
|
if (area2Times < 0)
|
||||||
|
{
|
||||||
|
// Counter-clockwise, do nothing this is what we want.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Clockwise swap any point to make it clockwise
|
||||||
|
DQN_SWAP(DqnV2, b, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE_SCOPE void DR_ClearRenderBuffer(PlatformRenderBuffer *const renderBuffer, DqnV3 color)
|
/*
|
||||||
|
NOTE(doyle): Given two points that form a line and an extra point
|
||||||
|
to test, we can determine whether a point lies on the line, or is
|
||||||
|
to the left or right of a the line.
|
||||||
|
|
||||||
|
First forming a 3x3 matrix of our terms and deriving a 2x2 matrix
|
||||||
|
by subtracting the 1st column from the 2nd and 1st column from
|
||||||
|
the third.
|
||||||
|
|
||||||
|
| ax bx cx | | (bx - ax) (cx - ax) |
|
||||||
|
m = | ay by cy | ==> | (by - ay) (cy - ay) |
|
||||||
|
| 1 1 1 |
|
||||||
|
|
||||||
|
From our 2x2 representation we can calculate the determinant
|
||||||
|
which gives us the signed area of the triangle extended into
|
||||||
|
a parallelogram.
|
||||||
|
|
||||||
|
det(m) = (bx - ax)(cy - ay) - (by - ay)(cx - ax)
|
||||||
|
|
||||||
|
Depending on the order of the vertices supplied, if it's
|
||||||
|
- CCW and c(x,y) is outside the triangle, the signed area is negative
|
||||||
|
- CCW and c(x,y) is inside the triangle, the signed area is positive
|
||||||
|
- CW and c(x,y) is outside the triangle, the signed area is positive
|
||||||
|
- CW and c(x,y) is inside the triangle, the signed area is negative
|
||||||
|
|
||||||
|
NOTE(doyle): The det(m) can be rearranged if expanded to be
|
||||||
|
SignedArea(cx, cy) = (ay - by)cx + (bx - ay)cy + (ax*by + ay*bx)
|
||||||
|
|
||||||
|
When we scan to fill our triangle we go pixel by pixel, left to right,
|
||||||
|
bottom to top, notice that this translates to +1 for x and +1 for y, i.e.
|
||||||
|
|
||||||
|
The first pixel's signed area is cx, then cx+1, cx+2 .. etc
|
||||||
|
SignedArea(cx, cy) = (ay - by)cx + (bx - ax)cy + (ax*by + ay*bx)
|
||||||
|
SignedArea(cx+1, cy) = (ay - by)cx+1 + (bx - ax)cy + (ax*by + ay*bx)
|
||||||
|
|
||||||
|
Then
|
||||||
|
SignedArea(cx+1, cy) - SignedArea(cx, cy) =
|
||||||
|
(ay - by)cx+1 + (bx - ax)cy + (ax*by + ay*bx)
|
||||||
|
- (ay - by)cx + (bx - ax)cy + (ax*by + ay*bx)
|
||||||
|
= (ay - by)cx+1 - (ay - by)cx
|
||||||
|
= (ay - by)(cx+1 - cx)
|
||||||
|
= (ay - by)(1) = (ay - by)
|
||||||
|
|
||||||
|
Similarly when progressing in y
|
||||||
|
SignedArea(cx, cy) = (ay - by)cx + (bx - ay)cy + (ax*by + ay*bx)
|
||||||
|
SignedArea(cx, cy+1) = (ay - by)cx + (bx - ay)cy+1 + (ax*by + ay*bx)
|
||||||
|
|
||||||
|
Then
|
||||||
|
SignedArea(cx, cy+1) - SignedArea(cx, cy) =
|
||||||
|
(ay - by)cx + (bx - ax)cy+1 + (ax*by + ay*bx)
|
||||||
|
- (ay - by)cx + (bx - ax)cy + (ax*by + ay*bx)
|
||||||
|
= (bx - ax)cy+1 - (bx - ax)cy
|
||||||
|
= (bx - ax)(cy+1 - cy)
|
||||||
|
= (bx - ax)(1) = (bx - ax)
|
||||||
|
|
||||||
|
Then we can see that when we progress along x, we only need to change by
|
||||||
|
the value of SignedArea by (ay - by) and similarly for y, (bx - ay)
|
||||||
|
*/
|
||||||
|
|
||||||
|
DqnV2i scanP = DqnV2i_2i(min.x, min.y);
|
||||||
|
f32 signedArea1 = ((b.x - a.x) * (scanP.y - a.y)) - ((b.y - a.y) * (scanP.x - a.x));
|
||||||
|
f32 signedArea1DeltaX = a.y - b.y;
|
||||||
|
f32 signedArea1DeltaY = b.x - a.x;
|
||||||
|
|
||||||
|
f32 signedArea2 = ((c.x - b.x) * (scanP.y - b.y)) - ((c.y - b.y) * (scanP.x - b.x));
|
||||||
|
f32 signedArea2DeltaX = b.y - c.y;
|
||||||
|
f32 signedArea2DeltaY = c.x - b.x;
|
||||||
|
|
||||||
|
f32 signedArea3 = ((a.x - c.x) * (scanP.y - c.y)) - ((a.y - c.y) * (scanP.x - c.x));
|
||||||
|
f32 signedArea3DeltaX = c.y - a.y;
|
||||||
|
f32 signedArea3DeltaY = a.x - c.x;
|
||||||
|
|
||||||
|
for (scanP.y = min.y; scanP.y < max.y; scanP.y++)
|
||||||
|
{
|
||||||
|
|
||||||
|
f32 signedArea1Row = signedArea1;
|
||||||
|
f32 signedArea2Row = signedArea2;
|
||||||
|
f32 signedArea3Row = signedArea3;
|
||||||
|
|
||||||
|
for (scanP.x = min.x; scanP.x < max.x; scanP.x++)
|
||||||
|
{
|
||||||
|
if (signedArea1Row >= 0 && signedArea2Row >= 0 && signedArea3Row >= 0)
|
||||||
|
{
|
||||||
|
SetPixel(renderBuffer, scanP.x, scanP.y, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
signedArea1Row += signedArea1DeltaX;
|
||||||
|
signedArea2Row += signedArea2DeltaX;
|
||||||
|
signedArea3Row += signedArea3DeltaX;
|
||||||
|
}
|
||||||
|
|
||||||
|
signedArea1 += signedArea1DeltaY;
|
||||||
|
signedArea2 += signedArea2DeltaY;
|
||||||
|
signedArea3 += signedArea3DeltaY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE_SCOPE void ClearRenderBuffer(PlatformRenderBuffer *const renderBuffer, DqnV3 color)
|
||||||
{
|
{
|
||||||
if (!renderBuffer) return;
|
if (!renderBuffer) return;
|
||||||
|
|
||||||
@ -111,7 +233,7 @@ extern "C" void DR_Update(PlatformRenderBuffer *const renderBuffer,
|
|||||||
PlatformInput *const input,
|
PlatformInput *const input,
|
||||||
PlatformMemory *const memory)
|
PlatformMemory *const memory)
|
||||||
{
|
{
|
||||||
DR_ClearRenderBuffer(renderBuffer, DqnV3_3f(0, 0, 0));
|
ClearRenderBuffer(renderBuffer, DqnV3_3f(0, 0, 0));
|
||||||
|
|
||||||
DqnV3 colorRed = DqnV3_3i(255, 0, 0);
|
DqnV3 colorRed = DqnV3_3i(255, 0, 0);
|
||||||
DqnV2i bufferMidP = DqnV2i_2f(renderBuffer->width * 0.5f, renderBuffer->height * 0.5f);
|
DqnV2i bufferMidP = DqnV2i_2f(renderBuffer->width * 0.5f, renderBuffer->height * 0.5f);
|
||||||
@ -120,13 +242,31 @@ extern "C" void DR_Update(PlatformRenderBuffer *const renderBuffer,
|
|||||||
DqnV2 t0[3] = {DqnV2_2i(10, 70), DqnV2_2i(50, 160), DqnV2_2i(70, 80)};
|
DqnV2 t0[3] = {DqnV2_2i(10, 70), DqnV2_2i(50, 160), DqnV2_2i(70, 80)};
|
||||||
DqnV2 t1[3] = {DqnV2_2i(180, 50), DqnV2_2i(150, 1), DqnV2_2i(70, 180)};
|
DqnV2 t1[3] = {DqnV2_2i(180, 50), DqnV2_2i(150, 1), DqnV2_2i(70, 180)};
|
||||||
DqnV2 t2[3] = {DqnV2_2i(180, 150), DqnV2_2i(120, 160), DqnV2_2i(130, 180)};
|
DqnV2 t2[3] = {DqnV2_2i(180, 150), DqnV2_2i(120, 160), DqnV2_2i(130, 180)};
|
||||||
DqnV2 t3[3] = {
|
LOCAL_PERSIST DqnV2 t3[3] = {
|
||||||
DqnV2_2i(boundsOffset, boundsOffset),
|
DqnV2_2i(boundsOffset, boundsOffset),
|
||||||
DqnV2_2i(bufferMidP.w, renderBuffer->height - boundsOffset),
|
DqnV2_2i(bufferMidP.w, renderBuffer->height - boundsOffset),
|
||||||
DqnV2_2i(renderBuffer->width - boundsOffset, boundsOffset)};
|
DqnV2_2i(renderBuffer->width - boundsOffset, boundsOffset)};
|
||||||
|
|
||||||
DR_DrawTriangle(renderBuffer, t0[0], t0[1], t0[2], colorRed);
|
f32 minX = (f32)(renderBuffer->width - 1);
|
||||||
// DR_DrawTriangle(renderBuffer, t1[0], t1[1], t1[2], colorRed);
|
f32 maxX = 0;
|
||||||
// DR_DrawTriangle(renderBuffer, t2[0], t2[1], t2[2], colorRed);
|
for (i32 i = 0; i < DQN_ARRAY_COUNT(t3); i++)
|
||||||
// DR_DrawTriangle(renderBuffer, t3[0], t3[1], t3[2], colorRed);
|
{
|
||||||
|
t3[i].x += input->deltaForFrame * 2.0f;
|
||||||
|
minX = DQN_MIN(t3[i].x, minX);
|
||||||
|
maxX = DQN_MAX(t3[i].x, maxX);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minX >= renderBuffer->width - 1)
|
||||||
|
{
|
||||||
|
f32 triangleWidth = maxX - minX;
|
||||||
|
for (i32 i = 0; i < DQN_ARRAY_COUNT(t3); i++)
|
||||||
|
{
|
||||||
|
t3[i].x -= (minX + triangleWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawTriangle(renderBuffer, t0[0], t0[1], t0[2], colorRed);
|
||||||
|
DrawTriangle(renderBuffer, t1[0], t1[1], t1[2], colorRed);
|
||||||
|
DrawTriangle(renderBuffer, t2[0], t2[1], t2[2], colorRed);
|
||||||
|
DrawTriangle(renderBuffer, t3[0], t3[1], t3[2], colorRed);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user