use crate::model; pub fn load_stl(path: std::path::PathBuf) -> model::Model { let name = path.file_name().map(|v| v.to_string_lossy().to_string()); let mut file = std::fs::File::open(path).unwrap(); let stl = stl::read_stl(&mut file).unwrap(); let mut vertices = Vec::new(); let mut indices = Vec::new(); for tri in stl.triangles.iter() { indices.push(vertices.len() as u32); vertices.push(model::BasicVertex { position: tri.v1.into(), }); indices.push(vertices.len() as u32); vertices.push(model::BasicVertex { position: tri.v2.into(), }); indices.push(vertices.len() as u32); vertices.push(model::BasicVertex { position: tri.v3.into(), }); } model::Model { name: name.clone(), objects: vec![model::Object { name, transform: Default::default(), meshes: vec![model::Mesh { vertices, indices }], children: vec![], }], } } pub fn load_gltf(path: std::path::PathBuf) -> model::Model { let name = path.file_name().map(|v| v.to_string_lossy().to_string()); let (document, buffers, _images) = gltf::import(path).unwrap(); let buffers = buffers.into_iter().map(|v| v.to_vec()).collect(); let loader = GltfLoader { name, document, buffers, }; loader.load() } pub struct GltfLoader { pub name: Option, pub document: gltf::Document, pub buffers: Vec>, } impl GltfLoader { pub fn load(mut self) -> model::Model { let mut model = model::Model { name: self.name.clone(), objects: vec![], }; for scene in self.document.scenes() { for node in scene.nodes() { model.objects.push(self.load_node(node, glam::Vec3::ONE)); } } model } pub fn load_buffers(model: &gltf::Gltf) -> Vec> { let mut buffer_data = Vec::>::new(); for buffer in model.buffers() { match buffer.source() { gltf::buffer::Source::Bin => { buffer_data.push(model.blob.as_deref().unwrap().into()); } _ => panic!("URI buffer sources are unsupported"), } } buffer_data } pub fn load_node(&self, node: gltf::Node, parent_scale: glam::Vec3) -> model::Object { let (translation, orientation, scale) = node.transform().decomposed(); let orientation = glam::Quat::from_array(orientation); let rotation = orientation.to_euler(glam::EulerRot::XYZ); let scale: glam::Vec3 = scale.into(); let scale = scale * parent_scale; let transform = model::Transform { position: translation.into(), rotation: rotation.into(), scale, }; let mut object = model::Object { name: node.name().map(str::to_string), transform, meshes: vec![], children: vec![], }; if let Some(mesh) = node.mesh() { for primitive in mesh.primitives() { object.meshes.push(self.load_primitive_mesh(primitive)); } } for child in node.children() { object.children.push(self.load_node(child, scale)); } object } pub fn load_primitive_mesh(&self, primitive: gltf::Primitive) -> model::Mesh { use gltf::mesh::util::{ReadIndices, ReadTexCoords}; if primitive.mode() != gltf::mesh::Mode::Triangles { panic!("glTF primitive must be triangle list"); } let reader = primitive.reader(|buffer| Some(&self.buffers[buffer.index()])); let positions = reader.read_positions().unwrap(); let mut normals = reader.read_normals().unwrap(); let tex_coords = reader.read_tex_coords(0).unwrap(); let mut tex_coords = if let ReadTexCoords::F32(tex_coords) = tex_coords { tex_coords } else { panic!("only f32 texture coordinates are supported") }; let mut vertices = Vec::new(); for position in positions { let normal = normals.next().unwrap(); let tex_coords = tex_coords.next().unwrap(); vertices.push(model::BasicVertex { position: position.into(), }); } let indices = match reader.read_indices().unwrap() { ReadIndices::U32(indices) => indices.collect(), ReadIndices::U16(indices) => indices.map(|i| i as u32).collect(), ReadIndices::U8(indices) => indices.map(|i| i as u32).collect(), }; model::Mesh { vertices, indices } } }