1use super::*;
2
3pub(super) fn generate_edge_indices(triangle_indices: &[u32]) -> Vec<u32> {
4 use std::collections::HashSet;
5 let mut edges: HashSet<(u32, u32)> = HashSet::new();
6 let mut result = Vec::new();
7
8 for tri in triangle_indices.chunks(3) {
9 if tri.len() < 3 {
10 continue;
11 }
12 let pairs = [(tri[0], tri[1]), (tri[1], tri[2]), (tri[2], tri[0])];
13 for (a, b) in &pairs {
14 let edge = if a < b { (*a, *b) } else { (*b, *a) };
16 if edges.insert(edge) {
17 result.push(*a);
18 result.push(*b);
19 }
20 }
21 }
22 result
23}
24
25pub(super) fn build_unit_cube() -> (Vec<Vertex>, Vec<u32>) {
34 let white = [1.0f32, 1.0, 1.0, 1.0];
35 let mut verts: Vec<Vertex> = Vec::with_capacity(24);
36 let mut idx: Vec<u32> = Vec::with_capacity(36);
37
38 let mut add_face = |positions: [[f32; 3]; 4], normal: [f32; 3]| {
40 let base = verts.len() as u32;
41 for pos in &positions {
42 verts.push(Vertex {
43 position: *pos,
44 normal,
45 colour: white,
46 uv: [0.0, 0.0],
47 tangent: [0.0, 0.0, 0.0, 1.0],
48 });
49 }
50 idx.extend_from_slice(&[base, base + 1, base + 2, base, base + 2, base + 3]);
52 };
53
54 add_face(
56 [
57 [0.5, -0.5, -0.5],
58 [0.5, 0.5, -0.5],
59 [0.5, 0.5, 0.5],
60 [0.5, -0.5, 0.5],
61 ],
62 [1.0, 0.0, 0.0],
63 );
64
65 add_face(
67 [
68 [-0.5, -0.5, 0.5],
69 [-0.5, 0.5, 0.5],
70 [-0.5, 0.5, -0.5],
71 [-0.5, -0.5, -0.5],
72 ],
73 [-1.0, 0.0, 0.0],
74 );
75
76 add_face(
78 [
79 [-0.5, 0.5, -0.5],
80 [-0.5, 0.5, 0.5],
81 [0.5, 0.5, 0.5],
82 [0.5, 0.5, -0.5],
83 ],
84 [0.0, 1.0, 0.0],
85 );
86
87 add_face(
89 [
90 [-0.5, -0.5, 0.5],
91 [-0.5, -0.5, -0.5],
92 [0.5, -0.5, -0.5],
93 [0.5, -0.5, 0.5],
94 ],
95 [0.0, -1.0, 0.0],
96 );
97
98 add_face(
100 [
101 [-0.5, -0.5, 0.5],
102 [0.5, -0.5, 0.5],
103 [0.5, 0.5, 0.5],
104 [-0.5, 0.5, 0.5],
105 ],
106 [0.0, 0.0, 1.0],
107 );
108
109 add_face(
111 [
112 [0.5, -0.5, -0.5],
113 [-0.5, -0.5, -0.5],
114 [-0.5, 0.5, -0.5],
115 [0.5, 0.5, -0.5],
116 ],
117 [0.0, 0.0, -1.0],
118 );
119
120 (verts, idx)
121}
122
123pub(super) fn build_glyph_arrow() -> (Vec<Vertex>, Vec<u32>) {
135 let white = [1.0f32, 1.0, 1.0, 1.0];
136 let segments = 16usize;
137 let mut verts: Vec<Vertex> = Vec::new();
138 let mut idx: Vec<u32> = Vec::new();
139
140 let shaft_r = 0.05f32;
141 let shaft_bot = 0.0f32;
142 let shaft_top = 0.7f32;
143 let cone_r = 0.12f32;
144 let cone_bot = shaft_top;
145 let cone_tip = 1.0f32;
146
147 let ring_verts = |verts: &mut Vec<Vertex>, z: f32, r: f32, normal_z: f32| {
149 for i in 0..segments {
150 let angle = 2.0 * std::f32::consts::PI * (i as f32) / (segments as f32);
151 let (s, c) = angle.sin_cos();
152 let nx = if r > 0.0 { c } else { 0.0 };
153 let ny = if r > 0.0 { s } else { 0.0 };
154 let len = (nx * nx + ny * ny + normal_z * normal_z).sqrt();
155 verts.push(Vertex {
156 position: [c * r, s * r, z],
157 normal: [nx / len, ny / len, normal_z / len],
158 colour: white,
159 uv: [0.0, 0.0],
160 tangent: [0.0, 0.0, 0.0, 1.0],
161 });
162 }
163 };
164
165 let shaft_bot_base = verts.len() as u32;
168 ring_verts(&mut verts, shaft_bot, shaft_r, 0.0);
169
170 let shaft_bot_center = verts.len() as u32;
172 verts.push(Vertex {
173 position: [0.0, 0.0, shaft_bot],
174 normal: [0.0, 0.0, -1.0],
175 colour: white,
176 uv: [0.0, 0.0],
177 tangent: [0.0, 0.0, 0.0, 1.0],
178 });
179
180 for i in 0..segments {
182 let a = shaft_bot_base + i as u32;
183 let b = shaft_bot_base + ((i + 1) % segments) as u32;
184 idx.extend_from_slice(&[shaft_bot_center, b, a]);
185 }
186
187 let shaft_top_ring_base = verts.len() as u32;
189 ring_verts(&mut verts, shaft_bot, shaft_r, 0.0); let shaft_top_ring_top = verts.len() as u32;
191 ring_verts(&mut verts, shaft_top, shaft_r, 0.0);
192 for i in 0..segments {
193 let a = shaft_top_ring_base + i as u32;
194 let b = shaft_top_ring_base + ((i + 1) % segments) as u32;
195 let c = shaft_top_ring_top + i as u32;
196 let d = shaft_top_ring_top + ((i + 1) % segments) as u32;
197 idx.extend_from_slice(&[a, b, d, a, d, c]);
198 }
199
200 let cone_len = ((cone_tip - cone_bot).powi(2) + cone_r * cone_r).sqrt();
203 let normal_z_cone = cone_r / cone_len; let normal_r_cone = (cone_tip - cone_bot) / cone_len;
205
206 let cone_base_ring = verts.len() as u32;
207 for i in 0..segments {
208 let angle = 2.0 * std::f32::consts::PI * (i as f32) / (segments as f32);
209 let (s, c) = angle.sin_cos();
210 verts.push(Vertex {
211 position: [c * cone_r, s * cone_r, cone_bot],
212 normal: [c * normal_r_cone, s * normal_r_cone, normal_z_cone],
213 colour: white,
214 uv: [0.0, 0.0],
215 tangent: [0.0, 0.0, 0.0, 1.0],
216 });
217 }
218
219 let cone_tip_v = verts.len() as u32;
221 verts.push(Vertex {
222 position: [0.0, 0.0, cone_tip],
223 normal: [0.0, 0.0, 1.0],
224 colour: white,
225 uv: [0.0, 0.0],
226 tangent: [0.0, 0.0, 0.0, 1.0],
227 });
228
229 for i in 0..segments {
230 let a = cone_base_ring + i as u32;
231 let b = cone_base_ring + ((i + 1) % segments) as u32;
232 idx.extend_from_slice(&[a, b, cone_tip_v]);
233 }
234
235 let cone_cap_base = verts.len() as u32;
237 for i in 0..segments {
238 let angle = 2.0 * std::f32::consts::PI * (i as f32) / (segments as f32);
239 let (s, c) = angle.sin_cos();
240 verts.push(Vertex {
241 position: [c * cone_r, s * cone_r, cone_bot],
242 normal: [0.0, 0.0, -1.0],
243 colour: white,
244 uv: [0.0, 0.0],
245 tangent: [0.0, 0.0, 0.0, 1.0],
246 });
247 }
248 let cone_cap_center = verts.len() as u32;
249 verts.push(Vertex {
250 position: [0.0, 0.0, cone_bot],
251 normal: [0.0, 0.0, -1.0],
252 colour: white,
253 uv: [0.0, 0.0],
254 tangent: [0.0, 0.0, 0.0, 1.0],
255 });
256 for i in 0..segments {
257 let a = cone_cap_base + i as u32;
258 let b = cone_cap_base + ((i + 1) % segments) as u32;
259 idx.extend_from_slice(&[cone_cap_center, b, a]);
260 }
261
262 (verts, idx)
263}
264
265pub(super) fn build_glyph_sphere() -> (Vec<Vertex>, Vec<u32>) {
273 let white = [1.0f32, 1.0, 1.0, 1.0];
274
275 let t = (1.0 + 5.0f32.sqrt()) / 2.0;
277
278 let raw_verts = [
280 [-1.0, t, 0.0],
281 [1.0, t, 0.0],
282 [-1.0, -t, 0.0],
283 [1.0, -t, 0.0],
284 [0.0, -1.0, t],
285 [0.0, 1.0, t],
286 [0.0, -1.0, -t],
287 [0.0, 1.0, -t],
288 [t, 0.0, -1.0],
289 [t, 0.0, 1.0],
290 [-t, 0.0, -1.0],
291 [-t, 0.0, 1.0],
292 ];
293
294 let mut positions: Vec<[f32; 3]> = raw_verts
295 .iter()
296 .map(|v| {
297 let l = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
298 [v[0] / l, v[1] / l, v[2] / l]
299 })
300 .collect();
301
302 let mut triangles: Vec<[usize; 3]> = vec![
304 [0, 11, 5],
305 [0, 5, 1],
306 [0, 1, 7],
307 [0, 7, 10],
308 [0, 10, 11],
309 [1, 5, 9],
310 [5, 11, 4],
311 [11, 10, 2],
312 [10, 7, 6],
313 [7, 1, 8],
314 [3, 9, 4],
315 [3, 4, 2],
316 [3, 2, 6],
317 [3, 6, 8],
318 [3, 8, 9],
319 [4, 9, 5],
320 [2, 4, 11],
321 [6, 2, 10],
322 [8, 6, 7],
323 [9, 8, 1],
324 ];
325
326 for _ in 0..2 {
328 let mut mid_cache: std::collections::HashMap<(usize, usize), usize> =
329 std::collections::HashMap::new();
330 let mut new_triangles: Vec<[usize; 3]> = Vec::with_capacity(triangles.len() * 4);
331
332 let midpoint = |positions: &mut Vec<[f32; 3]>,
333 a: usize,
334 b: usize,
335 cache: &mut std::collections::HashMap<(usize, usize), usize>|
336 -> usize {
337 let key = if a < b { (a, b) } else { (b, a) };
338 if let Some(&idx) = cache.get(&key) {
339 return idx;
340 }
341 let pa = positions[a];
342 let pb = positions[b];
343 let mx = (pa[0] + pb[0]) * 0.5;
344 let my = (pa[1] + pb[1]) * 0.5;
345 let mz = (pa[2] + pb[2]) * 0.5;
346 let l = (mx * mx + my * my + mz * mz).sqrt();
347 let idx = positions.len();
348 positions.push([mx / l, my / l, mz / l]);
349 cache.insert(key, idx);
350 idx
351 };
352
353 for tri in &triangles {
354 let a = tri[0];
355 let b = tri[1];
356 let c = tri[2];
357 let ab = midpoint(&mut positions, a, b, &mut mid_cache);
358 let bc = midpoint(&mut positions, b, c, &mut mid_cache);
359 let ca = midpoint(&mut positions, c, a, &mut mid_cache);
360 new_triangles.push([a, ab, ca]);
361 new_triangles.push([b, bc, ab]);
362 new_triangles.push([c, ca, bc]);
363 new_triangles.push([ab, bc, ca]);
364 }
365 triangles = new_triangles;
366 }
367
368 let verts: Vec<Vertex> = positions
369 .iter()
370 .map(|&p| Vertex {
371 position: p,
372 normal: p, colour: white,
374 uv: [0.0, 0.0],
375 tangent: [0.0, 0.0, 0.0, 1.0],
376 })
377 .collect();
378
379 let idx: Vec<u32> = triangles
380 .iter()
381 .flat_map(|t| [t[0] as u32, t[1] as u32, t[2] as u32])
382 .collect();
383
384 (verts, idx)
385}
386
387impl ViewportGpuResources {
396 pub fn replace_attribute(
409 &mut self,
410 queue: &wgpu::Queue,
411 mesh_id: crate::resources::mesh_store::MeshId,
412 name: &str,
413 data: &[f32],
414 ) -> crate::error::ViewportResult<()> {
415 let gpu_mesh =
417 self.mesh_store
418 .get_mut(mesh_id)
419 .ok_or(crate::error::ViewportError::MeshSlotEmpty {
420 index: mesh_id.index(),
421 })?;
422
423 let buffer = gpu_mesh.attribute_buffers.get(name).ok_or_else(|| {
425 crate::error::ViewportError::AttributeNotFound {
426 mesh_id: mesh_id.index(),
427 name: name.to_string(),
428 }
429 })?;
430
431 let expected_elems = (buffer.size() / 4) as usize;
433 if data.len() != expected_elems {
434 return Err(crate::error::ViewportError::AttributeLengthMismatch {
435 expected: expected_elems,
436 got: data.len(),
437 });
438 }
439
440 queue.write_buffer(buffer, 0, bytemuck::cast_slice(data));
442
443 let (min, max) = data
445 .iter()
446 .fold((f32::MAX, f32::MIN), |(mn, mx), &v| (mn.min(v), mx.max(v)));
447 let range = if min > max { (0.0, 1.0) } else { (min, max) };
448 gpu_mesh.attribute_ranges.insert(name.to_string(), range);
449
450 gpu_mesh.last_tex_key = (
452 gpu_mesh.last_tex_key.0,
453 gpu_mesh.last_tex_key.1,
454 gpu_mesh.last_tex_key.2,
455 gpu_mesh.last_tex_key.3,
456 u64::MAX, gpu_mesh.last_tex_key.5,
458 gpu_mesh.last_tex_key.6,
459 gpu_mesh.last_tex_key.7,
460 gpu_mesh.last_tex_key.8,
461 gpu_mesh.last_tex_key.9,
462 gpu_mesh.last_tex_key.10,
463 );
464
465 Ok(())
466 }
467
468 pub(crate) fn create_camera_bind_group(
477 &self,
478 device: &wgpu::Device,
479 camera_buf: &wgpu::Buffer,
480 clip_planes_buf: &wgpu::Buffer,
481 shadow_info_buf: &wgpu::Buffer,
482 clip_volume_buf: &wgpu::Buffer,
483 debug_frag_buf: &wgpu::Buffer,
484 label: &str,
485 ) -> wgpu::BindGroup {
486 let irr = self
487 .ibl_irradiance_view
488 .as_ref()
489 .unwrap_or(&self.ibl_fallback_view);
490 let spec = self
491 .ibl_prefiltered_view
492 .as_ref()
493 .unwrap_or(&self.ibl_fallback_view);
494 let brdf = self
495 .ibl_brdf_lut_view
496 .as_ref()
497 .unwrap_or(&self.ibl_fallback_brdf_view);
498 let skybox = self
499 .ibl_skybox_view
500 .as_ref()
501 .unwrap_or(&self.ibl_fallback_view);
502
503 device.create_bind_group(&wgpu::BindGroupDescriptor {
504 label: Some(label),
505 layout: &self.camera_bind_group_layout,
506 entries: &[
507 wgpu::BindGroupEntry {
508 binding: 0,
509 resource: camera_buf.as_entire_binding(),
510 },
511 wgpu::BindGroupEntry {
512 binding: 1,
513 resource: wgpu::BindingResource::TextureView(&self.shadow_map_view),
514 },
515 wgpu::BindGroupEntry {
516 binding: 2,
517 resource: wgpu::BindingResource::Sampler(&self.shadow_sampler),
518 },
519 wgpu::BindGroupEntry {
520 binding: 3,
521 resource: self.light_uniform_buf.as_entire_binding(),
522 },
523 wgpu::BindGroupEntry {
524 binding: 4,
525 resource: clip_planes_buf.as_entire_binding(),
526 },
527 wgpu::BindGroupEntry {
528 binding: 5,
529 resource: shadow_info_buf.as_entire_binding(),
530 },
531 wgpu::BindGroupEntry {
532 binding: 6,
533 resource: clip_volume_buf.as_entire_binding(),
534 },
535 wgpu::BindGroupEntry {
536 binding: 7,
537 resource: wgpu::BindingResource::TextureView(irr),
538 },
539 wgpu::BindGroupEntry {
540 binding: 8,
541 resource: wgpu::BindingResource::TextureView(spec),
542 },
543 wgpu::BindGroupEntry {
544 binding: 9,
545 resource: wgpu::BindingResource::TextureView(brdf),
546 },
547 wgpu::BindGroupEntry {
548 binding: 10,
549 resource: wgpu::BindingResource::Sampler(&self.ibl_sampler),
550 },
551 wgpu::BindGroupEntry {
552 binding: 11,
553 resource: wgpu::BindingResource::TextureView(skybox),
554 },
555 wgpu::BindGroupEntry {
556 binding: 12,
557 resource: debug_frag_buf.as_entire_binding(),
558 },
559 wgpu::BindGroupEntry {
560 binding: 13,
561 resource: self.light_storage_buf.as_entire_binding(),
562 },
563 wgpu::BindGroupEntry {
564 binding: 14,
565 resource: self.clustered.grid_uniform_buf.as_entire_binding(),
566 },
567 wgpu::BindGroupEntry {
568 binding: 15,
569 resource: self.clustered.cluster_grid_buf.as_entire_binding(),
570 },
571 wgpu::BindGroupEntry {
572 binding: 16,
573 resource: self.clustered.light_index_buf.as_entire_binding(),
574 },
575 wgpu::BindGroupEntry {
576 binding: 17,
577 resource: wgpu::BindingResource::TextureView(&self.point_shadow_cube_view),
578 },
579 ],
580 })
581 }
582}
583
584pub struct ComputeFilterResult {
593 pub index_buffer: wgpu::Buffer,
595 pub index_count: u32,
597 pub mesh_id: crate::resources::mesh_store::MeshId,
599}
600
601impl ViewportGpuResources {
602 fn ensure_compute_filter_pipeline(&mut self, device: &wgpu::Device) {
604 if self.compute_filter_pipeline.is_some() {
605 return;
606 }
607
608 let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
610 label: Some("compute_filter_bgl"),
611 entries: &[
612 wgpu::BindGroupLayoutEntry {
614 binding: 0,
615 visibility: wgpu::ShaderStages::COMPUTE,
616 ty: wgpu::BindingType::Buffer {
617 ty: wgpu::BufferBindingType::Uniform,
618 has_dynamic_offset: false,
619 min_binding_size: None,
620 },
621 count: None,
622 },
623 wgpu::BindGroupLayoutEntry {
625 binding: 1,
626 visibility: wgpu::ShaderStages::COMPUTE,
627 ty: wgpu::BindingType::Buffer {
628 ty: wgpu::BufferBindingType::Storage { read_only: true },
629 has_dynamic_offset: false,
630 min_binding_size: None,
631 },
632 count: None,
633 },
634 wgpu::BindGroupLayoutEntry {
636 binding: 2,
637 visibility: wgpu::ShaderStages::COMPUTE,
638 ty: wgpu::BindingType::Buffer {
639 ty: wgpu::BufferBindingType::Storage { read_only: true },
640 has_dynamic_offset: false,
641 min_binding_size: None,
642 },
643 count: None,
644 },
645 wgpu::BindGroupLayoutEntry {
647 binding: 3,
648 visibility: wgpu::ShaderStages::COMPUTE,
649 ty: wgpu::BindingType::Buffer {
650 ty: wgpu::BufferBindingType::Storage { read_only: true },
651 has_dynamic_offset: false,
652 min_binding_size: None,
653 },
654 count: None,
655 },
656 wgpu::BindGroupLayoutEntry {
658 binding: 4,
659 visibility: wgpu::ShaderStages::COMPUTE,
660 ty: wgpu::BindingType::Buffer {
661 ty: wgpu::BufferBindingType::Storage { read_only: false },
662 has_dynamic_offset: false,
663 min_binding_size: None,
664 },
665 count: None,
666 },
667 wgpu::BindGroupLayoutEntry {
669 binding: 5,
670 visibility: wgpu::ShaderStages::COMPUTE,
671 ty: wgpu::BindingType::Buffer {
672 ty: wgpu::BufferBindingType::Storage { read_only: false },
673 has_dynamic_offset: false,
674 min_binding_size: None,
675 },
676 count: None,
677 },
678 ],
679 });
680
681 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
682 label: Some("compute_filter_layout"),
683 bind_group_layouts: &[&bgl],
684 push_constant_ranges: &[],
685 });
686
687 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
688 label: Some("compute_filter_shader"),
689 source: wgpu::ShaderSource::Wgsl(
690 include_str!(concat!(env!("OUT_DIR"), "/compute_filter.wgsl")).into(),
691 ),
692 });
693
694 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
695 label: Some("compute_filter_pipeline"),
696 layout: Some(&pipeline_layout),
697 module: &shader,
698 entry_point: Some("main"),
699 compilation_options: Default::default(),
700 cache: None,
701 });
702
703 self.compute_filter_bgl = Some(bgl);
704 self.compute_filter_pipeline = Some(pipeline);
705 }
706
707 pub fn run_compute_filters(
716 &mut self,
717 device: &wgpu::Device,
718 queue: &wgpu::Queue,
719 items: &[crate::renderer::ComputeFilterItem],
720 ) -> Vec<ComputeFilterResult> {
721 if items.is_empty() {
722 return Vec::new();
723 }
724
725 self.ensure_compute_filter_pipeline(device);
726
727 let dummy_scalar_buf = device.create_buffer(&wgpu::BufferDescriptor {
729 label: Some("compute_filter_dummy_scalar"),
730 size: 4,
731 usage: wgpu::BufferUsages::STORAGE,
732 mapped_at_creation: false,
733 });
734
735 let mut results = Vec::with_capacity(items.len());
736
737 for item in items {
738 let gpu_mesh = match self.mesh_store.get(item.mesh_id) {
740 Some(m) => m,
741 None => continue,
742 };
743
744 let triangle_count = gpu_mesh.index_count / 3;
745 if triangle_count == 0 {
746 continue;
747 }
748
749 const VERTEX_STRIDE_F32: u32 = 16;
751
752 #[repr(C)]
754 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
755 struct FilterParams {
756 mode: u32,
757 clip_type: u32,
758 threshold_min: f32,
759 threshold_max: f32,
760 triangle_count: u32,
761 vertex_stride_f32: u32,
762 _pad: [u32; 2],
763 plane_nx: f32,
765 plane_ny: f32,
766 plane_nz: f32,
767 plane_dist: f32,
768 box_cx: f32,
770 box_cy: f32,
771 box_cz: f32,
772 _padb0: f32,
773 box_hex: f32,
774 box_hey: f32,
775 box_hez: f32,
776 _padb1: f32,
777 box_col0x: f32,
778 box_col0y: f32,
779 box_col0z: f32,
780 _padb2: f32,
781 box_col1x: f32,
782 box_col1y: f32,
783 box_col1z: f32,
784 _padb3: f32,
785 box_col2x: f32,
786 box_col2y: f32,
787 box_col2z: f32,
788 _padb4: f32,
789 sphere_cx: f32,
791 sphere_cy: f32,
792 sphere_cz: f32,
793 sphere_radius: f32,
794 }
795
796 let mut params: FilterParams = bytemuck::Zeroable::zeroed();
797 params.triangle_count = triangle_count;
798 params.vertex_stride_f32 = VERTEX_STRIDE_F32;
799
800 match item.kind {
801 crate::renderer::ComputeFilterKind::Clip {
802 plane_normal,
803 plane_dist,
804 } => {
805 params.mode = 0;
806 params.clip_type = 1;
807 params.plane_nx = plane_normal[0];
808 params.plane_ny = plane_normal[1];
809 params.plane_nz = plane_normal[2];
810 params.plane_dist = plane_dist;
811 }
812 crate::renderer::ComputeFilterKind::ClipBox {
813 center,
814 half_extents,
815 orientation,
816 } => {
817 params.mode = 0;
818 params.clip_type = 2;
819 params.box_cx = center[0];
820 params.box_cy = center[1];
821 params.box_cz = center[2];
822 params.box_hex = half_extents[0];
823 params.box_hey = half_extents[1];
824 params.box_hez = half_extents[2];
825 params.box_col0x = orientation[0][0];
826 params.box_col0y = orientation[0][1];
827 params.box_col0z = orientation[0][2];
828 params.box_col1x = orientation[1][0];
829 params.box_col1y = orientation[1][1];
830 params.box_col1z = orientation[1][2];
831 params.box_col2x = orientation[2][0];
832 params.box_col2y = orientation[2][1];
833 params.box_col2z = orientation[2][2];
834 }
835 crate::renderer::ComputeFilterKind::ClipSphere { center, radius } => {
836 params.mode = 0;
837 params.clip_type = 3;
838 params.sphere_cx = center[0];
839 params.sphere_cy = center[1];
840 params.sphere_cz = center[2];
841 params.sphere_radius = radius;
842 }
843 crate::renderer::ComputeFilterKind::Threshold { min, max } => {
844 params.mode = 1;
845 params.threshold_min = min;
846 params.threshold_max = max;
847 }
848 }
849
850 let params_buf = device.create_buffer(&wgpu::BufferDescriptor {
851 label: Some("compute_filter_params"),
852 size: std::mem::size_of::<FilterParams>() as u64,
853 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
854 mapped_at_creation: false,
855 });
856 queue.write_buffer(¶ms_buf, 0, bytemuck::bytes_of(¶ms));
857
858 let out_index_size = (gpu_mesh.index_count as u64) * 4;
860 let out_index_buf = device.create_buffer(&wgpu::BufferDescriptor {
861 label: Some("compute_filter_out_indices"),
862 size: out_index_size.max(4),
863 usage: wgpu::BufferUsages::STORAGE
864 | wgpu::BufferUsages::INDEX
865 | wgpu::BufferUsages::COPY_SRC,
866 mapped_at_creation: false,
867 });
868
869 let counter_buf = device.create_buffer(&wgpu::BufferDescriptor {
871 label: Some("compute_filter_counter"),
872 size: 4,
873 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
874 mapped_at_creation: true,
875 });
876 {
877 let mut view = counter_buf.slice(..).get_mapped_range_mut();
878 view[0..4].copy_from_slice(&0u32.to_le_bytes());
879 }
880 counter_buf.unmap();
881
882 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
884 label: Some("compute_filter_counter_staging"),
885 size: 4,
886 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
887 mapped_at_creation: false,
888 });
889
890 let scalar_buf_ref: &wgpu::Buffer = match &item.kind {
892 crate::renderer::ComputeFilterKind::Threshold { .. } => {
893 if let Some(attr_name) = &item.attribute_name {
894 gpu_mesh
895 .attribute_buffers
896 .get(attr_name.as_str())
897 .unwrap_or(&dummy_scalar_buf)
898 } else {
899 &dummy_scalar_buf
900 }
901 }
902 _ => &dummy_scalar_buf,
904 };
905
906 let bgl = self.compute_filter_bgl.as_ref().unwrap();
908 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
909 label: Some("compute_filter_bg"),
910 layout: bgl,
911 entries: &[
912 wgpu::BindGroupEntry {
913 binding: 0,
914 resource: params_buf.as_entire_binding(),
915 },
916 wgpu::BindGroupEntry {
917 binding: 1,
918 resource: gpu_mesh.vertex_buffer.as_entire_binding(),
919 },
920 wgpu::BindGroupEntry {
921 binding: 2,
922 resource: gpu_mesh.index_buffer.as_entire_binding(),
923 },
924 wgpu::BindGroupEntry {
925 binding: 3,
926 resource: scalar_buf_ref.as_entire_binding(),
927 },
928 wgpu::BindGroupEntry {
929 binding: 4,
930 resource: out_index_buf.as_entire_binding(),
931 },
932 wgpu::BindGroupEntry {
933 binding: 5,
934 resource: counter_buf.as_entire_binding(),
935 },
936 ],
937 });
938
939 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
941 label: Some("compute_filter_encoder"),
942 });
943
944 {
945 let pipeline = self.compute_filter_pipeline.as_ref().unwrap();
946 let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
947 label: Some("compute_filter_pass"),
948 timestamp_writes: None,
949 });
950 cpass.set_pipeline(pipeline);
951 cpass.set_bind_group(0, &bind_group, &[]);
952 let workgroups = triangle_count.div_ceil(64);
953 cpass.dispatch_workgroups(workgroups, 1, 1);
954 }
955
956 encoder.copy_buffer_to_buffer(&counter_buf, 0, &staging_buf, 0, 4);
957 queue.submit(std::iter::once(encoder.finish()));
958
959 let slice = staging_buf.slice(..);
961 slice.map_async(wgpu::MapMode::Read, |_| {});
962 let _ = device.poll(wgpu::PollType::Wait {
963 submission_index: None,
964 timeout: Some(std::time::Duration::from_secs(5)),
965 });
966
967 let index_count = {
968 let data = slice.get_mapped_range();
969 u32::from_le_bytes([data[0], data[1], data[2], data[3]])
970 };
971 staging_buf.unmap();
972
973 results.push(ComputeFilterResult {
974 index_buffer: out_index_buf,
975 index_count,
976 mesh_id: item.mesh_id,
977 });
978 }
979
980 results
981 }
982
983 pub(crate) fn ensure_pick_pipeline(&mut self, device: &wgpu::Device) {
992 if self.pick_pipeline.is_some() {
993 return;
994 }
995
996 let pick_camera_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1001 label: Some("pick_camera_bgl"),
1002 entries: &[
1003 wgpu::BindGroupLayoutEntry {
1004 binding: 0,
1005 visibility: wgpu::ShaderStages::VERTEX,
1006 ty: wgpu::BindingType::Buffer {
1007 ty: wgpu::BufferBindingType::Uniform,
1008 has_dynamic_offset: false,
1009 min_binding_size: None,
1010 },
1011 count: None,
1012 },
1013 wgpu::BindGroupLayoutEntry {
1014 binding: 6,
1015 visibility: wgpu::ShaderStages::FRAGMENT,
1016 ty: wgpu::BindingType::Buffer {
1017 ty: wgpu::BufferBindingType::Uniform,
1018 has_dynamic_offset: false,
1019 min_binding_size: None,
1020 },
1021 count: None,
1022 },
1023 ],
1024 });
1025
1026 let pick_instance_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1028 label: Some("pick_instance_bgl"),
1029 entries: &[wgpu::BindGroupLayoutEntry {
1030 binding: 0,
1031 visibility: wgpu::ShaderStages::VERTEX,
1032 ty: wgpu::BindingType::Buffer {
1033 ty: wgpu::BufferBindingType::Storage { read_only: true },
1034 has_dynamic_offset: false,
1035 min_binding_size: None,
1036 },
1037 count: None,
1038 }],
1039 });
1040
1041 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1042 label: Some("pick_id_shader"),
1043 source: wgpu::ShaderSource::Wgsl(
1044 include_str!(concat!(env!("OUT_DIR"), "/pick_id.wgsl")).into(),
1045 ),
1046 });
1047
1048 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1049 label: Some("pick_pipeline_layout"),
1050 bind_group_layouts: &[&pick_camera_bgl, &pick_instance_bgl],
1051 push_constant_ranges: &[],
1052 });
1053
1054 let pick_vertex_layout = wgpu::VertexBufferLayout {
1056 array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex,
1058 attributes: &[wgpu::VertexAttribute {
1059 offset: 0,
1060 shader_location: 0,
1061 format: wgpu::VertexFormat::Float32x3,
1062 }],
1063 };
1064
1065 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1066 label: Some("pick_pipeline"),
1067 layout: Some(&layout),
1068 vertex: wgpu::VertexState {
1069 module: &shader,
1070 entry_point: Some("vs_main"),
1071 buffers: &[pick_vertex_layout],
1072 compilation_options: wgpu::PipelineCompilationOptions::default(),
1073 },
1074 fragment: Some(wgpu::FragmentState {
1075 module: &shader,
1076 entry_point: Some("fs_main"),
1077 targets: &[
1078 Some(wgpu::ColorTargetState {
1080 format: wgpu::TextureFormat::R32Uint,
1081 blend: None, write_mask: wgpu::ColorWrites::ALL,
1083 }),
1084 Some(wgpu::ColorTargetState {
1086 format: wgpu::TextureFormat::R32Float,
1087 blend: None,
1088 write_mask: wgpu::ColorWrites::ALL,
1089 }),
1090 ],
1091 compilation_options: wgpu::PipelineCompilationOptions::default(),
1092 }),
1093 primitive: wgpu::PrimitiveState {
1094 topology: wgpu::PrimitiveTopology::TriangleList,
1095 front_face: wgpu::FrontFace::Ccw,
1096 cull_mode: None, ..Default::default()
1098 },
1099 depth_stencil: Some(wgpu::DepthStencilState {
1100 format: wgpu::TextureFormat::Depth24PlusStencil8,
1101 depth_write_enabled: true,
1102 depth_compare: wgpu::CompareFunction::Less,
1103 stencil: wgpu::StencilState::default(),
1104 bias: wgpu::DepthBiasState::default(),
1105 }),
1106 multisample: wgpu::MultisampleState {
1107 count: 1, ..Default::default()
1109 },
1110 multiview: None,
1111 cache: None,
1112 });
1113
1114 self.pick_camera_bgl = Some(pick_camera_bgl);
1115 self.pick_bind_group_layout_1 = Some(pick_instance_bgl);
1116 self.pick_pipeline = Some(pipeline);
1117 }
1118}
1119
1120pub fn lerp_attributes(a: &[f32], b: &[f32], t: f32) -> Vec<f32> {
1132 let t = t.clamp(0.0, 1.0);
1133 let one_minus_t = 1.0 - t;
1134 a.iter()
1135 .zip(b.iter())
1136 .map(|(&av, &bv)| av * one_minus_t + bv * t)
1137 .collect()
1138}