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: 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 }
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 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), 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 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 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: 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}