123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- // ┏┳┓┏━╸┏━┓╻ ╻ ╻┏━┓
- // ┃┃┃┣╸ ┗━┓┣━┫ ┃┃ ┃
- // ╹ ╹┗━╸┗━┛╹ ╹ ╹┗━┛
- use std::fs::File;
- use std::io::BufReader;
- use std::io::BufRead;
- use std::collections::HashMap;
- use std::path::Path;
- use crate::geometry::Vec3;
- use crate::geometry::Ray;
- /** Data structure for a single triangle face. */
- pub struct Face {
- /** Triangle coordinates indexed inside mesh */
- v: [usize; 3],
- /** Triangle normals for each vertex */
- n: [usize; 3],
-
- /** Texture coordinates */
- tex: [usize; 3],
- /** Center point. Needed for BVH construction */
- pub center: Vec3,
- /** Surface Area */
- pub area: f32
- }
- impl Face {
- pub fn from(m: &Mesh, t: &Vec<[usize; 3]>) -> Face {
- let mut face = Face {
- v: [t[0][0] - 1, t[1][0] - 1, t[2][0] - 1],
- n: [t[0][1] - 1, t[1][1] - 1, t[2][1] - 1],
- tex: [t[0][2] - 1, t[1][2] - 1, t[2][2] - 1],
- center: Vec3::new(0.0, 0.0, 0.0),
- area: 0.0
- };
- // Calculate centroid
- face.center.x = face.v.iter().map(|i| m.vertices[*i].x).sum::<f32>() / 3.0;
- face.center.y = face.v.iter().map(|i| m.vertices[*i].y).sum::<f32>() / 3.0;
- face.center.z = face.v.iter().map(|i| m.vertices[*i].z).sum::<f32>() / 3.0;
- // Calculate surface area
- let ab = m.vertices[face.v[1]] - m.vertices[face.v[0]];
- let ac = m.vertices[face.v[2]] - m.vertices[face.v[0]];
- face.area = ab.cross(ac).length() / 2.0;
- return face;
- }
- }
- #[derive(Copy,Clone,Debug)]
- pub struct Material {
- pub color: Vec3,
- pub roughness: f32,
- pub metallic: f32,
- }
- impl Material {
- pub fn new() -> Material {
- return Material {
- color: Vec3::new(0.0, 0.0, 0.0),
- roughness: 0.2,
- metallic: 0.0,
- }
- }
- }
- pub struct Mesh {
- pub name: String,
- pub vertices: Vec<Vec3>,
- pub normals: Vec<Vec3>,
- pub texture_coordinates: Vec<Vec3>,
- pub faces: Vec<Face>,
- pub material: Material
- }
- fn parse_vec3<'a>(it: &mut impl Iterator<Item = &'a str>) -> Option<Vec3> {
- let x = it.next()?.parse::<f32>().ok()?;
- let y = it.next()?.parse::<f32>().ok()?;
- let z = it.next()?.parse::<f32>().ok()?;
- return Some(Vec3::new(x,y,z));
- }
- fn parse_uv<'a>(a: &mut impl Iterator<Item = &'a str>) -> Option<Vec3> {
- let x = a.next()?.parse::<f32>().ok()?;
- let y = a.next()?.parse::<f32>().ok()?;
- return Some(Vec3::new(x,y, 0.0));
- }
- fn parse_triple(a: &str) -> Option<[usize; 3]> {
- let v : Vec<usize> = a.split("/")
- .map(|idx| idx.parse::<usize>().ok())
- .collect::<Option<Vec<usize>>>()?;
- if v.len() != 3 {
- return None;
- } else {
- return Some([v[0], v[1], v[2]]);
- }
- }
- /* Parses a line into individual arguments.
- * Returns None if the line is empty.
- */
- fn parse_line<'a>(line: &'a mut String) -> Option<impl Iterator<Item = &'a str>> {
- let whitespace_offset = line.find(char::is_alphanumeric).unwrap_or(0);
- let comment_offset = line.find('#').unwrap_or(line.len());
- if whitespace_offset >= comment_offset {
- // Line is factually empty except for whitespace and commentary.
- return None;
- }
- // Remove comments
- let instruction = &line[whitespace_offset..comment_offset];
- // Skip if line is empty
- if instruction.is_empty() {
- return None;
- }
- return Some(instruction.split(' '));
- }
- pub fn read_mtllib(path: &str) -> HashMap<String, Material> {
- let mut library = HashMap::new();
- let file = File::open(path).expect("Could not open mtllib");
- let reader = BufReader::new(file);
- let mut name = String::default();
- for (line_counter, l) in reader.lines().enumerate() {
- let mut line = l.expect("Failed to read line from file");
- let result = parse_line(&mut line);
- if result.is_none() {
- // Empty line
- continue;
- }
- let mut arguments = result.unwrap();
- let command = arguments.next()
- .expect(format!("Line {} does not contain a command", line_counter).as_str());
- let error_msg = format!("Invalid instruction in line {} (missing newmtl?)", line_counter);
- match command {
- "newmtl" => {
- name = String::from(arguments.next().expect("Expected name in newmtl instruction"));
- println!("Creating material {}", name);
- library.insert(name.clone(), Material::new());
- println!("Have '{}'", name);
- },
- "Kd" => {
- println!("Contained: {}", library.keys().next().unwrap_or(&String::from("NONE")));
- println!("Have '{}'", name);
- let mtl = library.get_mut(&name).expect(error_msg.as_str());
- let color = parse_vec3(&mut arguments).expect("Unable to parse color vector");
- mtl.color = color;
- },
- _ => {}
- }
- }
- return library;
- }
- impl Mesh {
- pub fn load(obj_path: &str) -> Vec<Mesh> {
- let mut objects : Vec<Mesh> = Vec::new();
- let file = File::open(obj_path).expect("Could not open OBJ file");
- let reader = BufReader::new(file);
- let mut mtllib : HashMap<String, Material> = HashMap::default();
- let default_material = Material::new();
- // Points to last created object
- let mut object_count : usize = 0;
- for (line_counter, l) in reader.lines().enumerate() {
- let mut line = l.expect("Could not read line from file");
- let result= parse_line(&mut line);
- // Handle empty line
- if result.is_none() {
- continue;
- }
- let mut arguments = result.unwrap();
- let instruction_code = arguments.next().expect(
- format!("Did not find instruction code in line {}'", line_counter).as_str());
- match instruction_code {
- "o" => {
- let name = arguments.next().expect(format!("Missing name argument in line {}", line_counter).as_str());
- // Construct new mesh to work on.
- objects.push(Mesh {
- name: String::from(name),
- vertices: Vec::new(),
- normals: Vec::new(),
- texture_coordinates: Vec::new(),
- faces: Vec::new(),
- material: default_material
- });
- },
- "v" | "vn" => {
- let o : &mut Mesh = objects.last_mut().expect("Vertices added before object was constructed");
- let coords = parse_vec3(&mut arguments).expect(
- format!("Could not parse coords in line {}", line_counter).as_str()
- );
- if instruction_code == "v" {
- o.vertices.push(coords);
- } else {
- o.normals.push(coords);
- }
- }
- "vt" => {
- let o : &mut Mesh = objects.last_mut().expect("Texture coordinates added before object was constructed");
- let coords = parse_uv(&mut arguments).expect(
- format!("Could not parse uv coords in line {}", line_counter).as_str()
- );
- o.texture_coordinates.push(coords);
- }
- "f" => {
- let o : &mut Mesh = objects.last_mut().expect("Face added before object was constructed");
- let error_msg = format!("Could not parse face in line {}", line_counter);
- let t : Vec<[usize; 3]> = arguments
- .map(|x| parse_triple(x))
- .collect::<Option<Vec<[usize; 3]>>>()
- .expect(&error_msg);
- if t.len() != 3 {
- panic!("{}", error_msg);
- }
- let face = Face::from(&o, &t);
- o.faces.push(face);
- }
- "mtllib" => {
- let filename = arguments.next().expect(format!("Missing filename in line {}", line_counter).as_str());
- let basedir = Path::new(obj_path).parent().expect("Could not find parent directory of obj file");
- mtllib = read_mtllib(basedir.join(filename).to_str().expect("Could not construct path to mtl lib"));
- }
- "usemtl" => {
- let o : &mut Mesh = objects.last_mut().expect("Face added before object was constructed");
- let material_name = String::from(arguments.next().expect("No material name specified"));
- o.material = *mtllib.get(&material_name).expect("Material not found in library");
- }
- _ => {}
- }
- }
- // Print message with number of objects and total number of faces
- let total_faces : usize = objects.iter()
- .map(|o| o.faces.len())
- .sum();
- let object_names = objects.iter()
- .map(|o| o.name.clone())
- .collect::<Vec<String>>()
- .join(",");
- println!("Read {} objects ({}) with a total of {} faces.", objects.len(), object_names, total_faces);
- return objects;
- }
- pub fn face_coords(&self, f: &Face) -> [&Vec3; 3] {
- return [
- &self.vertices[f.v[0]],
- &self.vertices[f.v[1]],
- &self.vertices[f.v[2]]
- ];
- }
- }
|