A better triangle fill algorithm

This commit is contained in:
Bobby Lucero 2024-04-28 14:50:02 -04:00
parent c93eab9963
commit 7c4c6d5bcb
4 changed files with 162 additions and 87 deletions

View File

@ -49,3 +49,5 @@ def update():

View File

@ -1,6 +1,7 @@
from scene import Scene
from triangles import draw_line
from triangles import lineTri
import math
white = 63
fire1 = 6
fire2 = 5
@ -132,5 +133,24 @@ class Particles(Scene):
text("Num Particles: " + str(len(self.fireParts) + len(self.waterParts)), 2, 15, 30)
text("FPS: " + str(fps()), 3, 29, 59)
text("FPS: " + str(fps()), 2, 28, 29)
# triangle(10, 10, 50, 10, mouseX, mouseY, 5)
# lineTri(10, 10, 50, 10, mouseX, mouseY, 9)
pixel(mouseX, mouseY, 9)
cx = width/2
cy = height/2
ang = math.atan2(mouseY - cy, mouseX - cx)
dist = math.sqrt(math.pow(mouseX - cx, 2) + math.pow(mouseY - cy, 2))
x2 = cx + cos(ang + (math.pi * 2 / 3)) * dist
y2 = cy + sin(ang + (math.pi * 2 / 3)) * dist
x3 = cx + cos(ang + (math.pi * 2 / 3 * 2)) * dist
y3 = cy + sin(ang + (math.pi * 2 / 3 * 2)) * dist
# triangle(mouseX, mouseY, x2, y2, x3, y3, 9)
# lineTri(mouseX, mouseY, x2, y2, x3, y3, 8)

View File

