midpoint_ui/renderer/
Model.rs

1use nalgebra::{Matrix4, Point3, Vector3};
2use wgpu::util::DeviceExt;
3
4use gltf::buffer::{Source, View};
5use gltf::Glb;
6use gltf::Gltf;
7use std::sync::Arc;
8
9use crate::renderer::core::Vertex;
10use crate::renderer::Transform::{matrix4_to_raw_array, Transform};
11
12pub struct Mesh {
13    // pub transform: Matrix4<f32>,
14    pub transform: Transform,
15    pub vertex_buffer: wgpu::Buffer,
16    pub index_buffer: wgpu::Buffer,
17    pub index_count: u32,
18    pub bind_group: wgpu::BindGroup,
19    pub texture_bind_group: wgpu::BindGroup,
20}
21
22pub struct Model {
23    pub meshes: Vec<Mesh>,
24    // pub transform: Transform,
25}
26
27impl Model {
28    pub async fn from_glb(
29        bytes: &Vec<u8>,
30        device: &wgpu::Device,
31        queue: &wgpu::Queue,
32        bind_group_layout: &wgpu::BindGroupLayout,
33        texture_bind_group_layout: &wgpu::BindGroupLayout,
34        texture_render_mode_buffer: &wgpu::Buffer,
35        color_render_mode_buffer: &wgpu::Buffer,
36    ) -> Self {
37        // let response = reqwest::get(uri).await;
38        // let bytes = response
39        //     .expect("Response failed")
40        //     .bytes()
41        //     .await
42        //     .expect("Couldnt fetch bytes")
43        //     .to_vec();
44
45        web_sys::console::log_1(&format!("Bytes len: {:?}", bytes.len()).into());
46
47        let glb = Glb::from_slice(&bytes).expect("Couldn't create glb from slice");
48
49        let mut meshes = Vec::new();
50
51        let gltf = Gltf::from_slice(&glb.json).expect("Failed to parse GLTF JSON");
52
53        let buffer_data = match glb.bin {
54            Some(bin) => bin,
55            None => panic!("No binary data found in GLB file"),
56        };
57
58        let uses_textures = gltf.textures().len().gt(&0);
59
60        web_sys::console::log_1(&format!("Textures count: {:?}", gltf.textures().len()).into());
61
62        let mut textures = Vec::new();
63        for texture in gltf.textures() {
64            match texture.source().source() {
65                gltf::image::Source::View { view, mime_type: _ } => {
66                    let img_data = &buffer_data[view.offset()..view.offset() + view.length()];
67                    let img = image::load_from_memory(img_data).unwrap().to_rgba8();
68                    let (width, height) = img.dimensions();
69
70                    let size = wgpu::Extent3d {
71                        width,
72                        height,
73                        depth_or_array_layers: 1,
74                    };
75
76                    let texture = device.create_texture(&wgpu::TextureDescriptor {
77                        label: Some("GLB Texture"),
78                        size,
79                        mip_level_count: 1,
80                        sample_count: 1,
81                        dimension: wgpu::TextureDimension::D2,
82                        format: wgpu::TextureFormat::Rgba8UnormSrgb,
83                        usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
84                        view_formats: &[],
85                    });
86
87                    queue.write_texture(
88                        wgpu::ImageCopyTexture {
89                            texture: &texture,
90                            mip_level: 0,
91                            origin: wgpu::Origin3d::ZERO,
92                            aspect: wgpu::TextureAspect::All,
93                        },
94                        &img,
95                        wgpu::ImageDataLayout {
96                            offset: 0,
97                            bytes_per_row: Some(4 * width), // TODO: is this correct?
98                            rows_per_image: Some(height),
99                        },
100                        size,
101                    );
102
103                    let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
104                    let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
105                        address_mode_u: wgpu::AddressMode::ClampToEdge,
106                        address_mode_v: wgpu::AddressMode::ClampToEdge,
107                        address_mode_w: wgpu::AddressMode::ClampToEdge,
108                        mag_filter: wgpu::FilterMode::Linear,
109                        min_filter: wgpu::FilterMode::Linear,
110                        mipmap_filter: wgpu::FilterMode::Nearest,
111                        ..Default::default()
112                    });
113
114                    textures.push((texture_view, sampler));
115                }
116                gltf::image::Source::Uri { uri, mime_type: _ } => {
117                    panic!(
118                        "External URI image sources are not yet supported in glb files: {}",
119                        uri
120                    );
121                }
122            }
123        }
124
125        // Create a default empty texture and sampler
126        let default_texture = device.create_texture(&wgpu::TextureDescriptor {
127            label: Some("Default Empty Texture"),
128            size: wgpu::Extent3d {
129                width: 1,
130                height: 1,
131                depth_or_array_layers: 1,
132            },
133            mip_level_count: 1,
134            sample_count: 1,
135            dimension: wgpu::TextureDimension::D2,
136            format: wgpu::TextureFormat::Rgba8UnormSrgb,
137            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
138            view_formats: &[],
139        });
140
141        let default_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
142            address_mode_u: wgpu::AddressMode::ClampToEdge,
143            address_mode_v: wgpu::AddressMode::ClampToEdge,
144            address_mode_w: wgpu::AddressMode::ClampToEdge,
145            mag_filter: wgpu::FilterMode::Linear,
146            min_filter: wgpu::FilterMode::Linear,
147            mipmap_filter: wgpu::FilterMode::Nearest,
148            ..Default::default()
149        });
150
151        let default_texture_view =
152            default_texture.create_view(&wgpu::TextureViewDescriptor::default());
153
154        for mesh in gltf.meshes() {
155            for primitive in mesh.primitives() {
156                let reader = primitive.reader(|buffer| Some(&buffer_data));
157
158                let positions = reader
159                    .read_positions()
160                    .expect("Positions not existing in glb");
161                let colors = reader
162                    .read_colors(0)
163                    .map(|v| v.into_rgb_f32().collect())
164                    .unwrap_or_else(|| vec![[1.0, 1.0, 1.0]; positions.len()]);
165                let normals: Vec<[f32; 3]> = reader
166                    .read_normals()
167                    .map(|iter| iter.collect())
168                    .unwrap_or_else(|| vec![[0.0, 0.0, 1.0]; positions.len()]);
169                let tex_coords: Vec<[f32; 2]> = reader
170                    .read_tex_coords(0)
171                    .map(|v| v.into_f32().collect())
172                    .unwrap_or_else(|| vec![[0.0, 0.0]; positions.len()]);
173
174                let vertices: Vec<Vertex> = positions
175                    .zip(normals.iter())
176                    .zip(tex_coords.iter())
177                    .zip(colors.iter())
178                    .map(|(((p, n), t), c)| Vertex {
179                        position: p,
180                        normal: *n,
181                        tex_coords: *t,
182                        color: *c,
183                    })
184                    .collect();
185
186                let indices_u32: Vec<u32> = reader
187                    .read_indices()
188                    .map(|iter| iter.into_u32().collect())
189                    .unwrap_or_default();
190
191                let indices: Vec<u16> = indices_u32.iter().map(|&i| i as u16).collect();
192
193                web_sys::console::log_1(&format!("Model vertices: {:?}", vertices.len()).into());
194                web_sys::console::log_1(&format!("Model indices: {:?}", indices.len()).into());
195
196                let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
197                    label: Some("Model GLB Vertex Buffer"),
198                    contents: bytemuck::cast_slice(&vertices),
199                    usage: wgpu::BufferUsages::VERTEX,
200                });
201
202                let index_buffer: wgpu::Buffer =
203                    device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
204                        label: Some("Model GLB Index Buffer"),
205                        contents: bytemuck::cast_slice(&indices),
206                        usage: wgpu::BufferUsages::INDEX,
207                    });
208
209                let empty_buffer = Matrix4::<f32>::identity();
210                let raw_matrix = matrix4_to_raw_array(&empty_buffer);
211
212                let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
213                    label: Some("Model GLB Uniform Buffer"),
214                    contents: bytemuck::cast_slice(&raw_matrix),
215                    usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
216                });
217
218                let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
219                    layout: &bind_group_layout,
220                    entries: &[wgpu::BindGroupEntry {
221                        binding: 0,
222                        resource: uniform_buffer.as_entire_binding(),
223                    }],
224                    label: None,
225                });
226
227                let render_mode_buffer = if uses_textures {
228                    texture_render_mode_buffer
229                } else {
230                    color_render_mode_buffer
231                };
232
233                // Handle the texture bind group conditionally
234                let texture_bind_group = if uses_textures && !textures.is_empty() {
235                    let material = primitive.material();
236                    let texture_index = material
237                        .pbr_metallic_roughness()
238                        .base_color_texture()
239                        .map_or(0, |info| info.texture().index());
240                    let (texture_view, sampler) = &textures[texture_index];
241
242                    device.create_bind_group(&wgpu::BindGroupDescriptor {
243                        layout: &texture_bind_group_layout,
244                        entries: &[
245                            wgpu::BindGroupEntry {
246                                binding: 0,
247                                resource: wgpu::BindingResource::TextureView(texture_view),
248                            },
249                            wgpu::BindGroupEntry {
250                                binding: 1,
251                                resource: wgpu::BindingResource::Sampler(sampler),
252                            },
253                            wgpu::BindGroupEntry {
254                                binding: 2,
255                                resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
256                                    buffer: render_mode_buffer,
257                                    offset: 0,
258                                    size: None,
259                                }),
260                            },
261                        ],
262                        label: None,
263                    })
264                } else {
265                    device.create_bind_group(&wgpu::BindGroupDescriptor {
266                        layout: &texture_bind_group_layout,
267                        entries: &[
268                            wgpu::BindGroupEntry {
269                                binding: 0,
270                                resource: wgpu::BindingResource::TextureView(&default_texture_view),
271                            },
272                            wgpu::BindGroupEntry {
273                                binding: 1,
274                                resource: wgpu::BindingResource::Sampler(&default_sampler),
275                            },
276                            wgpu::BindGroupEntry {
277                                binding: 2,
278                                resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
279                                    buffer: render_mode_buffer,
280                                    offset: 0,
281                                    size: None,
282                                }),
283                            },
284                        ],
285                        label: None,
286                    })
287                };
288
289                meshes.push(Mesh {
290                    // transform: Matrix4::identity(),
291                    transform: Transform::new(
292                        Vector3::new(0.0, 0.0, 0.0),
293                        Vector3::new(0.0, 0.0, 0.0),
294                        Vector3::new(1.0, 1.0, 1.0),
295                        uniform_buffer,
296                    ),
297                    vertex_buffer,
298                    index_buffer,
299                    index_count: indices.len() as u32,
300                    bind_group,
301                    texture_bind_group,
302                });
303            }
304        }
305
306        Model { meshes }
307    }
308}