From 7c4c6d5bcb0cfbc16683b844602484d1f6e7447b Mon Sep 17 00:00:00 2001 From: Bobby Lucero Date: Sun, 28 Apr 2024 14:50:02 -0400 Subject: [PATCH] A better triangle fill algorithm --- python/main.py | 2 + python/particles.py | 22 +++- src/Graphics/Graphics.cpp | 221 +++++++++++++++++++++++--------------- src/Graphics/Graphics.h | 4 +- 4 files changed, 162 insertions(+), 87 deletions(-) diff --git a/python/main.py b/python/main.py index c23afb1..60b0001 100644 --- a/python/main.py +++ b/python/main.py @@ -46,6 +46,8 @@ def update(): text(scenes[current_scene].title, 2, 15, 63) + + diff --git a/python/particles.py b/python/particles.py index b5a6320..c4b708c 100644 --- a/python/particles.py +++ b/python/particles.py @@ -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) + + diff --git a/src/Graphics/Graphics.cpp b/src/Graphics/Graphics.cpp index d929d3f..838ea3b 100644 --- a/src/Graphics/Graphics.cpp +++ b/src/Graphics/Graphics.cpp @@ -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; + } + // 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::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; - } -} - - -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; } diff --git a/src/Graphics/Graphics.h b/src/Graphics/Graphics.h index d96ca07..6fe1ab4 100644 --- a/src/Graphics/Graphics.h +++ b/src/Graphics/Graphics.h @@ -26,6 +26,8 @@ private: std::vector 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);