use std::{
path::{Path, PathBuf},
str::FromStr,
};
use wgpu::util::DeviceExt;
pub fn load_model(file_path: &str, state: &crate::state::State, position: glam::Vec3) -> Model {
let (models, materials) = tobj::load_obj(
file_path,
&tobj::LoadOptions {
triangulate: true,
single_index: true,
..Default::default()
},
)
.unwrap();
let materials = materials.unwrap();
let file_path = Path::new(file_path);
Model::new(
models,
file_path,
&state.device,
&state.queue,
materials,
position,
)
}
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vertex {
position: [f32; 3],
tex_coords: [f32; 2],
}
impl Vertex {
pub fn desc() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 1,
format: wgpu::VertexFormat::Float32x2,
},
],
}
}
}
#[derive(Debug)]
pub struct Model {
pub mesh: Vec<Mesh>,
pub material: Vec<Material>,
}
#[derive(Debug)]
pub struct Material {
pub name: String,
pub texture: crate::texture::Texture,
pub bind_group: wgpu::BindGroup,
}
#[derive(Debug)]
pub struct Mesh {
pub name: String,
pub vertex_buffer: wgpu::Buffer,
pub index_buffer: wgpu::Buffer,
pub num_elements: u32,
pub material: usize,
}
impl Model {
pub fn new(
models: Vec<tobj::Model>,
file_path: &Path,
device: &wgpu::Device,
queue: &wgpu::Queue,
model_materials: Vec<tobj::Material>,
position: glam::Vec3,
) -> Self {
let mut materials = Vec::new();
for m in model_materials {
let texture_filename = m.diffuse_texture.unwrap();
let texture_path = PathBuf::from_str(&texture_filename).unwrap();
let image_bytes = std::fs::read(
file_path
.parent()
.unwrap()
.join(texture_path.file_name().unwrap()),
)
.expect("Failed to read file");
let texture =
crate::texture::Texture::from_bytes(device, queue, &image_bytes, "Texture");
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Texture Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Texture Bind Group"),
layout: &texture_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&texture.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&texture.sampler),
},
],
});
materials.push(Material {
name: m.name,
texture,
bind_group,
});
}
let meshes = models
.into_iter()
.map(|m| {
let vertices = (0..m.mesh.positions.len() / 3)
.map(|i| Vertex {
position: [
m.mesh.positions[i * 3] + position.x,
m.mesh.positions[i * 3 + 1] + position.y,
m.mesh.positions[i * 3 + 2] + position.z,
],
tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]],
})
.collect::<Vec<_>>();
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", file_path.to_str().unwrap())),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", file_path.to_str().unwrap())),
contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsages::INDEX,
});
Mesh {
name: file_path.to_str().unwrap().to_string(),
vertex_buffer,
index_buffer,
num_elements: m.mesh.indices.len() as u32,
material: m.mesh.material_id.unwrap_or(0),
}
})
.collect::<Vec<_>>();
Model {
mesh: meshes,
material: materials,
}
}
}