Use barycentric coordinates instead of half space for triangle fill

This commit is contained in:
Abdelrahman Said 2024-01-21 22:36:36 +00:00
parent b329e0feb4
commit f72497e1b7

View File

@ -1,5 +1,6 @@
#include "window.h"
#include "aliases/aliases.h"
#include "math_utils.h"
#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_video.h>
@ -89,42 +90,44 @@ void draw_triangle(const window *wnd, triangle triangle, colour colour) {
draw_line(wnd, &ln2, colour);
}
INTERNAL inline i32 min(i32 a, i32 b, i32 c) {
i32 _min = a <= b ? a : b;
return _min <= c ? _min : c;
INTERNAL inline bool inside_triangle(triangle tri, point p) {
// Based on the following video:
// https://www.youtube.com/watch?v=HYAgJN3x4GA
f32 cy_min_ay = tri.p2.y - tri.p0.y;
f32 cx_min_ax = tri.p2.x - tri.p0.x;
f32 by_min_ay = tri.p1.y - tri.p0.y;
f32 bx_min_ax = tri.p1.x - tri.p0.x;
f32 w1 =
(tri.p0.x * cy_min_ay + (p.y - tri.p0.y) * cx_min_ax - p.x * cy_min_ay) /
(by_min_ay * cx_min_ax - bx_min_ax * cy_min_ay);
f32 w2 = (p.y - tri.p0.y - w1 * by_min_ay) / cy_min_ay;
return w1 >= 0.0f && w2 >= 0.0f && (w1 + w2) <= 1.0f;
}
INTERNAL inline i32 max(i32 a, i32 b, i32 c) {
i32 _max = a >= b ? a : b;
return _max >= c ? _max : c;
}
INTERNAL inline i32 half_space(i32 x1, i32 x2, i32 y1, i32 y2, i32 x, i32 y) {
return (x2 - x1) * (y - y1) - (y2 - y1) * (x - x1);
}
void fill_triangle(const window *wnd, triangle triangle, colour colour) {
// Basic triangle filling algorithm from
void fill_triangle(const window *wnd, triangle tri, colour colour) {
// Basic triangle filling algorithm inspired by
// https://web.archive.org/web/20050408192410/http://sw-shader.sourceforge.net/rasterizer.html
i32 x1 = triangle.p0.x;
i32 x2 = triangle.p1.x;
i32 x3 = triangle.p2.x;
// but uses barycentric coordinates instead of half space
i32 x1 = tri.p0.x;
i32 x2 = tri.p1.x;
i32 x3 = tri.p2.x;
i32 y1 = triangle.p0.y;
i32 y2 = triangle.p1.y;
i32 y3 = triangle.p2.y;
i32 y1 = tri.p0.y;
i32 y2 = tri.p1.y;
i32 y3 = tri.p2.y;
// Find bounding rect
i32 min_x = min(x1, x2, x3);
i32 max_x = max(x1, x2, x3);
i32 min_y = min(y1, y2, y3);
i32 max_y = max(y1, y2, y3);
i32 min_x = min(min(x1, x2), x3);
i32 max_x = max(max(x1, x2), x3);
i32 min_y = min(min(y1, y2), y3);
i32 max_y = max(max(y1, y2), y3);
for (i32 y = min_y; y < max_y; ++y) {
for (i32 x = min_x; x < max_x; ++x) {
if (half_space(x2, x1, y2, y1, x, y) >= 0 &&
half_space(x3, x2, y3, y2, x, y) >= 0 &&
half_space(x1, x3, y1, y3, x, y) >= 0) {
if (inside_triangle(tri, (point){x, y})) {
draw_point(wnd, (point){x, y}, colour);
}
}
@ -144,8 +147,8 @@ void draw_quad(const window *wnd, quad qd, colour colour) {
}
void fill_quad(const window *wnd, quad qd, colour colour) {
triangle t0 = (triangle){qd.p3, qd.p1, qd.p0};
triangle t1 = (triangle){qd.p0, qd.p2, qd.p3};
triangle t0 = (triangle){qd.p0, qd.p1, qd.p2};
triangle t1 = (triangle){qd.p1, qd.p2, qd.p3};
fill_triangle(wnd, t0, colour);
fill_triangle(wnd, t1, colour);