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 color: 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 color: 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 color: 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 color: 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 color: 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 color: 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 color: 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, color: 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 );
460
461 Ok(())
462 }
463
464 pub(crate) fn create_camera_bind_group(
473 &self,
474 device: &wgpu::Device,
475 camera_buf: &wgpu::Buffer,
476 clip_planes_buf: &wgpu::Buffer,
477 shadow_info_buf: &wgpu::Buffer,
478 clip_volume_buf: &wgpu::Buffer,
479 label: &str,
480 ) -> wgpu::BindGroup {
481 let irr = self
482 .ibl_irradiance_view
483 .as_ref()
484 .unwrap_or(&self.ibl_fallback_view);
485 let spec = self
486 .ibl_prefiltered_view
487 .as_ref()
488 .unwrap_or(&self.ibl_fallback_view);
489 let brdf = self
490 .ibl_brdf_lut_view
491 .as_ref()
492 .unwrap_or(&self.ibl_fallback_brdf_view);
493 let skybox = self
494 .ibl_skybox_view
495 .as_ref()
496 .unwrap_or(&self.ibl_fallback_view);
497
498 device.create_bind_group(&wgpu::BindGroupDescriptor {
499 label: Some(label),
500 layout: &self.camera_bind_group_layout,
501 entries: &[
502 wgpu::BindGroupEntry {
503 binding: 0,
504 resource: camera_buf.as_entire_binding(),
505 },
506 wgpu::BindGroupEntry {
507 binding: 1,
508 resource: wgpu::BindingResource::TextureView(&self.shadow_map_view),
509 },
510 wgpu::BindGroupEntry {
511 binding: 2,
512 resource: wgpu::BindingResource::Sampler(&self.shadow_sampler),
513 },
514 wgpu::BindGroupEntry {
515 binding: 3,
516 resource: self.light_uniform_buf.as_entire_binding(),
517 },
518 wgpu::BindGroupEntry {
519 binding: 4,
520 resource: clip_planes_buf.as_entire_binding(),
521 },
522 wgpu::BindGroupEntry {
523 binding: 5,
524 resource: shadow_info_buf.as_entire_binding(),
525 },
526 wgpu::BindGroupEntry {
527 binding: 6,
528 resource: clip_volume_buf.as_entire_binding(),
529 },
530 wgpu::BindGroupEntry {
531 binding: 7,
532 resource: wgpu::BindingResource::TextureView(irr),
533 },
534 wgpu::BindGroupEntry {
535 binding: 8,
536 resource: wgpu::BindingResource::TextureView(spec),
537 },
538 wgpu::BindGroupEntry {
539 binding: 9,
540 resource: wgpu::BindingResource::TextureView(brdf),
541 },
542 wgpu::BindGroupEntry {
543 binding: 10,
544 resource: wgpu::BindingResource::Sampler(&self.ibl_sampler),
545 },
546 wgpu::BindGroupEntry {
547 binding: 11,
548 resource: wgpu::BindingResource::TextureView(skybox),
549 },
550 ],
551 })
552 }
553}
554
555pub struct ComputeFilterResult {
564 pub index_buffer: wgpu::Buffer,
566 pub index_count: u32,
568 pub mesh_id: crate::resources::mesh_store::MeshId,
570}
571
572impl ViewportGpuResources {
573 fn ensure_compute_filter_pipeline(&mut self, device: &wgpu::Device) {
575 if self.compute_filter_pipeline.is_some() {
576 return;
577 }
578
579 let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
581 label: Some("compute_filter_bgl"),
582 entries: &[
583 wgpu::BindGroupLayoutEntry {
585 binding: 0,
586 visibility: wgpu::ShaderStages::COMPUTE,
587 ty: wgpu::BindingType::Buffer {
588 ty: wgpu::BufferBindingType::Uniform,
589 has_dynamic_offset: false,
590 min_binding_size: None,
591 },
592 count: None,
593 },
594 wgpu::BindGroupLayoutEntry {
596 binding: 1,
597 visibility: wgpu::ShaderStages::COMPUTE,
598 ty: wgpu::BindingType::Buffer {
599 ty: wgpu::BufferBindingType::Storage { read_only: true },
600 has_dynamic_offset: false,
601 min_binding_size: None,
602 },
603 count: None,
604 },
605 wgpu::BindGroupLayoutEntry {
607 binding: 2,
608 visibility: wgpu::ShaderStages::COMPUTE,
609 ty: wgpu::BindingType::Buffer {
610 ty: wgpu::BufferBindingType::Storage { read_only: true },
611 has_dynamic_offset: false,
612 min_binding_size: None,
613 },
614 count: None,
615 },
616 wgpu::BindGroupLayoutEntry {
618 binding: 3,
619 visibility: wgpu::ShaderStages::COMPUTE,
620 ty: wgpu::BindingType::Buffer {
621 ty: wgpu::BufferBindingType::Storage { read_only: true },
622 has_dynamic_offset: false,
623 min_binding_size: None,
624 },
625 count: None,
626 },
627 wgpu::BindGroupLayoutEntry {
629 binding: 4,
630 visibility: wgpu::ShaderStages::COMPUTE,
631 ty: wgpu::BindingType::Buffer {
632 ty: wgpu::BufferBindingType::Storage { read_only: false },
633 has_dynamic_offset: false,
634 min_binding_size: None,
635 },
636 count: None,
637 },
638 wgpu::BindGroupLayoutEntry {
640 binding: 5,
641 visibility: wgpu::ShaderStages::COMPUTE,
642 ty: wgpu::BindingType::Buffer {
643 ty: wgpu::BufferBindingType::Storage { read_only: false },
644 has_dynamic_offset: false,
645 min_binding_size: None,
646 },
647 count: None,
648 },
649 ],
650 });
651
652 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
653 label: Some("compute_filter_layout"),
654 bind_group_layouts: &[&bgl],
655 push_constant_ranges: &[],
656 });
657
658 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
659 label: Some("compute_filter_shader"),
660 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/compute_filter.wgsl").into()),
661 });
662
663 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
664 label: Some("compute_filter_pipeline"),
665 layout: Some(&pipeline_layout),
666 module: &shader,
667 entry_point: Some("main"),
668 compilation_options: Default::default(),
669 cache: None,
670 });
671
672 self.compute_filter_bgl = Some(bgl);
673 self.compute_filter_pipeline = Some(pipeline);
674 }
675
676 #[allow(dead_code)]
685 pub(crate) fn ensure_oit_targets(&mut self, device: &wgpu::Device, w: u32, h: u32) {
686 let w = w.max(1);
687 let h = h.max(1);
688
689 let need_textures = self.oit_size != [w, h] || self.oit_accum_texture.is_none();
691
692 if need_textures {
693 self.oit_size = [w, h];
694
695 let accum_tex = device.create_texture(&wgpu::TextureDescriptor {
697 label: Some("oit_accum_texture"),
698 size: wgpu::Extent3d {
699 width: w,
700 height: h,
701 depth_or_array_layers: 1,
702 },
703 mip_level_count: 1,
704 sample_count: 1,
705 dimension: wgpu::TextureDimension::D2,
706 format: wgpu::TextureFormat::Rgba16Float,
707 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
708 | wgpu::TextureUsages::TEXTURE_BINDING,
709 view_formats: &[],
710 });
711 let accum_view = accum_tex.create_view(&wgpu::TextureViewDescriptor::default());
712
713 let reveal_tex = device.create_texture(&wgpu::TextureDescriptor {
715 label: Some("oit_reveal_texture"),
716 size: wgpu::Extent3d {
717 width: w,
718 height: h,
719 depth_or_array_layers: 1,
720 },
721 mip_level_count: 1,
722 sample_count: 1,
723 dimension: wgpu::TextureDimension::D2,
724 format: wgpu::TextureFormat::R8Unorm,
725 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
726 | wgpu::TextureUsages::TEXTURE_BINDING,
727 view_formats: &[],
728 });
729 let reveal_view = reveal_tex.create_view(&wgpu::TextureViewDescriptor::default());
730
731 let sampler = if self.oit_composite_sampler.is_none() {
733 device.create_sampler(&wgpu::SamplerDescriptor {
734 label: Some("oit_composite_sampler"),
735 address_mode_u: wgpu::AddressMode::ClampToEdge,
736 address_mode_v: wgpu::AddressMode::ClampToEdge,
737 address_mode_w: wgpu::AddressMode::ClampToEdge,
738 mag_filter: wgpu::FilterMode::Linear,
739 min_filter: wgpu::FilterMode::Linear,
740 ..Default::default()
741 })
742 } else {
743 device.create_sampler(&wgpu::SamplerDescriptor {
745 label: Some("oit_composite_sampler"),
746 address_mode_u: wgpu::AddressMode::ClampToEdge,
747 address_mode_v: wgpu::AddressMode::ClampToEdge,
748 address_mode_w: wgpu::AddressMode::ClampToEdge,
749 mag_filter: wgpu::FilterMode::Linear,
750 min_filter: wgpu::FilterMode::Linear,
751 ..Default::default()
752 })
753 };
754
755 let bgl = if self.oit_composite_bgl.is_none() {
757 let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
758 label: Some("oit_composite_bgl"),
759 entries: &[
760 wgpu::BindGroupLayoutEntry {
761 binding: 0,
762 visibility: wgpu::ShaderStages::FRAGMENT,
763 ty: wgpu::BindingType::Texture {
764 sample_type: wgpu::TextureSampleType::Float { filterable: true },
765 view_dimension: wgpu::TextureViewDimension::D2,
766 multisampled: false,
767 },
768 count: None,
769 },
770 wgpu::BindGroupLayoutEntry {
771 binding: 1,
772 visibility: wgpu::ShaderStages::FRAGMENT,
773 ty: wgpu::BindingType::Texture {
774 sample_type: wgpu::TextureSampleType::Float { filterable: true },
775 view_dimension: wgpu::TextureViewDimension::D2,
776 multisampled: false,
777 },
778 count: None,
779 },
780 wgpu::BindGroupLayoutEntry {
781 binding: 2,
782 visibility: wgpu::ShaderStages::FRAGMENT,
783 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
784 count: None,
785 },
786 ],
787 });
788 self.oit_composite_bgl = Some(bgl);
789 self.oit_composite_bgl.as_ref().unwrap()
790 } else {
791 self.oit_composite_bgl.as_ref().unwrap()
792 };
793
794 let composite_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
796 label: Some("oit_composite_bind_group"),
797 layout: bgl,
798 entries: &[
799 wgpu::BindGroupEntry {
800 binding: 0,
801 resource: wgpu::BindingResource::TextureView(&accum_view),
802 },
803 wgpu::BindGroupEntry {
804 binding: 1,
805 resource: wgpu::BindingResource::TextureView(&reveal_view),
806 },
807 wgpu::BindGroupEntry {
808 binding: 2,
809 resource: wgpu::BindingResource::Sampler(&sampler),
810 },
811 ],
812 });
813
814 self.oit_accum_texture = Some(accum_tex);
815 self.oit_accum_view = Some(accum_view);
816 self.oit_reveal_texture = Some(reveal_tex);
817 self.oit_reveal_view = Some(reveal_view);
818 self.oit_composite_sampler = Some(sampler);
819 self.oit_composite_bind_group = Some(composite_bg);
820 }
821
822 if self.oit_pipeline.is_none() {
824 let oit_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
826 label: Some("mesh_oit_shader"),
827 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/mesh_oit.wgsl").into()),
828 });
829 let oit_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
830 label: Some("oit_pipeline_layout"),
831 bind_group_layouts: &[
832 &self.camera_bind_group_layout,
833 &self.object_bind_group_layout,
834 ],
835 push_constant_ranges: &[],
836 });
837
838 let accum_blend = wgpu::BlendState {
840 color: wgpu::BlendComponent {
841 src_factor: wgpu::BlendFactor::One,
842 dst_factor: wgpu::BlendFactor::One,
843 operation: wgpu::BlendOperation::Add,
844 },
845 alpha: wgpu::BlendComponent {
846 src_factor: wgpu::BlendFactor::One,
847 dst_factor: wgpu::BlendFactor::One,
848 operation: wgpu::BlendOperation::Add,
849 },
850 };
851
852 let reveal_blend = wgpu::BlendState {
854 color: wgpu::BlendComponent {
855 src_factor: wgpu::BlendFactor::Zero,
856 dst_factor: wgpu::BlendFactor::OneMinusSrc,
857 operation: wgpu::BlendOperation::Add,
858 },
859 alpha: wgpu::BlendComponent {
860 src_factor: wgpu::BlendFactor::Zero,
861 dst_factor: wgpu::BlendFactor::OneMinusSrc,
862 operation: wgpu::BlendOperation::Add,
863 },
864 };
865
866 let oit_depth_stencil = wgpu::DepthStencilState {
867 format: wgpu::TextureFormat::Depth24PlusStencil8,
868 depth_write_enabled: false,
869 depth_compare: wgpu::CompareFunction::LessEqual,
870 stencil: wgpu::StencilState::default(),
871 bias: wgpu::DepthBiasState::default(),
872 };
873
874 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
875 label: Some("oit_pipeline"),
876 layout: Some(&oit_layout),
877 vertex: wgpu::VertexState {
878 module: &oit_shader,
879 entry_point: Some("vs_main"),
880 buffers: &[Vertex::buffer_layout()],
881 compilation_options: wgpu::PipelineCompilationOptions::default(),
882 },
883 fragment: Some(wgpu::FragmentState {
884 module: &oit_shader,
885 entry_point: Some("fs_oit_main"),
886 targets: &[
887 Some(wgpu::ColorTargetState {
888 format: wgpu::TextureFormat::Rgba16Float,
889 blend: Some(accum_blend),
890 write_mask: wgpu::ColorWrites::ALL,
891 }),
892 Some(wgpu::ColorTargetState {
893 format: wgpu::TextureFormat::R8Unorm,
894 blend: Some(reveal_blend),
895 write_mask: wgpu::ColorWrites::RED,
896 }),
897 ],
898 compilation_options: wgpu::PipelineCompilationOptions::default(),
899 }),
900 primitive: wgpu::PrimitiveState {
901 topology: wgpu::PrimitiveTopology::TriangleList,
902 cull_mode: Some(wgpu::Face::Back),
903 ..Default::default()
904 },
905 depth_stencil: Some(oit_depth_stencil.clone()),
906 multisample: wgpu::MultisampleState {
907 count: 1,
908 ..Default::default()
909 },
910 multiview: None,
911 cache: None,
912 });
913 self.oit_pipeline = Some(pipeline);
914
915 if let Some(ref instance_bgl) = self.instance_bind_group_layout {
917 let instanced_oit_shader =
918 device.create_shader_module(wgpu::ShaderModuleDescriptor {
919 label: Some("mesh_instanced_oit_shader"),
920 source: wgpu::ShaderSource::Wgsl(
921 include_str!("../shaders/mesh_instanced_oit.wgsl").into(),
922 ),
923 });
924 let instanced_oit_layout =
925 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
926 label: Some("oit_instanced_pipeline_layout"),
927 bind_group_layouts: &[&self.camera_bind_group_layout, instance_bgl],
928 push_constant_ranges: &[],
929 });
930 let instanced_pipeline =
931 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
932 label: Some("oit_instanced_pipeline"),
933 layout: Some(&instanced_oit_layout),
934 vertex: wgpu::VertexState {
935 module: &instanced_oit_shader,
936 entry_point: Some("vs_main"),
937 buffers: &[Vertex::buffer_layout()],
938 compilation_options: wgpu::PipelineCompilationOptions::default(),
939 },
940 fragment: Some(wgpu::FragmentState {
941 module: &instanced_oit_shader,
942 entry_point: Some("fs_oit_main"),
943 targets: &[
944 Some(wgpu::ColorTargetState {
945 format: wgpu::TextureFormat::Rgba16Float,
946 blend: Some(accum_blend),
947 write_mask: wgpu::ColorWrites::ALL,
948 }),
949 Some(wgpu::ColorTargetState {
950 format: wgpu::TextureFormat::R8Unorm,
951 blend: Some(reveal_blend),
952 write_mask: wgpu::ColorWrites::RED,
953 }),
954 ],
955 compilation_options: wgpu::PipelineCompilationOptions::default(),
956 }),
957 primitive: wgpu::PrimitiveState {
958 topology: wgpu::PrimitiveTopology::TriangleList,
959 cull_mode: Some(wgpu::Face::Back),
960 ..Default::default()
961 },
962 depth_stencil: Some(oit_depth_stencil),
963 multisample: wgpu::MultisampleState {
964 count: 1,
965 ..Default::default()
966 },
967 multiview: None,
968 cache: None,
969 });
970 self.oit_instanced_pipeline = Some(instanced_pipeline);
971 }
972 }
973
974 if self.oit_composite_pipeline.is_none() {
975 let comp_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
976 label: Some("oit_composite_shader"),
977 source: wgpu::ShaderSource::Wgsl(
978 include_str!("../shaders/oit_composite.wgsl").into(),
979 ),
980 });
981 let bgl = self
982 .oit_composite_bgl
983 .as_ref()
984 .expect("oit_composite_bgl must exist");
985 let comp_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
986 label: Some("oit_composite_pipeline_layout"),
987 bind_group_layouts: &[bgl],
988 push_constant_ranges: &[],
989 });
990 let premul_blend = wgpu::BlendState {
992 color: wgpu::BlendComponent {
993 src_factor: wgpu::BlendFactor::One,
994 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
995 operation: wgpu::BlendOperation::Add,
996 },
997 alpha: wgpu::BlendComponent {
998 src_factor: wgpu::BlendFactor::One,
999 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
1000 operation: wgpu::BlendOperation::Add,
1001 },
1002 };
1003 let comp_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1004 label: Some("oit_composite_pipeline"),
1005 layout: Some(&comp_layout),
1006 vertex: wgpu::VertexState {
1007 module: &comp_shader,
1008 entry_point: Some("vs_main"),
1009 buffers: &[],
1010 compilation_options: wgpu::PipelineCompilationOptions::default(),
1011 },
1012 fragment: Some(wgpu::FragmentState {
1013 module: &comp_shader,
1014 entry_point: Some("fs_main"),
1015 targets: &[Some(wgpu::ColorTargetState {
1016 format: wgpu::TextureFormat::Rgba16Float,
1017 blend: Some(premul_blend),
1018 write_mask: wgpu::ColorWrites::ALL,
1019 })],
1020 compilation_options: wgpu::PipelineCompilationOptions::default(),
1021 }),
1022 primitive: wgpu::PrimitiveState {
1023 topology: wgpu::PrimitiveTopology::TriangleList,
1024 ..Default::default()
1025 },
1026 depth_stencil: None,
1027 multisample: wgpu::MultisampleState {
1028 count: 1,
1029 ..Default::default()
1030 },
1031 multiview: None,
1032 cache: None,
1033 });
1034 self.oit_composite_pipeline = Some(comp_pipeline);
1035 }
1036 }
1037
1038 pub fn run_compute_filters(
1047 &mut self,
1048 device: &wgpu::Device,
1049 queue: &wgpu::Queue,
1050 items: &[crate::renderer::ComputeFilterItem],
1051 ) -> Vec<ComputeFilterResult> {
1052 if items.is_empty() {
1053 return Vec::new();
1054 }
1055
1056 self.ensure_compute_filter_pipeline(device);
1057
1058 let dummy_scalar_buf = device.create_buffer(&wgpu::BufferDescriptor {
1060 label: Some("compute_filter_dummy_scalar"),
1061 size: 4,
1062 usage: wgpu::BufferUsages::STORAGE,
1063 mapped_at_creation: false,
1064 });
1065
1066 let mut results = Vec::with_capacity(items.len());
1067
1068 for item in items {
1069 let gpu_mesh = match self
1071 .mesh_store
1072 .get(item.mesh_id)
1073 {
1074 Some(m) => m,
1075 None => continue,
1076 };
1077
1078 let triangle_count = gpu_mesh.index_count / 3;
1079 if triangle_count == 0 {
1080 continue;
1081 }
1082
1083 const VERTEX_STRIDE_F32: u32 = 16;
1085
1086 #[repr(C)]
1088 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
1089 struct FilterParams {
1090 mode: u32,
1091 clip_type: u32,
1092 threshold_min: f32,
1093 threshold_max: f32,
1094 triangle_count: u32,
1095 vertex_stride_f32: u32,
1096 _pad: [u32; 2],
1097 plane_nx: f32,
1099 plane_ny: f32,
1100 plane_nz: f32,
1101 plane_dist: f32,
1102 box_cx: f32,
1104 box_cy: f32,
1105 box_cz: f32,
1106 _padb0: f32,
1107 box_hex: f32,
1108 box_hey: f32,
1109 box_hez: f32,
1110 _padb1: f32,
1111 box_col0x: f32,
1112 box_col0y: f32,
1113 box_col0z: f32,
1114 _padb2: f32,
1115 box_col1x: f32,
1116 box_col1y: f32,
1117 box_col1z: f32,
1118 _padb3: f32,
1119 box_col2x: f32,
1120 box_col2y: f32,
1121 box_col2z: f32,
1122 _padb4: f32,
1123 sphere_cx: f32,
1125 sphere_cy: f32,
1126 sphere_cz: f32,
1127 sphere_radius: f32,
1128 }
1129
1130 let mut params: FilterParams = bytemuck::Zeroable::zeroed();
1131 params.triangle_count = triangle_count;
1132 params.vertex_stride_f32 = VERTEX_STRIDE_F32;
1133
1134 match item.kind {
1135 crate::renderer::ComputeFilterKind::Clip {
1136 plane_normal,
1137 plane_dist,
1138 } => {
1139 params.mode = 0;
1140 params.clip_type = 1;
1141 params.plane_nx = plane_normal[0];
1142 params.plane_ny = plane_normal[1];
1143 params.plane_nz = plane_normal[2];
1144 params.plane_dist = plane_dist;
1145 }
1146 crate::renderer::ComputeFilterKind::ClipBox {
1147 center,
1148 half_extents,
1149 orientation,
1150 } => {
1151 params.mode = 0;
1152 params.clip_type = 2;
1153 params.box_cx = center[0];
1154 params.box_cy = center[1];
1155 params.box_cz = center[2];
1156 params.box_hex = half_extents[0];
1157 params.box_hey = half_extents[1];
1158 params.box_hez = half_extents[2];
1159 params.box_col0x = orientation[0][0];
1160 params.box_col0y = orientation[0][1];
1161 params.box_col0z = orientation[0][2];
1162 params.box_col1x = orientation[1][0];
1163 params.box_col1y = orientation[1][1];
1164 params.box_col1z = orientation[1][2];
1165 params.box_col2x = orientation[2][0];
1166 params.box_col2y = orientation[2][1];
1167 params.box_col2z = orientation[2][2];
1168 }
1169 crate::renderer::ComputeFilterKind::ClipSphere { center, radius } => {
1170 params.mode = 0;
1171 params.clip_type = 3;
1172 params.sphere_cx = center[0];
1173 params.sphere_cy = center[1];
1174 params.sphere_cz = center[2];
1175 params.sphere_radius = radius;
1176 }
1177 crate::renderer::ComputeFilterKind::Threshold { min, max } => {
1178 params.mode = 1;
1179 params.threshold_min = min;
1180 params.threshold_max = max;
1181 }
1182 }
1183
1184 let params_buf = device.create_buffer(&wgpu::BufferDescriptor {
1185 label: Some("compute_filter_params"),
1186 size: std::mem::size_of::<FilterParams>() as u64,
1187 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
1188 mapped_at_creation: false,
1189 });
1190 queue.write_buffer(¶ms_buf, 0, bytemuck::bytes_of(¶ms));
1191
1192 let out_index_size = (gpu_mesh.index_count as u64) * 4;
1194 let out_index_buf = device.create_buffer(&wgpu::BufferDescriptor {
1195 label: Some("compute_filter_out_indices"),
1196 size: out_index_size.max(4),
1197 usage: wgpu::BufferUsages::STORAGE
1198 | wgpu::BufferUsages::INDEX
1199 | wgpu::BufferUsages::COPY_SRC,
1200 mapped_at_creation: false,
1201 });
1202
1203 let counter_buf = device.create_buffer(&wgpu::BufferDescriptor {
1205 label: Some("compute_filter_counter"),
1206 size: 4,
1207 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
1208 mapped_at_creation: true,
1209 });
1210 {
1211 let mut view = counter_buf.slice(..).get_mapped_range_mut();
1212 view[0..4].copy_from_slice(&0u32.to_le_bytes());
1213 }
1214 counter_buf.unmap();
1215
1216 let staging_buf = device.create_buffer(&wgpu::BufferDescriptor {
1218 label: Some("compute_filter_counter_staging"),
1219 size: 4,
1220 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
1221 mapped_at_creation: false,
1222 });
1223
1224 let scalar_buf_ref: &wgpu::Buffer = match &item.kind {
1226 crate::renderer::ComputeFilterKind::Threshold { .. } => {
1227 if let Some(attr_name) = &item.attribute_name {
1228 gpu_mesh
1229 .attribute_buffers
1230 .get(attr_name.as_str())
1231 .unwrap_or(&dummy_scalar_buf)
1232 } else {
1233 &dummy_scalar_buf
1234 }
1235 }
1236 _ => &dummy_scalar_buf,
1238 };
1239
1240 let bgl = self.compute_filter_bgl.as_ref().unwrap();
1242 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
1243 label: Some("compute_filter_bg"),
1244 layout: bgl,
1245 entries: &[
1246 wgpu::BindGroupEntry {
1247 binding: 0,
1248 resource: params_buf.as_entire_binding(),
1249 },
1250 wgpu::BindGroupEntry {
1251 binding: 1,
1252 resource: gpu_mesh.vertex_buffer.as_entire_binding(),
1253 },
1254 wgpu::BindGroupEntry {
1255 binding: 2,
1256 resource: gpu_mesh.index_buffer.as_entire_binding(),
1257 },
1258 wgpu::BindGroupEntry {
1259 binding: 3,
1260 resource: scalar_buf_ref.as_entire_binding(),
1261 },
1262 wgpu::BindGroupEntry {
1263 binding: 4,
1264 resource: out_index_buf.as_entire_binding(),
1265 },
1266 wgpu::BindGroupEntry {
1267 binding: 5,
1268 resource: counter_buf.as_entire_binding(),
1269 },
1270 ],
1271 });
1272
1273 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
1275 label: Some("compute_filter_encoder"),
1276 });
1277
1278 {
1279 let pipeline = self.compute_filter_pipeline.as_ref().unwrap();
1280 let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
1281 label: Some("compute_filter_pass"),
1282 timestamp_writes: None,
1283 });
1284 cpass.set_pipeline(pipeline);
1285 cpass.set_bind_group(0, &bind_group, &[]);
1286 let workgroups = triangle_count.div_ceil(64);
1287 cpass.dispatch_workgroups(workgroups, 1, 1);
1288 }
1289
1290 encoder.copy_buffer_to_buffer(&counter_buf, 0, &staging_buf, 0, 4);
1291 queue.submit(std::iter::once(encoder.finish()));
1292
1293 let slice = staging_buf.slice(..);
1295 slice.map_async(wgpu::MapMode::Read, |_| {});
1296 let _ = device.poll(wgpu::PollType::Wait {
1297 submission_index: None,
1298 timeout: Some(std::time::Duration::from_secs(5)),
1299 });
1300
1301 let index_count = {
1302 let data = slice.get_mapped_range();
1303 u32::from_le_bytes([data[0], data[1], data[2], data[3]])
1304 };
1305 staging_buf.unmap();
1306
1307 results.push(ComputeFilterResult {
1308 index_buffer: out_index_buf,
1309 index_count,
1310 mesh_id: item.mesh_id,
1311 });
1312 }
1313
1314 results
1315 }
1316
1317 pub(crate) fn ensure_pick_pipeline(&mut self, device: &wgpu::Device) {
1326 if self.pick_pipeline.is_some() {
1327 return;
1328 }
1329
1330 let pick_camera_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1335 label: Some("pick_camera_bgl"),
1336 entries: &[
1337 wgpu::BindGroupLayoutEntry {
1338 binding: 0,
1339 visibility: wgpu::ShaderStages::VERTEX,
1340 ty: wgpu::BindingType::Buffer {
1341 ty: wgpu::BufferBindingType::Uniform,
1342 has_dynamic_offset: false,
1343 min_binding_size: None,
1344 },
1345 count: None,
1346 },
1347 wgpu::BindGroupLayoutEntry {
1348 binding: 6,
1349 visibility: wgpu::ShaderStages::FRAGMENT,
1350 ty: wgpu::BindingType::Buffer {
1351 ty: wgpu::BufferBindingType::Uniform,
1352 has_dynamic_offset: false,
1353 min_binding_size: None,
1354 },
1355 count: None,
1356 },
1357 ],
1358 });
1359
1360 let pick_instance_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1362 label: Some("pick_instance_bgl"),
1363 entries: &[wgpu::BindGroupLayoutEntry {
1364 binding: 0,
1365 visibility: wgpu::ShaderStages::VERTEX,
1366 ty: wgpu::BindingType::Buffer {
1367 ty: wgpu::BufferBindingType::Storage { read_only: true },
1368 has_dynamic_offset: false,
1369 min_binding_size: None,
1370 },
1371 count: None,
1372 }],
1373 });
1374
1375 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1376 label: Some("pick_id_shader"),
1377 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/pick_id.wgsl").into()),
1378 });
1379
1380 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1381 label: Some("pick_pipeline_layout"),
1382 bind_group_layouts: &[&pick_camera_bgl, &pick_instance_bgl],
1383 push_constant_ranges: &[],
1384 });
1385
1386 let pick_vertex_layout = wgpu::VertexBufferLayout {
1388 array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex,
1390 attributes: &[wgpu::VertexAttribute {
1391 offset: 0,
1392 shader_location: 0,
1393 format: wgpu::VertexFormat::Float32x3,
1394 }],
1395 };
1396
1397 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1398 label: Some("pick_pipeline"),
1399 layout: Some(&layout),
1400 vertex: wgpu::VertexState {
1401 module: &shader,
1402 entry_point: Some("vs_main"),
1403 buffers: &[pick_vertex_layout],
1404 compilation_options: wgpu::PipelineCompilationOptions::default(),
1405 },
1406 fragment: Some(wgpu::FragmentState {
1407 module: &shader,
1408 entry_point: Some("fs_main"),
1409 targets: &[
1410 Some(wgpu::ColorTargetState {
1412 format: wgpu::TextureFormat::R32Uint,
1413 blend: None, write_mask: wgpu::ColorWrites::ALL,
1415 }),
1416 Some(wgpu::ColorTargetState {
1418 format: wgpu::TextureFormat::R32Float,
1419 blend: None,
1420 write_mask: wgpu::ColorWrites::ALL,
1421 }),
1422 ],
1423 compilation_options: wgpu::PipelineCompilationOptions::default(),
1424 }),
1425 primitive: wgpu::PrimitiveState {
1426 topology: wgpu::PrimitiveTopology::TriangleList,
1427 front_face: wgpu::FrontFace::Ccw,
1428 cull_mode: None, ..Default::default()
1430 },
1431 depth_stencil: Some(wgpu::DepthStencilState {
1432 format: wgpu::TextureFormat::Depth24PlusStencil8,
1433 depth_write_enabled: true,
1434 depth_compare: wgpu::CompareFunction::Less,
1435 stencil: wgpu::StencilState::default(),
1436 bias: wgpu::DepthBiasState::default(),
1437 }),
1438 multisample: wgpu::MultisampleState {
1439 count: 1, ..Default::default()
1441 },
1442 multiview: None,
1443 cache: None,
1444 });
1445
1446 self.pick_camera_bgl = Some(pick_camera_bgl);
1447 self.pick_bind_group_layout_1 = Some(pick_instance_bgl);
1448 self.pick_pipeline = Some(pipeline);
1449 }
1450}
1451
1452pub fn lerp_attributes(a: &[f32], b: &[f32], t: f32) -> Vec<f32> {
1464 let t = t.clamp(0.0, 1.0);
1465 let one_minus_t = 1.0 - t;
1466 a.iter()
1467 .zip(b.iter())
1468 .map(|(&av, &bv)| av * one_minus_t + bv * t)
1469 .collect()
1470}