@ -363,97 +363,148 @@ int Graphics::GetPixel(int x, int y) {
return m_virtualScreenColorBuffer[y * m_screenWidth + x];
}
void Graphics::fillBottomFlatTriangle(float x1, float y1, float x2, float y2, float x3, float y3, int paletteIndex)
// https://github.com/OneLoneCoder/olcPixelGameEngine/blob/master/olcPixelGameEngine.h#L2456
void Graphics::Triangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, int paletteIndex)
{
float invslope1 = (x2 - x1) / (y2 - y1);
float invslope2 = (x3 - x1) / (y3 - y1);
auto drawline = [&](int sx, int ex, int ny) { for (int i = sx; i <= ex; i++) Pixel(i, ny, paletteIndex); };
float curx1 = x1;
float curx2 = x1;
int t1x, t2x, y, minx, maxx, t1xp, t2xp;
bool changed1 = false;
bool changed2 = false;
int signx1, signx2, dx1, dy1, dx2, dy2;
int e1, e2;
// Sort vertices
if (y1 > y2) { std::swap(y1, y2); std::swap(x1, x2); }
if (y1 > y3) { std::swap(y1, y3); std::swap(x1, x3); }
if (y2 > y3) { std::swap(y2, y3); std::swap(x2, x3); }
for (int scanlineY = y1; scanlineY <= y2; scanlineY++)
{
h_line((int)curx1, scanlineY, (int)curx2, paletteIndex);
curx1 += invslope1;
curx2 += invslope2;
t1x = t2x = x1; y = y1; // Starting points
dx1 = (int)(x2 - x1);
if (dx1 < 0) { dx1 = -dx1; signx1 = -1; }
else signx1 = 1;
dy1 = (int)(y2 - y1);
dx2 = (int)(x3 - x1);
if (dx2 < 0) { dx2 = -dx2; signx2 = -1; }
else signx2 = 1;
dy2 = (int)(y3 - y1);
if (dy1 > dx1) { std::swap(dx1, dy1); changed1 = true; }
if (dy2 > dx2) { std::swap(dy2, dx2); changed2 = true; }
e2 = (int)(dx2 >> 1);
// Flat top, just process the second half
if (y1 == y2) goto next;
e1 = (int)(dx1 >> 1);
for (int i = 0; i < dx1;) {
t1xp = 0; t2xp = 0;
if (t1x < t2x) { minx = t1x; maxx = t2x; }
else { minx = t2x; maxx = t1x; }
// process first line until y value is about to change
while (i < dx1) {
i++;
e1 += dy1;
while (e1 >= dx1) {
e1 -= dx1;
if (changed1) t1xp = signx1;//t1x += signx1;
else goto next1;
}
if (changed1) break;
else t1x += signx1;
}
void Graphics::fillTopFlatTriangle(float x1, float y1, float x2, float y2, float x3, float y3, int paletteIndex)
{
float invslope1 = (x3 - x1) / (y3 - y1);
float invslope2 = (x3 - x2) / (y3 - y2);
float curx1 = x3;
float curx2 = x3;
for (int scanlineY = y3; scanlineY > y1; scanlineY--)
{
h_line((int)curx1, scanlineY, (int)curx2, paletteIndex);
curx1 -= invslope1;
curx2 -= invslope2;
// Move line
next1:
// process second line until y value is about to change
while (1) {
e2 += dy2;
while (e2 >= dx2) {
e2 -= dx2;
if (changed2) t2xp = signx2;//t2x += signx2;
else goto next2;
}
if (changed2) break;
else t2x += signx2;
}
next2:
if (minx > t1x) minx = t1x;
if (minx > t2x) minx = t2x;
if (maxx < t1x) maxx = t1x;
if (maxx < t2x) maxx = t2x;
drawline(minx, maxx, y); // Draw line from min to max points found on the y
// Now increase y
if (!changed1) t1x += signx1;
t1x += t1xp;
if (!changed2) t2x += signx2;
t2x += t2xp;
y += 1;
if (y == y2) break;
}
next:
// Second half
dx1 = (int)(x3 - x2); if (dx1 < 0) { dx1 = -dx1; signx1 = -1; }
else signx1 = 1;
dy1 = (int)(y3 - y2);
t1x = x2;
if (dy1 > dx1) { // swap values
std::swap(dy1, dx1);
changed1 = true;
}
else changed1 = false;
e1 = (int)(dx1 >> 1);
for (int i = 0; i <= dx1; i++) {
t1xp = 0; t2xp = 0;
if (t1x < t2x) { minx = t1x; maxx = t2x; }
else { minx = t2x; maxx = t1x; }
// process first line until y value is about to change
while (i < dx1) {
e1 += dy1;
while (e1 >= dx1) {
e1 -= dx1;
if (changed1) { t1xp = signx1; break; }//t1x += signx1;
else goto next3;
}
if (changed1) break;
else t1x += signx1;
if (i < dx1) i++;
}
next3:
// process second line until y value is about to change
while (t2x != x3) {
e2 += dy2;
while (e2 >= dx2) {
e2 -= dx2;
if (changed2) t2xp = signx2;
else goto next4;
}
if (changed2) break;
else t2x += signx2;
}
next4:
if (minx > t1x) minx = t1x;
if (minx > t2x) minx = t2x;
if (maxx < t1x) maxx = t1x;
if (maxx < t2x) maxx = t2x;
drawline(minx, maxx, y);
if (!changed1) t1x += signx1;
t1x += t1xp;
if (!changed2) t2x += signx2;
t2x += t2xp;
y += 1;
if (y > y3) return;
}
void Graphics::Triangle(int x1, int y1, int x2, int y2, int x3, int y3, int paletteIndex)
{
int aX = x1;
int aY = y1;
int bX = x2;
int bY = y2;
int cX = x3;
int cY = y3;
if(bY < aY){
int t = bY;
bY = aY;
aY = t;
t = bX;
bX = aX;
aX = t;
}
if(cY < aY){
int t = cY;
cY = aY;
aY = t;
t = cX;
cX = aX;
aX = t;
}
if(cY < bY){
int t = cY;
cY = bY;
bY = t;
t = cX;
cX = bX;
bX = t;
}
//std::cout << aX << " " << aY << " " << bX << " " << bY << " " << cX << " " << cY << "\n";
/* here we know that v1.y <= v2.y <= v3.y */
/* check for trivial case of bottom-flat triangle */
if (bY == cY)
{
fillBottomFlatTriangle(aX, aY, bX, bY, cX, cY, paletteIndex);
}
/* check for trivial case of top-flat triangle */
else if (aY == bY)
{
fillTopFlatTriangle(aX, aY, bX, bY, cX, cY, paletteIndex);
}
else
{
/* general case - split the triangle in a topflat and bottom-flat one */
int dX = aX + ((float)(bY - aY) / (float)(cY - aY)) * (cX - aX);
int dY = bY;
fillBottomFlatTriangle(aX, aY, bX, bY, dX, dY, paletteIndex);
fillTopFlatTriangle(bX, bY, dX, dY, cX, cY, paletteIndex);
}
void Graphics::swap(float &a, float &b) {
float temp = a;
a = b;
b = temp;
}

View File

@ -26,6 +26,8 @@ private:
std::vector<uint8_t> m_virtualScreenColorBuffer;
Image m_virtualScreenImageBuffer;
static void swap(float &a, float &b);
private:
void renderVirtualScreen();
@ -72,7 +74,7 @@ public:
void Rect(int x, int y, int width, int height, int paletteIndex);
void RectBorder(int x, int y, int width, int height, int paletteIndex);
void Text(std::string s, int x, int y, int paletteIndex);
void Triangle(int x1, int y1, int x2, int y2, int x3, int y3, int paletteIndex);
void Triangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, int paletteIndex);
int GetPixel(int x, int y);