implement top-left fill rules in triangle rendering
This commit is contained in:
parent
49a8b23568
commit
68bbd8c13c
|
@ -1,12 +1,27 @@
|
||||||
use crate::graphics::bitmap::Bitmap;
|
use crate::graphics::bitmap::Bitmap;
|
||||||
use crate::graphics::Pixel;
|
use crate::graphics::Pixel;
|
||||||
use crate::math::vector2::Vector2;
|
use crate::math::vector2::Vector2;
|
||||||
|
use crate::math::NearlyEqual;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn edge_function(a: Vector2, b: Vector2, c: Vector2) -> f32 {
|
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)
|
(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]
|
#[inline]
|
||||||
pub fn per_pixel_triangle_2d<PixelType: Pixel>(
|
pub fn per_pixel_triangle_2d<PixelType: Pixel>(
|
||||||
dest: &mut Bitmap<PixelType>,
|
dest: &mut Bitmap<PixelType>,
|
||||||
|
@ -39,6 +54,10 @@ pub fn per_pixel_triangle_2d<PixelType: Pixel>(
|
||||||
let a20 = c.y - a.y;
|
let a20 = c.y - a.y;
|
||||||
let b20 = a.x - c.x;
|
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 p = Vector2::new(min_x as f32, min_y as f32);
|
||||||
let mut w0_row = edge_function(b, c, p);
|
let mut w0_row = edge_function(b, c, p);
|
||||||
let mut w1_row = edge_function(c, a, 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) };
|
let row_pixels = unsafe { std::slice::from_raw_parts_mut(pixels, draw_width) };
|
||||||
for pixel in row_pixels.iter_mut() {
|
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
|
// 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
|
// 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 {
|
if w0 <= 0.0 && w1 <= 0.0 && w2 <= 0.0 {
|
||||||
|
|
Loading…
Reference in a new issue