diff --git a/Cargo.toml b/Cargo.toml index cd4c70b..60bf8dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] bytemuck = { version="1.7", features=["derive"] } glam = "0.20" -gltf = "1.0" +gltf = { version="1.0", features=["utils"] } image = "0.24" pollster = "0.2" slab = "0.4" diff --git a/src/main.rs b/src/main.rs index 799994f..4ad898a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ struct Grid { impl Grid { fn new(ren: &mut Renderer) -> Self { - let model = ObjModel::load(ren); + let model = GltfModel::load(ren); let mut meshes = Vec::new(); for x in -5..5 { for y in -5..5 { @@ -61,13 +61,13 @@ struct Planet { struct Planets { start: std::time::Instant, planets: Vec, - model: ObjModel, + model: GltfModel, } impl Planets { fn new(ren: &mut Renderer) -> Self { let start = std::time::Instant::now(); - let model = ObjModel::load(ren); + let model = GltfModel::load(ren); let mut planets = Vec::new(); for i in 0..10 { diff --git a/src/model.rs b/src/model.rs index 08f748f..0b90c27 100644 --- a/src/model.rs +++ b/src/model.rs @@ -4,11 +4,25 @@ use crate::pool::{MaterialData, TextureData}; use crate::renderer::Renderer; use crate::scene::MeshInstance; -pub struct ObjModel { +pub struct BasicMesh { pub mesh_handle: MeshHandle, pub material_handle: MaterialHandle, } +impl BasicMesh { + pub fn instantiate(&self, transform: glam::Mat4) -> MeshInstance { + MeshInstance { + mesh: self.mesh_handle, + material: self.material_handle, + transform, + } + } +} + +pub struct ObjModel { + mesh: BasicMesh, +} + impl ObjModel { pub fn load(ren: &mut Renderer) -> Self { use tobj::*; @@ -80,43 +94,59 @@ impl ObjModel { ren.material_pool .allocate(&ren.device, &ren.texture_pool, &material_data); - Self { + let mesh = BasicMesh { mesh_handle, material_handle, - } + }; + Self { mesh } } pub fn draw(&self, meshes: &mut Vec, transform: glam::Mat4) { - meshes.push(MeshInstance { - mesh: self.mesh_handle, - material: self.material_handle, - transform, - }); + meshes.push(self.mesh.instantiate(transform)); } } -pub struct GltfModel {} +pub struct GltfModel { + meshes: Vec, +} impl GltfModel { - pub fn new(ren: &mut Renderer) -> Self { + pub fn load(ren: &mut Renderer) -> Self { use gltf::*; let model_data = include_bytes!("assets/DamagedHelmet.glb"); let model = Gltf::from_slice(model_data.as_slice()).unwrap(); let loader = GltfLoader::load(&model, ren); - Self {} + Self { + meshes: loader.meshes, + } + } + + pub fn draw(&self, meshes: &mut Vec, transform: glam::Mat4) { + for mesh in self.meshes.iter() { + meshes.push(mesh.instantiate(transform)); + } } } pub struct GltfLoader<'a> { pub model: &'a gltf::Gltf, + pub buffers: Vec>, pub ren: &'a mut Renderer, + pub meshes: Vec, } impl<'a> GltfLoader<'a> { pub fn load(model: &'a gltf::Gltf, ren: &'a mut Renderer) -> Self { - let mut loader = Self { model, ren }; + let buffers = Self::load_buffers(model); + + let mut loader = Self { + model, + buffers, + ren, + meshes: Default::default(), + }; for scene in loader.model.scenes() { for node in scene.nodes() { @@ -127,10 +157,25 @@ impl<'a> GltfLoader<'a> { loader } + 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(&mut self, node: gltf::Node) { if let Some(mesh) = node.mesh() { for primitive in mesh.primitives() { - self.load_primitive(primitive); + let mesh = self.load_primitive(primitive); + self.meshes.push(mesh); } } @@ -139,15 +184,86 @@ impl<'a> GltfLoader<'a> { } } - pub fn load_primitive(&mut self, primitive: gltf::Primitive) { + pub fn load_primitive(&mut self, primitive: gltf::Primitive) -> BasicMesh { + let material_handle = self.load_primitive_material(primitive.clone()); + let mesh_handle = self.load_primitive_mesh(primitive); + BasicMesh { + material_handle, + mesh_handle, + } + } + + pub fn load_primitive_material(&mut self, primitive: gltf::Primitive) -> MaterialHandle { + let pbr = primitive.material().pbr_metallic_roughness(); + let base_texture = pbr.base_color_texture().unwrap(); + let base_texture = base_texture.texture().source().source(); + + let base_view = if let gltf::image::Source::View { view, .. } = base_texture { + view + } else { + panic!("texture must be embedded"); + }; + + // TODO put real data in albedo texture + + let albedo_data = TextureData { + width: 256, + height: 256, + data: vec![0xffu8; 256 * 256 * 4], + }; + + let albedo = + self.ren + .texture_pool + .allocate(&self.ren.device, &self.ren.queue, &albedo_data); + + let material_data = MaterialData { albedo }; + + self.ren + .material_pool + .allocate(&self.ren.device, &self.ren.texture_pool, &material_data) + } + + pub fn load_primitive_mesh(&mut self, primitive: gltf::Primitive) -> MeshHandle { + use gltf::mesh::util::{ReadIndices, ReadTexCoords}; + if primitive.mode() != gltf::mesh::Mode::Triangles { panic!("glTF primitive must be triangle list"); } - let attributes = GltfPrimitiveAttributes::new(primitive); - let positions = attributes.positions.unwrap(); - let normals = attributes.normals.unwrap(); - let texcoords = attributes.texcoords.unwrap(); + println!("primitive: {:#?}", primitive); + + 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(Vertex { + position, + normal, + tex_coords, + }); + } + + 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(), + }; + + let mesh_data = MeshData { vertices, indices }; + self.ren.mesh_pool.allocate(&self.ren.device, &mesh_data) } }