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 ],
576 })
577 }
578}
579
580pub struct ComputeFilterResult {
589 pub index_buffer: wgpu::Buffer,
591 pub index_count: u32,
593 pub mesh_id: crate::resources::mesh_store::MeshId,
595}
596
597impl ViewportGpuResources {
598 fn ensure_compute_filter_pipeline(&mut self, device: &wgpu::Device) {
600 if self.compute_filter_pipeline.is_some() {
601 return;
602 }
603
604 let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
606 label: Some("compute_filter_bgl"),
607 entries: &[
608 wgpu::BindGroupLayoutEntry {
610 binding: 0,
611 visibility: wgpu::ShaderStages::COMPUTE,
612 ty: wgpu::BindingType::Buffer {
613 ty: wgpu::BufferBindingType::Uniform,
614 has_dynamic_offset: false,
615 min_binding_size: None,
616 },
617 count: None,
618 },
619 wgpu::BindGroupLayoutEntry {
621 binding: 1,
622 visibility: wgpu::ShaderStages::COMPUTE,
623 ty: wgpu::BindingType::Buffer {
624 ty: wgpu::BufferBindingType::Storage { read_only: true },
625 has_dynamic_offset: false,
626 min_binding_size: None,
627 },
628 count: None,
629 },
630 wgpu::BindGroupLayoutEntry {
632 binding: 2,
633 visibility: wgpu::ShaderStages::COMPUTE,
634 ty: wgpu::BindingType::Buffer {
635 ty: wgpu::BufferBindingType::Storage { read_only: true },
636 has_dynamic_offset: false,
637 min_binding_size: None,
638 },
639 count: None,
640 },
641 wgpu::BindGroupLayoutEntry {
643 binding: 3,
644 visibility: wgpu::ShaderStages::COMPUTE,
645 ty: wgpu::BindingType::Buffer {
646 ty: wgpu::BufferBindingType::Storage { read_only: true },
647 has_dynamic_offset: false,
648 min_binding_size: None,
649 },
650 count: None,
651 },
652 wgpu::BindGroupLayoutEntry {
654 binding: 4,
655 visibility: wgpu::ShaderStages::COMPUTE,
656 ty: wgpu::BindingType::Buffer {
657 ty: wgpu::BufferBindingType::Storage { read_only: false },
658 has_dynamic_offset: false,
659 min_binding_size: None,
660 },
661 count: None,
662 },
663 wgpu::BindGroupLayoutEntry {
665 binding: 5,
666 visibility: wgpu::ShaderStages::COMPUTE,
667 ty: wgpu::BindingType::Buffer {
668 ty: wgpu::BufferBindingType::Storage { read_only: false },
669 has_dynamic_offset: false,
670 min_binding_size: None,
671 },
672 count: None,
673 },
674 ],
675 });
676
677 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
678 label: Some("compute_filter_layout"),
679 bind_group_layouts: &[&bgl],
680 push_constant_ranges: &[],
681 });
682
683 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
684 label: Some("compute_filter_shader"),
685 source: wgpu::ShaderSource::Wgsl(
686 include_str!(concat!(env!("OUT_DIR"), "/compute_filter.wgsl")).into(),
687 ),
688 });
689
690 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
691 label: Some("compute_filter_pipeline"),
692 layout: Some(&pipeline_layout),
693 module: &shader,
694 entry_point: Some("main"),
695 compilation_options: Default::default(),
696 cache: None,
697 });
698
699 self.compute_filter_bgl = Some(bgl);
700 self.compute_filter_pipeline = Some(pipeline);
701 }
702
703 pub fn run_compute_filters(
712 &mut self,
713 device: &wgpu::Device,
714 queue: &wgpu::Queue,
715 items: &[crate::renderer::ComputeFilterItem],
716 ) -> Vec<ComputeFilterResult> {
717 if items.is_empty() {
718 return Vec::new();
719 }
720
721 self.ensure_compute_filter_pipeline(device);
722
723 let dummy_scalar_buf = device.create_buffer(&wgpu::BufferDescriptor {
725 label: Some("compute_filter_dummy_scalar"),
726 size: 4,
727 usage: wgpu::BufferUsages::STORAGE,
728 mapped_at_creation: false,
729 });
730
731 let mut results = Vec::with_capacity(items.len());
732
733 for item in items {
734 let gpu_mesh = match self.mesh_store.get(item.mesh_id) {
736 Some(m) => m,
737 None => continue,
738 };
739
740 let triangle_count = gpu_mesh.index_count / 3;
741 if triangle_count == 0 {
742 continue;
743 }
744
745 const VERTEX_STRIDE_F32: u32 = 16;
747
748 #[repr(C)]
750 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
751 struct FilterParams {
752 mode: u32,
753 clip_type: u32,
754 threshold_min: f32,
755 threshold_max: f32,
756 triangle_count: u32,
757 vertex_stride_f32: u32,
758 _pad: [u32; 2],
759 plane_nx: f32,
761 plane_ny: f32,
762 plane_nz: f32,
763 plane_dist: f32,
764 box_cx: f32,
766 box_cy: f32,
767 box_cz: f32,
768 _padb0: f32,
769 box_hex: f32,
770 box_hey: f32,
771 box_hez: f32,
772 _padb1: f32,
773 box_col0x: f32,
774 box_col0y: f32,
775 box_col0z: f32,
776 _padb2: f32,
777 box_col1x: f32,
778 box_col1y: f32,
779 box_col1z: f32,
780 _padb3: f32,
781 box_col2x: f32,
782 box_col2y: f32,
783 box_col2z: f32,
784 _padb4: f32,
785 sphere_cx: f32,
787 sphere_cy: f32,
788 sphere_cz: f32,
789 sphere_radius: f32,
790 }
791
792 let mut params: FilterParams = bytemuck::Zeroable::zeroed();
793 params.triangle_count = triangle_count;
794 params.vertex_stride_f32 = VERTEX_STRIDE_F32;
795
796 match item.kind {
797 crate::renderer::ComputeFilterKind::Clip {
798 plane_normal,
799 plane_dist,
800 } => {
801 params.mode = 0;
802 params.clip_type = 1;
803 params.plane_nx = plane_normal[0];
804 params.plane_ny = plane_normal[1];
805 params.plane_nz = plane_normal[2];
806 params.plane_dist = plane_dist;
807 }
808 crate::renderer::ComputeFilterKind::ClipBox {
809 center,
810 half_extents,
811 orientation,
812 } => {
813 params.mode = 0;
814 params.clip_type = 2;
815 params.box_cx = center[0];
816 params.box_cy = center[1];
817 params.box_cz = center[2];
818 params.box_hex = half_extents[0];
819 params.box_hey = half_extents[1];
820 params.box_hez = half_extents[2];
821 params.box_col0x = orientation[0][0];
822 params.box_col0y = orientation[0][1];
823 params.box_col0z = orientation[0][2];
824 params.box_col1x = orientation[1][0];
825 params.box_col1y = orientation[1][1];
826 params.box_col1z = orientation[1][2];
827 params.box_col2x = orientation[2][0];
828 params.box_col2y = orientation[2][1];
829 params.box_col2z = orientation[2][2];
830 }
831 crate::renderer::ComputeFilterKind::ClipSphere { center, radius } => {
832 params.mode = 0;
833 params.clip_type = 3;
834 params.sphere_cx = center[0];
835 params.sphere_cy = center[1];
836 params.sphere_cz = center[2];
837 params.sphere_radius = radius;
838 }
839 crate::renderer::ComputeFilterKind::Threshold { min, max } => {
840 params.mode = 1;
841 params.threshold_min = min;
842 params.threshold_max = max;
843 }
844 }
845
846 let params_buf = device.create_buffer(&wgpu::BufferDescriptor {
847 label: Some("compute_filter_params"),
848 size: std::mem::size_of::<FilterParams>() as u64,
849 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
850 mapped_at_creation: false,
851 });
852 queue.write_buffer(¶ms_buf, 0, bytemuck::bytes_of(¶ms));
853
854 let out_index_size = (gpu_mesh.index_count as u64) * 4;
856 let out_index_buf = device.create_buffer(&wgpu::BufferDescriptor {
857 label: Some("compute_filter_out_indices"),
858 size: out_index_size.max(4),
859 usage: wgpu::BufferUsages::STORAGE
860 | wgpu::BufferUsages::INDEX
861 | wgpu::BufferUsages::COPY_SRC,
862 mapped_at_creation: false,
863 });
864
865 let counter_buf = device.create_buffer(&wgpu::BufferDescriptor {
867 label: Some("compute_filter_counter"),
868 size: 4,
869 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
870 mapped_at_creation: true,
871 });
872 {
873 let mut view = counter_buf.slice(..).get_mapped_range_mut();
874 view[0..4].copy_from_slice(&0u32.to_le_bytes());
875 }
876 counter_buf.unmap();
877
878 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
880 label: Some("compute_filter_counter_staging"),
881 size: 4,
882 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
883 mapped_at_creation: false,
884 });
885
886 let scalar_buf_ref: &wgpu::Buffer = match &item.kind {
888 crate::renderer::ComputeFilterKind::Threshold { .. } => {
889 if let Some(attr_name) = &item.attribute_name {
890 gpu_mesh
891 .attribute_buffers
892 .get(attr_name.as_str())
893 .unwrap_or(&dummy_scalar_buf)
894 } else {
895 &dummy_scalar_buf
896 }
897 }
898 _ => &dummy_scalar_buf,
900 };
901
902 let bgl = self.compute_filter_bgl.as_ref().unwrap();
904 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
905 label: Some("compute_filter_bg"),
906 layout: bgl,
907 entries: &[
908 wgpu::BindGroupEntry {
909 binding: 0,
910 resource: params_buf.as_entire_binding(),
911 },
912 wgpu::BindGroupEntry {
913 binding: 1,
914 resource: gpu_mesh.vertex_buffer.as_entire_binding(),
915 },
916 wgpu::BindGroupEntry {
917 binding: 2,
918 resource: gpu_mesh.index_buffer.as_entire_binding(),
919 },
920 wgpu::BindGroupEntry {
921 binding: 3,
922 resource: scalar_buf_ref.as_entire_binding(),
923 },
924 wgpu::BindGroupEntry {
925 binding: 4,
926 resource: out_index_buf.as_entire_binding(),
927 },
928 wgpu::BindGroupEntry {
929 binding: 5,
930 resource: counter_buf.as_entire_binding(),
931 },
932 ],
933 });
934
935 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
937 label: Some("compute_filter_encoder"),
938 });
939
940 {
941 let pipeline = self.compute_filter_pipeline.as_ref().unwrap();
942 let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
943 label: Some("compute_filter_pass"),
944 timestamp_writes: None,
945 });
946 cpass.set_pipeline(pipeline);
947 cpass.set_bind_group(0, &bind_group, &[]);
948 let workgroups = triangle_count.div_ceil(64);
949 cpass.dispatch_workgroups(workgroups, 1, 1);
950 }
951
952 encoder.copy_buffer_to_buffer(&counter_buf, 0, &staging_buf, 0, 4);
953 queue.submit(std::iter::once(encoder.finish()));
954
955 let slice = staging_buf.slice(..);
957 slice.map_async(wgpu::MapMode::Read, |_| {});
958 let _ = device.poll(wgpu::PollType::Wait {
959 submission_index: None,
960 timeout: Some(std::time::Duration::from_secs(5)),
961 });
962
963 let index_count = {
964 let data = slice.get_mapped_range();
965 u32::from_le_bytes([data[0], data[1], data[2], data[3]])
966 };
967 staging_buf.unmap();
968
969 results.push(ComputeFilterResult {
970 index_buffer: out_index_buf,
971 index_count,
972 mesh_id: item.mesh_id,
973 });
974 }
975
976 results
977 }
978
979 pub(crate) fn ensure_pick_pipeline(&mut self, device: &wgpu::Device) {
988 if self.pick_pipeline.is_some() {
989 return;
990 }
991
992 let pick_camera_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
997 label: Some("pick_camera_bgl"),
998 entries: &[
999 wgpu::BindGroupLayoutEntry {
1000 binding: 0,
1001 visibility: wgpu::ShaderStages::VERTEX,
1002 ty: wgpu::BindingType::Buffer {
1003 ty: wgpu::BufferBindingType::Uniform,
1004 has_dynamic_offset: false,
1005 min_binding_size: None,
1006 },
1007 count: None,
1008 },
1009 wgpu::BindGroupLayoutEntry {
1010 binding: 6,
1011 visibility: wgpu::ShaderStages::FRAGMENT,
1012 ty: wgpu::BindingType::Buffer {
1013 ty: wgpu::BufferBindingType::Uniform,
1014 has_dynamic_offset: false,
1015 min_binding_size: None,
1016 },
1017 count: None,
1018 },
1019 ],
1020 });
1021
1022 let pick_instance_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1024 label: Some("pick_instance_bgl"),
1025 entries: &[wgpu::BindGroupLayoutEntry {
1026 binding: 0,
1027 visibility: wgpu::ShaderStages::VERTEX,
1028 ty: wgpu::BindingType::Buffer {
1029 ty: wgpu::BufferBindingType::Storage { read_only: true },
1030 has_dynamic_offset: false,
1031 min_binding_size: None,
1032 },
1033 count: None,
1034 }],
1035 });
1036
1037 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1038 label: Some("pick_id_shader"),
1039 source: wgpu::ShaderSource::Wgsl(
1040 include_str!(concat!(env!("OUT_DIR"), "/pick_id.wgsl")).into(),
1041 ),
1042 });
1043
1044 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1045 label: Some("pick_pipeline_layout"),
1046 bind_group_layouts: &[&pick_camera_bgl, &pick_instance_bgl],
1047 push_constant_ranges: &[],
1048 });
1049
1050 let pick_vertex_layout = wgpu::VertexBufferLayout {
1052 array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex,
1054 attributes: &[wgpu::VertexAttribute {
1055 offset: 0,
1056 shader_location: 0,
1057 format: wgpu::VertexFormat::Float32x3,
1058 }],
1059 };
1060
1061 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1062 label: Some("pick_pipeline"),
1063 layout: Some(&layout),
1064 vertex: wgpu::VertexState {
1065 module: &shader,
1066 entry_point: Some("vs_main"),
1067 buffers: &[pick_vertex_layout],
1068 compilation_options: wgpu::PipelineCompilationOptions::default(),
1069 },
1070 fragment: Some(wgpu::FragmentState {
1071 module: &shader,
1072 entry_point: Some("fs_main"),
1073 targets: &[
1074 Some(wgpu::ColorTargetState {
1076 format: wgpu::TextureFormat::R32Uint,
1077 blend: None, write_mask: wgpu::ColorWrites::ALL,
1079 }),
1080 Some(wgpu::ColorTargetState {
1082 format: wgpu::TextureFormat::R32Float,
1083 blend: None,
1084 write_mask: wgpu::ColorWrites::ALL,
1085 }),
1086 ],
1087 compilation_options: wgpu::PipelineCompilationOptions::default(),
1088 }),
1089 primitive: wgpu::PrimitiveState {
1090 topology: wgpu::PrimitiveTopology::TriangleList,
1091 front_face: wgpu::FrontFace::Ccw,
1092 cull_mode: None, ..Default::default()
1094 },
1095 depth_stencil: Some(wgpu::DepthStencilState {
1096 format: wgpu::TextureFormat::Depth24PlusStencil8,
1097 depth_write_enabled: true,
1098 depth_compare: wgpu::CompareFunction::Less,
1099 stencil: wgpu::StencilState::default(),
1100 bias: wgpu::DepthBiasState::default(),
1101 }),
1102 multisample: wgpu::MultisampleState {
1103 count: 1, ..Default::default()
1105 },
1106 multiview: None,
1107 cache: None,
1108 });
1109
1110 self.pick_camera_bgl = Some(pick_camera_bgl);
1111 self.pick_bind_group_layout_1 = Some(pick_instance_bgl);
1112 self.pick_pipeline = Some(pipeline);
1113 }
1114}
1115
1116pub fn lerp_attributes(a: &[f32], b: &[f32], t: f32) -> Vec<f32> {
1128 let t = t.clamp(0.0, 1.0);
1129 let one_minus_t = 1.0 - t;
1130 a.iter()
1131 .zip(b.iter())
1132 .map(|(&av, &bv)| av * one_minus_t + bv * t)
1133 .collect()
1134}