Display tessellation plans (3/4): display meshes from plans, GPU evaluation in the viewport (#805)

Display meshes are built from tessellation plans, with GPU evaluation in the viewport.
This commit is contained in:
Irakli Kourtis authored and GitHub committed 2026-10-13 16:27:16 -04:00
1 parent 534393ed5a
commit ff7a347013
19 files changed
+1474 -77

No files matched your search

+32 -12
View File
@@ -970,20 +970,32 @@ pub(crate) fn section(plane: &Plane, m: &Mesh, face: Option<u32>) -> Vec<LinkGeo
let size = m.bounds().diagonal().max(1e-9);
let q = size * 1e-7;
let key = |p: Vec3| ((p.x / q).round() as i64, (p.y / q).round() as i64, (p.z / q).round() as i64);
// Surface normal at each mesh vertex where the surface is smooth (None at creases).
let mut normals: BTreeMap<(i64, i64, i64), Option<Vec3>> = BTreeMap::new();
for (t, _) in m.triangles.iter().zip(&m.tri_face) {
// Surface normal at each mesh vertex where the surface is smooth (None at creases), over
// all faces and per face.
type Key = (i64, i64, i64);
let mut normals: BTreeMap<Key, Option<Vec3>> = BTreeMap::new();
let mut face_normals: BTreeMap<(u32, Key), Option<Vec3>> = BTreeMap::new();
// Faces on either side of each mesh edge.
let mut edge_faces: BTreeMap<(Key, Key), Vec<u32>> = BTreeMap::new();
let merge = |e: &mut Option<Vec3>, n: Vec3| {
if e.is_some_and(|o| o.dot(n) < 1.0 - 1e-6) {
*e = None;
}
};
for (t, f) in m.triangles.iter().zip(&m.tri_face) {
for k in t {
let (Some(p), Some(n)) = (m.positions.get(*k as usize), m.normals.get(*k as usize)) else { continue };
let Some(n) = n.normalized() else { continue };
normals
.entry(key(*p))
.and_modify(|e| {
if e.is_some_and(|o| o.dot(n) < 1.0 - 1e-6) {
*e = None;
}
})
.or_insert(Some(n));
normals.entry(key(*p)).and_modify(|e| merge(e, n)).or_insert(Some(n));
face_normals.entry((*f, key(*p))).and_modify(|e| merge(e, n)).or_insert(Some(n));
}
for (i, j) in [(0usize, 1usize), (1, 2), (2, 0)] {
let (Some(a), Some(b)) = (t.get(i).and_then(|k| m.positions.get(*k as usize)), t.get(j).and_then(|k| m.positions.get(*k as usize)))
else {
continue;
};
let (ka, kb) = (key(*a), key(*b));
edge_faces.entry(if ka <= kb { (ka, kb) } else { (kb, ka) }).or_default().push(*f);
}
}
// Where the plane crosses a mesh edge: on a cubic through the end points whose end
@@ -997,8 +1009,16 @@ pub(crate) fn section(plane: &Plane, m: &Mesh, face: Option<u32>) -> Vec<LinkGeo
return None;
}
let d = b - a;
// An edge inside one face takes that face's normals (its ends may lie on the face's
// boundary, a crease for the whole mesh); an edge between faces, the merged ones, so
// both faces agree on the crossing.
let inside = edge_faces.get(&(key(a), key(b))).and_then(|fs| fs.first().filter(|f| fs.len() == 2 && fs.iter().all(|x| x == *f)).copied());
let normal = |p: Vec3| match inside {
Some(f) => face_normals.get(&(f, key(p))).copied().flatten(),
None => normals.get(&key(p)).copied().flatten(),
};
// Tangent length: the arc length of a circle through both ends with these normals.
let (na, nb) = (normals.get(&key(a)).copied().flatten(), normals.get(&key(b)).copied().flatten());
let (na, nb) = (normal(a), normal(b));
let l = match (na, nb) {
(Some(x), Some(y)) => {
let half = 0.5 * x.dot(y).clamp(-1.0, 1.0).acos();