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