mod access;
mod elements;
pub mod integrations;
mod iter;
mod ops;
mod plane_slice;
pub mod primitives;
#[cfg(feature = "rerun")]
mod rerun_impl;
mod selection;
#[cfg(feature = "serde")]
mod serialize;
pub mod utils;
pub use elements::*;
pub use iter::*;
pub use ops::*;
pub use plane_slice::*;
pub use selection::*;
use hashbrown::HashMap;
use parry3d::partitioning::{Bvh, BvhWorkspace};
use glam::Vec3;
use slotmap::{SecondaryMap, SlotMap};
use tracing::{error, instrument};
use crate::utils::unwrap_or_return;
#[cfg(feature = "rerun")]
lazy_static::lazy_static! {
pub static ref RR: rerun::RecordingStream = rerun::RecordingStreamBuilder::new("mesh_graph").spawn().unwrap();
}
#[derive(Clone, Default)]
#[cfg_attr(feature = "bevy", derive(bevy::prelude::Component))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(from = "crate::serialize::MeshGraphIntermediate")
)]
pub struct MeshGraph {
#[cfg_attr(feature = "serde", serde(skip))]
pub bvh: Bvh,
#[cfg_attr(feature = "serde", serde(skip))]
pub bvh_workspace: BvhWorkspace,
#[cfg_attr(feature = "serde", serde(skip))]
pub index_to_face_id: HashMap<u32, FaceId>,
#[cfg_attr(feature = "serde", serde(skip))]
pub next_index: u32,
pub vertices: SlotMap<VertexId, Vertex>,
pub halfedges: SlotMap<HalfedgeId, Halfedge>,
pub faces: SlotMap<FaceId, Face>,
pub positions: SecondaryMap<VertexId, Vec3>,
pub vertex_normals: Option<SecondaryMap<VertexId, Vec3>>,
#[cfg_attr(feature = "serde", serde(skip))]
pub outgoing_halfedges: SecondaryMap<VertexId, Vec<HalfedgeId>>,
}
impl MeshGraph {
#[inline]
pub fn new() -> Self {
Self::default()
}
pub fn triangles(vertex_positions: &[Vec3]) -> Self {
assert!(
vertex_positions.len().is_multiple_of(3),
"Number of vertex positions should be a multiple of 3"
);
let mut unique_positions: Vec<Vec3> = Vec::with_capacity(vertex_positions.len() / 3);
let mut face_indices = Vec::with_capacity(vertex_positions.len());
for vertex_pos in vertex_positions {
let mut idx = None;
for (j, pos) in unique_positions.iter().enumerate() {
const EPSILON: f32 = 1e-5;
if pos.distance_squared(*vertex_pos) < EPSILON {
idx = Some(j);
break;
}
}
let vertex_idx = if let Some(idx) = idx {
idx
} else {
let new_idx = unique_positions.len();
unique_positions.push(*vertex_pos);
#[cfg(feature = "rerun")]
RR.log(
"meshgraph/construct/vertices",
&rerun::Points3D::new(unique_positions.iter().map(crate::utils::vec3_array)),
)
.unwrap();
new_idx
};
face_indices.push(vertex_idx);
}
Self::indexed_triangles(&unique_positions, &face_indices)
}
#[instrument]
pub fn indexed_triangles(vertex_positions: &[Vec3], face_indices: &[usize]) -> Self {
let mut mesh_graph = Self {
bvh: Bvh::new(),
bvh_workspace: BvhWorkspace::default(),
index_to_face_id: HashMap::with_capacity(face_indices.len() / 3),
next_index: 0,
vertices: SlotMap::with_capacity_and_key(vertex_positions.len()),
halfedges: SlotMap::with_capacity_and_key(face_indices.len()),
faces: SlotMap::with_capacity_and_key(face_indices.len() / 3),
positions: SecondaryMap::with_capacity(vertex_positions.len()),
vertex_normals: None,
outgoing_halfedges: SecondaryMap::with_capacity(vertex_positions.len()),
};
let mut vertex_ids = Vec::with_capacity(vertex_positions.len());
for pos in vertex_positions {
vertex_ids.push(mesh_graph.add_vertex(*pos));
}
for chunk in face_indices.chunks_exact(3) {
let a = vertex_ids[chunk[0]];
let b = vertex_ids[chunk[1]];
let c = vertex_ids[chunk[2]];
if a == b || b == c || c == a {
#[cfg(feature = "rerun")]
RR.log(
"meshgraph/construct/zero_face",
&rerun::Points3D::new(
[
mesh_graph.positions[a],
mesh_graph.positions[b],
mesh_graph.positions[c],
]
.iter()
.map(crate::utils::vec3_array),
),
)
.unwrap();
continue;
}
let he_a_id = mesh_graph.add_or_get_edge(a, b).start_to_end_he_id;
let he_b_id = mesh_graph.add_or_get_edge(b, c).start_to_end_he_id;
let he_c_id = mesh_graph.add_or_get_edge(c, a).start_to_end_he_id;
let _face_id = mesh_graph.add_face(he_a_id, he_b_id, he_c_id);
}
mesh_graph.make_all_outgoing_halfedges_boundary_if_possible();
mesh_graph.rebuild_bvh();
mesh_graph
}
pub fn compute_vertex_normal(&mut self, vertex_id: VertexId) {
if self.vertex_normals.is_none() {
return;
}
let vertex = unwrap_or_return!(self.vertices.get(vertex_id), "Vertex not found");
let mut normal = Vec3::ZERO;
for face_id in vertex.faces(self) {
let face = unwrap_or_return!(self.faces.get(face_id), "Face not found");
normal += unwrap_or_return!(face.normal(self), "Face normal not found");
}
self.vertex_normals
.as_mut()
.unwrap()
.insert(vertex_id, normal.normalize());
}
#[instrument(skip(self))]
pub fn compute_vertex_normals(&mut self) {
let mut normals = SecondaryMap::with_capacity(self.vertices.len());
for face in self.faces.values() {
let ha_a_id = face.halfedge;
let he_a = self.halfedges[ha_a_id];
let he_b_id = he_a
.next
.expect("Halfedge has definitely a face and thus a next halfedge");
let he_b = self.halfedges[he_b_id];
let a = match he_a.start_vertex(self) {
Some(v) => v,
None => {
error!("Start vertex not found");
continue;
}
};
let b = he_a.end_vertex;
let c = he_b.end_vertex;
let diff_a = self.positions[c] - self.positions[a];
let diff_b = self.positions[c] - self.positions[b];
let face_normal = diff_a.cross(diff_b);
*normals.entry(a).unwrap().or_default() += face_normal;
*normals.entry(b).unwrap().or_default() += face_normal;
*normals.entry(c).unwrap().or_default() += face_normal;
}
self.vertex_normals = Some(normals);
self.normalize_vertex_normals();
}
pub fn normalize_vertex_normals(&mut self) {
if let Some(normals) = &mut self.vertex_normals {
for normal in normals.values_mut() {
*normal = normal.normalize_or_zero();
}
}
}
#[inline]
pub fn optimize_bvh_incremental(&mut self) {
self.bvh.optimize_incremental(&mut self.bvh_workspace);
}
#[inline]
pub fn refit_bvh(&mut self) {
self.bvh.refit(&mut self.bvh_workspace);
}
#[inline]
pub fn rebuild_bvh(&mut self) {
self.bvh = Bvh::new();
self.bvh_workspace = BvhWorkspace::default();
for face in self.faces.values() {
self.bvh
.insert_or_update_partially(face.aabb(self), face.index, 0.0);
}
self.bvh
.rebuild(&mut self.bvh_workspace, Default::default());
}
#[instrument(skip_all)]
pub fn rebuild_outgoing_halfedges(&mut self) {
self.outgoing_halfedges.clear();
for halfedge in self.halfedges.values() {
let Some(twin_id) = halfedge.twin else {
error!("Halfedge has no twin");
continue;
};
let Some(entry) = self.outgoing_halfedges.entry(halfedge.end_vertex) else {
error!("Vertex key invalid");
continue;
};
entry.or_default().push(twin_id);
}
}
}