1mod color_quantity;
45mod scalar_quantity;
46pub mod slice_geometry;
47mod vector_quantity;
48
49pub use color_quantity::*;
50pub use scalar_quantity::*;
51pub use slice_geometry::{CellSliceResult, slice_hex, slice_tet};
52pub use vector_quantity::*;
53
54use glam::{Mat4, Vec3, Vec4};
57use polyscope_core::pick::PickResult;
58use polyscope_core::quantity::Quantity;
59use polyscope_core::structure::{HasQuantities, RenderContext, Structure};
60use polyscope_render::{
61 MeshPickUniforms, MeshUniforms, SliceMeshRenderData, SurfaceMeshRenderData,
62};
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum VolumeCellType {
67 Tet,
69 Hex,
71}
72
73pub struct VolumeMesh {
78 name: String,
79
80 vertices: Vec<Vec3>,
82 cells: Vec<[u32; 8]>, enabled: bool,
86 transform: Mat4,
87 quantities: Vec<Box<dyn Quantity>>,
88
89 color: Vec4,
91 interior_color: Vec4,
92 edge_color: Vec4,
93 edge_width: f32,
94
95 render_data: Option<SurfaceMeshRenderData>,
97
98 pick_uniform_buffer: Option<wgpu::Buffer>,
100 pick_bind_group: Option<wgpu::BindGroup>,
101 pick_cell_index_buffer: Option<wgpu::Buffer>,
102 global_start: u32,
103
104 slice_render_data: Option<SliceMeshRenderData>,
106 slice_plane_cache: Option<(Vec3, Vec3)>,
108 culling_plane_cache: Option<Vec<(Vec3, Vec3)>>,
111}
112
113impl VolumeMesh {
114 pub fn new(name: impl Into<String>, vertices: Vec<Vec3>, cells: Vec<[u32; 8]>) -> Self {
121 let color = Vec4::new(0.25, 0.50, 0.75, 1.0);
122 let interior_color = Vec4::new(0.45, 0.50, 0.55, 1.0);
124
125 Self {
126 name: name.into(),
127 vertices,
128 cells,
129 enabled: true,
130 transform: Mat4::IDENTITY,
131 quantities: Vec::new(),
132 color,
133 interior_color,
134 edge_color: Vec4::new(0.0, 0.0, 0.0, 1.0),
135 edge_width: 0.0,
136 render_data: None,
137 pick_uniform_buffer: None,
138 pick_bind_group: None,
139 pick_cell_index_buffer: None,
140 global_start: 0,
141 slice_render_data: None,
142 slice_plane_cache: None,
143 culling_plane_cache: None,
144 }
145 }
146
147 pub fn new_tet_mesh(name: impl Into<String>, vertices: Vec<Vec3>, tets: Vec<[u32; 4]>) -> Self {
149 let cells: Vec<[u32; 8]> = tets
151 .into_iter()
152 .map(|t| {
153 [
154 t[0],
155 t[1],
156 t[2],
157 t[3],
158 u32::MAX,
159 u32::MAX,
160 u32::MAX,
161 u32::MAX,
162 ]
163 })
164 .collect();
165 Self::new(name, vertices, cells)
166 }
167
168 pub fn new_hex_mesh(
170 name: impl Into<String>,
171 vertices: Vec<Vec3>,
172 hexes: Vec<[u32; 8]>,
173 ) -> Self {
174 Self::new(name, vertices, hexes)
175 }
176
177 #[must_use]
179 pub fn num_vertices(&self) -> usize {
180 self.vertices.len()
181 }
182
183 #[must_use]
185 pub fn num_cells(&self) -> usize {
186 self.cells.len()
187 }
188
189 #[must_use]
191 pub fn cell_type(&self, cell_idx: usize) -> VolumeCellType {
192 if self.cells[cell_idx][4] == u32::MAX {
193 VolumeCellType::Tet
194 } else {
195 VolumeCellType::Hex
196 }
197 }
198
199 #[must_use]
201 pub fn vertices(&self) -> &[Vec3] {
202 &self.vertices
203 }
204
205 #[must_use]
207 pub fn cells(&self) -> &[[u32; 8]] {
208 &self.cells
209 }
210
211 #[must_use]
213 pub fn color(&self) -> Vec4 {
214 self.color
215 }
216
217 pub fn set_color(&mut self, color: Vec3) -> &mut Self {
219 self.color = color.extend(1.0);
220 self
221 }
222
223 #[must_use]
225 pub fn interior_color(&self) -> Vec4 {
226 self.interior_color
227 }
228
229 pub fn set_interior_color(&mut self, color: Vec3) -> &mut Self {
231 self.interior_color = color.extend(1.0);
232 self
233 }
234
235 #[must_use]
237 pub fn edge_color(&self) -> Vec4 {
238 self.edge_color
239 }
240
241 pub fn set_edge_color(&mut self, color: Vec3) -> &mut Self {
243 self.edge_color = color.extend(1.0);
244 self
245 }
246
247 #[must_use]
249 pub fn edge_width(&self) -> f32 {
250 self.edge_width
251 }
252
253 pub fn set_edge_width(&mut self, width: f32) -> &mut Self {
255 self.edge_width = width;
256 self
257 }
258
259 #[must_use]
262 pub fn decompose_to_tets(&self) -> Vec<[u32; 4]> {
263 let mut tets = Vec::new();
264
265 for cell in &self.cells {
266 if cell[4] == u32::MAX {
267 tets.push([cell[0], cell[1], cell[2], cell[3]]);
269 } else {
270 for tet_local in &HEX_TO_TET_PATTERN {
272 let tet = [
273 cell[tet_local[0]],
274 cell[tet_local[1]],
275 cell[tet_local[2]],
276 cell[tet_local[3]],
277 ];
278 tets.push(tet);
279 }
280 }
281 }
282
283 tets
284 }
285
286 #[must_use]
288 pub fn num_tets(&self) -> usize {
289 self.decompose_to_tets().len()
290 }
291
292 fn compute_face_counts(&self) -> HashMap<[u32; 4], usize> {
294 let mut face_counts: HashMap<[u32; 4], usize> = HashMap::new();
295
296 for cell in &self.cells {
297 if cell[4] == u32::MAX {
298 for [a, b, c] in TET_FACE_STENCIL {
300 let key = canonical_face_key(cell[a], cell[b], cell[c], None);
301 *face_counts.entry(key).or_insert(0) += 1;
302 }
303 } else {
304 for quad in HEX_FACE_STENCIL {
306 let v0 = cell[quad[0][0]];
308 let v1 = cell[quad[0][1]];
309 let v2 = cell[quad[0][2]];
310 let v3 = cell[quad[1][2]]; let key = canonical_face_key(v0, v1, v2, Some(v3));
312 *face_counts.entry(key).or_insert(0) += 1;
313 }
314 }
315 }
316
317 face_counts
318 }
319
320 fn cell_centroid(&self, cell: &[u32; 8]) -> Vec3 {
322 if cell[4] == u32::MAX {
323 let sum = self.vertices[cell[0] as usize]
325 + self.vertices[cell[1] as usize]
326 + self.vertices[cell[2] as usize]
327 + self.vertices[cell[3] as usize];
328 sum / 4.0
329 } else {
330 let sum = (0..8)
332 .map(|i| self.vertices[cell[i] as usize])
333 .fold(Vec3::ZERO, |a, b| a + b);
334 sum / 8.0
335 }
336 }
337
338 fn is_cell_visible(&self, cell: &[u32; 8], planes: &[(Vec3, Vec3)]) -> bool {
341 if planes.is_empty() {
342 return true;
343 }
344 let centroid = self.cell_centroid(cell);
345 let centroid_world = (self.transform * centroid.extend(1.0)).truncate();
346 for (plane_origin, plane_normal) in planes {
347 let signed_dist = (centroid_world - *plane_origin).dot(*plane_normal);
348 if signed_dist < 0.0 {
350 return false;
351 }
352 }
353 true
354 }
355
356 fn compute_face_counts_with_culling(
358 &self,
359 planes: &[(Vec3, Vec3)],
360 ) -> HashMap<[u32; 4], usize> {
361 let mut face_counts: HashMap<[u32; 4], usize> = HashMap::new();
362
363 for cell in &self.cells {
364 if !self.is_cell_visible(cell, planes) {
366 continue;
367 }
368
369 if cell[4] == u32::MAX {
370 for [a, b, c] in TET_FACE_STENCIL {
372 let key = canonical_face_key(cell[a], cell[b], cell[c], None);
373 *face_counts.entry(key).or_insert(0) += 1;
374 }
375 } else {
376 for quad in HEX_FACE_STENCIL {
378 let v0 = cell[quad[0][0]];
379 let v1 = cell[quad[0][1]];
380 let v2 = cell[quad[0][2]];
381 let v3 = cell[quad[1][2]];
382 let key = canonical_face_key(v0, v1, v2, Some(v3));
383 *face_counts.entry(key).or_insert(0) += 1;
384 }
385 }
386 }
387
388 face_counts
389 }
390
391 fn generate_render_geometry(&self) -> (Vec<Vec3>, Vec<[u32; 3]>) {
393 let face_counts = self.compute_face_counts();
394 let mut positions = Vec::new();
395 let mut faces = Vec::new();
396
397 for cell in &self.cells {
398 if cell[4] == u32::MAX {
399 for [a, b, c] in TET_FACE_STENCIL {
401 let key = canonical_face_key(cell[a], cell[b], cell[c], None);
402 if face_counts[&key] == 1 {
403 let base_idx = positions.len() as u32;
405 positions.push(self.vertices[cell[a] as usize]);
406 positions.push(self.vertices[cell[b] as usize]);
407 positions.push(self.vertices[cell[c] as usize]);
408 faces.push([base_idx, base_idx + 1, base_idx + 2]);
409 }
410 }
411 } else {
412 for quad in HEX_FACE_STENCIL {
414 let v0 = cell[quad[0][0]];
415 let v1 = cell[quad[0][1]];
416 let v2 = cell[quad[0][2]];
417 let v3 = cell[quad[1][2]];
418 let key = canonical_face_key(v0, v1, v2, Some(v3));
419 if face_counts[&key] == 1 {
420 for [a, b, c] in quad {
422 let base_idx = positions.len() as u32;
423 positions.push(self.vertices[cell[a] as usize]);
424 positions.push(self.vertices[cell[b] as usize]);
425 positions.push(self.vertices[cell[c] as usize]);
426 faces.push([base_idx, base_idx + 1, base_idx + 2]);
427 }
428 }
429 }
430 }
431 }
432
433 (positions, faces)
434 }
435
436 fn generate_render_geometry_with_culling(
439 &self,
440 planes: &[(Vec3, Vec3)],
441 ) -> (Vec<Vec3>, Vec<[u32; 3]>) {
442 let face_counts = self.compute_face_counts_with_culling(planes);
444 let mut positions = Vec::new();
445 let mut faces = Vec::new();
446
447 for cell in &self.cells {
448 if !self.is_cell_visible(cell, planes) {
450 continue;
451 }
452
453 if cell[4] == u32::MAX {
454 for [a, b, c] in TET_FACE_STENCIL {
456 let key = canonical_face_key(cell[a], cell[b], cell[c], None);
457 if face_counts.get(&key) == Some(&1) {
459 let base_idx = positions.len() as u32;
460 positions.push(self.vertices[cell[a] as usize]);
461 positions.push(self.vertices[cell[b] as usize]);
462 positions.push(self.vertices[cell[c] as usize]);
463 faces.push([base_idx, base_idx + 1, base_idx + 2]);
464 }
465 }
466 } else {
467 for quad in HEX_FACE_STENCIL {
469 let v0 = cell[quad[0][0]];
470 let v1 = cell[quad[0][1]];
471 let v2 = cell[quad[0][2]];
472 let v3 = cell[quad[1][2]];
473 let key = canonical_face_key(v0, v1, v2, Some(v3));
474 if face_counts.get(&key) == Some(&1) {
475 for [a, b, c] in quad {
476 let base_idx = positions.len() as u32;
477 positions.push(self.vertices[cell[a] as usize]);
478 positions.push(self.vertices[cell[b] as usize]);
479 positions.push(self.vertices[cell[c] as usize]);
480 faces.push([base_idx, base_idx + 1, base_idx + 2]);
481 }
482 }
483 }
484 }
485 }
486
487 (positions, faces)
488 }
489
490 #[must_use]
492 pub fn generate_render_geometry_with_quantities(&self) -> VolumeMeshRenderGeometry {
493 let face_counts = self.compute_face_counts();
494 let mut positions = Vec::new();
495 let mut faces = Vec::new();
496 let mut vertex_indices = Vec::new(); let mut cell_indices = Vec::new(); for (cell_idx, cell) in self.cells.iter().enumerate() {
501 if cell[4] == u32::MAX {
502 for [a, b, c] in TET_FACE_STENCIL {
504 let key = canonical_face_key(cell[a], cell[b], cell[c], None);
505 if face_counts[&key] == 1 {
506 let base_idx = positions.len() as u32;
507 positions.push(self.vertices[cell[a] as usize]);
508 positions.push(self.vertices[cell[b] as usize]);
509 positions.push(self.vertices[cell[c] as usize]);
510 vertex_indices.push(cell[a] as usize);
511 vertex_indices.push(cell[b] as usize);
512 vertex_indices.push(cell[c] as usize);
513 cell_indices.push(cell_idx);
514 cell_indices.push(cell_idx);
515 cell_indices.push(cell_idx);
516 faces.push([base_idx, base_idx + 1, base_idx + 2]);
517 }
518 }
519 } else {
520 for quad in HEX_FACE_STENCIL {
522 let v0 = cell[quad[0][0]];
523 let v1 = cell[quad[0][1]];
524 let v2 = cell[quad[0][2]];
525 let v3 = cell[quad[1][2]];
526 let key = canonical_face_key(v0, v1, v2, Some(v3));
527 if face_counts[&key] == 1 {
528 for [a, b, c] in quad {
529 let base_idx = positions.len() as u32;
530 positions.push(self.vertices[cell[a] as usize]);
531 positions.push(self.vertices[cell[b] as usize]);
532 positions.push(self.vertices[cell[c] as usize]);
533 vertex_indices.push(cell[a] as usize);
534 vertex_indices.push(cell[b] as usize);
535 vertex_indices.push(cell[c] as usize);
536 cell_indices.push(cell_idx);
537 cell_indices.push(cell_idx);
538 cell_indices.push(cell_idx);
539 faces.push([base_idx, base_idx + 1, base_idx + 2]);
540 }
541 }
542 }
543 }
544 }
545
546 let mut normals = vec![Vec3::ZERO; positions.len()];
548 for [a, b, c] in &faces {
549 let p0 = positions[*a as usize];
550 let p1 = positions[*b as usize];
551 let p2 = positions[*c as usize];
552 let normal = (p1 - p0).cross(p2 - p0).normalize_or_zero();
553 normals[*a as usize] = normal;
554 normals[*b as usize] = normal;
555 normals[*c as usize] = normal;
556 }
557
558 let mut vertex_values = None;
560 let mut vertex_colors = None;
561
562 for q in &self.quantities {
563 if q.is_enabled() {
564 if let Some(scalar) = q.as_any().downcast_ref::<VolumeMeshVertexScalarQuantity>() {
565 let values: Vec<f32> = vertex_indices
566 .iter()
567 .map(|&idx| scalar.values().get(idx).copied().unwrap_or(0.0))
568 .collect();
569 vertex_values = Some(values);
570 break;
571 }
572 if let Some(color) = q.as_any().downcast_ref::<VolumeMeshVertexColorQuantity>() {
573 let colors: Vec<Vec3> = vertex_indices
574 .iter()
575 .map(|&idx| {
576 color
577 .colors()
578 .get(idx)
579 .copied()
580 .unwrap_or(Vec4::new(1.0, 1.0, 1.0, 1.0))
581 .truncate()
582 })
583 .collect();
584 vertex_colors = Some(colors);
585 break;
586 }
587 if let Some(scalar) = q.as_any().downcast_ref::<VolumeMeshCellScalarQuantity>() {
588 let values: Vec<f32> = cell_indices
589 .iter()
590 .map(|&idx| scalar.values().get(idx).copied().unwrap_or(0.0))
591 .collect();
592 vertex_values = Some(values);
593 break;
594 }
595 if let Some(color) = q.as_any().downcast_ref::<VolumeMeshCellColorQuantity>() {
596 let colors: Vec<Vec3> = cell_indices
597 .iter()
598 .map(|&idx| {
599 color
600 .colors()
601 .get(idx)
602 .copied()
603 .unwrap_or(Vec4::new(1.0, 1.0, 1.0, 1.0))
604 .truncate()
605 })
606 .collect();
607 vertex_colors = Some(colors);
608 break;
609 }
610 }
611 }
612
613 VolumeMeshRenderGeometry {
614 positions,
615 faces,
616 normals,
617 vertex_values,
618 vertex_colors,
619 }
620 }
621
622 pub fn init_render_data(
624 &mut self,
625 device: &wgpu::Device,
626 bind_group_layout: &wgpu::BindGroupLayout,
627 camera_buffer: &wgpu::Buffer,
628 ) {
629 let (positions, triangles) = self.generate_render_geometry();
630
631 if triangles.is_empty() {
632 return;
633 }
634
635 let mut normals = vec![Vec3::ZERO; positions.len()];
637 for [a, b, c] in &triangles {
638 let p0 = positions[*a as usize];
639 let p1 = positions[*b as usize];
640 let p2 = positions[*c as usize];
641 let normal = (p1 - p0).cross(p2 - p0).normalize_or_zero();
642 normals[*a as usize] = normal;
643 normals[*b as usize] = normal;
644 normals[*c as usize] = normal;
645 }
646
647 let edge_is_real: Vec<Vec3> = vec![Vec3::ONE; triangles.len() * 3];
650
651 let render_data = SurfaceMeshRenderData::new(
652 device,
653 bind_group_layout,
654 camera_buffer,
655 &positions,
656 &triangles,
657 &normals,
658 &edge_is_real,
659 );
660
661 self.render_data = Some(render_data);
662 }
663
664 pub fn update_render_data_with_culling(
668 &mut self,
669 device: &wgpu::Device,
670 bind_group_layout: &wgpu::BindGroupLayout,
671 camera_buffer: &wgpu::Buffer,
672 planes: &[(Vec3, Vec3)],
673 ) {
674 let cache_valid = self.culling_plane_cache.as_ref().is_some_and(|cache| {
676 if cache.len() != planes.len() {
677 return false;
678 }
679 cache.iter().zip(planes.iter()).all(|((o, n), (po, pn))| {
680 (*o - *po).length_squared() < 1e-10 && (*n - *pn).length_squared() < 1e-10
681 })
682 });
683
684 if cache_valid && self.render_data.is_some() {
685 return;
687 }
688
689 let (positions, triangles) = self.generate_render_geometry_with_culling(planes);
690
691 if triangles.is_empty() {
692 self.render_data = None;
693 self.culling_plane_cache = Some(planes.to_vec());
694 return;
695 }
696
697 let mut normals = vec![Vec3::ZERO; positions.len()];
699 for [a, b, c] in &triangles {
700 let p0 = positions[*a as usize];
701 let p1 = positions[*b as usize];
702 let p2 = positions[*c as usize];
703 let normal = (p1 - p0).cross(p2 - p0).normalize_or_zero();
704 normals[*a as usize] = normal;
705 normals[*b as usize] = normal;
706 normals[*c as usize] = normal;
707 }
708
709 let edge_is_real: Vec<Vec3> = vec![Vec3::ONE; triangles.len() * 3];
710
711 let render_data = SurfaceMeshRenderData::new(
712 device,
713 bind_group_layout,
714 camera_buffer,
715 &positions,
716 &triangles,
717 &normals,
718 &edge_is_real,
719 );
720
721 self.render_data = Some(render_data);
722 self.culling_plane_cache = Some(planes.to_vec());
723 }
724
725 pub fn reset_render_data(
727 &mut self,
728 device: &wgpu::Device,
729 bind_group_layout: &wgpu::BindGroupLayout,
730 camera_buffer: &wgpu::Buffer,
731 ) {
732 self.culling_plane_cache = None;
733 self.init_render_data(device, bind_group_layout, camera_buffer);
734 }
735
736 #[must_use]
738 pub fn is_culled(&self) -> bool {
739 self.culling_plane_cache.is_some()
740 }
741
742 #[must_use]
744 pub fn render_data(&self) -> Option<&SurfaceMeshRenderData> {
745 self.render_data.as_ref()
746 }
747
748 #[must_use]
751 pub fn pick_triangles(&self, planes: &[(Vec3, Vec3)]) -> (Vec<Vec3>, Vec<[u32; 3]>) {
752 if planes.is_empty() {
753 self.generate_render_geometry()
754 } else {
755 self.generate_render_geometry_with_culling(planes)
756 }
757 }
758
759 fn generate_cell_index_per_triangle(&self) -> Vec<u32> {
764 let face_counts = self.compute_face_counts();
765 let mut cell_indices = Vec::new();
766
767 for (cell_idx, cell) in self.cells.iter().enumerate() {
768 if cell[4] == u32::MAX {
769 for [a, b, c] in TET_FACE_STENCIL {
771 let key = canonical_face_key(cell[a], cell[b], cell[c], None);
772 if face_counts[&key] == 1 {
773 cell_indices.push(cell_idx as u32);
774 }
775 }
776 } else {
777 for quad in HEX_FACE_STENCIL {
779 let v0 = cell[quad[0][0]];
780 let v1 = cell[quad[0][1]];
781 let v2 = cell[quad[0][2]];
782 let v3 = cell[quad[1][2]];
783 let key = canonical_face_key(v0, v1, v2, Some(v3));
784 if face_counts[&key] == 1 {
785 cell_indices.push(cell_idx as u32);
787 cell_indices.push(cell_idx as u32);
788 }
789 }
790 }
791 }
792
793 cell_indices
794 }
795
796 fn generate_cell_index_per_triangle_with_culling(&self, planes: &[(Vec3, Vec3)]) -> Vec<u32> {
798 let face_counts = self.compute_face_counts_with_culling(planes);
799 let mut cell_indices = Vec::new();
800
801 for (cell_idx, cell) in self.cells.iter().enumerate() {
802 if !self.is_cell_visible(cell, planes) {
803 continue;
804 }
805
806 if cell[4] == u32::MAX {
807 for [a, b, c] in TET_FACE_STENCIL {
808 let key = canonical_face_key(cell[a], cell[b], cell[c], None);
809 if face_counts.get(&key) == Some(&1) {
810 cell_indices.push(cell_idx as u32);
811 }
812 }
813 } else {
814 for quad in HEX_FACE_STENCIL {
815 let v0 = cell[quad[0][0]];
816 let v1 = cell[quad[0][1]];
817 let v2 = cell[quad[0][2]];
818 let v3 = cell[quad[1][2]];
819 let key = canonical_face_key(v0, v1, v2, Some(v3));
820 if face_counts.get(&key) == Some(&1) {
821 cell_indices.push(cell_idx as u32);
822 cell_indices.push(cell_idx as u32);
823 }
824 }
825 }
826 }
827
828 cell_indices
829 }
830
831 pub fn init_pick_resources(
836 &mut self,
837 device: &wgpu::Device,
838 mesh_pick_bind_group_layout: &wgpu::BindGroupLayout,
839 camera_buffer: &wgpu::Buffer,
840 global_start: u32,
841 ) {
842 use wgpu::util::DeviceExt;
843
844 self.global_start = global_start;
845
846 let model = self.transform.to_cols_array_2d();
847 let pick_uniforms = MeshPickUniforms {
848 global_start,
849 _padding: [0.0; 3],
850 model,
851 };
852 let pick_uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
853 label: Some("volume mesh pick uniforms"),
854 contents: bytemuck::cast_slice(&[pick_uniforms]),
855 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
856 });
857
858 let cell_index_data = if self.culling_plane_cache.is_some() {
860 self.generate_cell_index_per_triangle_with_culling(
861 self.culling_plane_cache.as_deref().unwrap_or(&[]),
862 )
863 } else {
864 self.generate_cell_index_per_triangle()
865 };
866
867 let pick_cell_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
868 label: Some("volume mesh pick cell indices"),
869 contents: bytemuck::cast_slice(&cell_index_data),
870 usage: wgpu::BufferUsages::STORAGE,
871 });
872
873 if let Some(render_data) = &self.render_data {
875 let pick_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
876 label: Some("volume mesh pick bind group"),
877 layout: mesh_pick_bind_group_layout,
878 entries: &[
879 wgpu::BindGroupEntry {
880 binding: 0,
881 resource: camera_buffer.as_entire_binding(),
882 },
883 wgpu::BindGroupEntry {
884 binding: 1,
885 resource: pick_uniform_buffer.as_entire_binding(),
886 },
887 wgpu::BindGroupEntry {
888 binding: 2,
889 resource: render_data.vertex_buffer.as_entire_binding(),
890 },
891 wgpu::BindGroupEntry {
892 binding: 3,
893 resource: pick_cell_index_buffer.as_entire_binding(),
894 },
895 ],
896 });
897 self.pick_bind_group = Some(pick_bind_group);
898 }
899
900 self.pick_uniform_buffer = Some(pick_uniform_buffer);
901 self.pick_cell_index_buffer = Some(pick_cell_index_buffer);
902 }
903
904 #[must_use]
906 pub fn pick_bind_group(&self) -> Option<&wgpu::BindGroup> {
907 self.pick_bind_group.as_ref()
908 }
909
910 pub fn update_pick_uniforms(&self, queue: &wgpu::Queue) {
912 if let Some(buffer) = &self.pick_uniform_buffer {
913 let model = self.transform.to_cols_array_2d();
914 let pick_uniforms = MeshPickUniforms {
915 global_start: self.global_start,
916 _padding: [0.0; 3],
917 model,
918 };
919 queue.write_buffer(buffer, 0, bytemuck::cast_slice(&[pick_uniforms]));
920 }
921 }
922
923 #[must_use]
925 pub fn num_render_vertices(&self) -> u32 {
926 self.render_data
927 .as_ref()
928 .map_or(0, SurfaceMeshRenderData::num_vertices)
929 }
930
931 pub fn update_gpu_buffers(&self, queue: &wgpu::Queue) {
933 if let Some(ref rd) = self.render_data {
934 let model_matrix = self.transform.to_cols_array_2d();
936
937 let uniforms = MeshUniforms {
938 model_matrix,
939 shade_style: 0, show_edges: u32::from(self.edge_width > 0.0),
941 edge_width: self.edge_width,
942 transparency: 0.0,
943 surface_color: self.color.to_array(),
944 edge_color: self.edge_color.to_array(),
945 backface_policy: 0,
946 slice_planes_enabled: 0,
947 ..Default::default()
948 };
949 rd.update_uniforms(queue, &uniforms);
950 }
951 }
952
953 pub fn update_slice_render_data(
957 &mut self,
958 device: &wgpu::Device,
959 queue: &wgpu::Queue,
960 bind_group_layout: &wgpu::BindGroupLayout,
961 camera_buffer: &wgpu::Buffer,
962 plane_origin: Vec3,
963 plane_normal: Vec3,
964 ) -> bool {
965 let cache_valid = self.slice_plane_cache.is_some_and(|(o, n)| {
967 (o - plane_origin).length_squared() < 1e-10
968 && (n - plane_normal).length_squared() < 1e-10
969 });
970
971 if cache_valid {
972 if let Some(ref data) = self.slice_render_data {
973 return !data.is_empty();
974 }
975 }
976
977 if let Some(slice_data) = self.generate_slice_geometry(plane_origin, plane_normal) {
979 if let Some(ref mut rd) = self.slice_render_data {
980 rd.update(
982 device,
983 queue,
984 bind_group_layout,
985 camera_buffer,
986 &slice_data.vertices,
987 &slice_data.normals,
988 &slice_data.colors,
989 );
990 } else {
991 self.slice_render_data = Some(SliceMeshRenderData::new(
993 device,
994 bind_group_layout,
995 camera_buffer,
996 &slice_data.vertices,
997 &slice_data.normals,
998 &slice_data.colors,
999 ));
1000 }
1001
1002 if let Some(ref rd) = self.slice_render_data {
1004 rd.update_uniforms(queue, self.interior_color.truncate());
1005 }
1006
1007 self.slice_plane_cache = Some((plane_origin, plane_normal));
1008 true
1009 } else {
1010 self.slice_render_data = None;
1012 self.slice_plane_cache = None;
1013 false
1014 }
1015 }
1016
1017 #[must_use]
1019 pub fn slice_render_data(&self) -> Option<&SliceMeshRenderData> {
1020 self.slice_render_data.as_ref()
1021 }
1022
1023 pub fn clear_slice_render_data(&mut self) {
1025 self.slice_render_data = None;
1026 self.slice_plane_cache = None;
1027 }
1028
1029 pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) {
1031 let num_tets = self.cells.iter().filter(|c| c[4] == u32::MAX).count();
1033 let num_hexes = self.num_cells() - num_tets;
1034 ui.label(format!(
1035 "{} verts, {} cells ({} tets, {} hexes)",
1036 self.num_vertices(),
1037 self.num_cells(),
1038 num_tets,
1039 num_hexes
1040 ));
1041
1042 ui.horizontal(|ui| {
1044 ui.label("Color:");
1045 let mut color = [self.color.x, self.color.y, self.color.z];
1046 if ui.color_edit_button_rgb(&mut color).changed() {
1047 self.color = Vec4::new(color[0], color[1], color[2], self.color.w);
1048 }
1049 });
1050
1051 ui.horizontal(|ui| {
1053 let mut show_edges = self.edge_width > 0.0;
1054 if ui.checkbox(&mut show_edges, "Edges").changed() {
1055 self.set_edge_width(if show_edges { 1.0 } else { 0.0 });
1056 }
1057 if show_edges {
1058 let mut width = self.edge_width;
1059 if ui
1060 .add(
1061 egui::DragValue::new(&mut width)
1062 .speed(0.01)
1063 .range(0.01..=5.0),
1064 )
1065 .changed()
1066 {
1067 self.set_edge_width(width);
1068 }
1069 }
1070 });
1071 }
1072
1073 pub fn add_vertex_scalar_quantity(
1075 &mut self,
1076 name: impl Into<String>,
1077 values: Vec<f32>,
1078 ) -> &mut Self {
1079 let name = name.into();
1080 let quantity = VolumeMeshVertexScalarQuantity::new(name.clone(), self.name.clone(), values);
1081 self.add_quantity(Box::new(quantity));
1082 self
1083 }
1084
1085 pub fn add_cell_scalar_quantity(
1087 &mut self,
1088 name: impl Into<String>,
1089 values: Vec<f32>,
1090 ) -> &mut Self {
1091 let name = name.into();
1092 let quantity = VolumeMeshCellScalarQuantity::new(name.clone(), self.name.clone(), values);
1093 self.add_quantity(Box::new(quantity));
1094 self
1095 }
1096
1097 pub fn add_vertex_color_quantity(
1099 &mut self,
1100 name: impl Into<String>,
1101 colors: Vec<Vec3>,
1102 ) -> &mut Self {
1103 let name = name.into();
1104 let quantity = VolumeMeshVertexColorQuantity::new(name.clone(), self.name.clone(), colors);
1105 self.add_quantity(Box::new(quantity));
1106 self
1107 }
1108
1109 pub fn add_cell_color_quantity(
1111 &mut self,
1112 name: impl Into<String>,
1113 colors: Vec<Vec3>,
1114 ) -> &mut Self {
1115 let name = name.into();
1116 let quantity = VolumeMeshCellColorQuantity::new(name.clone(), self.name.clone(), colors);
1117 self.add_quantity(Box::new(quantity));
1118 self
1119 }
1120
1121 pub fn add_vertex_vector_quantity(
1123 &mut self,
1124 name: impl Into<String>,
1125 vectors: Vec<Vec3>,
1126 ) -> &mut Self {
1127 let name = name.into();
1128 let quantity =
1129 VolumeMeshVertexVectorQuantity::new(name.clone(), self.name.clone(), vectors);
1130 self.add_quantity(Box::new(quantity));
1131 self
1132 }
1133
1134 pub fn add_cell_vector_quantity(
1136 &mut self,
1137 name: impl Into<String>,
1138 vectors: Vec<Vec3>,
1139 ) -> &mut Self {
1140 let name = name.into();
1141 let quantity = VolumeMeshCellVectorQuantity::new(name.clone(), self.name.clone(), vectors);
1142 self.add_quantity(Box::new(quantity));
1143 self
1144 }
1145
1146 fn active_vertex_color_quantity(&self) -> Option<&VolumeMeshVertexColorQuantity> {
1148 for q in &self.quantities {
1149 if q.is_enabled() {
1150 if let Some(vcq) = q.as_any().downcast_ref::<VolumeMeshVertexColorQuantity>() {
1151 return Some(vcq);
1152 }
1153 }
1154 }
1155 None
1156 }
1157
1158 #[must_use]
1171 pub fn generate_slice_geometry(
1172 &self,
1173 plane_origin: Vec3,
1174 plane_normal: Vec3,
1175 ) -> Option<SliceMeshData> {
1176 let mut vertices = Vec::new();
1177 let mut normals = Vec::new();
1178 let mut colors = Vec::new();
1179
1180 let vertex_colors = self
1182 .active_vertex_color_quantity()
1183 .map(color_quantity::VolumeMeshVertexColorQuantity::colors);
1184
1185 for (cell_idx, cell) in self.cells.iter().enumerate() {
1186 let cell_type = self.cell_type(cell_idx);
1187
1188 let slice = match cell_type {
1189 VolumeCellType::Tet => slice_tet(
1190 self.vertices[cell[0] as usize],
1191 self.vertices[cell[1] as usize],
1192 self.vertices[cell[2] as usize],
1193 self.vertices[cell[3] as usize],
1194 plane_origin,
1195 plane_normal,
1196 ),
1197 VolumeCellType::Hex => {
1198 let hex_verts: [Vec3; 8] =
1199 std::array::from_fn(|i| self.vertices[cell[i] as usize]);
1200 slice_hex(hex_verts, plane_origin, plane_normal)
1201 }
1202 };
1203
1204 if slice.has_intersection() {
1205 let slice_colors: Vec<Vec4> = if let Some(vc) = vertex_colors {
1207 slice
1208 .interpolation
1209 .iter()
1210 .map(|&(a, b, t)| {
1211 let va_idx = cell[a as usize] as usize;
1213 let vb_idx = cell[b as usize] as usize;
1214 vc[va_idx].lerp(vc[vb_idx], t)
1216 })
1217 .collect()
1218 } else {
1219 vec![self.interior_color; slice.vertices.len()]
1220 };
1221
1222 for i in 1..slice.vertices.len() - 1 {
1224 vertices.push(slice.vertices[0]);
1225 vertices.push(slice.vertices[i]);
1226 vertices.push(slice.vertices[i + 1]);
1227
1228 normals.push(plane_normal);
1230 normals.push(plane_normal);
1231 normals.push(plane_normal);
1232
1233 colors.push(slice_colors[0]);
1235 colors.push(slice_colors[i]);
1236 colors.push(slice_colors[i + 1]);
1237 }
1238 }
1239 }
1240
1241 if vertices.is_empty() {
1242 return None;
1243 }
1244
1245 Some(SliceMeshData {
1246 vertices,
1247 normals,
1248 colors,
1249 })
1250 }
1251}
1252
1253#[derive(Debug, Clone)]
1258pub struct SliceMeshData {
1259 pub vertices: Vec<Vec3>,
1261 pub normals: Vec<Vec3>,
1263 pub colors: Vec<Vec4>,
1265}
1266
1267impl SliceMeshData {
1268 #[must_use]
1270 pub fn num_triangles(&self) -> usize {
1271 self.vertices.len() / 3
1272 }
1273
1274 #[must_use]
1276 pub fn is_empty(&self) -> bool {
1277 self.vertices.is_empty()
1278 }
1279}
1280
1281impl Structure for VolumeMesh {
1282 fn as_any(&self) -> &dyn std::any::Any {
1283 self
1284 }
1285
1286 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
1287 self
1288 }
1289
1290 fn name(&self) -> &str {
1291 &self.name
1292 }
1293
1294 fn type_name(&self) -> &'static str {
1295 "VolumeMesh"
1296 }
1297
1298 fn bounding_box(&self) -> Option<(Vec3, Vec3)> {
1299 if self.vertices.is_empty() {
1300 return None;
1301 }
1302
1303 let mut min = Vec3::splat(f32::MAX);
1304 let mut max = Vec3::splat(f32::MIN);
1305
1306 for &v in &self.vertices {
1307 min = min.min(v);
1308 max = max.max(v);
1309 }
1310
1311 Some((min, max))
1312 }
1313
1314 fn length_scale(&self) -> f32 {
1315 self.bounding_box()
1316 .map_or(1.0, |(min, max)| (max - min).length())
1317 }
1318
1319 fn transform(&self) -> Mat4 {
1320 self.transform
1321 }
1322
1323 fn set_transform(&mut self, transform: Mat4) {
1324 self.transform = transform;
1325 self.culling_plane_cache = None;
1326 self.pick_bind_group = None;
1328 self.pick_cell_index_buffer = None;
1329 }
1330
1331 fn is_enabled(&self) -> bool {
1332 self.enabled
1333 }
1334
1335 fn set_enabled(&mut self, enabled: bool) {
1336 self.enabled = enabled;
1337 }
1338
1339 fn draw(&self, _ctx: &mut dyn RenderContext) {
1340 }
1342
1343 fn draw_pick(&self, _ctx: &mut dyn RenderContext) {
1344 }
1346
1347 fn build_ui(&mut self, _ui: &dyn std::any::Any) {
1348 }
1350
1351 fn build_pick_ui(&self, _ui: &dyn std::any::Any, _pick: &PickResult) {
1352 }
1354
1355 fn clear_gpu_resources(&mut self) {
1356 self.render_data = None;
1357 self.pick_uniform_buffer = None;
1358 self.pick_bind_group = None;
1359 self.pick_cell_index_buffer = None;
1360 self.slice_render_data = None;
1361 self.slice_plane_cache = None;
1362 self.culling_plane_cache = None;
1363 for quantity in &mut self.quantities {
1364 quantity.clear_gpu_resources();
1365 }
1366 }
1367
1368 fn refresh(&mut self) {
1369 self.render_data = None;
1370 self.pick_uniform_buffer = None;
1371 self.pick_bind_group = None;
1372 self.pick_cell_index_buffer = None;
1373 self.slice_render_data = None;
1374 self.slice_plane_cache = None;
1375 self.culling_plane_cache = None;
1376 for quantity in &mut self.quantities {
1377 quantity.refresh();
1378 }
1379 }
1380}
1381
1382impl HasQuantities for VolumeMesh {
1383 fn add_quantity(&mut self, quantity: Box<dyn Quantity>) {
1384 self.quantities.push(quantity);
1385 }
1386
1387 fn get_quantity(&self, name: &str) -> Option<&dyn Quantity> {
1388 self.quantities
1389 .iter()
1390 .find(|q| q.name() == name)
1391 .map(std::convert::AsRef::as_ref)
1392 }
1393
1394 fn get_quantity_mut(&mut self, name: &str) -> Option<&mut Box<dyn Quantity>> {
1395 self.quantities.iter_mut().find(|q| q.name() == name)
1396 }
1397
1398 fn remove_quantity(&mut self, name: &str) -> Option<Box<dyn Quantity>> {
1399 let idx = self.quantities.iter().position(|q| q.name() == name)?;
1400 Some(self.quantities.remove(idx))
1401 }
1402
1403 fn quantities(&self) -> &[Box<dyn Quantity>] {
1404 &self.quantities
1405 }
1406}
1407
1408use std::collections::HashMap;
1409
1410fn canonical_face_key(v0: u32, v1: u32, v2: u32, v3: Option<u32>) -> [u32; 4] {
1413 let mut key = [v0, v1, v2, v3.unwrap_or(u32::MAX)];
1414 key.sort_unstable();
1415 key
1416}
1417
1418const TET_FACE_STENCIL: [[usize; 3]; 4] = [[0, 2, 1], [0, 1, 3], [0, 3, 2], [1, 2, 3]];
1420
1421const HEX_FACE_STENCIL: [[[usize; 3]; 2]; 6] = [
1423 [[2, 1, 0], [2, 0, 3]], [[4, 0, 1], [4, 1, 5]], [[5, 1, 2], [5, 2, 6]], [[7, 3, 0], [7, 0, 4]], [[6, 2, 3], [6, 3, 7]], [[7, 4, 5], [7, 5, 6]], ];
1430
1431pub struct VolumeMeshRenderGeometry {
1433 pub positions: Vec<Vec3>,
1434 pub faces: Vec<[u32; 3]>,
1435 pub normals: Vec<Vec3>,
1436 pub vertex_values: Option<Vec<f32>>,
1438 pub vertex_colors: Option<Vec<Vec3>>,
1440}
1441
1442const HEX_TO_TET_PATTERN: [[usize; 4]; 5] = [
1444 [0, 1, 2, 5],
1445 [0, 2, 7, 5],
1446 [0, 2, 3, 7],
1447 [0, 5, 7, 4],
1448 [2, 7, 5, 6],
1449];
1450
1451#[cfg(test)]
1452mod tests {
1453 use super::*;
1454
1455 #[test]
1456 fn test_interior_face_detection() {
1457 let vertices = vec![
1459 Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 0.0, 0.0), Vec3::new(0.5, 1.0, 0.0), Vec3::new(0.5, 0.5, 1.0), Vec3::new(0.5, 0.5, -1.0), ];
1465 let tets = vec![[0, 1, 2, 3], [0, 2, 1, 4]];
1467 let mesh = VolumeMesh::new_tet_mesh("test", vertices, tets);
1468
1469 let (_, faces) = mesh.generate_render_geometry();
1472 assert_eq!(faces.len(), 6, "Should only render exterior faces");
1473 }
1474
1475 #[test]
1476 fn test_single_tet_all_exterior() {
1477 let vertices = vec![
1478 Vec3::new(0.0, 0.0, 0.0),
1479 Vec3::new(1.0, 0.0, 0.0),
1480 Vec3::new(0.5, 1.0, 0.0),
1481 Vec3::new(0.5, 0.5, 1.0),
1482 ];
1483 let tets = vec![[0, 1, 2, 3]];
1484 let mesh = VolumeMesh::new_tet_mesh("test", vertices, tets);
1485
1486 let (_, faces) = mesh.generate_render_geometry();
1487 assert_eq!(faces.len(), 4, "Single tet should have 4 exterior faces");
1488 }
1489
1490 #[test]
1491 fn test_single_hex_all_exterior() {
1492 let vertices = vec![
1493 Vec3::new(0.0, 0.0, 0.0),
1494 Vec3::new(1.0, 0.0, 0.0),
1495 Vec3::new(1.0, 1.0, 0.0),
1496 Vec3::new(0.0, 1.0, 0.0),
1497 Vec3::new(0.0, 0.0, 1.0),
1498 Vec3::new(1.0, 0.0, 1.0),
1499 Vec3::new(1.0, 1.0, 1.0),
1500 Vec3::new(0.0, 1.0, 1.0),
1501 ];
1502 let hexes = vec![[0, 1, 2, 3, 4, 5, 6, 7]];
1503 let mesh = VolumeMesh::new_hex_mesh("test", vertices, hexes);
1504
1505 let (_, faces) = mesh.generate_render_geometry();
1506 assert_eq!(
1508 faces.len(),
1509 12,
1510 "Single hex should have 12 triangles (6 quads)"
1511 );
1512 }
1513
1514 #[test]
1515 fn test_hex_to_tet_decomposition() {
1516 let vertices = vec![
1517 Vec3::new(0.0, 0.0, 0.0),
1518 Vec3::new(1.0, 0.0, 0.0),
1519 Vec3::new(1.0, 1.0, 0.0),
1520 Vec3::new(0.0, 1.0, 0.0),
1521 Vec3::new(0.0, 0.0, 1.0),
1522 Vec3::new(1.0, 0.0, 1.0),
1523 Vec3::new(1.0, 1.0, 1.0),
1524 Vec3::new(0.0, 1.0, 1.0),
1525 ];
1526 let hexes = vec![[0, 1, 2, 3, 4, 5, 6, 7]];
1527 let mesh = VolumeMesh::new_hex_mesh("test", vertices, hexes);
1528
1529 let tets = mesh.decompose_to_tets();
1530 assert_eq!(tets.len(), 5);
1532
1533 for tet in &tets {
1535 assert!(tet[0] < 8);
1536 assert!(tet[1] < 8);
1537 assert!(tet[2] < 8);
1538 assert!(tet[3] < 8);
1539 }
1540 }
1541
1542 #[test]
1543 fn test_quantity_aware_geometry_generation() {
1544 let vertices = vec![
1545 Vec3::new(0.0, 0.0, 0.0),
1546 Vec3::new(1.0, 0.0, 0.0),
1547 Vec3::new(0.5, 1.0, 0.0),
1548 Vec3::new(0.5, 0.5, 1.0),
1549 ];
1550 let tets = vec![[0, 1, 2, 3]];
1551 let mut mesh = VolumeMesh::new_tet_mesh("test", vertices, tets);
1552
1553 mesh.add_vertex_scalar_quantity("temp", vec![0.0, 0.5, 1.0, 0.25]);
1555
1556 if let Some(q) = mesh.get_quantity_mut("temp") {
1558 q.set_enabled(true);
1559 }
1560
1561 let render_data = mesh.generate_render_geometry_with_quantities();
1563 assert!(render_data.vertex_values.is_some());
1564 assert_eq!(
1565 render_data.vertex_values.as_ref().unwrap().len(),
1566 render_data.positions.len()
1567 );
1568 }
1569}