diff --git a/obj_loader/src/lib.rs b/obj_loader/src/lib.rs index e7eff00..07e3cb3 100644 --- a/obj_loader/src/lib.rs +++ b/obj_loader/src/lib.rs @@ -1,15 +1,164 @@ -pub mod obj; -pub fn add(left: usize, right: usize) -> usize { - left + right -} +use cimvr_common::render::{Mesh, Vertex}; +// use cimvr_engine_interface::{dbg, prelude::*}; +// use std::{io::Read, str::FromStr}; + +/// Read OBJ lines into the mesh +/// OBJ line specs: https://all3dp.com/1/obj-file-format-3d-printing-cad/ +pub fn obj_lines_to_mesh(obj: &str) -> Mesh { + let mut m = Mesh::new(); + let mut tex_coords: Vec<[f32; 3]> = vec![]; + let mut normals: Vec<[f32; 3]> = vec![]; + + for line in obj.lines() { + // Split the line by whitespace + let mut line = line.split_whitespace(); + + // Break the first bit off + let (first, mut rest) = (line.next(), line); + + // Which kind of line is it? + match first { + Some("v") => { + // Vertex + // Treat the line as two arrays of 3 elements (x, y, z) coords and perhaps (u, v, w) + let mut parts = [[0.; 3], [1.; 3]]; + + for part in &mut parts { + // Get strings from the rest of the line + // The by_ref() here allows us to keep eating the line on the next loop + for dim in part { + let Some(text) = rest.next() else { break }; + *dim = text.parse().expect("Invalid float"); + } + } + + // Split the parts back up + let [pos, uvw] = parts; + + // Assemble the vertex + m.vertices.push(Vertex { pos, uvw }); + } + Some("l") => { + // Line + // Do the same for indices + let mut indices = [0; 2]; + for dim in &mut indices { + let Some(text) = rest.next() else { break }; + *dim = text.parse().expect("Invalid index"); + + // OBJ files are one-indexed + *dim -= 1; + } + m.indices.extend(indices); + } + Some("vt") => { + // Vertex textures + // We treat this similarly to how we treat a vertex line, but just as + // 1 array of 3 elements: (u,v,w) + // Each vt line will look like: 'vt u v [w]' + let mut uvw = [0.; 3]; + + for dim in &mut uvw { + if let Some(text) = rest.next() { + *dim = text.parse().expect("Invalid float"); + } else { + break; + } + } + // Store parsed texture coord + tex_coords.push(uvw); + } + Some("f") => { + // Faces + // At this point all vertices, texture coordinates, vertex normals etc. + // have been declared + // Treat the line as a list of indices to be divided into triangles + // Drawing faces as a triangle fan + + //faces don't just include indices-- they can also include vertex normals (vn) and texture coordinates (vt) + // We have to anticipate that + + // A face must have at least 3 vertices and at most 30. + let mut faces = [0; 3]; + let max_indices = 30; -#[cfg(test)] -mod tests { - use super::*; + // We don't know how many indices will be on a line, so we will just initialize it without a size for now + let mut parsed_line = vec![]; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + // Allocate a collection so we can better manage the indices + // How do we parse through the line AND add it to a mutable array? Use .push()? + // Potentially infitie loops are terrifying. Re evaluate this later + loop { + let Some(text) = rest.next() else { break }; // Refutable pattern match - if nothing left, break + // Index from string to int, check if index exists + // Add the index to the vector + let index_info: Vec<&str> = text.split("/").collect(); // Split and collect-- from v/vt/vn to + + let idx: u32 = index_info[0].parse().expect("Invalid index"); + parsed_line.push(idx - 1); // OBJ files are one-indexed + + // Vertex might also have vt, vn index associated + // Still need to find a way to apply these + if index_info.len() >= 2 { + let vt = index_info[1].parse::().expect("Invalid vt"); + + if index_info.len() == 3 { + let vn = index_info[2].parse::().expect("Invalid vn"); + } + } + + // We don't want a face with more than ten triangles. Break the loop. + // Probably need to produce an error here + if parsed_line.len() > max_indices { + break; + }; + } + + // When we read the entire line, we need to divide the indexes into triangles + // i.e. if we have a face with 5 verts: + // read in [0,1,2] as a triangle, [0,2,3] as another triangle, [0,3,4] as the next triangle + // Delimit first by whitespace -- then need to check for slashes to delimit texture/vertex normals later + + // Will loop through the parsed line and divide them into triangles + let mut i = 0; + while i + 1 < parsed_line.len() { + // For each iteration, while i is less than the size of the parsed line: + // 3 elements will be pushed at a time + // First element will always be the first index in the parsed line + faces[0] = parsed_line[0]; + // Second element will always be the ith index + faces[1] = parsed_line[i]; + // Third element will always be the (i+1)th index + //If there is no third element, return error + faces[2] = parsed_line[i + 1]; + + // Add those indices to be rendered + m.indices.extend(faces); + + // Increment index + i += 1; + } + } + + Some("vn") => { + // Vertex normals + // Each vn line will look like: 'vn x y z' + // Represents normal vector component at given vertex + // Will get ref in f definition + let mut normal = [0.; 3]; + for dim in &mut normal { + if let Some(text) = rest.next(){ + *dim = text.parse().expect("Invalid float"); + } + } + // Store vn + normals.push(normal); + }, + + // Ignore the rest + _ => (), + } } + + m }