1use glam::{Vec3, Vec4};
4use wgpu::util::DeviceExt;
5
6#[repr(C)]
10#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
11#[allow(clippy::pub_underscore_fields)]
12pub struct MeshUniforms {
13 pub model_matrix: [[f32; 4]; 4],
15 pub shade_style: u32,
17 pub show_edges: u32,
19 pub edge_width: f32,
21 pub transparency: f32,
23 pub surface_color: [f32; 4],
25 pub edge_color: [f32; 4],
27 pub backface_policy: u32,
29 pub slice_planes_enabled: u32,
31 pub use_vertex_color: u32,
33 pub _pad1: f32,
35 pub _pad2: [f32; 3],
37 pub _pad3: f32,
39 pub backface_color: [f32; 4],
41}
42
43#[repr(C)]
46#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
47pub struct ShadowModelUniforms {
48 pub model: [[f32; 4]; 4],
50}
51
52impl Default for ShadowModelUniforms {
53 fn default() -> Self {
54 Self {
55 model: [
56 [1.0, 0.0, 0.0, 0.0],
57 [0.0, 1.0, 0.0, 0.0],
58 [0.0, 0.0, 1.0, 0.0],
59 [0.0, 0.0, 0.0, 1.0],
60 ],
61 }
62 }
63}
64
65impl Default for MeshUniforms {
66 fn default() -> Self {
67 Self {
68 model_matrix: [
69 [1.0, 0.0, 0.0, 0.0],
70 [0.0, 1.0, 0.0, 0.0],
71 [0.0, 0.0, 1.0, 0.0],
72 [0.0, 0.0, 0.0, 1.0],
73 ],
74 shade_style: 0, show_edges: 0, edge_width: 1.0, transparency: 0.0, surface_color: [0.5, 0.5, 0.5, 1.0], edge_color: [0.0, 0.0, 0.0, 1.0], backface_policy: 0, slice_planes_enabled: 1,
82 use_vertex_color: 0,
83 _pad1: 0.0,
84 _pad2: [0.0; 3],
85 _pad3: 0.0,
86 backface_color: [0.3, 0.3, 0.3, 1.0], }
88 }
89}
90
91pub struct SurfaceMeshRenderData {
93 pub vertex_buffer: wgpu::Buffer,
95 pub index_buffer: wgpu::Buffer,
97 pub normal_buffer: wgpu::Buffer,
99 pub barycentric_buffer: wgpu::Buffer,
102 pub color_buffer: wgpu::Buffer,
104 pub edge_is_real_buffer: wgpu::Buffer,
106 pub uniform_buffer: wgpu::Buffer,
108 pub bind_group: wgpu::BindGroup,
110 pub num_triangles: u32,
112 pub num_indices: u32,
114 pub shadow_bind_group: Option<wgpu::BindGroup>,
116 pub shadow_model_buffer: Option<wgpu::Buffer>,
118}
119
120impl SurfaceMeshRenderData {
121 #[must_use]
132 pub fn new(
133 device: &wgpu::Device,
134 bind_group_layout: &wgpu::BindGroupLayout,
135 camera_buffer: &wgpu::Buffer,
136 vertices: &[Vec3],
137 triangles: &[[u32; 3]],
138 vertex_normals: &[Vec3],
139 edge_is_real: &[Vec3],
140 ) -> Self {
141 let num_triangles = triangles.len() as u32;
142 let num_indices = num_triangles * 3;
143
144 let mut expanded_positions: Vec<f32> = Vec::with_capacity(triangles.len() * 3 * 4);
148 let mut expanded_normals: Vec<f32> = Vec::with_capacity(triangles.len() * 3 * 4);
149 let mut expanded_colors: Vec<f32> = Vec::with_capacity(triangles.len() * 3 * 4);
150 let mut barycentric_data: Vec<f32> = Vec::with_capacity(triangles.len() * 3 * 4);
151 let mut edge_is_real_data: Vec<f32> = Vec::with_capacity(triangles.len() * 3 * 4);
152
153 let mut tri_vertex_idx = 0;
154 for tri in triangles {
155 let bary_coords = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
157
158 for (i, &vi) in tri.iter().enumerate() {
159 let v = vertices[vi as usize];
160 expanded_positions.extend_from_slice(&[v.x, v.y, v.z, 1.0]);
161
162 let n = vertex_normals[vi as usize];
163 expanded_normals.extend_from_slice(&[n.x, n.y, n.z, 0.0]);
164
165 expanded_colors.extend_from_slice(&[0.0, 0.0, 0.0, 0.0]);
167
168 barycentric_data.extend_from_slice(&[
169 bary_coords[i][0],
170 bary_coords[i][1],
171 bary_coords[i][2],
172 0.0,
173 ]);
174
175 let eir = edge_is_real[tri_vertex_idx];
177 edge_is_real_data.extend_from_slice(&[eir.x, eir.y, eir.z, 0.0]);
178
179 tri_vertex_idx += 1;
180 }
181 }
182
183 let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
185 label: Some("mesh vertices"),
186 contents: bytemuck::cast_slice(&expanded_positions),
187 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
188 });
189
190 let index_data: Vec<u32> = (0..num_indices).collect();
192 let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
193 label: Some("mesh indices"),
194 contents: bytemuck::cast_slice(&index_data),
195 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
196 });
197
198 let normal_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
200 label: Some("mesh normals"),
201 contents: bytemuck::cast_slice(&expanded_normals),
202 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
203 });
204
205 let barycentric_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
207 label: Some("mesh barycentrics"),
208 contents: bytemuck::cast_slice(&barycentric_data),
209 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
210 });
211
212 let color_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
214 label: Some("mesh colors"),
215 contents: bytemuck::cast_slice(&expanded_colors),
216 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
217 });
218
219 let edge_is_real_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
221 label: Some("mesh edge_is_real"),
222 contents: bytemuck::cast_slice(&edge_is_real_data),
223 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
224 });
225
226 let uniforms = MeshUniforms::default();
228 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
229 label: Some("mesh uniforms"),
230 contents: bytemuck::cast_slice(&[uniforms]),
231 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
232 });
233
234 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
244 label: Some("surface mesh bind group"),
245 layout: bind_group_layout,
246 entries: &[
247 wgpu::BindGroupEntry {
248 binding: 0,
249 resource: camera_buffer.as_entire_binding(),
250 },
251 wgpu::BindGroupEntry {
252 binding: 1,
253 resource: uniform_buffer.as_entire_binding(),
254 },
255 wgpu::BindGroupEntry {
256 binding: 2,
257 resource: vertex_buffer.as_entire_binding(),
258 },
259 wgpu::BindGroupEntry {
260 binding: 3,
261 resource: normal_buffer.as_entire_binding(),
262 },
263 wgpu::BindGroupEntry {
264 binding: 4,
265 resource: barycentric_buffer.as_entire_binding(),
266 },
267 wgpu::BindGroupEntry {
268 binding: 5,
269 resource: color_buffer.as_entire_binding(),
270 },
271 wgpu::BindGroupEntry {
272 binding: 6,
273 resource: edge_is_real_buffer.as_entire_binding(),
274 },
275 ],
276 });
277
278 Self {
279 vertex_buffer,
280 index_buffer,
281 normal_buffer,
282 barycentric_buffer,
283 color_buffer,
284 edge_is_real_buffer,
285 uniform_buffer,
286 bind_group,
287 num_triangles,
288 num_indices,
289 shadow_bind_group: None,
290 shadow_model_buffer: None,
291 }
292 }
293
294 pub fn update_uniforms(&self, queue: &wgpu::Queue, uniforms: &MeshUniforms) {
296 queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[*uniforms]));
297 }
298
299 pub fn update_colors(&self, queue: &wgpu::Queue, colors: &[Vec4], triangles: &[[u32; 3]]) {
302 let mut expanded_colors: Vec<f32> = Vec::with_capacity(triangles.len() * 3 * 4);
304 for tri in triangles {
305 for &vi in tri {
306 let c = colors[vi as usize];
307 expanded_colors.extend_from_slice(&[c.x, c.y, c.z, c.w]);
308 }
309 }
310 queue.write_buffer(
311 &self.color_buffer,
312 0,
313 bytemuck::cast_slice(&expanded_colors),
314 );
315 }
316
317 pub fn clear_colors(&self, queue: &wgpu::Queue) {
319 let zero_colors: Vec<f32> = vec![0.0; self.num_indices as usize * 4];
320 queue.write_buffer(&self.color_buffer, 0, bytemuck::cast_slice(&zero_colors));
321 }
322
323 pub fn init_shadow_resources(
329 &mut self,
330 device: &wgpu::Device,
331 shadow_bind_group_layout: &wgpu::BindGroupLayout,
332 light_buffer: &wgpu::Buffer,
333 ) {
334 let shadow_model_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
336 label: Some("Shadow Model Uniform Buffer"),
337 contents: bytemuck::cast_slice(&[ShadowModelUniforms::default()]),
338 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
339 });
340
341 let shadow_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
346 label: Some("Surface Mesh Shadow Bind Group"),
347 layout: shadow_bind_group_layout,
348 entries: &[
349 wgpu::BindGroupEntry {
350 binding: 0,
351 resource: light_buffer.as_entire_binding(),
352 },
353 wgpu::BindGroupEntry {
354 binding: 1,
355 resource: shadow_model_buffer.as_entire_binding(),
356 },
357 wgpu::BindGroupEntry {
358 binding: 2,
359 resource: self.vertex_buffer.as_entire_binding(),
360 },
361 ],
362 });
363
364 self.shadow_model_buffer = Some(shadow_model_buffer);
365 self.shadow_bind_group = Some(shadow_bind_group);
366 }
367
368 #[must_use]
370 pub fn has_shadow_resources(&self) -> bool {
371 self.shadow_bind_group.is_some()
372 }
373
374 #[must_use]
379 pub fn num_vertices(&self) -> u32 {
380 self.num_indices }
382
383 pub fn update_shadow_model(&self, queue: &wgpu::Queue, model_matrix: [[f32; 4]; 4]) {
388 if let Some(buffer) = &self.shadow_model_buffer {
389 let uniforms = ShadowModelUniforms {
390 model: model_matrix,
391 };
392 queue.write_buffer(buffer, 0, bytemuck::cast_slice(&[uniforms]));
393 }
394 }
395
396 #[must_use]
400 pub fn uniform_buffer(&self) -> &wgpu::Buffer {
401 &self.uniform_buffer
402 }
403
404 #[must_use]
406 pub fn position_buffer(&self) -> &wgpu::Buffer {
407 &self.vertex_buffer
408 }
409
410 #[must_use]
412 pub fn normal_buffer(&self) -> &wgpu::Buffer {
413 &self.normal_buffer
414 }
415
416 #[must_use]
418 pub fn barycentric_buffer(&self) -> &wgpu::Buffer {
419 &self.barycentric_buffer
420 }
421
422 #[must_use]
424 pub fn color_buffer(&self) -> &wgpu::Buffer {
425 &self.color_buffer
426 }
427
428 #[must_use]
430 pub fn edge_is_real_buffer(&self) -> &wgpu::Buffer {
431 &self.edge_is_real_buffer
432 }
433
434 #[must_use]
436 pub fn vertex_count(&self) -> u32 {
437 self.num_indices
438 }
439}
440
441#[cfg(test)]
442mod tests {
443 use super::*;
444
445 #[test]
446 fn test_mesh_uniforms_size() {
447 let size = std::mem::size_of::<MeshUniforms>();
448
449 assert_eq!(
451 size % 16,
452 0,
453 "MeshUniforms size ({} bytes) must be 16-byte aligned",
454 size
455 );
456
457 assert_eq!(
474 size, 160,
475 "MeshUniforms should be 160 bytes, got {} bytes",
476 size
477 );
478 }
479}