miye/
models.rs

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}