implement top-left fill rules in triangle rendering

This commit is contained in:
Gered 2023-04-02 15:03:45 -04:00
parent 49a8b23568
commit 68bbd8c13c

View file

@ -1,12 +1,27 @@
use crate::graphics::bitmap::Bitmap;
use crate::graphics::Pixel;
use crate::math::vector2::Vector2;
use crate::math::NearlyEqual;
#[inline]
pub fn edge_function(a: Vector2, b: Vector2, c: Vector2) -> f32 {
(b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)
}
#[inline]
pub fn is_bottom_right_edge(v1: Vector2, v2: Vector2) -> bool {
// definitions of the different edges for counter-clockwise vertex winding
// - top: is horizontal (v1.y - v2.y == 0) and X decreases as you go across the screen (the edge points left)
// - left: Y increases as you go down the screen
// - bottom: is horizontal (v1.y - v2.y == 0) and X increases as you go across the screen (the edge points right)
// - right: Y decreases as you go up the screen
// (basically, picture a box where each edge is a vector pointing in a direction, and then move from edge to edge
// counter-clockwise and think of the X and Y directions as you move along each edge)
let edge = v1 - v2;
edge.y < 0.0 || (edge.y.nearly_equal(0.0, f32::EPSILON) && edge.x > 0.0)
}
#[inline]
pub fn per_pixel_triangle_2d<PixelType: Pixel>(
dest: &mut Bitmap<PixelType>,
@ -39,6 +54,10 @@ pub fn per_pixel_triangle_2d<PixelType: Pixel>(
let a20 = c.y - a.y;
let b20 = a.x - c.x;
let is_cb_bottom_right = is_bottom_right_edge(c, b);
let is_ac_bottom_right = is_bottom_right_edge(a, c);
let is_ba_bottom_right = is_bottom_right_edge(b, a);
let p = Vector2::new(min_x as f32, min_y as f32);
let mut w0_row = edge_function(b, c, p);
let mut w1_row = edge_function(c, a, p);
@ -55,6 +74,15 @@ pub fn per_pixel_triangle_2d<PixelType: Pixel>(
let row_pixels = unsafe { std::slice::from_raw_parts_mut(pixels, draw_width) };
for pixel in row_pixels.iter_mut() {
// skip bottom-right edge pixels so we only draw pixels inside the triangle as well as those that lie
// on the top-left edges. this fixes seam issues with triangles drawn with blending that share an edge
if (w0.nearly_equal(0.0, f32::EPSILON) && is_cb_bottom_right)
|| (w1.nearly_equal(0.0, f32::EPSILON) && is_ac_bottom_right)
|| (w2.nearly_equal(0.0, f32::EPSILON) && is_ba_bottom_right)
{
continue;
}
// note that for a counter-clockwise vertex winding order with the direction of Y+ going down instead
// of up, we need to test for *negative* area when checking if we're inside the triangle
if w0 <= 0.0 && w1 <= 0.0 && w2 <= 0.0 {