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>, y: f32, r: f32, normal_y: 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 nz = if r > 0.0 { s } else { 0.0 };
154 let len = (nx * nx + normal_y * normal_y + nz * nz).sqrt();
155 verts.push(Vertex {
156 position: [c * r, y, s * r],
157 normal: [nx / len, normal_y / len, nz / 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, shaft_bot, 0.0],
174 normal: [0.0, -1.0, 0.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_y_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, cone_bot, s * cone_r],
212 normal: [c * normal_r_cone, normal_y_cone, s * normal_r_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, cone_tip, 0.0],
223 normal: [0.0, 1.0, 0.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, cone_bot, s * cone_r],
242 normal: [0.0, -1.0, 0.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, cone_bot, 0.0],
251 normal: [0.0, -1.0, 0.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 ],
560 })
561 }
562}
563
564pub struct ComputeFilterResult {
573 pub index_buffer: wgpu::Buffer,
575 pub index_count: u32,
577 pub mesh_id: crate::resources::mesh_store::MeshId,
579}
580
581impl ViewportGpuResources {
582 fn ensure_compute_filter_pipeline(&mut self, device: &wgpu::Device) {
584 if self.compute_filter_pipeline.is_some() {
585 return;
586 }
587
588 let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
590 label: Some("compute_filter_bgl"),
591 entries: &[
592 wgpu::BindGroupLayoutEntry {
594 binding: 0,
595 visibility: wgpu::ShaderStages::COMPUTE,
596 ty: wgpu::BindingType::Buffer {
597 ty: wgpu::BufferBindingType::Uniform,
598 has_dynamic_offset: false,
599 min_binding_size: None,
600 },
601 count: None,
602 },
603 wgpu::BindGroupLayoutEntry {
605 binding: 1,
606 visibility: wgpu::ShaderStages::COMPUTE,
607 ty: wgpu::BindingType::Buffer {
608 ty: wgpu::BufferBindingType::Storage { read_only: true },
609 has_dynamic_offset: false,
610 min_binding_size: None,
611 },
612 count: None,
613 },
614 wgpu::BindGroupLayoutEntry {
616 binding: 2,
617 visibility: wgpu::ShaderStages::COMPUTE,
618 ty: wgpu::BindingType::Buffer {
619 ty: wgpu::BufferBindingType::Storage { read_only: true },
620 has_dynamic_offset: false,
621 min_binding_size: None,
622 },
623 count: None,
624 },
625 wgpu::BindGroupLayoutEntry {
627 binding: 3,
628 visibility: wgpu::ShaderStages::COMPUTE,
629 ty: wgpu::BindingType::Buffer {
630 ty: wgpu::BufferBindingType::Storage { read_only: true },
631 has_dynamic_offset: false,
632 min_binding_size: None,
633 },
634 count: None,
635 },
636 wgpu::BindGroupLayoutEntry {
638 binding: 4,
639 visibility: wgpu::ShaderStages::COMPUTE,
640 ty: wgpu::BindingType::Buffer {
641 ty: wgpu::BufferBindingType::Storage { read_only: false },
642 has_dynamic_offset: false,
643 min_binding_size: None,
644 },
645 count: None,
646 },
647 wgpu::BindGroupLayoutEntry {
649 binding: 5,
650 visibility: wgpu::ShaderStages::COMPUTE,
651 ty: wgpu::BindingType::Buffer {
652 ty: wgpu::BufferBindingType::Storage { read_only: false },
653 has_dynamic_offset: false,
654 min_binding_size: None,
655 },
656 count: None,
657 },
658 ],
659 });
660
661 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
662 label: Some("compute_filter_layout"),
663 bind_group_layouts: &[&bgl],
664 push_constant_ranges: &[],
665 });
666
667 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
668 label: Some("compute_filter_shader"),
669 source: wgpu::ShaderSource::Wgsl(include_str!(concat!(env!("OUT_DIR"), "/compute_filter.wgsl")).into()),
670 });
671
672 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
673 label: Some("compute_filter_pipeline"),
674 layout: Some(&pipeline_layout),
675 module: &shader,
676 entry_point: Some("main"),
677 compilation_options: Default::default(),
678 cache: None,
679 });
680
681 self.compute_filter_bgl = Some(bgl);
682 self.compute_filter_pipeline = Some(pipeline);
683 }
684
685 #[allow(dead_code)]
694 pub(crate) fn ensure_oit_targets(&mut self, device: &wgpu::Device, w: u32, h: u32) {
695 let w = w.max(1);
696 let h = h.max(1);
697
698 let need_textures = self.oit_size != [w, h] || self.oit_accum_texture.is_none();
700
701 if need_textures {
702 self.oit_size = [w, h];
703
704 let accum_tex = device.create_texture(&wgpu::TextureDescriptor {
706 label: Some("oit_accum_texture"),
707 size: wgpu::Extent3d {
708 width: w,
709 height: h,
710 depth_or_array_layers: 1,
711 },
712 mip_level_count: 1,
713 sample_count: 1,
714 dimension: wgpu::TextureDimension::D2,
715 format: wgpu::TextureFormat::Rgba16Float,
716 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
717 | wgpu::TextureUsages::TEXTURE_BINDING,
718 view_formats: &[],
719 });
720 let accum_view = accum_tex.create_view(&wgpu::TextureViewDescriptor::default());
721
722 let reveal_tex = device.create_texture(&wgpu::TextureDescriptor {
724 label: Some("oit_reveal_texture"),
725 size: wgpu::Extent3d {
726 width: w,
727 height: h,
728 depth_or_array_layers: 1,
729 },
730 mip_level_count: 1,
731 sample_count: 1,
732 dimension: wgpu::TextureDimension::D2,
733 format: wgpu::TextureFormat::R8Unorm,
734 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
735 | wgpu::TextureUsages::TEXTURE_BINDING,
736 view_formats: &[],
737 });
738 let reveal_view = reveal_tex.create_view(&wgpu::TextureViewDescriptor::default());
739
740 let sampler = if self.oit_composite_sampler.is_none() {
742 device.create_sampler(&wgpu::SamplerDescriptor {
743 label: Some("oit_composite_sampler"),
744 address_mode_u: wgpu::AddressMode::ClampToEdge,
745 address_mode_v: wgpu::AddressMode::ClampToEdge,
746 address_mode_w: wgpu::AddressMode::ClampToEdge,
747 mag_filter: wgpu::FilterMode::Linear,
748 min_filter: wgpu::FilterMode::Linear,
749 ..Default::default()
750 })
751 } else {
752 device.create_sampler(&wgpu::SamplerDescriptor {
754 label: Some("oit_composite_sampler"),
755 address_mode_u: wgpu::AddressMode::ClampToEdge,
756 address_mode_v: wgpu::AddressMode::ClampToEdge,
757 address_mode_w: wgpu::AddressMode::ClampToEdge,
758 mag_filter: wgpu::FilterMode::Linear,
759 min_filter: wgpu::FilterMode::Linear,
760 ..Default::default()
761 })
762 };
763
764 let bgl = if self.oit_composite_bgl.is_none() {
766 let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
767 label: Some("oit_composite_bgl"),
768 entries: &[
769 wgpu::BindGroupLayoutEntry {
770 binding: 0,
771 visibility: wgpu::ShaderStages::FRAGMENT,
772 ty: wgpu::BindingType::Texture {
773 sample_type: wgpu::TextureSampleType::Float { filterable: true },
774 view_dimension: wgpu::TextureViewDimension::D2,
775 multisampled: false,
776 },
777 count: None,
778 },
779 wgpu::BindGroupLayoutEntry {
780 binding: 1,
781 visibility: wgpu::ShaderStages::FRAGMENT,
782 ty: wgpu::BindingType::Texture {
783 sample_type: wgpu::TextureSampleType::Float { filterable: true },
784 view_dimension: wgpu::TextureViewDimension::D2,
785 multisampled: false,
786 },
787 count: None,
788 },
789 wgpu::BindGroupLayoutEntry {
790 binding: 2,
791 visibility: wgpu::ShaderStages::FRAGMENT,
792 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
793 count: None,
794 },
795 ],
796 });
797 self.oit_composite_bgl = Some(bgl);
798 self.oit_composite_bgl.as_ref().unwrap()
799 } else {
800 self.oit_composite_bgl.as_ref().unwrap()
801 };
802
803 let composite_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
805 label: Some("oit_composite_bind_group"),
806 layout: bgl,
807 entries: &[
808 wgpu::BindGroupEntry {
809 binding: 0,
810 resource: wgpu::BindingResource::TextureView(&accum_view),
811 },
812 wgpu::BindGroupEntry {
813 binding: 1,
814 resource: wgpu::BindingResource::TextureView(&reveal_view),
815 },
816 wgpu::BindGroupEntry {
817 binding: 2,
818 resource: wgpu::BindingResource::Sampler(&sampler),
819 },
820 ],
821 });
822
823 self.oit_accum_texture = Some(accum_tex);
824 self.oit_accum_view = Some(accum_view);
825 self.oit_reveal_texture = Some(reveal_tex);
826 self.oit_reveal_view = Some(reveal_view);
827 self.oit_composite_sampler = Some(sampler);
828 self.oit_composite_bind_group = Some(composite_bg);
829 }
830
831 if self.oit_pipeline.is_none() {
833 let oit_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
835 label: Some("mesh_oit_shader"),
836 source: wgpu::ShaderSource::Wgsl(include_str!(concat!(env!("OUT_DIR"), "/mesh_oit.wgsl")).into()),
837 });
838 let oit_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
839 label: Some("oit_pipeline_layout"),
840 bind_group_layouts: &[
841 &self.camera_bind_group_layout,
842 &self.object_bind_group_layout,
843 ],
844 push_constant_ranges: &[],
845 });
846
847 let accum_blend = wgpu::BlendState {
849 color: wgpu::BlendComponent {
850 src_factor: wgpu::BlendFactor::One,
851 dst_factor: wgpu::BlendFactor::One,
852 operation: wgpu::BlendOperation::Add,
853 },
854 alpha: wgpu::BlendComponent {
855 src_factor: wgpu::BlendFactor::One,
856 dst_factor: wgpu::BlendFactor::One,
857 operation: wgpu::BlendOperation::Add,
858 },
859 };
860
861 let reveal_blend = wgpu::BlendState {
863 color: wgpu::BlendComponent {
864 src_factor: wgpu::BlendFactor::Zero,
865 dst_factor: wgpu::BlendFactor::OneMinusSrc,
866 operation: wgpu::BlendOperation::Add,
867 },
868 alpha: wgpu::BlendComponent {
869 src_factor: wgpu::BlendFactor::Zero,
870 dst_factor: wgpu::BlendFactor::OneMinusSrc,
871 operation: wgpu::BlendOperation::Add,
872 },
873 };
874
875 let oit_depth_stencil = wgpu::DepthStencilState {
876 format: wgpu::TextureFormat::Depth24PlusStencil8,
877 depth_write_enabled: false,
878 depth_compare: wgpu::CompareFunction::LessEqual,
879 stencil: wgpu::StencilState::default(),
880 bias: wgpu::DepthBiasState::default(),
881 };
882
883 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
884 label: Some("oit_pipeline"),
885 layout: Some(&oit_layout),
886 vertex: wgpu::VertexState {
887 module: &oit_shader,
888 entry_point: Some("vs_main"),
889 buffers: &[Vertex::buffer_layout()],
890 compilation_options: wgpu::PipelineCompilationOptions::default(),
891 },
892 fragment: Some(wgpu::FragmentState {
893 module: &oit_shader,
894 entry_point: Some("fs_oit_main"),
895 targets: &[
896 Some(wgpu::ColorTargetState {
897 format: wgpu::TextureFormat::Rgba16Float,
898 blend: Some(accum_blend),
899 write_mask: wgpu::ColorWrites::ALL,
900 }),
901 Some(wgpu::ColorTargetState {
902 format: wgpu::TextureFormat::R8Unorm,
903 blend: Some(reveal_blend),
904 write_mask: wgpu::ColorWrites::RED,
905 }),
906 ],
907 compilation_options: wgpu::PipelineCompilationOptions::default(),
908 }),
909 primitive: wgpu::PrimitiveState {
910 topology: wgpu::PrimitiveTopology::TriangleList,
911 cull_mode: Some(wgpu::Face::Back),
912 ..Default::default()
913 },
914 depth_stencil: Some(oit_depth_stencil.clone()),
915 multisample: wgpu::MultisampleState {
916 count: 1,
917 ..Default::default()
918 },
919 multiview: None,
920 cache: None,
921 });
922 self.oit_pipeline = Some(pipeline);
923
924 if let Some(ref instance_bgl) = self.instance_bind_group_layout {
926 let instanced_oit_shader =
927 device.create_shader_module(wgpu::ShaderModuleDescriptor {
928 label: Some("mesh_instanced_oit_shader"),
929 source: wgpu::ShaderSource::Wgsl(
930 include_str!(concat!(env!("OUT_DIR"), "/mesh_instanced_oit.wgsl")).into(),
931 ),
932 });
933 let instanced_oit_layout =
934 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
935 label: Some("oit_instanced_pipeline_layout"),
936 bind_group_layouts: &[&self.camera_bind_group_layout, instance_bgl],
937 push_constant_ranges: &[],
938 });
939 let instanced_pipeline =
940 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
941 label: Some("oit_instanced_pipeline"),
942 layout: Some(&instanced_oit_layout),
943 vertex: wgpu::VertexState {
944 module: &instanced_oit_shader,
945 entry_point: Some("vs_main"),
946 buffers: &[Vertex::buffer_layout()],
947 compilation_options: wgpu::PipelineCompilationOptions::default(),
948 },
949 fragment: Some(wgpu::FragmentState {
950 module: &instanced_oit_shader,
951 entry_point: Some("fs_oit_main"),
952 targets: &[
953 Some(wgpu::ColorTargetState {
954 format: wgpu::TextureFormat::Rgba16Float,
955 blend: Some(accum_blend),
956 write_mask: wgpu::ColorWrites::ALL,
957 }),
958 Some(wgpu::ColorTargetState {
959 format: wgpu::TextureFormat::R8Unorm,
960 blend: Some(reveal_blend),
961 write_mask: wgpu::ColorWrites::RED,
962 }),
963 ],
964 compilation_options: wgpu::PipelineCompilationOptions::default(),
965 }),
966 primitive: wgpu::PrimitiveState {
967 topology: wgpu::PrimitiveTopology::TriangleList,
968 cull_mode: Some(wgpu::Face::Back),
969 ..Default::default()
970 },
971 depth_stencil: Some(oit_depth_stencil),
972 multisample: wgpu::MultisampleState {
973 count: 1,
974 ..Default::default()
975 },
976 multiview: None,
977 cache: None,
978 });
979 self.oit_instanced_pipeline = Some(instanced_pipeline);
980 }
981 }
982
983 if self.oit_composite_pipeline.is_none() {
984 let comp_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
985 label: Some("oit_composite_shader"),
986 source: wgpu::ShaderSource::Wgsl(
987 include_str!(concat!(env!("OUT_DIR"), "/oit_composite.wgsl")).into(),
988 ),
989 });
990 let bgl = self
991 .oit_composite_bgl
992 .as_ref()
993 .expect("oit_composite_bgl must exist");
994 let comp_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
995 label: Some("oit_composite_pipeline_layout"),
996 bind_group_layouts: &[bgl],
997 push_constant_ranges: &[],
998 });
999 let premul_blend = wgpu::BlendState {
1001 color: wgpu::BlendComponent {
1002 src_factor: wgpu::BlendFactor::One,
1003 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
1004 operation: wgpu::BlendOperation::Add,
1005 },
1006 alpha: wgpu::BlendComponent {
1007 src_factor: wgpu::BlendFactor::One,
1008 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
1009 operation: wgpu::BlendOperation::Add,
1010 },
1011 };
1012 let comp_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1013 label: Some("oit_composite_pipeline"),
1014 layout: Some(&comp_layout),
1015 vertex: wgpu::VertexState {
1016 module: &comp_shader,
1017 entry_point: Some("vs_main"),
1018 buffers: &[],
1019 compilation_options: wgpu::PipelineCompilationOptions::default(),
1020 },
1021 fragment: Some(wgpu::FragmentState {
1022 module: &comp_shader,
1023 entry_point: Some("fs_main"),
1024 targets: &[Some(wgpu::ColorTargetState {
1025 format: wgpu::TextureFormat::Rgba16Float,
1026 blend: Some(premul_blend),
1027 write_mask: wgpu::ColorWrites::ALL,
1028 })],
1029 compilation_options: wgpu::PipelineCompilationOptions::default(),
1030 }),
1031 primitive: wgpu::PrimitiveState {
1032 topology: wgpu::PrimitiveTopology::TriangleList,
1033 ..Default::default()
1034 },
1035 depth_stencil: None,
1036 multisample: wgpu::MultisampleState {
1037 count: 1,
1038 ..Default::default()
1039 },
1040 multiview: None,
1041 cache: None,
1042 });
1043 self.oit_composite_pipeline = Some(comp_pipeline);
1044 }
1045 }
1046
1047 pub fn run_compute_filters(
1056 &mut self,
1057 device: &wgpu::Device,
1058 queue: &wgpu::Queue,
1059 items: &[crate::renderer::ComputeFilterItem],
1060 ) -> Vec<ComputeFilterResult> {
1061 if items.is_empty() {
1062 return Vec::new();
1063 }
1064
1065 self.ensure_compute_filter_pipeline(device);
1066
1067 let dummy_scalar_buf = device.create_buffer(&wgpu::BufferDescriptor {
1069 label: Some("compute_filter_dummy_scalar"),
1070 size: 4,
1071 usage: wgpu::BufferUsages::STORAGE,
1072 mapped_at_creation: false,
1073 });
1074
1075 let mut results = Vec::with_capacity(items.len());
1076
1077 for item in items {
1078 let gpu_mesh = match self.mesh_store.get(item.mesh_id) {
1080 Some(m) => m,
1081 None => continue,
1082 };
1083
1084 let triangle_count = gpu_mesh.index_count / 3;
1085 if triangle_count == 0 {
1086 continue;
1087 }
1088
1089 const VERTEX_STRIDE_F32: u32 = 16;
1091
1092 #[repr(C)]
1094 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
1095 struct FilterParams {
1096 mode: u32,
1097 clip_type: u32,
1098 threshold_min: f32,
1099 threshold_max: f32,
1100 triangle_count: u32,
1101 vertex_stride_f32: u32,
1102 _pad: [u32; 2],
1103 plane_nx: f32,
1105 plane_ny: f32,
1106 plane_nz: f32,
1107 plane_dist: f32,
1108 box_cx: f32,
1110 box_cy: f32,
1111 box_cz: f32,
1112 _padb0: f32,
1113 box_hex: f32,
1114 box_hey: f32,
1115 box_hez: f32,
1116 _padb1: f32,
1117 box_col0x: f32,
1118 box_col0y: f32,
1119 box_col0z: f32,
1120 _padb2: f32,
1121 box_col1x: f32,
1122 box_col1y: f32,
1123 box_col1z: f32,
1124 _padb3: f32,
1125 box_col2x: f32,
1126 box_col2y: f32,
1127 box_col2z: f32,
1128 _padb4: f32,
1129 sphere_cx: f32,
1131 sphere_cy: f32,
1132 sphere_cz: f32,
1133 sphere_radius: f32,
1134 }
1135
1136 let mut params: FilterParams = bytemuck::Zeroable::zeroed();
1137 params.triangle_count = triangle_count;
1138 params.vertex_stride_f32 = VERTEX_STRIDE_F32;
1139
1140 match item.kind {
1141 crate::renderer::ComputeFilterKind::Clip {
1142 plane_normal,
1143 plane_dist,
1144 } => {
1145 params.mode = 0;
1146 params.clip_type = 1;
1147 params.plane_nx = plane_normal[0];
1148 params.plane_ny = plane_normal[1];
1149 params.plane_nz = plane_normal[2];
1150 params.plane_dist = plane_dist;
1151 }
1152 crate::renderer::ComputeFilterKind::ClipBox {
1153 center,
1154 half_extents,
1155 orientation,
1156 } => {
1157 params.mode = 0;
1158 params.clip_type = 2;
1159 params.box_cx = center[0];
1160 params.box_cy = center[1];
1161 params.box_cz = center[2];
1162 params.box_hex = half_extents[0];
1163 params.box_hey = half_extents[1];
1164 params.box_hez = half_extents[2];
1165 params.box_col0x = orientation[0][0];
1166 params.box_col0y = orientation[0][1];
1167 params.box_col0z = orientation[0][2];
1168 params.box_col1x = orientation[1][0];
1169 params.box_col1y = orientation[1][1];
1170 params.box_col1z = orientation[1][2];
1171 params.box_col2x = orientation[2][0];
1172 params.box_col2y = orientation[2][1];
1173 params.box_col2z = orientation[2][2];
1174 }
1175 crate::renderer::ComputeFilterKind::ClipSphere { center, radius } => {
1176 params.mode = 0;
1177 params.clip_type = 3;
1178 params.sphere_cx = center[0];
1179 params.sphere_cy = center[1];
1180 params.sphere_cz = center[2];
1181 params.sphere_radius = radius;
1182 }
1183 crate::renderer::ComputeFilterKind::Threshold { min, max } => {
1184 params.mode = 1;
1185 params.threshold_min = min;
1186 params.threshold_max = max;
1187 }
1188 }
1189
1190 let params_buf = device.create_buffer(&wgpu::BufferDescriptor {
1191 label: Some("compute_filter_params"),
1192 size: std::mem::size_of::<FilterParams>() as u64,
1193 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
1194 mapped_at_creation: false,
1195 });
1196 queue.write_buffer(¶ms_buf, 0, bytemuck::bytes_of(¶ms));
1197
1198 let out_index_size = (gpu_mesh.index_count as u64) * 4;
1200 let out_index_buf = device.create_buffer(&wgpu::BufferDescriptor {
1201 label: Some("compute_filter_out_indices"),
1202 size: out_index_size.max(4),
1203 usage: wgpu::BufferUsages::STORAGE
1204 | wgpu::BufferUsages::INDEX
1205 | wgpu::BufferUsages::COPY_SRC,
1206 mapped_at_creation: false,
1207 });
1208
1209 let counter_buf = device.create_buffer(&wgpu::BufferDescriptor {
1211 label: Some("compute_filter_counter"),
1212 size: 4,
1213 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
1214 mapped_at_creation: true,
1215 });
1216 {
1217 let mut view = counter_buf.slice(..).get_mapped_range_mut();
1218 view[0..4].copy_from_slice(&0u32.to_le_bytes());
1219 }
1220 counter_buf.unmap();
1221
1222 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
1224 label: Some("compute_filter_counter_staging"),
1225 size: 4,
1226 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
1227 mapped_at_creation: false,
1228 });
1229
1230 let scalar_buf_ref: &wgpu::Buffer = match &item.kind {
1232 crate::renderer::ComputeFilterKind::Threshold { .. } => {
1233 if let Some(attr_name) = &item.attribute_name {
1234 gpu_mesh
1235 .attribute_buffers
1236 .get(attr_name.as_str())
1237 .unwrap_or(&dummy_scalar_buf)
1238 } else {
1239 &dummy_scalar_buf
1240 }
1241 }
1242 _ => &dummy_scalar_buf,
1244 };
1245
1246 let bgl = self.compute_filter_bgl.as_ref().unwrap();
1248 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
1249 label: Some("compute_filter_bg"),
1250 layout: bgl,
1251 entries: &[
1252 wgpu::BindGroupEntry {
1253 binding: 0,
1254 resource: params_buf.as_entire_binding(),
1255 },
1256 wgpu::BindGroupEntry {
1257 binding: 1,
1258 resource: gpu_mesh.vertex_buffer.as_entire_binding(),
1259 },
1260 wgpu::BindGroupEntry {
1261 binding: 2,
1262 resource: gpu_mesh.index_buffer.as_entire_binding(),
1263 },
1264 wgpu::BindGroupEntry {
1265 binding: 3,
1266 resource: scalar_buf_ref.as_entire_binding(),
1267 },
1268 wgpu::BindGroupEntry {
1269 binding: 4,
1270 resource: out_index_buf.as_entire_binding(),
1271 },
1272 wgpu::BindGroupEntry {
1273 binding: 5,
1274 resource: counter_buf.as_entire_binding(),
1275 },
1276 ],
1277 });
1278
1279 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
1281 label: Some("compute_filter_encoder"),
1282 });
1283
1284 {
1285 let pipeline = self.compute_filter_pipeline.as_ref().unwrap();
1286 let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
1287 label: Some("compute_filter_pass"),
1288 timestamp_writes: None,
1289 });
1290 cpass.set_pipeline(pipeline);
1291 cpass.set_bind_group(0, &bind_group, &[]);
1292 let workgroups = triangle_count.div_ceil(64);
1293 cpass.dispatch_workgroups(workgroups, 1, 1);
1294 }
1295
1296 encoder.copy_buffer_to_buffer(&counter_buf, 0, &staging_buf, 0, 4);
1297 queue.submit(std::iter::once(encoder.finish()));
1298
1299 let slice = staging_buf.slice(..);
1301 slice.map_async(wgpu::MapMode::Read, |_| {});
1302 let _ = device.poll(wgpu::PollType::Wait {
1303 submission_index: None,
1304 timeout: Some(std::time::Duration::from_secs(5)),
1305 });
1306
1307 let index_count = {
1308 let data = slice.get_mapped_range();
1309 u32::from_le_bytes([data[0], data[1], data[2], data[3]])
1310 };
1311 staging_buf.unmap();
1312
1313 results.push(ComputeFilterResult {
1314 index_buffer: out_index_buf,
1315 index_count,
1316 mesh_id: item.mesh_id,
1317 });
1318 }
1319
1320 results
1321 }
1322
1323 pub(crate) fn ensure_pick_pipeline(&mut self, device: &wgpu::Device) {
1332 if self.pick_pipeline.is_some() {
1333 return;
1334 }
1335
1336 let pick_camera_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1341 label: Some("pick_camera_bgl"),
1342 entries: &[
1343 wgpu::BindGroupLayoutEntry {
1344 binding: 0,
1345 visibility: wgpu::ShaderStages::VERTEX,
1346 ty: wgpu::BindingType::Buffer {
1347 ty: wgpu::BufferBindingType::Uniform,
1348 has_dynamic_offset: false,
1349 min_binding_size: None,
1350 },
1351 count: None,
1352 },
1353 wgpu::BindGroupLayoutEntry {
1354 binding: 6,
1355 visibility: wgpu::ShaderStages::FRAGMENT,
1356 ty: wgpu::BindingType::Buffer {
1357 ty: wgpu::BufferBindingType::Uniform,
1358 has_dynamic_offset: false,
1359 min_binding_size: None,
1360 },
1361 count: None,
1362 },
1363 ],
1364 });
1365
1366 let pick_instance_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1368 label: Some("pick_instance_bgl"),
1369 entries: &[wgpu::BindGroupLayoutEntry {
1370 binding: 0,
1371 visibility: wgpu::ShaderStages::VERTEX,
1372 ty: wgpu::BindingType::Buffer {
1373 ty: wgpu::BufferBindingType::Storage { read_only: true },
1374 has_dynamic_offset: false,
1375 min_binding_size: None,
1376 },
1377 count: None,
1378 }],
1379 });
1380
1381 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1382 label: Some("pick_id_shader"),
1383 source: wgpu::ShaderSource::Wgsl(include_str!(concat!(env!("OUT_DIR"), "/pick_id.wgsl")).into()),
1384 });
1385
1386 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1387 label: Some("pick_pipeline_layout"),
1388 bind_group_layouts: &[&pick_camera_bgl, &pick_instance_bgl],
1389 push_constant_ranges: &[],
1390 });
1391
1392 let pick_vertex_layout = wgpu::VertexBufferLayout {
1394 array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex,
1396 attributes: &[wgpu::VertexAttribute {
1397 offset: 0,
1398 shader_location: 0,
1399 format: wgpu::VertexFormat::Float32x3,
1400 }],
1401 };
1402
1403 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1404 label: Some("pick_pipeline"),
1405 layout: Some(&layout),
1406 vertex: wgpu::VertexState {
1407 module: &shader,
1408 entry_point: Some("vs_main"),
1409 buffers: &[pick_vertex_layout],
1410 compilation_options: wgpu::PipelineCompilationOptions::default(),
1411 },
1412 fragment: Some(wgpu::FragmentState {
1413 module: &shader,
1414 entry_point: Some("fs_main"),
1415 targets: &[
1416 Some(wgpu::ColorTargetState {
1418 format: wgpu::TextureFormat::R32Uint,
1419 blend: None, write_mask: wgpu::ColorWrites::ALL,
1421 }),
1422 Some(wgpu::ColorTargetState {
1424 format: wgpu::TextureFormat::R32Float,
1425 blend: None,
1426 write_mask: wgpu::ColorWrites::ALL,
1427 }),
1428 ],
1429 compilation_options: wgpu::PipelineCompilationOptions::default(),
1430 }),
1431 primitive: wgpu::PrimitiveState {
1432 topology: wgpu::PrimitiveTopology::TriangleList,
1433 front_face: wgpu::FrontFace::Ccw,
1434 cull_mode: None, ..Default::default()
1436 },
1437 depth_stencil: Some(wgpu::DepthStencilState {
1438 format: wgpu::TextureFormat::Depth24PlusStencil8,
1439 depth_write_enabled: true,
1440 depth_compare: wgpu::CompareFunction::Less,
1441 stencil: wgpu::StencilState::default(),
1442 bias: wgpu::DepthBiasState::default(),
1443 }),
1444 multisample: wgpu::MultisampleState {
1445 count: 1, ..Default::default()
1447 },
1448 multiview: None,
1449 cache: None,
1450 });
1451
1452 self.pick_camera_bgl = Some(pick_camera_bgl);
1453 self.pick_bind_group_layout_1 = Some(pick_instance_bgl);
1454 self.pick_pipeline = Some(pipeline);
1455 }
1456}
1457
1458pub fn lerp_attributes(a: &[f32], b: &[f32], t: f32) -> Vec<f32> {
1470 let t = t.clamp(0.0, 1.0);
1471 let one_minus_t = 1.0 - t;
1472 a.iter()
1473 .zip(b.iter())
1474 .map(|(&av, &bv)| av * one_minus_t + bv * t)
1475 .collect()
1476}