1use super::*;
2
3pub(super) fn generate_edge_indices(triangle_indices: &[u32]) -> Vec<u32> {
4 use std::collections::HashSet;
5 let mut edges: HashSet<(u32, u32)> = HashSet::new();
6 let mut result = Vec::new();
7
8 for tri in triangle_indices.chunks(3) {
9 if tri.len() < 3 {
10 continue;
11 }
12 let pairs = [(tri[0], tri[1]), (tri[1], tri[2]), (tri[2], tri[0])];
13 for (a, b) in &pairs {
14 let edge = if a < b { (*a, *b) } else { (*b, *a) };
16 if edges.insert(edge) {
17 result.push(*a);
18 result.push(*b);
19 }
20 }
21 }
22 result
23}
24
25pub(super) fn build_unit_cube() -> (Vec<Vertex>, Vec<u32>) {
34 let white = [1.0f32, 1.0, 1.0, 1.0];
35 let mut verts: Vec<Vertex> = Vec::with_capacity(24);
36 let mut idx: Vec<u32> = Vec::with_capacity(36);
37
38 let mut add_face = |positions: [[f32; 3]; 4], normal: [f32; 3]| {
40 let base = verts.len() as u32;
41 for pos in &positions {
42 verts.push(Vertex {
43 position: *pos,
44 normal,
45 colour: white,
46 uv: [0.0, 0.0],
47 tangent: [0.0, 0.0, 0.0, 1.0],
48 });
49 }
50 idx.extend_from_slice(&[base, base + 1, base + 2, base, base + 2, base + 3]);
52 };
53
54 add_face(
56 [
57 [0.5, -0.5, -0.5],
58 [0.5, 0.5, -0.5],
59 [0.5, 0.5, 0.5],
60 [0.5, -0.5, 0.5],
61 ],
62 [1.0, 0.0, 0.0],
63 );
64
65 add_face(
67 [
68 [-0.5, -0.5, 0.5],
69 [-0.5, 0.5, 0.5],
70 [-0.5, 0.5, -0.5],
71 [-0.5, -0.5, -0.5],
72 ],
73 [-1.0, 0.0, 0.0],
74 );
75
76 add_face(
78 [
79 [-0.5, 0.5, -0.5],
80 [-0.5, 0.5, 0.5],
81 [0.5, 0.5, 0.5],
82 [0.5, 0.5, -0.5],
83 ],
84 [0.0, 1.0, 0.0],
85 );
86
87 add_face(
89 [
90 [-0.5, -0.5, 0.5],
91 [-0.5, -0.5, -0.5],
92 [0.5, -0.5, -0.5],
93 [0.5, -0.5, 0.5],
94 ],
95 [0.0, -1.0, 0.0],
96 );
97
98 add_face(
100 [
101 [-0.5, -0.5, 0.5],
102 [0.5, -0.5, 0.5],
103 [0.5, 0.5, 0.5],
104 [-0.5, 0.5, 0.5],
105 ],
106 [0.0, 0.0, 1.0],
107 );
108
109 add_face(
111 [
112 [0.5, -0.5, -0.5],
113 [-0.5, -0.5, -0.5],
114 [-0.5, 0.5, -0.5],
115 [0.5, 0.5, -0.5],
116 ],
117 [0.0, 0.0, -1.0],
118 );
119
120 (verts, idx)
121}
122
123pub(super) fn build_glyph_arrow() -> (Vec<Vertex>, Vec<u32>) {
135 let white = [1.0f32, 1.0, 1.0, 1.0];
136 let segments = 16usize;
137 let mut verts: Vec<Vertex> = Vec::new();
138 let mut idx: Vec<u32> = Vec::new();
139
140 let shaft_r = 0.05f32;
141 let shaft_bot = 0.0f32;
142 let shaft_top = 0.7f32;
143 let cone_r = 0.12f32;
144 let cone_bot = shaft_top;
145 let cone_tip = 1.0f32;
146
147 let ring_verts = |verts: &mut Vec<Vertex>, y: f32, r: f32, normal_y: f32| {
149 for i in 0..segments {
150 let angle = 2.0 * std::f32::consts::PI * (i as f32) / (segments as f32);
151 let (s, c) = angle.sin_cos();
152 let nx = if r > 0.0 { c } else { 0.0 };
153 let nz = if r > 0.0 { s } else { 0.0 };
154 let len = (nx * nx + normal_y * normal_y + nz * nz).sqrt();
155 verts.push(Vertex {
156 position: [c * r, y, s * r],
157 normal: [nx / len, normal_y / len, nz / len],
158 colour: white,
159 uv: [0.0, 0.0],
160 tangent: [0.0, 0.0, 0.0, 1.0],
161 });
162 }
163 };
164
165 let shaft_bot_base = verts.len() as u32;
168 ring_verts(&mut verts, shaft_bot, shaft_r, 0.0);
169
170 let shaft_bot_center = verts.len() as u32;
172 verts.push(Vertex {
173 position: [0.0, shaft_bot, 0.0],
174 normal: [0.0, -1.0, 0.0],
175 colour: white,
176 uv: [0.0, 0.0],
177 tangent: [0.0, 0.0, 0.0, 1.0],
178 });
179
180 for i in 0..segments {
182 let a = shaft_bot_base + i as u32;
183 let b = shaft_bot_base + ((i + 1) % segments) as u32;
184 idx.extend_from_slice(&[shaft_bot_center, b, a]);
185 }
186
187 let shaft_top_ring_base = verts.len() as u32;
189 ring_verts(&mut verts, shaft_bot, shaft_r, 0.0); let shaft_top_ring_top = verts.len() as u32;
191 ring_verts(&mut verts, shaft_top, shaft_r, 0.0);
192 for i in 0..segments {
193 let a = shaft_top_ring_base + i as u32;
194 let b = shaft_top_ring_base + ((i + 1) % segments) as u32;
195 let c = shaft_top_ring_top + i as u32;
196 let d = shaft_top_ring_top + ((i + 1) % segments) as u32;
197 idx.extend_from_slice(&[a, b, d, a, d, c]);
198 }
199
200 let cone_len = ((cone_tip - cone_bot).powi(2) + cone_r * cone_r).sqrt();
203 let normal_y_cone = cone_r / cone_len; let normal_r_cone = (cone_tip - cone_bot) / cone_len;
205
206 let cone_base_ring = verts.len() as u32;
207 for i in 0..segments {
208 let angle = 2.0 * std::f32::consts::PI * (i as f32) / (segments as f32);
209 let (s, c) = angle.sin_cos();
210 verts.push(Vertex {
211 position: [c * cone_r, cone_bot, s * cone_r],
212 normal: [c * normal_r_cone, normal_y_cone, s * normal_r_cone],
213 colour: white,
214 uv: [0.0, 0.0],
215 tangent: [0.0, 0.0, 0.0, 1.0],
216 });
217 }
218
219 let cone_tip_v = verts.len() as u32;
221 verts.push(Vertex {
222 position: [0.0, cone_tip, 0.0],
223 normal: [0.0, 1.0, 0.0],
224 colour: white,
225 uv: [0.0, 0.0],
226 tangent: [0.0, 0.0, 0.0, 1.0],
227 });
228
229 for i in 0..segments {
230 let a = cone_base_ring + i as u32;
231 let b = cone_base_ring + ((i + 1) % segments) as u32;
232 idx.extend_from_slice(&[a, b, cone_tip_v]);
233 }
234
235 let cone_cap_base = verts.len() as u32;
237 for i in 0..segments {
238 let angle = 2.0 * std::f32::consts::PI * (i as f32) / (segments as f32);
239 let (s, c) = angle.sin_cos();
240 verts.push(Vertex {
241 position: [c * cone_r, cone_bot, s * cone_r],
242 normal: [0.0, -1.0, 0.0],
243 colour: white,
244 uv: [0.0, 0.0],
245 tangent: [0.0, 0.0, 0.0, 1.0],
246 });
247 }
248 let cone_cap_center = verts.len() as u32;
249 verts.push(Vertex {
250 position: [0.0, cone_bot, 0.0],
251 normal: [0.0, -1.0, 0.0],
252 colour: white,
253 uv: [0.0, 0.0],
254 tangent: [0.0, 0.0, 0.0, 1.0],
255 });
256 for i in 0..segments {
257 let a = cone_cap_base + i as u32;
258 let b = cone_cap_base + ((i + 1) % segments) as u32;
259 idx.extend_from_slice(&[cone_cap_center, b, a]);
260 }
261
262 (verts, idx)
263}
264
265pub(super) fn build_glyph_sphere() -> (Vec<Vertex>, Vec<u32>) {
273 let white = [1.0f32, 1.0, 1.0, 1.0];
274
275 let t = (1.0 + 5.0f32.sqrt()) / 2.0;
277
278 let raw_verts = [
280 [-1.0, t, 0.0],
281 [1.0, t, 0.0],
282 [-1.0, -t, 0.0],
283 [1.0, -t, 0.0],
284 [0.0, -1.0, t],
285 [0.0, 1.0, t],
286 [0.0, -1.0, -t],
287 [0.0, 1.0, -t],
288 [t, 0.0, -1.0],
289 [t, 0.0, 1.0],
290 [-t, 0.0, -1.0],
291 [-t, 0.0, 1.0],
292 ];
293
294 let mut positions: Vec<[f32; 3]> = raw_verts
295 .iter()
296 .map(|v| {
297 let l = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
298 [v[0] / l, v[1] / l, v[2] / l]
299 })
300 .collect();
301
302 let mut triangles: Vec<[usize; 3]> = vec![
304 [0, 11, 5],
305 [0, 5, 1],
306 [0, 1, 7],
307 [0, 7, 10],
308 [0, 10, 11],
309 [1, 5, 9],
310 [5, 11, 4],
311 [11, 10, 2],
312 [10, 7, 6],
313 [7, 1, 8],
314 [3, 9, 4],
315 [3, 4, 2],
316 [3, 2, 6],
317 [3, 6, 8],
318 [3, 8, 9],
319 [4, 9, 5],
320 [2, 4, 11],
321 [6, 2, 10],
322 [8, 6, 7],
323 [9, 8, 1],
324 ];
325
326 for _ in 0..2 {
328 let mut mid_cache: std::collections::HashMap<(usize, usize), usize> =
329 std::collections::HashMap::new();
330 let mut new_triangles: Vec<[usize; 3]> = Vec::with_capacity(triangles.len() * 4);
331
332 let midpoint = |positions: &mut Vec<[f32; 3]>,
333 a: usize,
334 b: usize,
335 cache: &mut std::collections::HashMap<(usize, usize), usize>|
336 -> usize {
337 let key = if a < b { (a, b) } else { (b, a) };
338 if let Some(&idx) = cache.get(&key) {
339 return idx;
340 }
341 let pa = positions[a];
342 let pb = positions[b];
343 let mx = (pa[0] + pb[0]) * 0.5;
344 let my = (pa[1] + pb[1]) * 0.5;
345 let mz = (pa[2] + pb[2]) * 0.5;
346 let l = (mx * mx + my * my + mz * mz).sqrt();
347 let idx = positions.len();
348 positions.push([mx / l, my / l, mz / l]);
349 cache.insert(key, idx);
350 idx
351 };
352
353 for tri in &triangles {
354 let a = tri[0];
355 let b = tri[1];
356 let c = tri[2];
357 let ab = midpoint(&mut positions, a, b, &mut mid_cache);
358 let bc = midpoint(&mut positions, b, c, &mut mid_cache);
359 let ca = midpoint(&mut positions, c, a, &mut mid_cache);
360 new_triangles.push([a, ab, ca]);
361 new_triangles.push([b, bc, ab]);
362 new_triangles.push([c, ca, bc]);
363 new_triangles.push([ab, bc, ca]);
364 }
365 triangles = new_triangles;
366 }
367
368 let verts: Vec<Vertex> = positions
369 .iter()
370 .map(|&p| Vertex {
371 position: p,
372 normal: p, colour: white,
374 uv: [0.0, 0.0],
375 tangent: [0.0, 0.0, 0.0, 1.0],
376 })
377 .collect();
378
379 let idx: Vec<u32> = triangles
380 .iter()
381 .flat_map(|t| [t[0] as u32, t[1] as u32, t[2] as u32])
382 .collect();
383
384 (verts, idx)
385}
386
387impl ViewportGpuResources {
396 pub fn replace_attribute(
409 &mut self,
410 queue: &wgpu::Queue,
411 mesh_id: crate::resources::mesh_store::MeshId,
412 name: &str,
413 data: &[f32],
414 ) -> crate::error::ViewportResult<()> {
415 let gpu_mesh =
417 self.mesh_store
418 .get_mut(mesh_id)
419 .ok_or(crate::error::ViewportError::MeshSlotEmpty {
420 index: mesh_id.index(),
421 })?;
422
423 let buffer = gpu_mesh.attribute_buffers.get(name).ok_or_else(|| {
425 crate::error::ViewportError::AttributeNotFound {
426 mesh_id: mesh_id.index(),
427 name: name.to_string(),
428 }
429 })?;
430
431 let expected_elems = (buffer.size() / 4) as usize;
433 if data.len() != expected_elems {
434 return Err(crate::error::ViewportError::AttributeLengthMismatch {
435 expected: expected_elems,
436 got: data.len(),
437 });
438 }
439
440 queue.write_buffer(buffer, 0, bytemuck::cast_slice(data));
442
443 let (min, max) = data
445 .iter()
446 .fold((f32::MAX, f32::MIN), |(mn, mx), &v| (mn.min(v), mx.max(v)));
447 let range = if min > max { (0.0, 1.0) } else { (min, max) };
448 gpu_mesh.attribute_ranges.insert(name.to_string(), range);
449
450 gpu_mesh.last_tex_key = (
452 gpu_mesh.last_tex_key.0,
453 gpu_mesh.last_tex_key.1,
454 gpu_mesh.last_tex_key.2,
455 gpu_mesh.last_tex_key.3,
456 u64::MAX, gpu_mesh.last_tex_key.5,
458 gpu_mesh.last_tex_key.6,
459 gpu_mesh.last_tex_key.7,
460 gpu_mesh.last_tex_key.8,
461 );
462
463 Ok(())
464 }
465
466 pub(crate) fn create_camera_bind_group(
475 &self,
476 device: &wgpu::Device,
477 camera_buf: &wgpu::Buffer,
478 clip_planes_buf: &wgpu::Buffer,
479 shadow_info_buf: &wgpu::Buffer,
480 clip_volume_buf: &wgpu::Buffer,
481 label: &str,
482 ) -> wgpu::BindGroup {
483 let irr = self
484 .ibl_irradiance_view
485 .as_ref()
486 .unwrap_or(&self.ibl_fallback_view);
487 let spec = self
488 .ibl_prefiltered_view
489 .as_ref()
490 .unwrap_or(&self.ibl_fallback_view);
491 let brdf = self
492 .ibl_brdf_lut_view
493 .as_ref()
494 .unwrap_or(&self.ibl_fallback_brdf_view);
495 let skybox = self
496 .ibl_skybox_view
497 .as_ref()
498 .unwrap_or(&self.ibl_fallback_view);
499
500 device.create_bind_group(&wgpu::BindGroupDescriptor {
501 label: Some(label),
502 layout: &self.camera_bind_group_layout,
503 entries: &[
504 wgpu::BindGroupEntry {
505 binding: 0,
506 resource: camera_buf.as_entire_binding(),
507 },
508 wgpu::BindGroupEntry {
509 binding: 1,
510 resource: wgpu::BindingResource::TextureView(&self.shadow_map_view),
511 },
512 wgpu::BindGroupEntry {
513 binding: 2,
514 resource: wgpu::BindingResource::Sampler(&self.shadow_sampler),
515 },
516 wgpu::BindGroupEntry {
517 binding: 3,
518 resource: self.light_uniform_buf.as_entire_binding(),
519 },
520 wgpu::BindGroupEntry {
521 binding: 4,
522 resource: clip_planes_buf.as_entire_binding(),
523 },
524 wgpu::BindGroupEntry {
525 binding: 5,
526 resource: shadow_info_buf.as_entire_binding(),
527 },
528 wgpu::BindGroupEntry {
529 binding: 6,
530 resource: clip_volume_buf.as_entire_binding(),
531 },
532 wgpu::BindGroupEntry {
533 binding: 7,
534 resource: wgpu::BindingResource::TextureView(irr),
535 },
536 wgpu::BindGroupEntry {
537 binding: 8,
538 resource: wgpu::BindingResource::TextureView(spec),
539 },
540 wgpu::BindGroupEntry {
541 binding: 9,
542 resource: wgpu::BindingResource::TextureView(brdf),
543 },
544 wgpu::BindGroupEntry {
545 binding: 10,
546 resource: wgpu::BindingResource::Sampler(&self.ibl_sampler),
547 },
548 wgpu::BindGroupEntry {
549 binding: 11,
550 resource: wgpu::BindingResource::TextureView(skybox),
551 },
552 ],
553 })
554 }
555}
556
557pub struct ComputeFilterResult {
566 pub index_buffer: wgpu::Buffer,
568 pub index_count: u32,
570 pub mesh_id: crate::resources::mesh_store::MeshId,
572}
573
574impl ViewportGpuResources {
575 fn ensure_compute_filter_pipeline(&mut self, device: &wgpu::Device) {
577 if self.compute_filter_pipeline.is_some() {
578 return;
579 }
580
581 let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
583 label: Some("compute_filter_bgl"),
584 entries: &[
585 wgpu::BindGroupLayoutEntry {
587 binding: 0,
588 visibility: wgpu::ShaderStages::COMPUTE,
589 ty: wgpu::BindingType::Buffer {
590 ty: wgpu::BufferBindingType::Uniform,
591 has_dynamic_offset: false,
592 min_binding_size: None,
593 },
594 count: None,
595 },
596 wgpu::BindGroupLayoutEntry {
598 binding: 1,
599 visibility: wgpu::ShaderStages::COMPUTE,
600 ty: wgpu::BindingType::Buffer {
601 ty: wgpu::BufferBindingType::Storage { read_only: true },
602 has_dynamic_offset: false,
603 min_binding_size: None,
604 },
605 count: None,
606 },
607 wgpu::BindGroupLayoutEntry {
609 binding: 2,
610 visibility: wgpu::ShaderStages::COMPUTE,
611 ty: wgpu::BindingType::Buffer {
612 ty: wgpu::BufferBindingType::Storage { read_only: true },
613 has_dynamic_offset: false,
614 min_binding_size: None,
615 },
616 count: None,
617 },
618 wgpu::BindGroupLayoutEntry {
620 binding: 3,
621 visibility: wgpu::ShaderStages::COMPUTE,
622 ty: wgpu::BindingType::Buffer {
623 ty: wgpu::BufferBindingType::Storage { read_only: true },
624 has_dynamic_offset: false,
625 min_binding_size: None,
626 },
627 count: None,
628 },
629 wgpu::BindGroupLayoutEntry {
631 binding: 4,
632 visibility: wgpu::ShaderStages::COMPUTE,
633 ty: wgpu::BindingType::Buffer {
634 ty: wgpu::BufferBindingType::Storage { read_only: false },
635 has_dynamic_offset: false,
636 min_binding_size: None,
637 },
638 count: None,
639 },
640 wgpu::BindGroupLayoutEntry {
642 binding: 5,
643 visibility: wgpu::ShaderStages::COMPUTE,
644 ty: wgpu::BindingType::Buffer {
645 ty: wgpu::BufferBindingType::Storage { read_only: false },
646 has_dynamic_offset: false,
647 min_binding_size: None,
648 },
649 count: None,
650 },
651 ],
652 });
653
654 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
655 label: Some("compute_filter_layout"),
656 bind_group_layouts: &[&bgl],
657 push_constant_ranges: &[],
658 });
659
660 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
661 label: Some("compute_filter_shader"),
662 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/compute_filter.wgsl").into()),
663 });
664
665 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
666 label: Some("compute_filter_pipeline"),
667 layout: Some(&pipeline_layout),
668 module: &shader,
669 entry_point: Some("main"),
670 compilation_options: Default::default(),
671 cache: None,
672 });
673
674 self.compute_filter_bgl = Some(bgl);
675 self.compute_filter_pipeline = Some(pipeline);
676 }
677
678 #[allow(dead_code)]
687 pub(crate) fn ensure_oit_targets(&mut self, device: &wgpu::Device, w: u32, h: u32) {
688 let w = w.max(1);
689 let h = h.max(1);
690
691 let need_textures = self.oit_size != [w, h] || self.oit_accum_texture.is_none();
693
694 if need_textures {
695 self.oit_size = [w, h];
696
697 let accum_tex = device.create_texture(&wgpu::TextureDescriptor {
699 label: Some("oit_accum_texture"),
700 size: wgpu::Extent3d {
701 width: w,
702 height: h,
703 depth_or_array_layers: 1,
704 },
705 mip_level_count: 1,
706 sample_count: 1,
707 dimension: wgpu::TextureDimension::D2,
708 format: wgpu::TextureFormat::Rgba16Float,
709 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
710 | wgpu::TextureUsages::TEXTURE_BINDING,
711 view_formats: &[],
712 });
713 let accum_view = accum_tex.create_view(&wgpu::TextureViewDescriptor::default());
714
715 let reveal_tex = device.create_texture(&wgpu::TextureDescriptor {
717 label: Some("oit_reveal_texture"),
718 size: wgpu::Extent3d {
719 width: w,
720 height: h,
721 depth_or_array_layers: 1,
722 },
723 mip_level_count: 1,
724 sample_count: 1,
725 dimension: wgpu::TextureDimension::D2,
726 format: wgpu::TextureFormat::R8Unorm,
727 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
728 | wgpu::TextureUsages::TEXTURE_BINDING,
729 view_formats: &[],
730 });
731 let reveal_view = reveal_tex.create_view(&wgpu::TextureViewDescriptor::default());
732
733 let sampler = if self.oit_composite_sampler.is_none() {
735 device.create_sampler(&wgpu::SamplerDescriptor {
736 label: Some("oit_composite_sampler"),
737 address_mode_u: wgpu::AddressMode::ClampToEdge,
738 address_mode_v: wgpu::AddressMode::ClampToEdge,
739 address_mode_w: wgpu::AddressMode::ClampToEdge,
740 mag_filter: wgpu::FilterMode::Linear,
741 min_filter: wgpu::FilterMode::Linear,
742 ..Default::default()
743 })
744 } else {
745 device.create_sampler(&wgpu::SamplerDescriptor {
747 label: Some("oit_composite_sampler"),
748 address_mode_u: wgpu::AddressMode::ClampToEdge,
749 address_mode_v: wgpu::AddressMode::ClampToEdge,
750 address_mode_w: wgpu::AddressMode::ClampToEdge,
751 mag_filter: wgpu::FilterMode::Linear,
752 min_filter: wgpu::FilterMode::Linear,
753 ..Default::default()
754 })
755 };
756
757 let bgl = if self.oit_composite_bgl.is_none() {
759 let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
760 label: Some("oit_composite_bgl"),
761 entries: &[
762 wgpu::BindGroupLayoutEntry {
763 binding: 0,
764 visibility: wgpu::ShaderStages::FRAGMENT,
765 ty: wgpu::BindingType::Texture {
766 sample_type: wgpu::TextureSampleType::Float { filterable: true },
767 view_dimension: wgpu::TextureViewDimension::D2,
768 multisampled: false,
769 },
770 count: None,
771 },
772 wgpu::BindGroupLayoutEntry {
773 binding: 1,
774 visibility: wgpu::ShaderStages::FRAGMENT,
775 ty: wgpu::BindingType::Texture {
776 sample_type: wgpu::TextureSampleType::Float { filterable: true },
777 view_dimension: wgpu::TextureViewDimension::D2,
778 multisampled: false,
779 },
780 count: None,
781 },
782 wgpu::BindGroupLayoutEntry {
783 binding: 2,
784 visibility: wgpu::ShaderStages::FRAGMENT,
785 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
786 count: None,
787 },
788 ],
789 });
790 self.oit_composite_bgl = Some(bgl);
791 self.oit_composite_bgl.as_ref().unwrap()
792 } else {
793 self.oit_composite_bgl.as_ref().unwrap()
794 };
795
796 let composite_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
798 label: Some("oit_composite_bind_group"),
799 layout: bgl,
800 entries: &[
801 wgpu::BindGroupEntry {
802 binding: 0,
803 resource: wgpu::BindingResource::TextureView(&accum_view),
804 },
805 wgpu::BindGroupEntry {
806 binding: 1,
807 resource: wgpu::BindingResource::TextureView(&reveal_view),
808 },
809 wgpu::BindGroupEntry {
810 binding: 2,
811 resource: wgpu::BindingResource::Sampler(&sampler),
812 },
813 ],
814 });
815
816 self.oit_accum_texture = Some(accum_tex);
817 self.oit_accum_view = Some(accum_view);
818 self.oit_reveal_texture = Some(reveal_tex);
819 self.oit_reveal_view = Some(reveal_view);
820 self.oit_composite_sampler = Some(sampler);
821 self.oit_composite_bind_group = Some(composite_bg);
822 }
823
824 if self.oit_pipeline.is_none() {
826 let oit_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
828 label: Some("mesh_oit_shader"),
829 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/mesh_oit.wgsl").into()),
830 });
831 let oit_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
832 label: Some("oit_pipeline_layout"),
833 bind_group_layouts: &[
834 &self.camera_bind_group_layout,
835 &self.object_bind_group_layout,
836 ],
837 push_constant_ranges: &[],
838 });
839
840 let accum_blend = wgpu::BlendState {
842 color: wgpu::BlendComponent {
843 src_factor: wgpu::BlendFactor::One,
844 dst_factor: wgpu::BlendFactor::One,
845 operation: wgpu::BlendOperation::Add,
846 },
847 alpha: wgpu::BlendComponent {
848 src_factor: wgpu::BlendFactor::One,
849 dst_factor: wgpu::BlendFactor::One,
850 operation: wgpu::BlendOperation::Add,
851 },
852 };
853
854 let reveal_blend = wgpu::BlendState {
856 color: wgpu::BlendComponent {
857 src_factor: wgpu::BlendFactor::Zero,
858 dst_factor: wgpu::BlendFactor::OneMinusSrc,
859 operation: wgpu::BlendOperation::Add,
860 },
861 alpha: wgpu::BlendComponent {
862 src_factor: wgpu::BlendFactor::Zero,
863 dst_factor: wgpu::BlendFactor::OneMinusSrc,
864 operation: wgpu::BlendOperation::Add,
865 },
866 };
867
868 let oit_depth_stencil = wgpu::DepthStencilState {
869 format: wgpu::TextureFormat::Depth24PlusStencil8,
870 depth_write_enabled: false,
871 depth_compare: wgpu::CompareFunction::LessEqual,
872 stencil: wgpu::StencilState::default(),
873 bias: wgpu::DepthBiasState::default(),
874 };
875
876 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
877 label: Some("oit_pipeline"),
878 layout: Some(&oit_layout),
879 vertex: wgpu::VertexState {
880 module: &oit_shader,
881 entry_point: Some("vs_main"),
882 buffers: &[Vertex::buffer_layout()],
883 compilation_options: wgpu::PipelineCompilationOptions::default(),
884 },
885 fragment: Some(wgpu::FragmentState {
886 module: &oit_shader,
887 entry_point: Some("fs_oit_main"),
888 targets: &[
889 Some(wgpu::ColorTargetState {
890 format: wgpu::TextureFormat::Rgba16Float,
891 blend: Some(accum_blend),
892 write_mask: wgpu::ColorWrites::ALL,
893 }),
894 Some(wgpu::ColorTargetState {
895 format: wgpu::TextureFormat::R8Unorm,
896 blend: Some(reveal_blend),
897 write_mask: wgpu::ColorWrites::RED,
898 }),
899 ],
900 compilation_options: wgpu::PipelineCompilationOptions::default(),
901 }),
902 primitive: wgpu::PrimitiveState {
903 topology: wgpu::PrimitiveTopology::TriangleList,
904 cull_mode: Some(wgpu::Face::Back),
905 ..Default::default()
906 },
907 depth_stencil: Some(oit_depth_stencil.clone()),
908 multisample: wgpu::MultisampleState {
909 count: 1,
910 ..Default::default()
911 },
912 multiview: None,
913 cache: None,
914 });
915 self.oit_pipeline = Some(pipeline);
916
917 if let Some(ref instance_bgl) = self.instance_bind_group_layout {
919 let instanced_oit_shader =
920 device.create_shader_module(wgpu::ShaderModuleDescriptor {
921 label: Some("mesh_instanced_oit_shader"),
922 source: wgpu::ShaderSource::Wgsl(
923 include_str!("../shaders/mesh_instanced_oit.wgsl").into(),
924 ),
925 });
926 let instanced_oit_layout =
927 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
928 label: Some("oit_instanced_pipeline_layout"),
929 bind_group_layouts: &[&self.camera_bind_group_layout, instance_bgl],
930 push_constant_ranges: &[],
931 });
932 let instanced_pipeline =
933 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
934 label: Some("oit_instanced_pipeline"),
935 layout: Some(&instanced_oit_layout),
936 vertex: wgpu::VertexState {
937 module: &instanced_oit_shader,
938 entry_point: Some("vs_main"),
939 buffers: &[Vertex::buffer_layout()],
940 compilation_options: wgpu::PipelineCompilationOptions::default(),
941 },
942 fragment: Some(wgpu::FragmentState {
943 module: &instanced_oit_shader,
944 entry_point: Some("fs_oit_main"),
945 targets: &[
946 Some(wgpu::ColorTargetState {
947 format: wgpu::TextureFormat::Rgba16Float,
948 blend: Some(accum_blend),
949 write_mask: wgpu::ColorWrites::ALL,
950 }),
951 Some(wgpu::ColorTargetState {
952 format: wgpu::TextureFormat::R8Unorm,
953 blend: Some(reveal_blend),
954 write_mask: wgpu::ColorWrites::RED,
955 }),
956 ],
957 compilation_options: wgpu::PipelineCompilationOptions::default(),
958 }),
959 primitive: wgpu::PrimitiveState {
960 topology: wgpu::PrimitiveTopology::TriangleList,
961 cull_mode: Some(wgpu::Face::Back),
962 ..Default::default()
963 },
964 depth_stencil: Some(oit_depth_stencil),
965 multisample: wgpu::MultisampleState {
966 count: 1,
967 ..Default::default()
968 },
969 multiview: None,
970 cache: None,
971 });
972 self.oit_instanced_pipeline = Some(instanced_pipeline);
973 }
974 }
975
976 if self.oit_composite_pipeline.is_none() {
977 let comp_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
978 label: Some("oit_composite_shader"),
979 source: wgpu::ShaderSource::Wgsl(
980 include_str!("../shaders/oit_composite.wgsl").into(),
981 ),
982 });
983 let bgl = self
984 .oit_composite_bgl
985 .as_ref()
986 .expect("oit_composite_bgl must exist");
987 let comp_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
988 label: Some("oit_composite_pipeline_layout"),
989 bind_group_layouts: &[bgl],
990 push_constant_ranges: &[],
991 });
992 let premul_blend = wgpu::BlendState {
994 color: wgpu::BlendComponent {
995 src_factor: wgpu::BlendFactor::One,
996 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
997 operation: wgpu::BlendOperation::Add,
998 },
999 alpha: wgpu::BlendComponent {
1000 src_factor: wgpu::BlendFactor::One,
1001 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
1002 operation: wgpu::BlendOperation::Add,
1003 },
1004 };
1005 let comp_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1006 label: Some("oit_composite_pipeline"),
1007 layout: Some(&comp_layout),
1008 vertex: wgpu::VertexState {
1009 module: &comp_shader,
1010 entry_point: Some("vs_main"),
1011 buffers: &[],
1012 compilation_options: wgpu::PipelineCompilationOptions::default(),
1013 },
1014 fragment: Some(wgpu::FragmentState {
1015 module: &comp_shader,
1016 entry_point: Some("fs_main"),
1017 targets: &[Some(wgpu::ColorTargetState {
1018 format: wgpu::TextureFormat::Rgba16Float,
1019 blend: Some(premul_blend),
1020 write_mask: wgpu::ColorWrites::ALL,
1021 })],
1022 compilation_options: wgpu::PipelineCompilationOptions::default(),
1023 }),
1024 primitive: wgpu::PrimitiveState {
1025 topology: wgpu::PrimitiveTopology::TriangleList,
1026 ..Default::default()
1027 },
1028 depth_stencil: None,
1029 multisample: wgpu::MultisampleState {
1030 count: 1,
1031 ..Default::default()
1032 },
1033 multiview: None,
1034 cache: None,
1035 });
1036 self.oit_composite_pipeline = Some(comp_pipeline);
1037 }
1038 }
1039
1040 pub fn run_compute_filters(
1049 &mut self,
1050 device: &wgpu::Device,
1051 queue: &wgpu::Queue,
1052 items: &[crate::renderer::ComputeFilterItem],
1053 ) -> Vec<ComputeFilterResult> {
1054 if items.is_empty() {
1055 return Vec::new();
1056 }
1057
1058 self.ensure_compute_filter_pipeline(device);
1059
1060 let dummy_scalar_buf = device.create_buffer(&wgpu::BufferDescriptor {
1062 label: Some("compute_filter_dummy_scalar"),
1063 size: 4,
1064 usage: wgpu::BufferUsages::STORAGE,
1065 mapped_at_creation: false,
1066 });
1067
1068 let mut results = Vec::with_capacity(items.len());
1069
1070 for item in items {
1071 let gpu_mesh = match self.mesh_store.get(item.mesh_id) {
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: &[
1336 wgpu::BindGroupLayoutEntry {
1337 binding: 0,
1338 visibility: wgpu::ShaderStages::VERTEX,
1339 ty: wgpu::BindingType::Buffer {
1340 ty: wgpu::BufferBindingType::Uniform,
1341 has_dynamic_offset: false,
1342 min_binding_size: None,
1343 },
1344 count: None,
1345 },
1346 wgpu::BindGroupLayoutEntry {
1347 binding: 6,
1348 visibility: wgpu::ShaderStages::FRAGMENT,
1349 ty: wgpu::BindingType::Buffer {
1350 ty: wgpu::BufferBindingType::Uniform,
1351 has_dynamic_offset: false,
1352 min_binding_size: None,
1353 },
1354 count: None,
1355 },
1356 ],
1357 });
1358
1359 let pick_instance_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1361 label: Some("pick_instance_bgl"),
1362 entries: &[wgpu::BindGroupLayoutEntry {
1363 binding: 0,
1364 visibility: wgpu::ShaderStages::VERTEX,
1365 ty: wgpu::BindingType::Buffer {
1366 ty: wgpu::BufferBindingType::Storage { read_only: true },
1367 has_dynamic_offset: false,
1368 min_binding_size: None,
1369 },
1370 count: None,
1371 }],
1372 });
1373
1374 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1375 label: Some("pick_id_shader"),
1376 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/pick_id.wgsl").into()),
1377 });
1378
1379 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1380 label: Some("pick_pipeline_layout"),
1381 bind_group_layouts: &[&pick_camera_bgl, &pick_instance_bgl],
1382 push_constant_ranges: &[],
1383 });
1384
1385 let pick_vertex_layout = wgpu::VertexBufferLayout {
1387 array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex,
1389 attributes: &[wgpu::VertexAttribute {
1390 offset: 0,
1391 shader_location: 0,
1392 format: wgpu::VertexFormat::Float32x3,
1393 }],
1394 };
1395
1396 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1397 label: Some("pick_pipeline"),
1398 layout: Some(&layout),
1399 vertex: wgpu::VertexState {
1400 module: &shader,
1401 entry_point: Some("vs_main"),
1402 buffers: &[pick_vertex_layout],
1403 compilation_options: wgpu::PipelineCompilationOptions::default(),
1404 },
1405 fragment: Some(wgpu::FragmentState {
1406 module: &shader,
1407 entry_point: Some("fs_main"),
1408 targets: &[
1409 Some(wgpu::ColorTargetState {
1411 format: wgpu::TextureFormat::R32Uint,
1412 blend: None, write_mask: wgpu::ColorWrites::ALL,
1414 }),
1415 Some(wgpu::ColorTargetState {
1417 format: wgpu::TextureFormat::R32Float,
1418 blend: None,
1419 write_mask: wgpu::ColorWrites::ALL,
1420 }),
1421 ],
1422 compilation_options: wgpu::PipelineCompilationOptions::default(),
1423 }),
1424 primitive: wgpu::PrimitiveState {
1425 topology: wgpu::PrimitiveTopology::TriangleList,
1426 front_face: wgpu::FrontFace::Ccw,
1427 cull_mode: None, ..Default::default()
1429 },
1430 depth_stencil: Some(wgpu::DepthStencilState {
1431 format: wgpu::TextureFormat::Depth24PlusStencil8,
1432 depth_write_enabled: true,
1433 depth_compare: wgpu::CompareFunction::Less,
1434 stencil: wgpu::StencilState::default(),
1435 bias: wgpu::DepthBiasState::default(),
1436 }),
1437 multisample: wgpu::MultisampleState {
1438 count: 1, ..Default::default()
1440 },
1441 multiview: None,
1442 cache: None,
1443 });
1444
1445 self.pick_camera_bgl = Some(pick_camera_bgl);
1446 self.pick_bind_group_layout_1 = Some(pick_instance_bgl);
1447 self.pick_pipeline = Some(pipeline);
1448 }
1449}
1450
1451pub fn lerp_attributes(a: &[f32], b: &[f32], t: f32) -> Vec<f32> {
1463 let t = t.clamp(0.0, 1.0);
1464 let one_minus_t = 1.0 - t;
1465 a.iter()
1466 .zip(b.iter())
1467 .map(|(&av, &bv)| av * one_minus_t + bv * t)
1468 .collect()
1469}