use super::Texture; use super::{Material, Mesh, Vertex}; use anyhow::Result; use bytemuck::{Pod, Zeroable}; use std::path::Path; use tobj::LoadOptions; use wgpu::util::DeviceExt; pub struct Model { pub meshes: Vec, pub materials: Vec, } impl Model { pub fn Load>( device: &wgpu::Device, queue: &wgpu::Queue, layout: &wgpu::BindGroupLayout, path: P, ) -> Result { let (obj_models, obj_materials) = tobj::load_obj( path.as_ref(), &LoadOptions { triangulate: true, single_index: true, ..Default::default() }, )?; let obj_materials = obj_materials?; // We're assuming that the texture files are stored with the obj file let containing_folder = path.as_ref().parent().expect("Directory has no parent"); let mut materials = Vec::new(); for mat in obj_materials { let diffuse_path = mat.diffuse_texture; let diffuse_texture = Texture::Load(device, queue, containing_folder.join(diffuse_path))?; let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout, entries: &[ wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), }, wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), }, ], label: None, }); materials.push(Material { name: mat.name, diffuse_texture, bind_group, }); } let mut meshes = Vec::new(); for m in obj_models { let mut vertices = Vec::new(); for i in 0..m.mesh.positions.len() / 3 { vertices.push(ModelVertex { position: [ m.mesh.positions[i * 3], m.mesh.positions[i * 3 + 1], m.mesh.positions[i * 3 + 2], ], tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]], normal: [ m.mesh.normals[i * 3], m.mesh.normals[i * 3 + 1], m.mesh.normals[i * 3 + 2], ], }); } let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some(&format!("{:?} Vertex Buffer", path.as_ref())), contents: bytemuck::cast_slice(&vertices), usage: wgpu::BufferUsages::VERTEX, }); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some(&format!("{:?} Index Buffer", path.as_ref())), contents: bytemuck::cast_slice(&m.mesh.indices), usage: wgpu::BufferUsages::INDEX, }); meshes.push(Mesh { name: m.name, vertex_buffer, index_buffer, num_elements: m.mesh.indices.len() as u32, material: m.mesh.material_id.unwrap_or(0), }); } Ok(Self { meshes, materials }) } } #[repr(C)] #[derive(Copy, Clone, Debug, Pod, Zeroable)] pub struct ModelVertex { position: [f32; 3], tex_coords: [f32; 2], normal: [f32; 3], } impl Vertex for ModelVertex { fn GetDescriptor<'a>() -> wgpu::VertexBufferLayout<'a> { use std::mem; wgpu::VertexBufferLayout { array_stride: mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, attributes: &[ wgpu::VertexAttribute { offset: 0, shader_location: 0, format: wgpu::VertexFormat::Float32x3, }, wgpu::VertexAttribute { offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, shader_location: 1, format: wgpu::VertexFormat::Float32x2, }, wgpu::VertexAttribute { offset: mem::size_of::<[f32; 5]>() as wgpu::BufferAddress, shader_location: 2, format: wgpu::VertexFormat::Float32x3, }, ], } } }