1mod elements;
50pub mod integrations;
51mod ops;
52pub mod primitives;
53mod selection;
54#[cfg(feature = "rerun")]
55pub mod utils;
56
57pub use elements::*;
58pub use selection::*;
59
60use hashbrown::HashMap;
61use itertools::Itertools;
62use parry3d::{
63 math::Point,
64 partitioning::{Qbvh, QbvhUpdateWorkspace},
65 shape::Triangle,
66};
67
68use glam::Vec3;
69use slotmap::{SecondaryMap, SlotMap};
70
71#[cfg(feature = "rerun")]
72lazy_static::lazy_static! {
73 pub static ref RR: rerun::RecordingStream = rerun::RecordingStreamBuilder::new("mesh_graph").spawn().unwrap();
74}
75
76#[derive(Clone)]
80#[cfg_attr(feature = "bevy", derive(bevy::prelude::Component))]
81pub struct MeshGraph {
82 pub qbvh: Qbvh<Face>,
84 pub qbvh_workspace: QbvhUpdateWorkspace,
86 pub index_to_face_id: Vec<FaceId>,
88
89 pub vertices: SlotMap<VertexId, Vertex>,
91 pub halfedges: SlotMap<HalfedgeId, Halfedge>,
93 pub faces: SlotMap<FaceId, Face>,
95
96 pub positions: SecondaryMap<VertexId, Vec3>,
98 pub vertex_normals: Option<SecondaryMap<VertexId, Vec3>>,
100}
101
102impl MeshGraph {
103 pub fn indexed_triangles(vertex_positions: &[Vec3], face_indices: &[usize]) -> Self {
106 let mut graph = Self {
107 qbvh: Qbvh::new(),
108 qbvh_workspace: QbvhUpdateWorkspace::default(),
109 index_to_face_id: Vec::with_capacity(face_indices.len() / 3),
110
111 vertices: SlotMap::with_capacity_and_key(vertex_positions.len()),
112 halfedges: SlotMap::with_capacity_and_key(face_indices.len()),
113 faces: SlotMap::with_capacity_and_key(face_indices.len() / 3),
114
115 positions: SecondaryMap::with_capacity(vertex_positions.len()),
116 vertex_normals: None,
117 };
118
119 let mut vertex_ids = Vec::with_capacity(vertex_positions.len());
120
121 for pos in vertex_positions {
122 vertex_ids.push(graph.insert_vertex(*pos));
123 }
124
125 let mut start_end_vertex_to_halfedge =
126 HashMap::<(VertexId, VertexId), HalfedgeId>::default();
127
128 for chunk in face_indices.chunks_exact(3) {
129 let a = vertex_ids[chunk[0]];
130 let b = vertex_ids[chunk[1]];
131 let c = vertex_ids[chunk[2]];
132
133 let he_a_id = graph.insert_halfedge(b);
134 let he_b_id = graph.insert_halfedge(c);
135 let he_c_id = graph.insert_halfedge(a);
136
137 start_end_vertex_to_halfedge.insert((b, c), he_b_id);
138 start_end_vertex_to_halfedge.insert((c, a), he_c_id);
139
140 let face_id = graph.insert_face(he_a_id);
141
142 let he_a = &mut graph.halfedges[he_a_id];
143 he_a.next = Some(he_b_id);
144 he_a.face = Some(face_id);
145
146 if let Some(twin_a_id) = start_end_vertex_to_halfedge.get(&(b, a)) {
147 he_a.twin = Some(*twin_a_id);
148 graph.halfedges[*twin_a_id].twin = Some(he_a_id);
149 } else {
150 start_end_vertex_to_halfedge.insert((a, b), he_a_id);
151 }
152
153 let he_b = &mut graph.halfedges[he_b_id];
154 he_b.next = Some(he_c_id);
155 he_b.face = Some(face_id);
156
157 if let Some(twin_b_id) = start_end_vertex_to_halfedge.get(&(c, b)) {
158 he_b.twin = Some(*twin_b_id);
159 graph.halfedges[*twin_b_id].twin = Some(he_b_id);
160 } else {
161 start_end_vertex_to_halfedge.insert((b, c), he_b_id);
162 }
163
164 let he_c = &mut graph.halfedges[he_c_id];
165 he_c.next = Some(he_a_id);
166 he_c.face = Some(face_id);
167
168 if let Some(twin_c_id) = start_end_vertex_to_halfedge.get(&(a, c)) {
169 he_c.twin = Some(*twin_c_id);
170 graph.halfedges[*twin_c_id].twin = Some(he_c_id);
171 } else {
172 start_end_vertex_to_halfedge.insert((c, a), he_c_id);
173 }
174
175 graph.vertices[a].outgoing_halfedge = Some(he_a_id);
176 graph.vertices[b].outgoing_halfedge = Some(he_b_id);
177 graph.vertices[c].outgoing_halfedge = Some(he_c_id);
178 }
179
180 graph.rebuild_qbvh();
181
182 graph
183 }
184
185 pub fn compute_vertex_normals(&mut self) {
187 let mut normals = SecondaryMap::with_capacity(self.vertices.len());
188
189 for face in self.faces.values() {
190 let ha_a_id = face.halfedge;
191 let he_a = self.halfedges[ha_a_id];
192
193 let he_b_id = he_a
194 .next
195 .expect("Halfedge has definitely a face and thus a next halfedge");
196 let he_b = self.halfedges[he_b_id];
197
198 let a = he_a.start_vertex(self);
199 let b = he_a.end_vertex;
200 let c = he_b.end_vertex;
201
202 let diff_a = self.positions[c] - self.positions[a];
203 let diff_b = self.positions[c] - self.positions[b];
204
205 let face_normal = diff_a.cross(diff_b);
207
208 *normals.entry(a).unwrap().or_default() += face_normal;
209 *normals.entry(b).unwrap().or_default() += face_normal;
210 *normals.entry(c).unwrap().or_default() += face_normal;
211 }
212
213 self.vertex_normals = Some(normals);
214 self.normalize_vertex_normals();
215 }
216
217 pub fn normalize_vertex_normals(&mut self) {
219 if let Some(normals) = &mut self.vertex_normals {
220 for normal in normals.values_mut() {
221 *normal = normal.normalize();
222 }
223 }
224 }
225
226 pub fn rebalance_qbvh(&mut self) {
227 self.qbvh.rebalance(0.0, &mut self.qbvh_workspace);
228 }
229
230 pub fn refit_qbvh(&mut self) {
234 let mesh_graph = self.clone();
235
236 self.qbvh.refit(0.0, &mut self.qbvh_workspace, |face| {
237 let pos = face
238 .vertices(&mesh_graph)
239 .into_iter()
240 .map(|v| {
241 let Vec3 { x, y, z } = mesh_graph.positions[v];
242 Point::new(x, y, z)
243 })
244 .collect_vec();
245
246 Triangle::new(pos[0], pos[1], pos[2]).local_aabb()
247 });
248 }
249
250 fn recount_faces(&mut self) {
251 self.index_to_face_id.clear();
252 for (id, face) in &mut self.faces {
253 face.index = self.index_to_face_id.len();
254 self.index_to_face_id.push(id);
255 }
256 }
257
258 pub fn rebuild_qbvh(&mut self) {
260 self.recount_faces();
261
262 let data = self
263 .faces
264 .values()
265 .map(|face| {
266 let pos = face
267 .vertices(self)
268 .into_iter()
269 .map(|v| {
270 let Vec3 { x, y, z } = self.positions[v];
271 Point::new(x, y, z)
272 })
273 .collect_vec();
274
275 (*face, Triangle::new(pos[0], pos[1], pos[2]).local_aabb())
276 })
277 .collect_vec();
278
279 self.qbvh.clear_and_rebuild(data.into_iter(), 0.0);
280 }
281}
282
283#[cfg(feature = "rerun")]
284impl MeshGraph {
285 pub fn log_vert_rerun(&self, name: &str, vert: VertexId) {
286 use crate::RR;
287 use crate::utils::*;
288
289 let pos = self.positions[vert];
290
291 RR.log(
292 format!("meshgraph/vertex/{name}"),
293 &rerun::Points3D::new(vec![vec3_array(pos)]),
294 )
295 .unwrap();
296 }
297
298 pub fn log_he_rerun(&self, name: &str, halfedge: HalfedgeId) {
299 use crate::RR;
300 use crate::utils::*;
301
302 let he = self.halfedges[halfedge];
303
304 let start = self.positions[he.start_vertex(self)];
305 let end = self.positions[he.end_vertex];
306
307 RR.log(
308 format!("meshgraph/halfedge/{name}"),
309 &rerun::Arrows3D::from_vectors([vec3_array(end - start)])
310 .with_origins([vec3_array(start)]),
311 )
312 .unwrap();
313 }
314
315 pub fn log_hes_rerun(&self, name: &str, halfedges: &[HalfedgeId]) {
316 use crate::RR;
317 use crate::utils::*;
318
319 let mut origins = Vec::with_capacity(halfedges.len());
320 let mut vectors = Vec::with_capacity(halfedges.len());
321
322 for &he_id in halfedges {
323 let he = self.halfedges[he_id];
324
325 let start = self.positions[he.start_vertex(self)];
326 let end = self.positions[he.end_vertex];
327
328 origins.push(vec3_array(start));
329 vectors.push(vec3_array(end - start));
330 }
331
332 RR.log(
333 format!("meshgraph/halfedge/{name}"),
334 &rerun::Arrows3D::from_vectors(vectors).with_origins(origins),
335 )
336 .unwrap();
337 }
338
339 pub fn log_face_rerun(&self, name: &str, face: FaceId) {
340 use crate::RR;
341 use crate::utils::*;
342
343 let mut origins = Vec::with_capacity(3);
344 let mut vectors = Vec::with_capacity(3);
345
346 let face = self.faces[face];
347
348 let pos = face
349 .vertices(self)
350 .iter()
351 .map(|v| self.positions[*v])
352 .collect::<Vec<_>>();
353
354 let center = pos.iter().copied().reduce(|a, b| a + b).unwrap() / pos.len() as f32;
355
356 let pos = face
357 .vertices(self)
358 .into_iter()
359 .zip(pos)
360 .map(|(v, p)| (v, center * 0.1 + p * 0.9))
361 .collect::<HashMap<_, _>>();
362
363 for he_id in face.halfedges(self) {
364 let he = self.halfedges[he_id];
365
366 let start = pos[&he.start_vertex(self)];
367 let end = pos[&he.end_vertex];
368
369 origins.push(vec3_array(start));
370 vectors.push(vec3_array(end - start));
371 }
372
373 #[cfg(feature = "rerun")]
374 {
375 RR.log(
376 format!("meshgraph/face/{name}"),
377 &rerun::Arrows3D::from_vectors(vectors).with_origins(origins),
378 )
379 .unwrap();
380 }
381 }
382
383 pub fn log_rerun(&self) {
384 use crate::RR;
385 use crate::utils::*;
386
387 let buffers = crate::integrations::VertexIndexBuffers::from(self.clone());
388 RR.log(
389 "meshgraph/mesh",
390 &rerun::Mesh3D::new(
391 buffers
392 .positions
393 .into_iter()
394 .zip(buffers.normals.iter().cloned())
395 .map(|(pos, norm)| vec3_array(pos - norm * 0.1)),
396 )
397 .with_triangle_indices(
398 buffers
399 .indices
400 .chunks(3)
401 .map(|chunk| rerun::datatypes::UVec3D::new(chunk[0], chunk[1], chunk[2])),
402 )
403 .with_vertex_colors(
404 buffers
405 .normals
406 .into_iter()
407 .map(|v| {
408 [
409 (100.0 + v.x * 100.0) as u8,
410 (100.0 + v.y * 100.0) as u8,
411 (100.0 + v.z * 100.0) as u8,
412 ]
413 })
414 .collect::<Vec<_>>(),
415 ),
416 )
417 .unwrap();
418
419 RR.log(
420 "meshgraph/positions",
421 &rerun::Points3D::new(self.positions.values().map(vec3_array))
422 .with_radii(self.positions.iter().map(|_| 0.1)),
423 )
424 .unwrap();
425
426 let mut origins = Vec::with_capacity(self.faces.len() * 3);
427 let mut vectors = Vec::with_capacity(self.faces.len() * 3);
428
429 let mut he_to_pos = HashMap::<HalfedgeId, (Vec3, Vec3)>::default();
430
431 for face in self.faces.values() {
432 let pos = face
433 .vertices(self)
434 .iter()
435 .map(|v| self.positions[*v])
436 .collect::<Vec<_>>();
437
438 let center = pos.iter().copied().reduce(|a, b| a + b).unwrap() / pos.len() as f32;
439
440 let pos = face
441 .vertices(self)
442 .into_iter()
443 .zip(pos)
444 .map(|(v, p)| (v, center * 0.1 + p * 0.9))
445 .collect::<HashMap<_, _>>();
446
447 for he_id in face.halfedges(self) {
448 let he = self.halfedges[he_id];
449
450 let start = pos[&he.start_vertex(self)];
451 let end = pos[&he.end_vertex];
452
453 he_to_pos.insert(he_id, (start, end));
454
455 origins.push(vec3_array(start));
456 vectors.push(vec3_array(end - start));
457 }
458 }
459
460 for (he_id, he) in &self.halfedges {
461 if he.is_boundary() {
462 let start_vertex = he.start_vertex(self);
463 let end_vertex = he.end_vertex;
464
465 let start = self.positions[start_vertex];
466 let end = self.positions[end_vertex];
467
468 let offset = if let Some(normals) = self.vertex_normals.as_ref() {
469 normals[start_vertex]
470 .lerp(normals[end_vertex], 0.5)
471 .cross(end - start)
472 .normalize()
473 * 0.1
474 } else {
475 Vec3::ZERO
476 };
477
478 let (start, end) = (start.lerp(end, 0.1) + offset, end.lerp(start, 0.1) + offset);
479
480 he_to_pos.insert(he_id, (start, end));
481
482 origins.push(vec3_array(start));
483 vectors.push(vec3_array(end - start));
484 }
485 }
486
487 RR.log(
488 "meshgraph/halfedges",
489 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
490 )
491 .unwrap();
492
493 origins.clear();
494 vectors.clear();
495
496 for (he_id, he) in &self.halfedges {
497 let twin = he.twin();
498
499 let (he_start, he_end) = he_to_pos[&he_id];
500 let (tw_start, tw_end) = he_to_pos[&twin];
501
502 let start = he_start * 0.8 + he_end * 0.2;
503 let end = tw_start * 0.2 + tw_end * 0.8;
504
505 origins.push(vec3_array(start));
506 vectors.push(vec3_array(end - start));
507 }
508
509 RR.log(
510 "meshgraph/twins",
511 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
512 )
513 .unwrap();
514
515 origins.clear();
516 vectors.clear();
517
518 for (v_id, v) in &self.vertices {
519 if let Some(he) = v.outgoing_halfedge.as_ref() {
520 let start = self.positions[v_id];
521
522 RR.log(
523 "meshgraph/the_vertex",
524 &rerun::Points3D::new([vec3_array(start)]),
525 )
526 .unwrap();
527
528 let (start_he, end_he) = he_to_pos[he];
529
530 let end = start_he.lerp(end_he, 0.05);
531
532 origins.push(vec3_array(start));
533 vectors.push(vec3_array(end - start));
534 }
535 }
536
537 RR.log(
538 "meshgraph/outgoing_halfedges",
539 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
540 )
541 .unwrap();
542
543 origins.clear();
544 vectors.clear();
545
546 for face in self.faces.values() {
547 let start = face.center(self);
548
549 let (he_start, he_end) = he_to_pos[&face.halfedge];
550 let end = he_start * 0.6 + he_end * 0.4;
551
552 origins.push(vec3_array(start));
553 vectors.push(vec3_array(end - start));
554 }
555
556 RR.log(
557 "meshgraph/face_halfedges",
558 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
559 )
560 .unwrap();
561
562 origins.clear();
563 vectors.clear();
564
565 for (he_id, he) in &self.halfedges {
566 if let Some(face_id) = he.face {
567 let (he_start, he_end) = he_to_pos[&he_id];
568 let start = he_start * 0.4 + he_end * 0.6;
569
570 let end = self.faces[face_id].center(self);
571
572 origins.push(vec3_array(start));
573 vectors.push(vec3_array((end - start) * 0.9));
574 }
575 }
576
577 RR.log(
578 "meshgraph/halfedge_faces",
579 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
580 )
581 .unwrap();
582 }
583}