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