1use std::{
2 path::{Path, PathBuf},
3 str::FromStr,
4};
5use wgpu::util::DeviceExt;
6
7pub fn load_model(file_path: &str, state: &crate::state::State, position: glam::Vec3) -> Model {
8 let (models, materials) = tobj::load_obj(
9 file_path,
10 &tobj::LoadOptions {
11 triangulate: true,
12 single_index: true,
13 ..Default::default()
14 },
15 )
16 .unwrap();
17 let materials = materials.unwrap();
18
19 let file_path = Path::new(file_path);
20 Model::new(
21 models,
22 file_path,
23 &state.device,
24 &state.queue,
25 materials,
26 position,
27 )
28}
29
30#[repr(C)]
31#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
32pub struct Vertex {
33 position: [f32; 3],
34 tex_coords: [f32; 2],
35}
36
37impl Vertex {
38 pub fn desc() -> wgpu::VertexBufferLayout<'static> {
39 wgpu::VertexBufferLayout {
40 array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
41 step_mode: wgpu::VertexStepMode::Vertex,
42 attributes: &[
43 wgpu::VertexAttribute {
44 offset: 0,
45 shader_location: 0,
46 format: wgpu::VertexFormat::Float32x3,
47 },
48 wgpu::VertexAttribute {
49 offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
50 shader_location: 1,
51 format: wgpu::VertexFormat::Float32x2,
52 },
53 ],
54 }
55 }
56}
57
58#[derive(Debug)]
59pub struct Model {
60 pub mesh: Vec<Mesh>,
61 pub material: Vec<Material>,
62}
63
64#[derive(Debug)]
65pub struct Material {
66 pub name: String,
67 pub texture: crate::texture::Texture,
68 pub bind_group: wgpu::BindGroup,
69}
70
71#[derive(Debug)]
72pub struct Mesh {
73 pub name: String,
74 pub vertex_buffer: wgpu::Buffer,
75 pub index_buffer: wgpu::Buffer,
76 pub num_elements: u32,
77 pub material: usize,
78}
79
80impl Model {
81 pub fn new(
82 models: Vec<tobj::Model>,
83 file_path: &Path,
84 device: &wgpu::Device,
85 queue: &wgpu::Queue,
86 model_materials: Vec<tobj::Material>,
87 position: glam::Vec3,
88 ) -> Self {
89 let mut materials = Vec::new();
90 for m in model_materials {
91 let texture_filename = m.diffuse_texture.unwrap();
92 let texture_path = PathBuf::from_str(&texture_filename).unwrap();
93
94 let image_bytes = std::fs::read(
95 file_path
96 .parent()
97 .unwrap()
98 .join(texture_path.file_name().unwrap()),
99 )
100 .expect("Failed to read file");
101
102 let texture =
103 crate::texture::Texture::from_bytes(device, queue, &image_bytes, "Texture");
104
105 let texture_bind_group_layout =
106 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
107 label: Some("Texture Bind Group Layout"),
108 entries: &[
109 wgpu::BindGroupLayoutEntry {
110 binding: 0,
111 visibility: wgpu::ShaderStages::FRAGMENT,
112 ty: wgpu::BindingType::Texture {
113 sample_type: wgpu::TextureSampleType::Float { filterable: true },
114 view_dimension: wgpu::TextureViewDimension::D2,
115 multisampled: false,
116 },
117 count: None,
118 },
119 wgpu::BindGroupLayoutEntry {
120 binding: 1,
121 visibility: wgpu::ShaderStages::FRAGMENT,
122 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
123 count: None,
124 },
125 ],
126 });
127
128 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
129 label: Some("Texture Bind Group"),
130 layout: &texture_bind_group_layout,
131 entries: &[
132 wgpu::BindGroupEntry {
133 binding: 0,
134 resource: wgpu::BindingResource::TextureView(&texture.view),
135 },
136 wgpu::BindGroupEntry {
137 binding: 1,
138 resource: wgpu::BindingResource::Sampler(&texture.sampler),
139 },
140 ],
141 });
142
143 materials.push(Material {
144 name: m.name,
145 texture,
146 bind_group,
147 });
148 }
149
150 let meshes = models
151 .into_iter()
152 .map(|m| {
153 let vertices = (0..m.mesh.positions.len() / 3)
154 .map(|i| Vertex {
155 position: [
156 m.mesh.positions[i * 3] + position.x,
157 m.mesh.positions[i * 3 + 1] + position.y,
158 m.mesh.positions[i * 3 + 2] + position.z,
159 ],
160 tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]],
161 })
162 .collect::<Vec<_>>();
163
164 let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
165 label: Some(&format!("{:?} Vertex Buffer", file_path.to_str().unwrap())),
166 contents: bytemuck::cast_slice(&vertices),
167 usage: wgpu::BufferUsages::VERTEX,
168 });
169 let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
170 label: Some(&format!("{:?} Index Buffer", file_path.to_str().unwrap())),
171 contents: bytemuck::cast_slice(&m.mesh.indices),
172 usage: wgpu::BufferUsages::INDEX,
173 });
174
175 Mesh {
176 name: file_path.to_str().unwrap().to_string(),
177 vertex_buffer,
178 index_buffer,
179 num_elements: m.mesh.indices.len() as u32,
180 material: m.mesh.material_id.unwrap_or(0),
181 }
182 })
183 .collect::<Vec<_>>();
184
185 Model {
186 mesh: meshes,
187 material: materials,
188 }
189 }
190}