From 90420bdc72ce1ad8ba3611652b788ff7bc568f63 Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sun, 7 Sep 2025 14:53:02 -0700 Subject: [PATCH] render head with random color triangles and no backface culling --- src/main.rs | 62 ++++++++++++++++++++-------------------------------- src/model.rs | 24 +++++++++++++++++++- 2 files changed, 47 insertions(+), 39 deletions(-) diff --git a/src/main.rs b/src/main.rs index d97f7c4..455fc32 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,11 +36,13 @@ const YELLOW: TGAColor = TGAColor { }; fn main() { - let w = 128; - let h = 128; + let w = 800; + let h = 800; let mut fb = TGAImage::new(w, h, TGAFormat::RGB); - fill_triangles(&mut fb); - fb.write_file("triangles.tga", true, true).unwrap(); + // fill_triangles(&mut fb); + // fb.write_file("triangles.tga", true, true).unwrap(); + + _render_head(&mut fb); } fn fill_triangles(fb: &mut TGAImage) { @@ -63,11 +65,17 @@ fn fill_triangles(fb: &mut TGAImage) { t3.render_filled(GREEN, fb); } -fn _render_diablo(model: Model, fb: &mut TGAImage) { +fn _render_diablo(fb: &mut TGAImage) { + let model = Model::from_obj("diablo3_pose.obj"); model.render_wireframe(fb); fb.write_file("diablo.tga", true, true).unwrap(); } +fn _render_head(fb: &mut TGAImage) { + let model = Model::from_obj("head.obj"); + model.render_triangles(fb); + fb.write_file("head.tga", true, true).unwrap(); +} fn _baseline(framebuffer: &mut TGAImage) { let ax = 7; let ay = 3; @@ -121,30 +129,17 @@ fn line(mut a: Point2i, mut b: Point2i, color: TGAColor, mut fb: &mut TGAImage) std::mem::swap(&mut a, &mut b); } - let fb = Arc::new(Mutex::new(fb)); - let y = Arc::new(Mutex::new(a.y as f32)); - let step = (b.y - a.y) as f32 / (b.x - a.x) as f32; - (a.x..b.x).into_par_iter().for_each(|x| { - let ny = { - if let Ok(y) = y.lock() { - *y - } else { - return; - } - }; + let mut y = a.y as f32; + for x in a.x..b.x { let (px, py) = if is_steep { - (ny.round_ties_even() as i32, x) + (y.round_ties_even() as i32, x) } else { - (x, ny.round_ties_even() as i32) + (x, y.round_ties_even() as i32) }; - if let Ok(mut fb) = fb.lock() { - fb.set(px as u32, py as u32, color); - } - if let Ok(mut y) = y.lock() { - *y += step; - } - }); + fb.set(px as u32, py as u32, color); + y += step; + } } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -159,15 +154,6 @@ impl Triangle { Self { a, b, c } } - pub fn sort(&mut self) { - let mut sorted = [self.a, self.b, self.c]; - sorted.sort_unstable_by(|a, b| a.y.cmp(&b.y)); - let [a, b, c] = sorted; - self.a = a; - self.b = b; - self.c = c; - } - pub fn signed_area(&self) -> f32 { 0.5 * ((self.b.y - self.a.y) * (self.a.x + self.b.x) + (self.c.y - self.b.y) * (self.c.x + self.b.x) @@ -189,13 +175,13 @@ impl Triangle { pub fn render_filled(&self, color: TGAColor, fb: &mut TGAImage) { let fb = Arc::new(Mutex::new(fb)); let bb = self.bb(); - let total_area = self.signed_area(); + let total_area_sign = self.signed_area().signum(); (bb.ymin()..=bb.ymax()).into_par_iter().for_each(|y| { (bb.xmin()..bb.xmax()).into_par_iter().for_each(|x| { let p = Point2i::new(x, y); - let a = Triangle::new(p, self.a, self.b).signed_area() / total_area; - let b = Triangle::new(p, self.b, self.c).signed_area() / total_area; - let c = Triangle::new(p, self.c, self.a).signed_area() / total_area; + let a = Triangle::new(p, self.a, self.b).signed_area().signum() * total_area_sign; + let b = Triangle::new(p, self.b, self.c).signed_area().signum() * total_area_sign; + let c = Triangle::new(p, self.c, self.a).signed_area().signum() * total_area_sign; if a.is_sign_positive() && b.is_sign_positive() diff --git a/src/model.rs b/src/model.rs index 2bb51ae..2ba6070 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1,7 +1,9 @@ use std::ops::{Deref, DerefMut}; +use rand::Rng; + use crate::{ - RED, line, + RED, Triangle, line, point::{Point2i, Point3f}, tga::TGAImage, }; @@ -92,6 +94,26 @@ impl Model { line(c, a, RED, framebuffer); } } + pub fn render_triangles(&self, framebuffer: &mut TGAImage) { + let width = framebuffer.width; + let height = framebuffer.height; + + let mut rng = rand::rng(); + for face in self.faces.iter() { + let a = world2view(self.verts[face[0]], width, height); + let b = world2view(self.verts[face[1]], width, height); + let c = world2view(self.verts[face[2]], width, height); + let triangle = Triangle::new(a, b, c); + let color = [ + rng.random_range(0..=255u8), + rng.random_range(0..=255u8), + rng.random_range(0..=255u8), + 255, + ] + .into(); + triangle.render_filled(color, framebuffer); + } + } } fn world2view(point: Point3f, width: u32, height: u32) -> Point2i {