mod mesh_loader; use std::fs::File; use std::{env, io}; use std::f32::consts::PI; use std::io::{BufRead, BufReader, BufWriter}; use std::io::Write; use rand::Rng; use glm::*; use crate::mesh_loader::load_mesh; const W: i32 = 320; const H: i32 = 240; const SCALE: f32 = 30.0; #[derive(Copy, Clone)] struct Color(u8, u8, u8); #[derive(Copy, Clone)] struct Comparer { coef: Vec2, thr: f32, } impl Comparer { fn new(a: Vec2, b: Vec2) -> Comparer { let d = b - a; let ortho = vec2(-d.y, d.x); Comparer { coef: ortho, thr: dot(a, ortho), } } fn dist(&self, p: Vec2) -> f32 { dot(p, self.coef) - self.thr } fn test(&self, p: Vec2) -> bool { self.dist(p) > 0.0 } } struct Image { w: i32, h: i32, data: Vec, } impl Image { fn data(&self) -> &[u8] { self.data.as_slice() } fn put_pixel(&mut self, x: i32, y: i32, color: Color) { if x < 0 || x >= self.w || y < 0 || y > self.h { return; } let index = 3 * (x + self.w * y) as usize; self.data[index] = color.0; self.data[index + 1] = color.1; self.data[index + 2] = color.2; } fn draw_line(&mut self, a: IVec2, b: IVec2, color: Color) {} fn draw_tri(&mut self, a: Vec2, b: Vec2, c: Vec2, color: Color) { let u = Comparer::new(a, b); let v = Comparer::new(b, c); let w = Comparer::new(c, a); for y in 0..self.h { for x in 0..self.w { let p = vec2(x as f32, y as f32); if u.test(p) && v.test(p) && w.test(p) || !u.test(p) && !v.test(p) && !w.test(p) { self.put_pixel(x, y, color); } } } } } fn main() -> io::Result<()> { let args: Vec = env::args().collect(); let mesh = { let f = File::open(&args[1])?; let mut f = BufReader::new(f); load_mesh(&mut f)? }; let mut img = Image { w: W, h: H, data: vec![0; (3 * W * H) as usize], }; let yaw = PI / 4.0; let pitch = PI / 6.0; let roll = 0.0f32; let m_roll = mat3( roll.cos(), roll.sin(), 0.0, -roll.sin(), roll.cos(), 0.0, 0.0, 0.0, 1.0); let m_yaw = mat3( -yaw.cos(), 0.0, yaw.sin(), 0.0, 1.0, 0.0, -yaw.sin(), 0.0, -yaw.cos()); let m_pitch = mat3( 1.0, 0.0, 0.0, 0.0, pitch.cos(), pitch.sin(), 0.0, -pitch.sin(), pitch.cos()); let m_view = m_roll * m_pitch * m_yaw; let m_camera = transpose(&m_view); let img_size = vec2(W as f32, H as f32); for y in 0..H { for x in 0..W { let img_coords: Vec2 = vec2(x as f32, y as f32); // perspective projection let off = (img_coords - img_size * 0.5) / img_size.y; let base = vec3(0.0, 0.0, -20.0); let ray = vec3(off.x, off.y, 1.0); // orthographic projection // let off = (img_coords - img_size * 0.5) / SCALE; // let base = vec3(off.x, off.y, -10.0); // let ray = vec3(0.0, 0.0, 1.0); let base = m_camera * base; let ray = m_camera * normalize(ray); let mut dist = f32::INFINITY; for f in &mesh { let color = clamp(to_ivec3(f.normal * 120.0 + 128.0), ivec3(0, 0, 0), ivec3(255, 255, 255)); let fs = (0..3).map(|k| edge_dist(f.vertices[k], f.vertices[(k + 1) % 3], base, ray)); if fs.into_iter().all(|f| f > 0.0) { let m = Mat3 { c0: f.vertices[1] - f.vertices[0], c1: f.vertices[2] - f.vertices[0], c2: -ray }; if let Some(m) = m.inverse() { let rel = m * (base - f.vertices[0]); if rel.z > dist { continue; } dist = rel.z; } else { continue; } img.put_pixel(x, y, Color(color.x as u8, color.y as u8, color.z as u8)); } } } } let f = File::create("1.ppm")?; let mut f = BufWriter::new(f); write!(f, "P6\n")?; write!(f, "{W} {H} 255\n")?; f.write(img.data())?; Ok(()) } fn edge_dist(a: Vec3, b: Vec3, base: Vec3, dir: Vec3) -> f32 { Mat3 { c0: b - a, c1: base - a, c2: dir }.determinant() Mat3 { c0: b - a, c1: base - a, c2: -dir }.determinant() }