1mod access;
48mod elements;
49pub mod integrations;
50mod iter;
51mod ops;
52mod plane_slice;
53pub mod primitives;
54#[cfg(feature = "rerun")]
55mod rerun_impl;
56mod selection;
57#[cfg(feature = "serde")]
58mod serialize;
59pub mod utils;
60
61pub use elements::*;
62pub use iter::*;
63pub use ops::*;
64pub use plane_slice::*;
65pub use selection::*;
66
67use hashbrown::HashMap;
68use parry3d::partitioning::{Bvh, BvhWorkspace};
69
70use glam::Vec3;
71use slotmap::{SecondaryMap, SlotMap};
72use tracing::{error, instrument};
73
74use crate::utils::unwrap_or_return;
75
76#[cfg(feature = "rerun")]
77lazy_static::lazy_static! {
78 pub static ref RR: rerun::RecordingStream = rerun::RecordingStreamBuilder::new("mesh_graph").spawn().unwrap();
79}
80
81#[derive(Clone, Default)]
85#[cfg_attr(feature = "bevy", derive(bevy::prelude::Component))]
86#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
87#[cfg_attr(
88 feature = "serde",
89 serde(from = "crate::serialize::MeshGraphIntermediate")
90)]
91pub struct MeshGraph {
92 #[cfg_attr(feature = "serde", serde(skip))]
94 pub bvh: Bvh,
95 #[cfg_attr(feature = "serde", serde(skip))]
97 pub bvh_workspace: BvhWorkspace,
98 #[cfg_attr(feature = "serde", serde(skip))]
100 pub index_to_face_id: HashMap<u32, FaceId>,
101 #[cfg_attr(feature = "serde", serde(skip))]
103 pub next_index: u32,
104
105 pub vertices: SlotMap<VertexId, Vertex>,
107 pub halfedges: SlotMap<HalfedgeId, Halfedge>,
109 pub faces: SlotMap<FaceId, Face>,
111
112 pub positions: SecondaryMap<VertexId, Vec3>,
114 pub vertex_normals: Option<SecondaryMap<VertexId, Vec3>>,
116
117 #[cfg_attr(feature = "serde", serde(skip))]
119 pub outgoing_halfedges: SecondaryMap<VertexId, Vec<HalfedgeId>>,
120}
121
122impl MeshGraph {
123 #[inline]
125 pub fn new() -> Self {
126 Self::default()
127 }
128
129 pub fn triangles(vertex_positions: &[Vec3]) -> Self {
134 assert!(
135 vertex_positions.len().is_multiple_of(3),
136 "Number of vertex positions should be a multiple of 3"
137 );
138
139 let mut unique_positions: Vec<Vec3> = Vec::with_capacity(vertex_positions.len() / 3);
141 let mut face_indices = Vec::with_capacity(vertex_positions.len());
142
143 for vertex_pos in vertex_positions {
144 let mut idx = None;
146 for (j, pos) in unique_positions.iter().enumerate() {
147 const EPSILON: f32 = 1e-5;
148
149 if pos.distance_squared(*vertex_pos) < EPSILON {
150 idx = Some(j);
151 break;
152 }
153 }
154
155 let vertex_idx = if let Some(idx) = idx {
157 idx
158 } else {
159 let new_idx = unique_positions.len();
160 unique_positions.push(*vertex_pos);
161
162 #[cfg(feature = "rerun")]
163 RR.log(
164 "meshgraph/construct/vertices",
165 &rerun::Points3D::new(unique_positions.iter().map(crate::utils::vec3_array)),
166 )
167 .unwrap();
168
169 new_idx
170 };
171
172 face_indices.push(vertex_idx);
174 }
175
176 Self::indexed_triangles(&unique_positions, &face_indices)
178 }
179
180 #[instrument]
183 pub fn indexed_triangles_with_custom_attribute<T>(
184 vertex_positions: &[Vec3],
185 face_indices: &[usize],
186 custom_attribute: &[T],
187 ) -> (Self, SecondaryMap<VertexId, T>)
188 where
189 T: Clone + std::fmt::Debug,
190 {
191 let (mesh_graph, vertex_ids) =
192 Self::indexed_triangles_and_vertex_ids(vertex_positions, face_indices);
193
194 let mut custom_attribute_map = SecondaryMap::with_capacity(custom_attribute.len());
195 for (attr, vertex_id) in custom_attribute.iter().zip(vertex_ids) {
196 custom_attribute_map.insert(vertex_id, attr.clone());
197 }
198
199 (mesh_graph, custom_attribute_map)
200 }
201
202 #[inline]
205 pub fn indexed_triangles(vertex_positions: &[Vec3], face_indices: &[usize]) -> Self {
206 Self::indexed_triangles_and_vertex_ids(vertex_positions, face_indices).0
207 }
208
209 #[instrument]
212 pub fn indexed_triangles_and_vertex_ids(
213 vertex_positions: &[Vec3],
214 face_indices: &[usize],
215 ) -> (Self, Vec<VertexId>) {
216 let mut mesh_graph = Self {
217 bvh: Bvh::new(),
218 bvh_workspace: BvhWorkspace::default(),
219 index_to_face_id: HashMap::with_capacity(face_indices.len() / 3),
220 next_index: 0,
221
222 vertices: SlotMap::with_capacity_and_key(vertex_positions.len()),
223 halfedges: SlotMap::with_capacity_and_key(face_indices.len()),
224 faces: SlotMap::with_capacity_and_key(face_indices.len() / 3),
225
226 positions: SecondaryMap::with_capacity(vertex_positions.len()),
227 vertex_normals: None,
228 outgoing_halfedges: SecondaryMap::with_capacity(vertex_positions.len()),
229 };
230
231 let mut vertex_ids = Vec::with_capacity(vertex_positions.len());
232
233 for pos in vertex_positions {
234 vertex_ids.push(mesh_graph.add_vertex(*pos));
235 }
236
237 for chunk in face_indices.chunks_exact(3) {
238 let a = vertex_ids[chunk[0]];
239 let b = vertex_ids[chunk[1]];
240 let c = vertex_ids[chunk[2]];
241
242 if a == b || b == c || c == a {
243 #[cfg(feature = "rerun")]
244 RR.log(
245 "meshgraph/construct/zero_face",
246 &rerun::Points3D::new(
247 [
248 mesh_graph.positions[a],
249 mesh_graph.positions[b],
250 mesh_graph.positions[c],
251 ]
252 .iter()
253 .map(crate::utils::vec3_array),
254 ),
255 )
256 .unwrap();
257
258 continue;
259 }
260
261 let he_a_id = mesh_graph.add_or_get_edge(a, b).unwrap().start_to_end_he_id;
263 let he_b_id = mesh_graph.add_or_get_edge(b, c).unwrap().start_to_end_he_id;
264 let he_c_id = mesh_graph.add_or_get_edge(c, a).unwrap().start_to_end_he_id;
265
266 let _face_id = mesh_graph.add_face(he_a_id, he_b_id, he_c_id);
267 }
268
269 mesh_graph.make_all_outgoing_halfedges_boundary_if_possible();
270 mesh_graph.rebuild_bvh();
271
272 (mesh_graph, vertex_ids)
273 }
274
275 pub fn compute_vertex_normal(&mut self, vertex_id: VertexId) {
277 if self.vertex_normals.is_none() {
278 return;
279 }
280
281 let vertex = unwrap_or_return!(self.vertices.get(vertex_id), "Vertex not found");
282
283 let mut normal = Vec3::ZERO;
284
285 for face_id in vertex.faces(self) {
286 let face = unwrap_or_return!(self.faces.get(face_id), "Face not found");
287 normal += unwrap_or_return!(face.normal(self), "Face normal not found");
288 }
289
290 self.vertex_normals
291 .as_mut()
292 .unwrap()
293 .insert(vertex_id, normal.try_normalize().unwrap_or(Vec3::ZERO));
294 }
295
296 #[instrument(skip(self))]
298 pub fn compute_vertex_normals(&mut self) {
299 let mut normals = SecondaryMap::with_capacity(self.vertices.len());
300
301 for face in self.faces.values() {
302 let ha_a_id = face.halfedge;
303 let he_a = self.halfedges[ha_a_id];
304
305 let he_b_id = he_a
306 .next
307 .expect("Halfedge has definitely a face and thus a next halfedge");
308 let he_b = self.halfedges[he_b_id];
309
310 let a = match he_a.start_vertex(self) {
311 Some(v) => v,
312 None => {
313 error!("Start vertex not found");
314 continue;
315 }
316 };
317 let b = he_a.end_vertex;
318 let c = he_b.end_vertex;
319
320 let (Some(pos_a), Some(pos_b), Some(pos_c)) = (
321 self.positions.get(a),
322 self.positions.get(b),
323 self.positions.get(c),
324 ) else {
325 continue;
326 };
327
328 let diff_a = pos_c - pos_a;
329 let diff_b = pos_c - pos_b;
330
331 let face_normal = diff_a.cross(diff_b);
333
334 *normals.entry(a).unwrap().or_default() += face_normal;
335 *normals.entry(b).unwrap().or_default() += face_normal;
336 *normals.entry(c).unwrap().or_default() += face_normal;
337 }
338
339 self.vertex_normals = Some(normals);
340 self.normalize_vertex_normals();
341 }
342
343 pub fn normalize_vertex_normals(&mut self) {
345 if let Some(normals) = &mut self.vertex_normals {
346 for normal in normals.values_mut() {
347 *normal = normal.normalize_or_zero();
348 }
349 }
350 }
351
352 #[inline]
354 pub fn optimize_bvh_incremental(&mut self) {
355 self.bvh.optimize_incremental(&mut self.bvh_workspace);
356 }
357
358 #[inline]
360 pub fn refit_bvh(&mut self) {
361 self.bvh.refit(&mut self.bvh_workspace);
362 }
363
364 #[inline]
366 pub fn rebuild_bvh(&mut self) {
367 self.bvh = Bvh::new();
368 self.bvh_workspace = BvhWorkspace::default();
369
370 for face in self.faces.values() {
371 self.bvh
372 .insert_or_update_partially(face.aabb(self), face.index, 0.0);
373 }
374 self.bvh
375 .rebuild(&mut self.bvh_workspace, Default::default());
376 }
377
378 #[instrument(skip_all)]
379 pub fn rebuild_outgoing_halfedges(&mut self) {
380 self.outgoing_halfedges.clear();
381
382 for halfedge in self.halfedges.values() {
383 let Some(twin_id) = halfedge.twin else {
384 error!("Halfedge has no twin");
385 continue;
386 };
387
388 let Some(entry) = self.outgoing_halfedges.entry(halfedge.end_vertex) else {
389 error!("Vertex key invalid");
390 continue;
391 };
392
393 entry.or_default().push(twin_id);
394 }
395 }
396}