diff --git a/TODO.md b/TODO.md index 7166d59..c8fdf03 100644 --- a/TODO.md +++ b/TODO.md @@ -28,5 +28,7 @@ - [x] Use filename for the root node - [x] Ship shaders directly in the executable - [ ] Have the program accept model files as CLI args + - [ ] Have it so that when an instance of 3dobs is open, any other file associated with 3dobs is opened in the same instance + - [ ] Show how many memory each loaded model is using - [ ] Bring back the regular camera movement (can maybe be a switch between them) (for later) diff --git a/src/importer/mod.rs b/src/importer/mod.rs index 61a18db..9e8b5dc 100644 --- a/src/importer/mod.rs +++ b/src/importer/mod.rs @@ -18,6 +18,7 @@ pub enum TextureType { Emissive } +// TODO: take the textures out of the material and put it on the model/object #[derive(Debug, Clone)] pub struct Material { pub name: String, diff --git a/src/importer/obj.rs b/src/importer/obj.rs index 75b2f29..b889883 100644 --- a/src/importer/obj.rs +++ b/src/importer/obj.rs @@ -232,29 +232,9 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result max_aabb.x { - max_aabb.x = x; - } - - if y > max_aabb.y { - max_aabb.y = y; - } - - if z > max_aabb.z { - max_aabb.z = z; - } + min_aabb = glm::vec3(min_aabb.x.min(x), min_aabb.y.min(y), min_aabb.z.min(z)); + max_aabb = glm::vec3(max_aabb.x.max(x), max_aabb.y.max(y), max_aabb.z.max(z)); } Some(ObjToken::Normal) => { diff --git a/src/importer/stl.rs b/src/importer/stl.rs index a2e73ed..6bce362 100644 --- a/src/importer/stl.rs +++ b/src/importer/stl.rs @@ -1,18 +1,113 @@ -use std::io::{Read, Seek, BufReader}; +use std::io::{Read, Seek, BufReader, BufRead}; use crate::{importer::ObjMesh, importer::Object, importer::Material, aabb::AABB, mesh::Vertex}; const STL_HEADER_SIZE: u64 = 80; -// might not be enough since it's not guaranteed. we'll see -const ASCII_STL_MAGIC: [u8; 11] = [0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x61, 0x73, 0x63, 0x69, 0x69]; // "solid ascii" +const STL_TRIANGLE_SIZE: usize = 50; #[repr(packed(2))] +#[derive(Debug)] struct STLTriangle { normal: glm::Vec3, verts: [glm::Vec3; 3], attribute_byte_count: u16 } +struct FacetIterator { + reader: BufReader, +} + +impl FacetIterator { + fn new(reader: R) -> Self { + FacetIterator { + reader: BufReader::new(reader), + } + } +} + +impl Iterator for FacetIterator { + type Item = STLTriangle; + + fn next(&mut self) -> Option { + let mut line = String::with_capacity(0x50); + let mut normal = glm::vec3(0.0, 0.0, 0.0); + let mut vertices = Vec::with_capacity(3); + + loop { + match self.reader.read_line(&mut line) { + Ok(0) => return None, // Reached EOF + Ok(_) => { + if line.contains("normal") { + let mut norm_iter = line.split_whitespace().skip(2).map(|s| s.parse::().unwrap()); + normal.x = norm_iter.next().unwrap(); + normal.y = norm_iter.next().unwrap(); + normal.z = norm_iter.next().unwrap(); + } else if line.contains("vertex") { + let mut vertex_iter = line.split_whitespace().skip(1).map(|s| s.parse::().unwrap()); + vertices.push(glm::vec3(vertex_iter.next().unwrap(), vertex_iter.next().unwrap(), vertex_iter.next().unwrap())); + } else if line.contains("endfacet") { + break; + } + } + // TODO: might need to panic and log here in the future + Err(_) => return None, // Error reading + } + line.clear(); + } + + let mut verts_iter = vertices.iter(); + + Some(STLTriangle{ + normal, + verts: [*verts_iter.next().unwrap(), *verts_iter.next().unwrap(), *verts_iter.next().unwrap()], + attribute_byte_count: 0 + }) + } +} + +fn parse_ascii_stl(file: std::fs::File) -> Result> { + let mut reader = BufReader::new(file); + let mut vertices = Vec::new(); + let mut indices = Vec::new(); + let mut min_aabb = glm::vec3(f32::MAX, f32::MAX, f32::MAX); + let mut max_aabb = glm::vec3(f32::MIN, f32::MIN, f32::MIN); + + reader.read_line(&mut String::new())?; // Skip the first line (solid name) + let facet_iter = FacetIterator::new(reader); + + let tex_coords = glm::vec2(0.0, 0.0); + + for (i, triangle) in facet_iter.enumerate() { + for vert in triangle.verts { + vertices.push(Vertex{ + position: glm::vec3(vert.x, vert.y, vert.z), + normal: glm::vec3(triangle.normal.x, triangle.normal.y, triangle.normal.z), + tex_coords, + }); + + min_aabb = glm::min(min_aabb, vert); + max_aabb = glm::max(max_aabb, vert); + } + + indices.push(i as u32 * 3); + indices.push(i as u32 * 3 + 1); + indices.push(i as u32 * 3 + 2); + } + + let aabb = AABB::new(min_aabb, max_aabb); + + Ok(Object{ + name: "default_object".to_string(), + meshes: vec![ObjMesh{ + name: "default_mesh".to_string(), + vertices, + indices, + material: Some(Material::default()) + }], + aabb, + }) +} + #[derive(Debug)] struct TrianglesIter { reader: R, @@ -25,7 +120,7 @@ impl TrianglesIter { fn new(reader: R, triangles_to_read: usize) -> Self { TrianglesIter { reader, - buf: vec![0u8; 50], + buf: vec![0u8; STL_TRIANGLE_SIZE], triangles_to_read, triangles_read: 0, } @@ -62,10 +157,6 @@ impl Iterator for TrianglesIter { } } -fn parse_ascii_stl(file: std::fs::File) -> Result> { - // TODO: -} - fn parse_binary_stl(mut file: std::fs::File) -> Result> { // skip header for now file.seek(std::io::SeekFrom::Start(STL_HEADER_SIZE))?; @@ -92,29 +183,8 @@ fn parse_binary_stl(mut file: std::fs::File) -> Result max_aabb.x { - max_aabb.x = vert.x; - } - - if vert.y > max_aabb.y { - max_aabb.y = vert.y; - } - - if vert.z > max_aabb.z { - max_aabb.z = vert.z; - } + min_aabb = glm::min(min_aabb, vert); + max_aabb = glm::max(max_aabb, vert); } indices.push(i as u32*3); @@ -136,21 +206,30 @@ fn parse_binary_stl(mut file: std::fs::File) -> Result bool { + for b in buf { + if *b > 127 { + return false; + } + } + + true +} + pub fn load_stl(mut file: std::fs::File) -> Result> { - let mut buf: [u8; 11] = [0; 11]; + let mut buf: [u8; 512] = [0; 512]; let _ = file.read_exact(&mut buf); _ = file.seek(std::io::SeekFrom::Start(0)); - let is_ascii = buf == ASCII_STL_MAGIC; + let is_ascii = is_ascii(&buf); + let now = std::time::Instant::now(); let obj = if is_ascii { parse_ascii_stl(file)? } else { parse_binary_stl(file)? }; - let now = std::time::Instant::now(); - let obj = parse_binary_stl(file)?; let elapsed = now.elapsed(); - println!("STL file loaded in {} ms", elapsed.as_millis()); + println!("STL file loaded in {} µs", elapsed.as_micros()); Ok(obj) }