diff --git a/Cargo.toml b/Cargo.toml index 2ad40d6..a7f446c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,9 @@ panic = 'abort' opt-level = 3 [dependencies] +rand = "0.8.5" glam = { version = "0.27.0", features = ["approx", "fast-math", "rand"] } +show-image = "0.14.0" flo_draw = "0.3.1" flo_canvas = "0.3.1" itertools-num = "0.1.3" diff --git a/models/spacecraft2.1.obj b/models/spacecraft2.1.obj new file mode 100644 index 0000000..8a38f7a --- /dev/null +++ b/models/spacecraft2.1.obj @@ -0,0 +1,140 @@ +# Blender 3.6.5 +# www.blender.org +mtllib spacecraft2.1.mtl +o Cube +v -1.000000 0.000000 5.000000 +v -3.000000 0.000000 4.000000 +v -4.000000 0.000000 7.000000 +v -8.000000 0.000000 7.000000 +v -9.000000 0.000000 4.000000 +v -3.000000 0.000000 0.000000 +v -6.000000 0.000000 -7.000000 +v -2.000000 0.000000 -3.000000 +v -7.000000 1.000000 4.000000 +v 0.000000 2.000000 0.000000 +v 0.000000 2.000000 2.000000 +v 0.000000 0.000000 6.000000 +v 0.000000 0.000000 -4.000000 +v -5.000000 0.000000 2.000000 +v 1.000000 0.000000 5.000000 +v 3.000000 0.000000 4.000000 +v 4.000000 0.000000 7.000000 +v 8.000000 0.000000 7.000000 +v 9.000000 0.000000 4.000000 +v 3.000000 0.000000 0.000000 +v 6.000000 0.000000 -7.000000 +v 2.000000 0.000000 -3.000000 +v 7.000000 1.000000 4.000000 +v 5.000000 0.000000 2.000000 +v -7.000000 -1.000000 4.000000 +v 0.000000 -2.000000 0.000000 +v 0.000000 -2.000000 2.000000 +v 7.000000 -1.000000 4.000000 +vn -0.5455 0.8182 -0.1818 +vn -0.4851 0.4851 -0.7276 +vn -0.4439 0.8878 -0.1211 +vn -0.0000 0.9487 0.3162 +vn -0.4423 0.8847 0.1474 +vn 0.2417 0.9670 0.0806 +vn -0.2408 0.8427 0.4815 +vn 0.1455 0.5819 0.8001 +vn -0.1414 0.9899 -0.0000 +vn -0.2182 0.8729 -0.4364 +vn -0.4082 0.8165 0.4082 +vn -0.4851 0.7276 -0.4851 +vn 0.4099 0.9110 -0.0455 +vn -0.0000 -1.0000 -0.0000 +vn 0.5455 0.8182 -0.1818 +vn 0.4851 0.4851 -0.7276 +vn 0.4439 0.8878 -0.1211 +vn 0.4423 0.8847 0.1474 +vn -0.2417 0.9670 0.0806 +vn 0.2408 0.8427 0.4815 +vn -0.1455 0.5819 0.8001 +vn 0.1414 0.9899 -0.0000 +vn 0.2182 0.8729 -0.4364 +vn 0.4082 0.8165 0.4082 +vn 0.4851 0.7276 -0.4851 +vn -0.4099 0.9110 -0.0455 +vn -0.5455 -0.8182 -0.1818 +vn -0.4851 -0.4851 -0.7276 +vn -0.4439 -0.8878 -0.1211 +vn -0.0000 -0.9487 0.3162 +vn -0.4423 -0.8847 0.1474 +vn 0.2417 -0.9670 0.0806 +vn -0.2408 -0.8427 0.4815 +vn 0.1455 -0.5819 0.8001 +vn -0.1414 -0.9899 -0.0000 +vn -0.2182 -0.8729 -0.4364 +vn -0.4082 -0.8165 0.4082 +vn -0.4851 -0.7276 -0.4851 +vn 0.4099 -0.9110 -0.0455 +vn 0.5455 -0.8182 -0.1818 +vn 0.4851 -0.4851 -0.7276 +vn 0.4439 -0.8878 -0.1211 +vn 0.4423 -0.8847 0.1474 +vn -0.2417 -0.9670 0.0806 +vn 0.2408 -0.8427 0.4815 +vn -0.1455 -0.5819 0.8001 +vn 0.1414 -0.9899 -0.0000 +vn 0.2182 -0.8729 -0.4364 +vn 0.4082 -0.8165 0.4082 +vn 0.4851 -0.7276 -0.4851 +vn -0.4099 -0.9110 -0.0455 +vt 0.000000 0.000000 +s 0 +usemtl Material +f 6/1/1 10/1/1 8/1/1 +f 10/1/2 14/1/2 9/1/2 +f 5/1/3 9/1/3 7/1/3 +f 3/1/4 9/1/4 4/1/4 +f 4/1/5 9/1/5 5/1/5 +f 2/1/6 9/1/6 3/1/6 +f 1/1/7 11/1/7 2/1/7 +f 9/1/8 2/1/8 11/1/8 +f 9/1/9 11/1/9 10/1/9 +f 8/1/10 10/1/10 13/1/10 +f 1/1/11 12/1/11 11/1/11 +f 10/1/12 6/1/12 14/1/12 +f 7/1/13 9/1/13 14/1/13 +f 7/1/14 14/1/14 5/1/14 +f 20/1/15 22/1/15 10/1/15 +f 10/1/16 23/1/16 24/1/16 +f 19/1/17 21/1/17 23/1/17 +f 17/1/4 18/1/4 23/1/4 +f 18/1/18 19/1/18 23/1/18 +f 16/1/19 17/1/19 23/1/19 +f 15/1/20 16/1/20 11/1/20 +f 23/1/21 11/1/21 16/1/21 +f 23/1/22 10/1/22 11/1/22 +f 22/1/23 13/1/23 10/1/23 +f 15/1/24 11/1/24 12/1/24 +f 10/1/25 24/1/25 20/1/25 +f 21/1/26 24/1/26 23/1/26 +f 21/1/14 19/1/14 24/1/14 +f 6/1/27 8/1/27 26/1/27 +f 26/1/28 25/1/28 14/1/28 +f 5/1/29 7/1/29 25/1/29 +f 3/1/30 4/1/30 25/1/30 +f 4/1/31 5/1/31 25/1/31 +f 2/1/32 3/1/32 25/1/32 +f 1/1/33 2/1/33 27/1/33 +f 25/1/34 27/1/34 2/1/34 +f 25/1/35 26/1/35 27/1/35 +f 8/1/36 13/1/36 26/1/36 +f 1/1/37 27/1/37 12/1/37 +f 26/1/38 14/1/38 6/1/38 +f 7/1/39 14/1/39 25/1/39 +f 20/1/40 26/1/40 22/1/40 +f 26/1/41 24/1/41 28/1/41 +f 19/1/42 28/1/42 21/1/42 +f 17/1/30 28/1/30 18/1/30 +f 18/1/43 28/1/43 19/1/43 +f 16/1/44 28/1/44 17/1/44 +f 15/1/45 27/1/45 16/1/45 +f 28/1/46 16/1/46 27/1/46 +f 28/1/47 27/1/47 26/1/47 +f 22/1/48 26/1/48 13/1/48 +f 15/1/49 12/1/49 27/1/49 +f 26/1/50 20/1/50 24/1/50 +f 21/1/51 28/1/51 24/1/51 diff --git a/models/spacecraft2.2.obj b/models/spacecraft2.2.obj new file mode 100644 index 0000000..550ad51 --- /dev/null +++ b/models/spacecraft2.2.obj @@ -0,0 +1,77 @@ +# Blender 3.6.5 +# www.blender.org +mtllib spacecraft2.2.mtl +o Cube +v -1.000000 0.000000 5.000000 +v -3.000000 0.000000 4.000000 +v -3.000000 0.000000 0.000000 +v -2.000000 0.000000 -3.000000 +v -7.000000 0.000000 4.000000 +v 0.000000 2.000000 0.000000 +v 0.000000 2.000000 2.000000 +v 0.000000 0.000000 6.000000 +v 0.000000 0.000000 -4.000000 +v 1.000000 0.000000 5.000000 +v 3.000000 0.000000 4.000000 +v 3.000000 0.000000 0.000000 +v 2.000000 0.000000 -3.000000 +v 7.000000 0.000000 4.000000 +v 0.000000 -2.000000 0.000000 +v 0.000000 -2.000000 2.000000 +vn -0.5455 0.8182 -0.1818 +vn -0.4851 0.7276 -0.4851 +vn -0.2408 0.8427 0.4815 +vn -0.0000 0.7071 0.7071 +vn -0.2747 0.9615 -0.0000 +vn -0.2182 0.8729 -0.4364 +vn -0.4082 0.8165 0.4082 +vn 0.5455 0.8182 -0.1818 +vn 0.4851 0.7276 -0.4851 +vn 0.2408 0.8427 0.4815 +vn 0.2747 0.9615 -0.0000 +vn 0.2182 0.8729 -0.4364 +vn 0.4082 0.8165 0.4082 +vn -0.5455 -0.8182 -0.1818 +vn -0.4851 -0.7276 -0.4851 +vn -0.2408 -0.8427 0.4815 +vn -0.0000 -0.7071 0.7071 +vn -0.2747 -0.9615 -0.0000 +vn -0.2182 -0.8729 -0.4364 +vn -0.4082 -0.8165 0.4082 +vn 0.5455 -0.8182 -0.1818 +vn 0.4851 -0.7276 -0.4851 +vn 0.2408 -0.8427 0.4815 +vn 0.2747 -0.9615 -0.0000 +vn 0.2182 -0.8729 -0.4364 +vn 0.4082 -0.8165 0.4082 +vt 0.000000 0.000000 +s 0 +usemtl Material +f 3/1/1 6/1/1 4/1/1 +f 6/1/2 3/1/2 5/1/2 +f 1/1/3 7/1/3 2/1/3 +f 5/1/4 2/1/4 7/1/4 +f 5/1/5 7/1/5 6/1/5 +f 4/1/6 6/1/6 9/1/6 +f 1/1/7 8/1/7 7/1/7 +f 12/1/8 13/1/8 6/1/8 +f 6/1/9 14/1/9 12/1/9 +f 10/1/10 11/1/10 7/1/10 +f 14/1/4 7/1/4 11/1/4 +f 14/1/11 6/1/11 7/1/11 +f 13/1/12 9/1/12 6/1/12 +f 10/1/13 7/1/13 8/1/13 +f 3/1/14 4/1/14 15/1/14 +f 15/1/15 5/1/15 3/1/15 +f 1/1/16 2/1/16 16/1/16 +f 5/1/17 16/1/17 2/1/17 +f 5/1/18 15/1/18 16/1/18 +f 4/1/19 9/1/19 15/1/19 +f 1/1/20 16/1/20 8/1/20 +f 12/1/21 15/1/21 13/1/21 +f 15/1/22 12/1/22 14/1/22 +f 10/1/23 16/1/23 11/1/23 +f 14/1/17 11/1/17 16/1/17 +f 14/1/24 16/1/24 15/1/24 +f 13/1/25 15/1/25 9/1/25 +f 10/1/26 8/1/26 16/1/26 diff --git a/models/spacecraft2.obj b/models/spacecraft2.obj new file mode 100644 index 0000000..cd571ee --- /dev/null +++ b/models/spacecraft2.obj @@ -0,0 +1,109 @@ +# Blender 3.6.5 +# www.blender.org +mtllib spacecraft2.mtl +o Cube +v -1.000000 0.000000 3.000000 +v -2.000000 0.000000 2.000000 +v -3.000000 0.000000 4.000000 +v -5.000000 0.000000 4.000000 +v -6.000000 0.000000 2.000000 +v -2.000000 0.000000 0.000000 +v -4.000000 0.000000 -6.000000 +v -1.000000 0.000000 -2.000000 +v 1.000000 0.000000 3.000000 +v 2.000000 0.000000 2.000000 +v 3.000000 0.000000 4.000000 +v 5.000000 0.000000 4.000000 +v 6.000000 0.000000 2.000000 +v 4.000000 0.000000 -6.000000 +v 2.000000 0.000000 0.000000 +v 1.000000 0.000000 -2.000000 +v -4.000000 1.000000 2.000000 +v 0.000000 1.000000 0.000000 +v 4.000000 1.000000 2.000000 +v 0.000000 1.000000 1.000000 +v -4.000000 -1.000000 2.000000 +v 0.000000 -1.000000 0.000000 +v 4.000000 -1.000000 2.000000 +v 0.000000 -1.000000 1.000000 +vn -0.0000 0.8944 -0.4472 +vn 0.4364 0.8729 -0.2182 +vn 0.3333 0.6667 -0.6667 +vn -0.3487 0.9300 -0.1162 +vn 0.4444 0.8889 -0.1111 +vn 0.4364 0.8729 0.2182 +vn -0.0000 0.8944 0.4472 +vn -0.4364 0.8729 0.2182 +vn -0.0000 1.0000 -0.0000 +vn 0.3015 0.9045 0.3015 +vn -0.2182 0.4364 0.8729 +vn -0.4364 0.8729 -0.2182 +vn -0.3333 0.6667 -0.6667 +vn 0.3487 0.9300 -0.1162 +vn -0.4444 0.8889 -0.1111 +vn -0.3015 0.9045 0.3015 +vn 0.2182 0.4364 0.8729 +vn -0.0000 -0.8944 -0.4472 +vn 0.4364 -0.8729 -0.2182 +vn 0.3333 -0.6667 -0.6667 +vn -0.3487 -0.9300 -0.1162 +vn 0.4444 -0.8889 -0.1111 +vn 0.4364 -0.8729 0.2182 +vn -0.0000 -0.8944 0.4472 +vn -0.4364 -0.8729 0.2182 +vn -0.0000 -1.0000 -0.0000 +vn 0.3015 -0.9045 0.3015 +vn -0.2182 -0.4364 0.8729 +vn -0.4364 -0.8729 -0.2182 +vn -0.3333 -0.6667 -0.6667 +vn 0.3487 -0.9300 -0.1162 +vn -0.4444 -0.8889 -0.1111 +vn -0.3015 -0.9045 0.3015 +vn 0.2182 -0.4364 0.8729 +vt 0.000000 0.000000 +s 0 +usemtl Material +f 8/1/1 18/1/1 16/1/1 +f 16/1/2 18/1/2 15/1/2 +f 18/1/3 19/1/3 15/1/3 +f 15/1/4 19/1/4 14/1/4 +f 14/1/5 19/1/5 13/1/5 +f 13/1/6 19/1/6 12/1/6 +f 12/1/7 19/1/7 11/1/7 +f 10/1/8 11/1/8 19/1/8 +f 20/1/9 18/1/9 17/1/9 +f 1/1/7 9/1/7 20/1/7 +f 9/1/10 10/1/10 20/1/10 +f 20/1/11 10/1/11 19/1/11 +f 6/1/12 18/1/12 8/1/12 +f 18/1/13 6/1/13 17/1/13 +f 6/1/14 7/1/14 17/1/14 +f 5/1/15 17/1/15 7/1/15 +f 3/1/7 17/1/7 4/1/7 +f 4/1/8 17/1/8 5/1/8 +f 2/1/6 17/1/6 3/1/6 +f 1/1/16 20/1/16 2/1/16 +f 17/1/17 2/1/17 20/1/17 +f 20/1/9 19/1/9 18/1/9 +f 8/1/18 16/1/18 22/1/18 +f 16/1/19 15/1/19 22/1/19 +f 22/1/20 15/1/20 23/1/20 +f 15/1/21 14/1/21 23/1/21 +f 14/1/22 13/1/22 23/1/22 +f 13/1/23 12/1/23 23/1/23 +f 12/1/24 11/1/24 23/1/24 +f 10/1/25 23/1/25 11/1/25 +f 24/1/26 21/1/26 22/1/26 +f 1/1/24 24/1/24 9/1/24 +f 9/1/27 24/1/27 10/1/27 +f 24/1/28 23/1/28 10/1/28 +f 6/1/29 8/1/29 22/1/29 +f 22/1/30 21/1/30 6/1/30 +f 6/1/31 21/1/31 7/1/31 +f 5/1/32 7/1/32 21/1/32 +f 3/1/24 4/1/24 21/1/24 +f 4/1/25 5/1/25 21/1/25 +f 2/1/23 3/1/23 21/1/23 +f 1/1/33 2/1/33 24/1/33 +f 21/1/34 24/1/34 2/1/34 +f 24/1/26 22/1/26 23/1/26 diff --git a/src/bin/mesh/main.rs b/src/bin/mesh/main.rs new file mode 100644 index 0000000..04a44d6 --- /dev/null +++ b/src/bin/mesh/main.rs @@ -0,0 +1,151 @@ +mod mesh_loader; + +use std::fs::File; +use std::{env}; +use std::error::Error; +use std::f32::consts::PI; +use std::io::{BufReader}; +use glam::*; +use show_image::{ImageInfo, ImageView, WindowOptions}; +use crate::mesh_loader::{Face, load_mesh}; + +const W: i32 = 320; +const H: i32 = 240; + +#[derive(Copy, Clone)] +struct Color(u8, u8, u8); + +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 ypr_to_mat(ypr: Vec3) -> Mat3 { + let Vec3 { x: yaw, y: pitch, z: roll } = ypr; + let m_roll = mat3( + vec3(roll.cos(), roll.sin(), 0.0), + vec3(-roll.sin(), roll.cos(), 0.0), + vec3(0.0, 0.0, 1.0)); + let m_yaw = mat3( + vec3(yaw.cos(), 0.0, yaw.sin()), + vec3(0.0, 1.0, 0.0), + vec3(-yaw.sin(), 0.0, yaw.cos())); + let m_pitch = mat3( + vec3(1.0, 0.0, 0.0), + vec3(0.0, pitch.cos(), -pitch.sin()), + vec3(0.0, pitch.sin(), pitch.cos())); + m_roll * m_pitch * m_yaw +} + +type Mesh = [Face]; + +struct TraceResult { + distance: f32, + normal: Vec3, +} + +fn trace_to_mesh(mesh: &Mesh, base: Vec3, ray: Vec3) -> Option { + let mut ret: Option = None; + let mut dist = f32::INFINITY; + for f in mesh { + 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(f.vertices[1] - f.vertices[0], f.vertices[2] - f.vertices[0], -ray); + let m = m.inverse(); + let rel = m * (base - f.vertices[0]); + if rel.z > dist { + continue; + } + dist = rel.z; + ret = Some(TraceResult { + distance: rel.z, + normal: f.normal, + }); + } + } + ret +} + +struct Location { + pos: Vec3, + rot: Vec4, +} + +fn render(mesh: &Mesh, camera: impl Fn(Vec2) -> (Vec3, Vec3)) -> Image { + let bkg = vec3(0.0, 0.0, 0.0); + let mut img = Image { + w: W, + h: H, + data: vec![0; (3 * W * H) as usize], + }; + let img_size = vec2(W as f32, H as f32); + for y in 0..H { + for x in 0..W { + let img_coords = vec2(x as f32, y as f32); + let off = (img_coords - img_size * 0.5) / img_size.y; + let (base, ray) = camera(off); + let color = if let Some(r) = trace_to_mesh(mesh, base, ray.normalize()) { + // to_vec3(0.45) * dot(r.normal, normalize(vec3(-1.0, 1.0, -1.0))) + 0.50 + r.normal * 0.45 + 0.50 + } else { + bkg + }; + let color = (color * 255.0).as_ivec3().clamp(IVec3::splat(0), IVec3::splat(255)); + img.put_pixel(x, y, Color(color.x as u8, color.y as u8, color.z as u8)); + } + } + img +} + +#[show_image::main] +fn main() -> Result<(), Box> { + 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 window = show_image::create_window("Raytracing", WindowOptions::default())?; + loop { + for phi in 0..360 { + let m_view = ypr_to_mat(vec3((135.0 + phi as f32) * PI / 180.0, -30.0 * PI / 180.0, 0.0f32)); + let m_camera = m_view.transpose(); + let img = render(mesh.as_slice(), |off| { + // perspective projection + let base = vec3(0.0, 0.0, -40.0); + let ray = vec3(off.x, off.y, 2.0); + + // orthographic projection + // let base = vec3(off.x, off.y, -10.0); + // let ray = vec3(0.0, 0.0, 1.0); + + (m_camera * base, m_camera * ray) + }); + + let image = ImageView::new(ImageInfo::rgb8(W as u32, H as u32), img.data()); + window.set_image("image", image)?; + } + } +} + +fn edge_dist(a: Vec3, b: Vec3, base: Vec3, dir: Vec3) -> f32 { + // Note: given that the input is not arbitrary but comes from a cartesian product of certain (a, b) pairs and certain (base, dir) pairs, this can be optimized from Cnm to an+bm+cnm with c, + normals: Vec, + tex_coords: Vec, + faces: Vec, +} + +impl ObjMesh { + fn parse_v2(tokens: &[&str]) -> Vec2 { + assert_eq!(tokens.len(), 2); + let tokens: Vec<_> = tokens.iter().map(|&t| t.parse().unwrap()).collect(); + vec2(tokens[0], tokens[1]) + } + + fn parse_v3(tokens: &[&str]) -> Vec3 { + assert_eq!(tokens.len(), 3); + let tokens: Vec<_> = tokens.iter().map(|&t| t.parse().unwrap()).collect(); + vec3(tokens[0], tokens[1], tokens[2]) + } + + fn parse_fv(desc: &&str) -> ObjVertex { + let tokens: Vec<_> = desc.split('/').map(|s| s.parse::().unwrap() - 1).collect(); + assert_eq!(tokens.len(), 3); + ObjVertex { vertex: tokens[0], tex_coord: tokens[1], normal: tokens[2] } + } + + fn parse_f(tokens: &[&str]) -> ObjFace { + let vertices: Vec<_> = tokens.iter().map(ObjMesh::parse_fv).collect(); + ObjFace { vertices: vertices.as_slice().try_into().unwrap() } + } + + fn read(f: &mut impl io::BufRead) -> io::Result { + let mut result = ObjMesh { + vertices: Vec::new(), + normals: Vec::new(), + tex_coords: Vec::new(), + faces: Vec::new(), + }; + loop { + let mut line = String::new(); + if f.read_line(&mut line)? == 0 { + break; + } + let tokens: Vec<&str> = line + .trim() + .split('#') + .next() + .unwrap() + .split(' ') + .collect(); + match tokens[0] { + "v" => result.vertices.push(Self::parse_v3(&tokens[1..])), + "vn" => result.normals.push(Self::parse_v3(&tokens[1..])), + "vt" => result.tex_coords.push(Self::parse_v2(&tokens[1..])), + "f" => result.faces.push(Self::parse_f(&tokens[1..])), + _ => (), + } + } + Ok(result) + } + + fn flatten(&self) -> Vec { + self.faces.iter().map(|face| { + Face { + vertices: face.vertices.map(|iv| self.vertices[iv.vertex]), + normal: self.normals[face.vertices[0].normal], + } + }).collect() + } +} + +#[derive(Copy, Clone, Debug)] +pub struct Face { + pub vertices: [Vec3; 3], + pub normal: Vec3, +} + +pub fn load_mesh(f: &mut impl io::BufRead) -> io::Result> { + Ok(ObjMesh::read(f)?.flatten()) +}