diff --git a/lyra-resource/src/loader/model.rs b/lyra-resource/src/loader/model.rs index 56eb5d8..1952c81 100644 --- a/lyra-resource/src/loader/model.rs +++ b/lyra-resource/src/loader/model.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use base64::Engine; use glam::Vec3; -use crate::{ResourceLoader, LoaderError, Mesh, Vertex, Model}; +use crate::{ResourceLoader, LoaderError, Mesh, Vertex, Model, MeshVertexAttribute, VertexAttributeData}; impl From for LoaderError { fn from(value: gltf::Error) -> Self { @@ -35,25 +35,44 @@ impl ModelLoader { let mut meshes = vec![]; if let Some(mesh) = node.mesh() { for prim in mesh.primitives() { - let pos_accessor = prim.get(&gltf::Semantic::Positions).unwrap(); - //assert_eq!(pos_accessor.dimensions(), gltf::accessor::Dimensions::Vec3); // TODO: dont do this - let reader = prim.reader(|buf| Some(buffers[buf.index()].as_slice())); - let pos: Vec = reader.read_positions() - .unwrap() - .map(|pos| Vec3::new(pos[0], pos[1], pos[2])) - .collect(); - let indices: Option> = reader.read_indices() - .map(|i| match i { + let mut new_mesh = Mesh::default(); + + // read the positions + if let Some(pos) = reader.read_positions() { + let pos: Vec = pos.map(|t| t.into()).collect(); + new_mesh.add_attribute(MeshVertexAttribute::Position, VertexAttributeData::Vec3(pos)); + } + + // read the normals + if let Some(norms) = reader.read_normals() { + let norms: Vec = norms.map(|t| t.into()).collect(); + new_mesh.add_attribute(MeshVertexAttribute::Normals, VertexAttributeData::Vec3(norms)); + } + + // read the tangents + if let Some(tangents) = reader.read_tangents() { + let tangents: Vec = tangents.map(|t| t.into()).collect(); + new_mesh.add_attribute(MeshVertexAttribute::Tangents, VertexAttributeData::Vec4(tangents)); + } + + // read tex coords + if let Some(tex_coords) = reader.read_tex_coords(0) { + let tex_coords: Vec = tex_coords.into_f32().map(|t| t.into()).collect(); // TODO: u16, u8 + new_mesh.add_attribute(MeshVertexAttribute::TexCoords, VertexAttributeData::Vec2(tex_coords)); + } + + // read the indices + if let Some(indices) = reader.read_indices() { + let indices: Vec = match indices { gltf::mesh::util::ReadIndices::U8(i) => i.map(|i| i as u32).collect(), gltf::mesh::util::ReadIndices::U16(i) => i.map(|i| i as u32).collect(), gltf::mesh::util::ReadIndices::U32(i) => i.collect(), - }); + }; - let mut new_mesh = Mesh::default(); - new_mesh.vertices = pos.into_iter().map(|p| Vertex::new(p, glam::Vec2::new(0.0, 0.0))).collect(); - new_mesh.indices = indices; + new_mesh.indices = Some(indices); + } meshes.push(new_mesh); } @@ -82,8 +101,6 @@ impl ResourceLoader for ModelLoader { } let gltf = gltf::Gltf::open(path)?; - - //let buffers: Vec> = gltf.buffers().collect(); let buffers: Vec> = gltf.buffers().map(|b| match b.source() { gltf::buffer::Source::Bin => gltf.blob.as_deref().map(|v| v.to_vec()), @@ -93,6 +110,8 @@ impl ResourceLoader for ModelLoader { // TODO: Read in multiple scenes let scene = gltf.scenes().next().unwrap(); + // TODO: materials + let meshes: Vec = scene.nodes() .map(|node| self.process_node(&buffers, node)) .flatten().collect(); @@ -119,8 +138,11 @@ mod tests { let loader = ModelLoader::default(); let model = loader.load(&path).unwrap(); let model = Arc::downcast::(model.as_arc_any()).unwrap(); + assert_eq!(model.meshes.len(), 1); // There should only be 1 mesh let mesh = &model.meshes[0]; - assert!(mesh.vertices.len() > 0); + assert!(mesh.position().unwrap().len() > 0); + assert!(mesh.normals().unwrap().len() > 0); + assert!(mesh.tex_coords().unwrap().len() > 0); assert!(mesh.indices.clone().unwrap().len() > 0); } } \ No newline at end of file diff --git a/lyra-resource/src/model.rs b/lyra-resource/src/model.rs index 8684b61..a7c7a10 100644 --- a/lyra-resource/src/model.rs +++ b/lyra-resource/src/model.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + #[repr(C)] #[derive(Copy, Clone, Debug)] @@ -15,12 +17,79 @@ impl Vertex { } } +#[repr(C)] +#[derive(Clone, Debug, PartialEq)] +pub enum VertexAttributeData { + Vec2(Vec), + Vec3(Vec), + Vec4(Vec), +} + +impl VertexAttributeData { + /// Take self as a list of Vec2's + pub fn as_vec2(&self) -> &Vec { + match self { + VertexAttributeData::Vec2(v) => v, + _ => panic!("Attempt to get {self:?} as `Vec2`"), + } + } + + pub fn as_vec3(&self) -> &Vec { + match self { + VertexAttributeData::Vec3(v) => v, + _ => panic!("Attempt to get {self:?} as `Vec3`"), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum MeshVertexAttribute { + Position, + Normals, + Tangents, + // Colors, // TODO: Store data in VertexAttributeData + Joints, // TODO: Animation + TexCoords, + Weights, // TODO: Animation + MorphTargets, // TODO: Animation + Other(String), +} + #[derive(Clone, Default)] pub struct Mesh { - pub vertices: Vec, + pub attributes: HashMap, pub indices: Option>, } +impl Mesh { + pub fn add_attribute(&mut self, attribute: MeshVertexAttribute, data: VertexAttributeData) { + self.attributes.insert(attribute, data); + } + + /// Try to get the position attribute of the Mesh + pub fn position(&self) -> Option<&Vec> { + self.attributes.get(&MeshVertexAttribute::Position) + .map(|p| p.as_vec3()) + } + + pub fn add_position(&mut self, pos: Vec) { + self.attributes.insert(MeshVertexAttribute::Position, VertexAttributeData::Vec3(pos)); + } + + /// Try to get the normals attribute of the Mesh + pub fn normals(&self) -> Option<&Vec> { + self.attributes.get(&MeshVertexAttribute::Normals) + .map(|p| p.as_vec3()) + } + + /// Try to get the texture coordinates attribute of the Mesh + pub fn tex_coords(&self) -> Option<&Vec> { + self.attributes.get(&MeshVertexAttribute::TexCoords) + .map(|p| p.as_vec2()) + } +} + +#[derive(Clone, Default)] pub struct Model { pub meshes: Vec, //pub material