diff --git a/ggdt/src/graphics/bitmap/mod.rs b/ggdt/src/graphics/bitmap/mod.rs index 0d43160..782065c 100644 --- a/ggdt/src/graphics/bitmap/mod.rs +++ b/ggdt/src/graphics/bitmap/mod.rs @@ -12,6 +12,7 @@ pub mod pcx; pub mod png; pub mod primitives; pub mod rgb; +pub mod triangles; #[derive(Error, Debug)] pub enum BitmapError { diff --git a/ggdt/src/graphics/bitmap/triangles.rs b/ggdt/src/graphics/bitmap/triangles.rs new file mode 100644 index 0000000..d18eee1 --- /dev/null +++ b/ggdt/src/graphics/bitmap/triangles.rs @@ -0,0 +1,71 @@ +use crate::graphics::bitmap::Bitmap; +use crate::graphics::Pixel; +use crate::math::vector2::Vector2; + +#[inline] +fn cross(a: Vector2, b: Vector2, c: Vector2) -> f32 { + (c.x - a.x) * (b.y - a.y) - (c.y - a.y) * (b.x - a.x) +} + +impl Bitmap { + pub fn triangle_2d_solid_color(&mut self, a: Vector2, b: Vector2, c: Vector2, color: PixelType) { + self.triangle_2d_custom( + a, // + b, + c, + |dest_pixels, _w0, _w1, _w2| unsafe { + *dest_pixels = color; + }, + ) + } + + #[inline] + pub fn triangle_2d_custom( + &mut self, + a: Vector2, + b: Vector2, + c: Vector2, + pixel_fn: impl Fn(*mut PixelType, f32, f32, f32), + ) { + // based off the triangle rasterization algorithm presented in these article series' (as well as others) + // https://fgiesen.wordpress.com/2013/02/17/optimizing-sw-occlusion-culling-index/ + // https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/rasterization-stage.html + // https://kitsunegames.com/post/development/2016/07/28/software-3d-rendering-in-javascript-pt2/ + + // TODO: optimize further. have had some trouble with some explanations of the optmizations presented in the + // linked article and the math presented. in particular i would prefer to have counter-clockwise vertex + // ordering but the math presented seems to only work as-is with clockwise ordering ... *grumble* + // TODO: implement fill rules, probably using top-left ordering as most 3d APIs do i guess + + let min_x = a.x.min(b.x).min(c.x); + let min_y = a.y.min(b.y).min(c.y); + let max_x = a.x.max(b.x).max(c.x); + let max_y = a.y.max(b.y).max(c.y); + + let min_x = std::cmp::max(self.clip_region().x, min_x as i32); + let min_y = std::cmp::max(self.clip_region().y, min_y as i32); + let max_x = std::cmp::min(self.clip_region().right(), max_x as i32); + let max_y = std::cmp::min(self.clip_region().bottom(), max_y as i32); + + let draw_width = (max_x - min_x + 1) as usize; + let next_row_inc = self.width() as usize; + let mut pixels = unsafe { self.pixels_at_mut_ptr_unchecked(min_x, min_y) }; + + for y in min_y..=max_y { + let row_pixels = unsafe { std::slice::from_raw_parts_mut(pixels, draw_width) }; + for (idx, pixel) in row_pixels.iter_mut().enumerate() { + let x = min_x + idx as i32; + + let p = Vector2::new(x as f32, y as f32); + let w0 = cross(b, c, p); + let w1 = cross(c, a, p); + let w2 = cross(a, b, p); + + if w0 >= 0.0 && w1 >= 0.0 && w2 >= 0.0 { + pixel_fn(pixel, w0, w1, w2); + } + } + pixels = unsafe { pixels.add(next_row_inc) }; + } + } +} diff --git a/ggdt/src/graphics/prelude.rs b/ggdt/src/graphics/prelude.rs index ff26141..d9e6510 100644 --- a/ggdt/src/graphics/prelude.rs +++ b/ggdt/src/graphics/prelude.rs @@ -10,6 +10,7 @@ pub use crate::graphics::{ png::*, primitives::*, rgb::{blit::*, primitives::*, *}, + triangles::*, *, }, bitmapatlas::*,