use anyhow::*; use cgmath::InnerSpace; use std::ops::Range; use std::path::Path; use tobj::LoadOptions; use wgpu::util::DeviceExt; use crate::texture; pub trait Vertex { fn desc<'a>() -> wgpu::VertexBufferLayout<'a>; } #[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct ModelVertex { position: [f32; 3], tex_coords: [f32; 2], normal: [f32; 3], tangent: [f32; 3], bitangent: [f32; 3], } impl Vertex for ModelVertex { fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { use std::mem; wgpu::VertexBufferLayout { array_stride: mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::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, }, // Tangent and bitangent wgpu::VertexAttribute { offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress, shader_location: 3, format: wgpu::VertexFormat::Float32x3, }, wgpu::VertexAttribute { offset: mem::size_of::<[f32; 11]>() as wgpu::BufferAddress, shader_location: 4, format: wgpu::VertexFormat::Float32x3, }, ], } } } pub struct Material { pub name: String, pub diffuse_texture: texture::Texture, pub normal_texture: texture::Texture, pub bind_group: wgpu::BindGroup, } impl Material { pub fn new( device: &wgpu::Device, name: &str, diffuse_texture: texture::Texture, normal_texture: texture::Texture, layout: &wgpu::BindGroupLayout, ) -> Self { 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), }, wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(&normal_texture.view), }, wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::Sampler(&normal_texture.sampler), }, ], label: Some(name), }); Self { name: String::from(name), diffuse_texture, normal_texture, bind_group, } } } pub struct Mesh { pub name: String, pub vertex_buffer: wgpu::Buffer, pub index_buffer: wgpu::Buffer, pub num_elements: u32, pub material: usize, } 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().context("Directory has no parent")?; let mut materials = Vec::new(); for mat in obj_materials { let diffuse_path = mat.diffuse_texture; let diffuse_texture = texture::Texture::load(device, queue, containing_folder.join(diffuse_path), false)?; let normal_path = mat.normal_texture; let normal_texture = texture::Texture::load(device, queue, containing_folder.join(normal_path), true)?; materials.push(Material::new( device, &mat.name, diffuse_texture, normal_texture, layout, )); } 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], ] .into(), tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]].into(), normal: [ m.mesh.normals[i * 3], m.mesh.normals[i * 3 + 1], m.mesh.normals[i * 3 + 2], ] .into(), // We'll calculate these later tangent: [0.0; 3].into(), bitangent: [0.0; 3].into(), }); } let indices = &m.mesh.indices; let mut triangles_included = (0..vertices.len()).collect::>(); // Calculate tangents and bitangets. We're going to // use the triangles, so we need to loop through the // indices in chunks of 3 for c in indices.chunks(3) { let v0 = vertices[c[0] as usize]; let v1 = vertices[c[1] as usize]; let v2 = vertices[c[2] as usize]; let pos0: cgmath::Vector3<_> = v0.position.into(); let pos1: cgmath::Vector3<_> = v1.position.into(); let pos2: cgmath::Vector3<_> = v2.position.into(); let uv0: cgmath::Vector2<_> = v0.tex_coords.into(); let uv1: cgmath::Vector2<_> = v1.tex_coords.into(); let uv2: cgmath::Vector2<_> = v2.tex_coords.into(); // Calculate the edges of the triangle let delta_pos1 = pos1 - pos0; let delta_pos2 = pos2 - pos0; // This will give us a direction to calculate the // tangent and bitangent let delta_uv1 = uv1 - uv0; let delta_uv2 = uv2 - uv0; // Solving the following system of equations will // give us the tangent and bitangent. // delta_pos1 = delta_uv1.x * T + delta_u.y * B // delta_pos2 = delta_uv2.x * T + delta_uv2.y * B // Luckily, the place I found this equation provided // the solution! let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x); let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r; // We'll use the same tangent/bitangent for each vertex in the triangle vertices[c[0] as usize].tangent = (tangent + cgmath::Vector3::from(vertices[c[0] as usize].tangent)).into(); vertices[c[1] as usize].tangent = (tangent + cgmath::Vector3::from(vertices[c[1] as usize].tangent)).into(); vertices[c[2] as usize].tangent = (tangent + cgmath::Vector3::from(vertices[c[2] as usize].tangent)).into(); vertices[c[0] as usize].bitangent = (bitangent + cgmath::Vector3::from(vertices[c[0] as usize].bitangent)).into(); vertices[c[1] as usize].bitangent = (bitangent + cgmath::Vector3::from(vertices[c[1] as usize].bitangent)).into(); vertices[c[2] as usize].bitangent = (bitangent + cgmath::Vector3::from(vertices[c[2] as usize].bitangent)).into(); // Used to average the tangents/bitangents triangles_included[c[0] as usize] += 1; triangles_included[c[1] as usize] += 1; triangles_included[c[2] as usize] += 1; } // Average the tangents/bitangents for (i, n) in triangles_included.into_iter().enumerate() { let denom = 1.0 / n as f32; let mut v = &mut vertices[i]; v.tangent = (cgmath::Vector3::from(v.tangent) * denom) .normalize() .into(); v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom) .normalize() .into(); } 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::BufferUsage::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::BufferUsage::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 }) } } pub trait DrawModel<'a> { fn draw_mesh( &mut self, mesh: &'a Mesh, material: &'a Material, camera: &'a wgpu::BindGroup, light: &'a wgpu::BindGroup, ); fn draw_mesh_instanced( &mut self, mesh: &'a Mesh, material: &'a Material, instances: Range, camera: &'a wgpu::BindGroup, light: &'a wgpu::BindGroup, ); fn draw_model( &mut self, model: &'a Model, camera: &'a wgpu::BindGroup, light: &'a wgpu::BindGroup, ); fn draw_model_instanced( &mut self, model: &'a Model, instances: Range, camera: &'a wgpu::BindGroup, light: &'a wgpu::BindGroup, ); fn draw_model_instanced_with_material( &mut self, model: &'a Model, material: &'a Material, instances: Range, camera: &'a wgpu::BindGroup, light: &'a wgpu::BindGroup, ); } impl<'a, 'b> DrawModel<'b> for wgpu::RenderPass<'a> where 'b: 'a, { fn draw_mesh( &mut self, mesh: &'b Mesh, material: &'b Material, camera: &'b wgpu::BindGroup, light: &'b wgpu::BindGroup, ) { self.draw_mesh_instanced(mesh, material, 0..1, camera, light); } fn draw_mesh_instanced( &mut self, mesh: &'b Mesh, material: &'b Material, instances: Range, camera: &'b wgpu::BindGroup, light: &'b wgpu::BindGroup, ) { self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32); self.set_bind_group(0, &material.bind_group, &[]); self.set_bind_group(1, camera, &[]); self.set_bind_group(2, light, &[]); self.draw_indexed(0..mesh.num_elements, 0, instances); } fn draw_model( &mut self, model: &'b Model, camera: &'b wgpu::BindGroup, light: &'b wgpu::BindGroup, ) { self.draw_model_instanced(model, 0..1, camera, light); } fn draw_model_instanced( &mut self, model: &'b Model, instances: Range, camera: &'b wgpu::BindGroup, light: &'b wgpu::BindGroup, ) { for mesh in &model.meshes { let material = &model.materials[mesh.material]; self.draw_mesh_instanced(mesh, material, instances.clone(), camera, light); } } fn draw_model_instanced_with_material( &mut self, model: &'b Model, material: &'b Material, instances: Range, camera: &'b wgpu::BindGroup, light: &'b wgpu::BindGroup, ) { for mesh in &model.meshes { self.draw_mesh_instanced(mesh, material, instances.clone(), camera, light); } } } pub trait DrawLight<'a> { fn draw_light_mesh( &mut self, mesh: &'a Mesh, camera: &'a wgpu::BindGroup, light: &'a wgpu::BindGroup, ); fn draw_light_mesh_instanced( &mut self, mesh: &'a Mesh, instances: Range, camera: &'a wgpu::BindGroup, light: &'a wgpu::BindGroup, ); fn draw_light_model( &mut self, model: &'a Model, camera: &'a wgpu::BindGroup, light: &'a wgpu::BindGroup, ); fn draw_light_model_instanced( &mut self, model: &'a Model, instances: Range, camera: &'a wgpu::BindGroup, light: &'a wgpu::BindGroup, ); } impl<'a, 'b> DrawLight<'b> for wgpu::RenderPass<'a> where 'b: 'a, { fn draw_light_mesh( &mut self, mesh: &'b Mesh, camera: &'b wgpu::BindGroup, light: &'b wgpu::BindGroup, ) { self.draw_light_mesh_instanced(mesh, 0..1, camera, light); } fn draw_light_mesh_instanced( &mut self, mesh: &'b Mesh, instances: Range, camera: &'b wgpu::BindGroup, light: &'b wgpu::BindGroup, ) { self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32); self.set_bind_group(0, camera, &[]); self.set_bind_group(1, light, &[]); self.draw_indexed(0..mesh.num_elements, 0, instances); } fn draw_light_model( &mut self, model: &'b Model, camera: &'b wgpu::BindGroup, light: &'b wgpu::BindGroup, ) { self.draw_light_model_instanced(model, 0..1, camera, light); } fn draw_light_model_instanced( &mut self, model: &'b Model, instances: Range, camera: &'b wgpu::BindGroup, light: &'b wgpu::BindGroup, ) { for mesh in &model.meshes { self.draw_light_mesh_instanced(mesh, instances.clone(), camera, light); } } }