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