1mod elements;
50pub mod integrations;
51mod ops;
52mod plane_slice;
53pub mod primitives;
54mod selection;
55#[cfg(feature = "rerun")]
56pub mod utils;
57
58pub use elements::*;
59pub use plane_slice::*;
60pub use selection::*;
61
62use hashbrown::HashMap;
63use itertools::Itertools;
64use parry3d::{
65 math::Point,
66 partitioning::{Qbvh, QbvhUpdateWorkspace},
67 shape::Triangle,
68};
69
70use glam::Vec3;
71use slotmap::{SecondaryMap, SlotMap};
72
73#[cfg(feature = "rerun")]
74lazy_static::lazy_static! {
75 pub static ref RR: rerun::RecordingStream = rerun::RecordingStreamBuilder::new("mesh_graph").spawn().unwrap();
76}
77
78#[derive(Clone)]
82#[cfg_attr(feature = "bevy", derive(bevy::prelude::Component))]
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84pub struct MeshGraph {
85 #[cfg_attr(feature = "serde", serde(skip))]
87 pub qbvh: Qbvh<Face>,
88 #[cfg_attr(feature = "serde", serde(skip))]
90 pub qbvh_workspace: QbvhUpdateWorkspace,
91 #[cfg_attr(feature = "serde", serde(skip))]
93 pub index_to_face_id: Vec<FaceId>,
94
95 pub vertices: SlotMap<VertexId, Vertex>,
97 pub halfedges: SlotMap<HalfedgeId, Halfedge>,
99 pub faces: SlotMap<FaceId, Face>,
101
102 pub positions: SecondaryMap<VertexId, Vec3>,
104 pub vertex_normals: Option<SecondaryMap<VertexId, Vec3>>,
106}
107
108impl MeshGraph {
109 pub fn triangles(vertex_positions: &[Vec3]) -> Self {
114 assert!(
115 vertex_positions.len().is_multiple_of(3),
116 "Number of vertex positions should be a multiple of 3"
117 );
118
119 let mut unique_positions: Vec<Vec3> = Vec::with_capacity(vertex_positions.len() / 3);
121 let mut face_indices = Vec::with_capacity(vertex_positions.len());
122
123 for vertex_pos in vertex_positions {
124 let mut idx = None;
126 for (j, pos) in unique_positions.iter().enumerate() {
127 const EPSILON: f32 = 1e-5;
128
129 if pos.distance_squared(*vertex_pos) < EPSILON {
130 idx = Some(j);
131 break;
132 }
133 }
134
135 let vertex_idx = if let Some(idx) = idx {
137 idx
138 } else {
139 let new_idx = unique_positions.len();
140 unique_positions.push(*vertex_pos);
141
142 #[cfg(feature = "rerun")]
143 RR.log(
144 "meshgraph/construct/vertices",
145 &rerun::Points3D::new(unique_positions.iter().map(crate::utils::vec3_array)),
146 )
147 .unwrap();
148
149 new_idx
150 };
151
152 face_indices.push(vertex_idx);
154 }
155
156 Self::indexed_triangles(&unique_positions, &face_indices)
158 }
159
160 pub fn indexed_triangles(vertex_positions: &[Vec3], face_indices: &[usize]) -> Self {
163 let mut graph = Self {
164 qbvh: Qbvh::new(),
165 qbvh_workspace: QbvhUpdateWorkspace::default(),
166 index_to_face_id: Vec::with_capacity(face_indices.len() / 3),
167
168 vertices: SlotMap::with_capacity_and_key(vertex_positions.len()),
169 halfedges: SlotMap::with_capacity_and_key(face_indices.len()),
170 faces: SlotMap::with_capacity_and_key(face_indices.len() / 3),
171
172 positions: SecondaryMap::with_capacity(vertex_positions.len()),
173 vertex_normals: None,
174 };
175
176 let mut vertex_ids = Vec::with_capacity(vertex_positions.len());
177
178 for pos in vertex_positions {
179 vertex_ids.push(graph.insert_vertex(*pos));
180 }
181
182 let mut start_end_vertex_to_halfedge =
183 HashMap::<(VertexId, VertexId), HalfedgeId>::default();
184
185 for chunk in face_indices.chunks_exact(3) {
186 let a = vertex_ids[chunk[0]];
187 let b = vertex_ids[chunk[1]];
188 let c = vertex_ids[chunk[2]];
189
190 if a == b || b == c || c == a {
191 #[cfg(feature = "rerun")]
192 RR.log(
193 "meshgraph/construct/zero_face",
194 &rerun::Points3D::new(
195 [graph.positions[a], graph.positions[b], graph.positions[c]]
196 .iter()
197 .map(crate::utils::vec3_array),
198 ),
199 )
200 .unwrap();
201
202 continue;
203 }
204
205 let he_a_id = graph.insert_halfedge(b);
206 let he_b_id = graph.insert_halfedge(c);
207 let he_c_id = graph.insert_halfedge(a);
208
209 start_end_vertex_to_halfedge.insert((b, c), he_b_id);
210 start_end_vertex_to_halfedge.insert((c, a), he_c_id);
211
212 let face_id = graph.insert_face(he_a_id);
213
214 let he_a = &mut graph.halfedges[he_a_id];
215 he_a.next = Some(he_b_id);
216 he_a.face = Some(face_id);
217
218 if let Some(twin_a_id) = start_end_vertex_to_halfedge.get(&(b, a)) {
219 he_a.twin = Some(*twin_a_id);
220 graph.halfedges[*twin_a_id].twin = Some(he_a_id);
221 } else {
222 start_end_vertex_to_halfedge.insert((a, b), he_a_id);
223 }
224
225 let he_b = &mut graph.halfedges[he_b_id];
226 he_b.next = Some(he_c_id);
227 he_b.face = Some(face_id);
228
229 if let Some(twin_b_id) = start_end_vertex_to_halfedge.get(&(c, b)) {
230 he_b.twin = Some(*twin_b_id);
231 graph.halfedges[*twin_b_id].twin = Some(he_b_id);
232 } else {
233 start_end_vertex_to_halfedge.insert((b, c), he_b_id);
234 }
235
236 let he_c = &mut graph.halfedges[he_c_id];
237 he_c.next = Some(he_a_id);
238 he_c.face = Some(face_id);
239
240 if let Some(twin_c_id) = start_end_vertex_to_halfedge.get(&(a, c)) {
241 he_c.twin = Some(*twin_c_id);
242 graph.halfedges[*twin_c_id].twin = Some(he_c_id);
243 } else {
244 start_end_vertex_to_halfedge.insert((c, a), he_c_id);
245 }
246
247 graph.vertices[a].outgoing_halfedge = Some(he_a_id);
248 graph.vertices[b].outgoing_halfedge = Some(he_b_id);
249 graph.vertices[c].outgoing_halfedge = Some(he_c_id);
250 }
251
252 graph.rebuild_qbvh();
253
254 graph
255 }
256
257 pub fn compute_vertex_normals(&mut self) {
259 let mut normals = SecondaryMap::with_capacity(self.vertices.len());
260
261 for face in self.faces.values() {
262 let ha_a_id = face.halfedge;
263 let he_a = self.halfedges[ha_a_id];
264
265 let he_b_id = he_a
266 .next
267 .expect("Halfedge has definitely a face and thus a next halfedge");
268 let he_b = self.halfedges[he_b_id];
269
270 let a = he_a.start_vertex(self);
271 let b = he_a.end_vertex;
272 let c = he_b.end_vertex;
273
274 let diff_a = self.positions[c] - self.positions[a];
275 let diff_b = self.positions[c] - self.positions[b];
276
277 let face_normal = diff_a.cross(diff_b);
279
280 *normals.entry(a).unwrap().or_default() += face_normal;
281 *normals.entry(b).unwrap().or_default() += face_normal;
282 *normals.entry(c).unwrap().or_default() += face_normal;
283 }
284
285 self.vertex_normals = Some(normals);
286 self.normalize_vertex_normals();
287 }
288
289 pub fn normalize_vertex_normals(&mut self) {
291 if let Some(normals) = &mut self.vertex_normals {
292 for normal in normals.values_mut() {
293 *normal = normal.normalize();
294 }
295 }
296 }
297
298 pub fn rebalance_qbvh(&mut self) {
299 self.qbvh.rebalance(0.0, &mut self.qbvh_workspace);
300 }
301
302 pub fn refit_qbvh(&mut self) {
306 let mesh_graph = self.clone();
307
308 self.qbvh.refit(0.0, &mut self.qbvh_workspace, |face| {
309 let pos = face
310 .vertices(&mesh_graph)
311 .into_iter()
312 .map(|v| {
313 let Vec3 { x, y, z } = mesh_graph.positions[v];
314 Point::new(x, y, z)
315 })
316 .collect_vec();
317
318 Triangle::new(pos[0], pos[1], pos[2]).local_aabb()
319 });
320 }
321
322 fn recount_faces(&mut self) {
323 self.index_to_face_id.clear();
324 for (id, face) in &mut self.faces {
325 face.index = self.index_to_face_id.len();
326 self.index_to_face_id.push(id);
327 }
328 }
329
330 pub fn rebuild_qbvh(&mut self) {
332 self.recount_faces();
333
334 let data = self
335 .faces
336 .values()
337 .map(|face| {
338 let pos = face
339 .vertices(self)
340 .into_iter()
341 .map(|v| {
342 let Vec3 { x, y, z } = self.positions[v];
343 Point::new(x, y, z)
344 })
345 .collect_vec();
346
347 (*face, Triangle::new(pos[0], pos[1], pos[2]).local_aabb())
348 })
349 .collect_vec();
350
351 self.qbvh.clear_and_rebuild(data.into_iter(), 0.0);
352 }
353}
354
355#[cfg(feature = "rerun")]
356impl MeshGraph {
357 pub fn log_selection_rerun(&self, name: &str, selection: &Selection) {
358 use crate::RR;
359 use crate::utils::*;
360
361 RR.log(
362 format!("meshgraph/selection/{name}/points"),
363 &rerun::Points3D::new(
364 selection
365 .vertices
366 .iter()
367 .map(|v| vec3_array(self.positions[*v]))
368 .collect_vec(),
369 ),
370 )
371 .unwrap();
372
373 self.log_hes_rerun_with_name(
374 format!("meshgraph/selection/{name}/halfedges"),
375 &selection.halfedges.iter().copied().collect::<Vec<_>>(),
376 );
377
378 let face_centers = selection
379 .faces
380 .iter()
381 .map(|face_id| {
382 let face = self.faces[*face_id];
383
384 let center = face
385 .vertices(self)
386 .into_iter()
387 .map(|v_id| self.positions[v_id])
388 .reduce(|acc, p| acc + p)
389 .unwrap()
390 / 3.0;
391
392 vec3_array(center)
393 })
394 .collect_vec();
395
396 RR.log(
397 format!("meshgraph/selection/{name}/faces"),
398 &rerun::Points3D::new(face_centers),
399 )
400 .unwrap();
401 }
402
403 pub fn log_vert_rerun(&self, name: &str, vert: VertexId) {
404 use crate::RR;
405 use crate::utils::*;
406
407 let pos = self.positions[vert];
408
409 RR.log(
410 format!("meshgraph/vertex/{name}"),
411 &rerun::Points3D::new(vec![vec3_array(pos)]),
412 )
413 .unwrap();
414 }
415
416 pub fn log_he_rerun(&self, name: &str, halfedge: HalfedgeId) {
417 use crate::RR;
418 use crate::utils::*;
419
420 let he = self.halfedges[halfedge];
421
422 let start = self.positions[he.start_vertex(self)];
423 let end = self.positions[he.end_vertex];
424
425 RR.log(
426 format!("meshgraph/halfedge/{name}"),
427 &rerun::Arrows3D::from_vectors([vec3_array(end - start)])
428 .with_origins([vec3_array(start)]),
429 )
430 .unwrap();
431 }
432
433 pub fn log_hes_rerun(&self, name: &str, halfedges: &[HalfedgeId]) {
434 self.log_hes_rerun_with_name(format!("meshgraph/halfedge/{name}"), halfedges);
435 }
436
437 fn log_hes_rerun_with_name(&self, name: String, halfedges: &[HalfedgeId]) {
438 use crate::RR;
439 use crate::utils::*;
440
441 let mut origins = Vec::with_capacity(halfedges.len());
442 let mut vectors = Vec::with_capacity(halfedges.len());
443
444 for &he_id in halfedges {
445 let he = self.halfedges[he_id];
446
447 let start = self.positions[he.start_vertex(self)];
448 let end = self.positions[he.end_vertex];
449
450 origins.push(vec3_array(start));
451 vectors.push(vec3_array(end - start));
452 }
453
454 RR.log(
455 name,
456 &rerun::Arrows3D::from_vectors(vectors).with_origins(origins),
457 )
458 .unwrap();
459 }
460
461 pub fn log_face_rerun(&self, name: &str, face: FaceId) {
462 use crate::RR;
463 use crate::utils::*;
464
465 let mut origins = Vec::with_capacity(3);
466 let mut vectors = Vec::with_capacity(3);
467
468 let face = self.faces[face];
469
470 let pos = face
471 .vertices(self)
472 .iter()
473 .map(|v| self.positions[*v])
474 .collect::<Vec<_>>();
475
476 let center = pos.iter().copied().reduce(|a, b| a + b).unwrap() / pos.len() as f32;
477
478 let pos = face
479 .vertices(self)
480 .into_iter()
481 .zip(pos)
482 .map(|(v, p)| (v, center * 0.1 + p * 0.9))
483 .collect::<HashMap<_, _>>();
484
485 for he_id in face.halfedges(self) {
486 let he = self.halfedges[he_id];
487
488 let start = pos[&he.start_vertex(self)];
489 let end = pos[&he.end_vertex];
490
491 origins.push(vec3_array(start));
492 vectors.push(vec3_array(end - start));
493 }
494
495 #[cfg(feature = "rerun")]
496 {
497 RR.log(
498 format!("meshgraph/face/{name}"),
499 &rerun::Arrows3D::from_vectors(vectors).with_origins(origins),
500 )
501 .unwrap();
502 }
503 }
504
505 pub fn log_rerun(&self) {
506 use crate::RR;
507 use crate::utils::*;
508
509 let buffers = crate::integrations::VertexIndexBuffers::from(self.clone());
510 RR.log(
511 "meshgraph/mesh",
512 &rerun::Mesh3D::new(
513 buffers
514 .positions
515 .into_iter()
516 .zip(buffers.normals.iter().cloned())
517 .map(|(pos, norm)| vec3_array(pos - norm * 0.1)),
518 )
519 .with_triangle_indices(
520 buffers
521 .indices
522 .chunks(3)
523 .map(|chunk| rerun::datatypes::UVec3D::new(chunk[0], chunk[1], chunk[2])),
524 )
525 .with_vertex_colors(
526 buffers
527 .normals
528 .into_iter()
529 .map(|v| {
530 [
531 (100.0 + v.x * 100.0) as u8,
532 (100.0 + v.y * 100.0) as u8,
533 (100.0 + v.z * 100.0) as u8,
534 ]
535 })
536 .collect::<Vec<_>>(),
537 ),
538 )
539 .unwrap();
540
541 RR.log(
542 "meshgraph/positions",
543 &rerun::Points3D::new(self.positions.values().map(vec3_array))
544 .with_radii(self.positions.iter().map(|_| 0.01)),
545 )
546 .unwrap();
547
548 let mut origins = Vec::with_capacity(self.faces.len() * 3);
549 let mut vectors = Vec::with_capacity(self.faces.len() * 3);
550
551 let mut he_to_pos = HashMap::<HalfedgeId, (Vec3, Vec3)>::default();
552
553 for face in self.faces.values() {
554 let pos = face
555 .vertices(self)
556 .iter()
557 .map(|v| self.positions[*v])
558 .collect::<Vec<_>>();
559
560 let center = pos.iter().copied().reduce(|a, b| a + b).unwrap() / pos.len() as f32;
561
562 let pos = face
563 .vertices(self)
564 .into_iter()
565 .zip(pos)
566 .map(|(v, p)| (v, center * 0.1 + p * 0.9))
567 .collect::<HashMap<_, _>>();
568
569 for he_id in face.halfedges(self) {
570 let he = self.halfedges[he_id];
571
572 let start = pos[&he.start_vertex(self)];
573 let end = pos[&he.end_vertex];
574
575 he_to_pos.insert(he_id, (start, end));
576
577 origins.push(vec3_array(start));
578 vectors.push(vec3_array(end - start));
579 }
580 }
581
582 for (he_id, he) in &self.halfedges {
583 if he.is_boundary() {
584 let start_vertex = he.start_vertex(self);
585 let end_vertex = he.end_vertex;
586
587 let start = self.positions[start_vertex];
588 let end = self.positions[end_vertex];
589
590 let offset = if let Some(normals) = self.vertex_normals.as_ref() {
591 normals[start_vertex]
592 .lerp(normals[end_vertex], 0.5)
593 .cross(end - start)
594 .normalize()
595 * 0.1
596 } else {
597 Vec3::ZERO
598 };
599
600 let (start, end) = (start.lerp(end, 0.1) + offset, end.lerp(start, 0.1) + offset);
601
602 he_to_pos.insert(he_id, (start, end));
603
604 origins.push(vec3_array(start));
605 vectors.push(vec3_array(end - start));
606 }
607 }
608
609 RR.log(
610 "meshgraph/halfedges",
611 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
612 )
613 .unwrap();
614
615 origins.clear();
616 vectors.clear();
617
618 for (he_id, he) in &self.halfedges {
619 let twin = he.twin();
620
621 let (he_start, he_end) = he_to_pos[&he_id];
622 let (tw_start, tw_end) = he_to_pos[&twin];
623
624 let start = he_start * 0.8 + he_end * 0.2;
625 let end = tw_start * 0.2 + tw_end * 0.8;
626
627 origins.push(vec3_array(start));
628 vectors.push(vec3_array(end - start));
629 }
630
631 RR.log(
632 "meshgraph/twins",
633 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
634 )
635 .unwrap();
636
637 origins.clear();
638 vectors.clear();
639
640 for (v_id, v) in &self.vertices {
641 if let Some(he) = v.outgoing_halfedge.as_ref() {
642 let start = self.positions[v_id];
643
644 RR.log(
645 "meshgraph/the_vertex",
646 &rerun::Points3D::new([vec3_array(start)]),
647 )
648 .unwrap();
649
650 let (start_he, end_he) = he_to_pos[he];
651
652 let end = start_he.lerp(end_he, 0.05);
653
654 origins.push(vec3_array(start));
655 vectors.push(vec3_array(end - start));
656 }
657 }
658
659 RR.log(
660 "meshgraph/outgoing_halfedges",
661 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
662 )
663 .unwrap();
664
665 origins.clear();
666 vectors.clear();
667
668 for face in self.faces.values() {
669 let start = face.center(self);
670
671 let (he_start, he_end) = he_to_pos[&face.halfedge];
672 let end = he_start * 0.6 + he_end * 0.4;
673
674 origins.push(vec3_array(start));
675 vectors.push(vec3_array(end - start));
676 }
677
678 RR.log(
679 "meshgraph/face_halfedges",
680 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
681 )
682 .unwrap();
683
684 origins.clear();
685 vectors.clear();
686
687 for (he_id, he) in &self.halfedges {
688 if let Some(face_id) = he.face {
689 let (he_start, he_end) = he_to_pos[&he_id];
690 let start = he_start * 0.4 + he_end * 0.6;
691
692 let end = self.faces[face_id].center(self);
693
694 origins.push(vec3_array(start));
695 vectors.push(vec3_array((end - start) * 0.9));
696 }
697 }
698
699 RR.log(
700 "meshgraph/halfedge_faces",
701 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
702 )
703 .unwrap();
704
705 origins.clear();
706 vectors.clear();
707
708 for (he_id, he) in &self.halfedges {
709 if let Some(next_id) = he.next {
710 let (he_start, he_end) = he_to_pos[&he_id];
711 let start = he_start * 0.15 + he_end * 0.85;
712
713 let (next_start, next_end) = he_to_pos[&next_id];
714 let end = next_start * 0.85 + next_end * 0.15;
715
716 origins.push(vec3_array(start));
717 vectors.push(vec3_array(end - start));
718 }
719 }
720
721 RR.log(
722 "meshgraph/halfedge_next",
723 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
724 )
725 .unwrap();
726 }
727}