diff --git a/ggdt/src/graphics/bitmap/triangles.rs b/ggdt/src/graphics/bitmap/triangles.rs index da7e7b0..08de122 100644 --- a/ggdt/src/graphics/bitmap/triangles.rs +++ b/ggdt/src/graphics/bitmap/triangles.rs @@ -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( dest: &mut Bitmap, @@ -39,6 +54,10 @@ pub fn per_pixel_triangle_2d( 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( 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 {