|
@@ -1,465 +1,18 @@
|
|
|
-use std::fs::File;
|
|
|
-use std::io::BufReader;
|
|
|
-use std::io::Read;
|
|
|
-use std::io::BufRead;
|
|
|
-
|
|
|
-// ┏━┓┏━┓┏┓╻╺┳┓┏━┓┏┳┓ ┏┓╻╻ ╻┏┳┓┏┓ ┏━╸┏━┓ ┏━╸┏━╸┏┓╻┏━╸┏━┓┏━┓╺┳╸╻┏━┓┏┓╻
|
|
|
-// ┣┳┛┣━┫┃┗┫ ┃┃┃ ┃┃┃┃ ┃┗┫┃ ┃┃┃┃┣┻┓┣╸ ┣┳┛ ┃╺┓┣╸ ┃┗┫┣╸ ┣┳┛┣━┫ ┃ ┃┃ ┃┃┗┫
|
|
|
-// ╹┗╸╹ ╹╹ ╹╺┻┛┗━┛╹ ╹ ╹ ╹┗━┛╹ ╹┗━┛┗━╸╹┗╸ ┗━┛┗━╸╹ ╹┗━╸╹┗╸╹ ╹ ╹ ╹┗━┛╹ ╹
|
|
|
-
|
|
|
-pub struct RNG {
|
|
|
- random_device: BufReader<File>,
|
|
|
-}
|
|
|
-
|
|
|
-impl RNG {
|
|
|
- pub fn next_float(&mut self) -> f32 {
|
|
|
- let mut buf: [u8; 4] = [0, 0, 0, 0];
|
|
|
-
|
|
|
- self.random_device
|
|
|
- .read(&mut buf)
|
|
|
- .expect("Read from /dev/random failed");
|
|
|
- let val: f32 = u32::from_be_bytes(buf) as f32;
|
|
|
- f32::from(val) / (u32::max_value() as f32 + 1.0)
|
|
|
- }
|
|
|
-
|
|
|
- pub fn create() -> RNG {
|
|
|
- let f = File::open("/dev/urandom").expect("Did not find source of randomness");
|
|
|
- let reader: BufReader<File> = BufReader::new(f);
|
|
|
-
|
|
|
- return RNG {
|
|
|
- random_device: reader,
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// ┏━╸┏━╸┏━┓┏┳┓┏━╸╺┳╸┏━┓╻ ╻
|
|
|
-// ┃╺┓┣╸ ┃ ┃┃┃┃┣╸ ┃ ┣┳┛┗┳┛
|
|
|
-// ┗━┛┗━╸┗━┛╹ ╹┗━╸ ╹ ╹┗╸ ╹
|
|
|
-
|
|
|
-#[derive(Copy, Clone)]
|
|
|
-pub struct Vec3 {
|
|
|
- x: f32,
|
|
|
- y: f32,
|
|
|
- z: f32,
|
|
|
-}
|
|
|
-
|
|
|
-impl Vec3 {
|
|
|
- pub fn new(x: f32, y: f32, z: f32) -> Vec3 {
|
|
|
- return Vec3 {
|
|
|
- x: x,
|
|
|
- y: y,
|
|
|
- z: z
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- pub fn length(&self) -> f32 {
|
|
|
- return (*self * *self).sqrt();
|
|
|
- }
|
|
|
-
|
|
|
- pub fn distance_to(&self, other: Vec3) -> f32 {
|
|
|
- return (*self - other).length();
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- pub fn normalize(&mut self) {
|
|
|
- let l = self.length();
|
|
|
- self.x /= l;
|
|
|
- self.y /= l;
|
|
|
- self.z /= l;
|
|
|
- }
|
|
|
-
|
|
|
- pub fn normalized(&self) -> Vec3 {
|
|
|
- let mut other = self.clone();
|
|
|
- other.normalize();
|
|
|
- return other;
|
|
|
- }
|
|
|
-
|
|
|
- pub fn cross(&self, _rhs: Vec3) -> Vec3 {
|
|
|
- return Vec3 {
|
|
|
- x: self.y * _rhs.z - self.z * _rhs.y,
|
|
|
- y: self.z * _rhs.x - self.x * _rhs.z,
|
|
|
- z: self.x * _rhs.y - self.y * _rhs.x,
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl std::fmt::Display for Vec3 {
|
|
|
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
- write!(f, "({}, {}, {})", self.x, self.y, self.z);
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl std::ops::Add<Vec3> for Vec3 {
|
|
|
- type Output = Vec3;
|
|
|
-
|
|
|
- fn add(self, _rhs: Vec3) -> Vec3 {
|
|
|
- return Vec3 {
|
|
|
- x: self.x + _rhs.x,
|
|
|
- y: self.y + _rhs.y,
|
|
|
- z: self.z + _rhs.z,
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl std::ops::AddAssign for Vec3 {
|
|
|
- fn add_assign(&mut self, other: Vec3) {
|
|
|
- self.x += other.x;
|
|
|
- self.y += other.y;
|
|
|
- self.z += other.z;
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-impl std::ops::Neg for Vec3 {
|
|
|
- type Output = Vec3;
|
|
|
-
|
|
|
- fn neg(self) -> Vec3 {
|
|
|
- return self * -1.0f32;
|
|
|
-
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl std::ops::Sub for Vec3 {
|
|
|
- type Output = Vec3;
|
|
|
-
|
|
|
- fn sub(self, _rhs: Vec3) -> Vec3 {
|
|
|
- return self + (-_rhs);
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-// Dot product
|
|
|
-impl std::ops::Mul<Vec3> for Vec3 {
|
|
|
- type Output = f32;
|
|
|
-
|
|
|
- fn mul(self, _rhs: Vec3) -> f32 {
|
|
|
- self.x * _rhs.x + self.y * _rhs.y + self.z * _rhs.z
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// Scalar product
|
|
|
-impl std::ops::Mul<f32> for Vec3 {
|
|
|
- type Output = Vec3;
|
|
|
-
|
|
|
- fn mul(self, _rhs: f32) -> Vec3 {
|
|
|
- return Vec3 {
|
|
|
- x: _rhs * self.x,
|
|
|
- y: _rhs * self.y,
|
|
|
- z: _rhs * self.z
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-impl std::ops::Mul<Vec3> for f32 {
|
|
|
- type Output = Vec3;
|
|
|
-
|
|
|
- fn mul(self, _rhs: Vec3) -> Vec3 {
|
|
|
- return _rhs * self;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-pub struct Ray {
|
|
|
- origin: Vec3,
|
|
|
- direction: Vec3,
|
|
|
-}
|
|
|
-
|
|
|
-pub struct Intersection {
|
|
|
- t: f32,
|
|
|
- pos: Vec3,
|
|
|
- normal: Vec3,
|
|
|
-}
|
|
|
-
|
|
|
-type IndexedVertex = [usize; 3];
|
|
|
-
|
|
|
-struct Face {
|
|
|
- a: IndexedVertex,
|
|
|
- b: IndexedVertex,
|
|
|
- c: IndexedVertex
|
|
|
-}
|
|
|
-
|
|
|
-struct Material {
|
|
|
-
|
|
|
-}
|
|
|
-struct Mesh {
|
|
|
- vertices: Vec<Vec3>,
|
|
|
- color: Vec<Vec3>,
|
|
|
- texture_coordinates: Vec<Vec3>,
|
|
|
- faces: Vec<[usize; 3]>,
|
|
|
- material: Material
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// ┏┳┓┏━╸┏━┓╻ ╻ ╻┏━┓
|
|
|
-// ┃┃┃┣╸ ┗━┓┣━┫ ┃┃ ┃
|
|
|
-// ╹ ╹┗━╸┗━┛╹ ╹ ╹┗━┛
|
|
|
-impl Mesh {
|
|
|
- fn load(obj_path: &str) -> Mesh {
|
|
|
- let file = File::open(obj_path).expect("Could not open OBJ file");
|
|
|
- let reader = BufReader::new(file);
|
|
|
-
|
|
|
- let mut line_counter = 0;
|
|
|
-
|
|
|
- let mut vertices : Vec<Vec3> = Vec::new();
|
|
|
- let mut normals : Vec<Vec3>= Vec::new();
|
|
|
- let mut texture_coords : Vec<Vec3> = Vec::new();
|
|
|
- let mut faces : Vec<Face>= Vec::new();
|
|
|
-
|
|
|
- for l in reader.lines() {
|
|
|
- line_counter += 1;
|
|
|
- let line = l.expect("Could not read line from file");
|
|
|
- let comment_offset = line.find('#').unwrap_or(line.len());
|
|
|
-
|
|
|
- // Remove comments
|
|
|
- let mut stripped_line = line.clone();
|
|
|
- let instruction = stripped_line.drain(0..comment_offset);
|
|
|
-
|
|
|
- // Skip if line is empty
|
|
|
- if instruction.as_str().is_empty() {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- let arguments : Vec<&str>= instruction.as_str().split(' ').collect();
|
|
|
- let instruction_code = arguments.get(0).expect(
|
|
|
- format!("Did not find instruction code in line {}: '{}'", line_counter, line).as_str())
|
|
|
- .clone();
|
|
|
-
|
|
|
- fn parse_vec3(a: &[&str]) -> Option<Vec3> {
|
|
|
- let x = f32::from_str(a[0]?)?;
|
|
|
- let y = f32::from_str(a[1]?)?;
|
|
|
- let z = f32::from_str(a[2]?)?;
|
|
|
-
|
|
|
- return Some(Vec3::new(x,y,z));
|
|
|
- }
|
|
|
-
|
|
|
- fn parse_uv(a: &[&str]) -> Option<Vec3> {
|
|
|
- let x = f32::from_str(a[0])?;
|
|
|
- let y = f32::from_str(a[1])?;
|
|
|
-
|
|
|
- return Some(Vec3::new(x,y, 0.0));
|
|
|
- }
|
|
|
-
|
|
|
- fn parse_triple(a : &str) -> Option<IndexedVertex> {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- match instruction_code {
|
|
|
- "v" | "vn" => {
|
|
|
- let coords = parse_vec3(&arguments[1..4]).expect(
|
|
|
- format!("Could not parse coords in line {}", line_counter).as_str()
|
|
|
- );
|
|
|
-
|
|
|
- match instruction_code {
|
|
|
- "v" => &vertices,
|
|
|
- "vt" => texture,
|
|
|
- _ => {}
|
|
|
- }.push(coords)
|
|
|
- }
|
|
|
- "vt" => {
|
|
|
- let coords = parse_uv(&arguments[1..3]).expect(
|
|
|
- format!("Could not parse uv coords in line {}", line_counter).as_str()
|
|
|
- );
|
|
|
-
|
|
|
- texture_coords.push(coords);
|
|
|
- }
|
|
|
- "f" => {
|
|
|
- faces.push()
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- _ => {}
|
|
|
- }
|
|
|
-
|
|
|
- //let args = line.split(' ').collect();
|
|
|
- println!("Line {}", arguments.get(0).unwrap_or(&"<EMPTY>"));
|
|
|
- }
|
|
|
-
|
|
|
- return Mesh{
|
|
|
- vertices: vec!() ,
|
|
|
- color: vec!(),
|
|
|
- texture_coordinates: vec!(),
|
|
|
- faces: vec!(),
|
|
|
- material: Material {}
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-// ╻┏┳┓┏━┓┏━╸┏━╸ ╻┏━┓
|
|
|
-// ┃┃┃┃┣━┫┃╺┓┣╸ ┃┃ ┃
|
|
|
-// ╹╹ ╹╹ ╹┗━┛┗━╸ ╹┗━┛
|
|
|
-
|
|
|
-use std::convert::From;
|
|
|
-use std::io::BufWriter;
|
|
|
-use std::io::Write;
|
|
|
-use std::ops::Index;
|
|
|
use std::path::Path;
|
|
|
-use std::str::FromStr;
|
|
|
|
|
|
-pub struct Image {
|
|
|
- width: u16,
|
|
|
- height: u16,
|
|
|
- data: Vec<Vec3>,
|
|
|
-}
|
|
|
-
|
|
|
-impl Image {
|
|
|
- fn get_pixel(&self, x: usize, y: usize) -> &Vec3 {
|
|
|
- &self.data[y * self.width as usize + x]
|
|
|
- }
|
|
|
-
|
|
|
- fn mod_pixel(&mut self, x: usize, y: usize) -> &mut Vec3 {
|
|
|
- &mut self.data[y * self.width as usize + x]
|
|
|
- }
|
|
|
-
|
|
|
- fn new(width: u16, height: u16) -> Image {
|
|
|
- let mut data: Vec<Vec3> = Vec::new();
|
|
|
- data.resize(
|
|
|
- usize::from(width) * usize::from(height),
|
|
|
- Vec3 {
|
|
|
- x: 0.0,
|
|
|
- y: 0.0,
|
|
|
- z: 0.0,
|
|
|
- },
|
|
|
- );
|
|
|
-
|
|
|
- Image {
|
|
|
- width: width,
|
|
|
- height: height,
|
|
|
- data: data,
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fn save_bmp(self: &Image, p: &Path) -> std::io::Result<()> {
|
|
|
- let f: File = File::create(p)?;
|
|
|
- let mut w = BufWriter::new(f);
|
|
|
-
|
|
|
- // File header
|
|
|
- w.write_all(&['B' as u8, 'M' as u8])?;
|
|
|
- write_int(&mut w, 0)?; // size, left blank for now
|
|
|
- write_int(&mut w, 0)?; // reserved bytes
|
|
|
- write_int(&mut w, 54)?; // offset
|
|
|
-
|
|
|
- // Bitmap info header
|
|
|
- write_int(&mut w, 40)?; // header size in bytes
|
|
|
- write_int(&mut w, self.width as u32)?;
|
|
|
- write_int(&mut w, self.height as u32)?;
|
|
|
- write_int16(&mut w, 1)?; // number of color planes
|
|
|
- write_int16(&mut w, 24)?; // bits per pixel
|
|
|
- write_int(&mut w, 0)?; // no compression
|
|
|
- write_int(&mut w, 0)?; // image size
|
|
|
- write_int(&mut w, 1000)?; // horizontal resolution in pixels per metre
|
|
|
- write_int(&mut w, 1000)?; // vertical resolution
|
|
|
- write_int(&mut w, 0)?; // number of colors
|
|
|
- write_int(&mut w, 0)?; // number of important colors
|
|
|
-
|
|
|
- // Pixel data
|
|
|
- for y in (0..self.height).rev() {
|
|
|
- for x in 0..self.width {
|
|
|
- write_vec3(&mut w, self.get_pixel(x.into(), y.into()))?;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-fn read_int(v: &[u8]) -> u32 {
|
|
|
- let mut result = 0;
|
|
|
-
|
|
|
- for i in 0..3 {
|
|
|
- result |= (v[i] as u32) << (i * 8);
|
|
|
- }
|
|
|
-
|
|
|
- result
|
|
|
-}
|
|
|
-
|
|
|
-fn read_int16(v: &[u8]) -> u16 {
|
|
|
- v[1] as u16 >> 8 | v[0] as u16
|
|
|
-}
|
|
|
-
|
|
|
-fn read_vec3(v: &[u8]) -> Vec3 {
|
|
|
- Vec3 {
|
|
|
- x: v[2] as f32,
|
|
|
- y: v[1] as f32,
|
|
|
- z: v[0] as f32,
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-fn round_row_size(width: u32) -> u32 {
|
|
|
- ((24 * width + 31) / 32) * 4
|
|
|
-}
|
|
|
-
|
|
|
-pub fn load_bmp(path: &Path) -> Image {
|
|
|
- // See https://en.wikipedia.org/wiki/BMP_file_format for more information.
|
|
|
- // BMP is little-endian (least-significant bytes first)
|
|
|
- // Some limitations here:
|
|
|
- // - we only support bottom-up bitmaps
|
|
|
- // - without compression
|
|
|
- // - and a depth of 24 bits per pixel
|
|
|
-
|
|
|
- let mut buf = Vec::new();
|
|
|
- let mut f = BufReader::new(File::open(path).expect("Could not find image file"));
|
|
|
- f.read_to_end(&mut buf).expect("Could not read image file");
|
|
|
-
|
|
|
- assert!(
|
|
|
- buf[0] == 'B' as u8 && buf[1] == 'M' as u8,
|
|
|
- "File is not a bitmap file"
|
|
|
- );
|
|
|
-
|
|
|
- let width = read_int(&buf[18..22]);
|
|
|
- let height = read_int(&buf[22..26]);
|
|
|
-
|
|
|
- let compression = read_int(&buf[30..34]);
|
|
|
- assert!(compression == 0, "Only uncompressed BMPs are supported");
|
|
|
-
|
|
|
- let depth = read_int16(&buf[28..30]);
|
|
|
- assert!(depth == 24, "Only 24 bits per pixel are supported");
|
|
|
-
|
|
|
- let offset = read_int(&buf[10..14]);
|
|
|
-
|
|
|
- let row_size = round_row_size(width);
|
|
|
-
|
|
|
- let mut image_data = Vec::new();
|
|
|
-
|
|
|
- for row in 0..height {
|
|
|
- for col in 0..width {
|
|
|
- let y = height - row - 1;
|
|
|
- let x = 3 * col;
|
|
|
- let idx = (offset + y * row_size + x) as usize;
|
|
|
- let pixel = read_vec3(&buf[idx..(idx + 3)]);
|
|
|
- image_data.push(pixel);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- assert!(image_data.len() == (width * height) as usize);
|
|
|
-
|
|
|
- Image {
|
|
|
- width: width as u16,
|
|
|
- height: height as u16,
|
|
|
- data: image_data,
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-fn write_int(f: &mut dyn Write, v: u32) -> std::io::Result<()> {
|
|
|
- let bytes = [v as u8, (v >> 8) as u8, (v >> 16) as u8, (v >> 24) as u8];
|
|
|
- f.write_all(&bytes)?;
|
|
|
-
|
|
|
- Ok(())
|
|
|
-}
|
|
|
-
|
|
|
-fn write_int16(f: &mut dyn Write, v: u16) -> std::io::Result<()> {
|
|
|
- let bytes = [v as u8, (v >> 8) as u8];
|
|
|
- f.write_all(&bytes)?;
|
|
|
-
|
|
|
- Ok(())
|
|
|
-}
|
|
|
-
|
|
|
-fn write_vec3(f: &mut dyn Write, v: &Vec3) -> std::io::Result<()> {
|
|
|
- let bytes = [v.z as u8, v.y as u8, v.x as u8];
|
|
|
- f.write_all(&bytes)?;
|
|
|
-
|
|
|
- Ok(())
|
|
|
-}
|
|
|
+mod random;
|
|
|
+mod geometry;
|
|
|
+mod image;
|
|
|
+mod mesh;
|
|
|
+mod bsdf;
|
|
|
+mod bvh;
|
|
|
|
|
|
+use crate::geometry::Vec3;
|
|
|
+use crate::random::RNG;
|
|
|
+use crate::mesh::Mesh;
|
|
|
+use crate::geometry::Ray;
|
|
|
+use crate::image::Image;
|
|
|
+use crate::geometry::Intersection;
|
|
|
|
|
|
// ╺┳╸┏━┓┏━┓┏━╸┏━╸ ┏━┓┏━┓╻ ╻┏━┓
|
|
|
// ┃ ┣┳┛┣━┫┃ ┣╸ ┣┳┛┣━┫┗┳┛┗━┓
|
|
@@ -504,26 +57,6 @@ fn raytrace(r: &Ray) -> Vec3 {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// ┏┓ ┏━┓╺┳┓┏━╸
|
|
|
-// ┣┻┓┗━┓ ┃┃┣╸
|
|
|
-// ┗━┛┗━┛╺┻┛╹
|
|
|
-
|
|
|
-trait BSDF {
|
|
|
- fn eval(i: Vec3, o: Vec3) -> f32;
|
|
|
- fn sample(r: &mut RNG) -> Vec3;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-struct Object {
|
|
|
- mesh: Mesh,
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-struct Scene {
|
|
|
- objects: Vec<Object>
|
|
|
-}
|
|
|
-
|
|
|
// ┏┳┓┏━┓╻┏┓╻
|
|
|
// ┃┃┃┣━┫┃┃┗┫
|
|
|
// ╹ ╹╹ ╹╹╹ ╹
|
|
@@ -531,6 +64,9 @@ struct Scene {
|
|
|
fn main() {
|
|
|
|
|
|
let m = Mesh::load("benchmarks/suzanne.obj");
|
|
|
+ println!("First vertex: {}", m[0].vertices[m[0].vertices.len() - 1]);
|
|
|
+ let aabb = crate::bvh::AABB::from(&m[0], &m[0].faces[0]);
|
|
|
+ println!("AABB: {} -> {}", aabb.min, aabb.max);
|
|
|
return;
|
|
|
let mut rng = RNG::create();
|
|
|
|