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

+29 -2
View File
@@ -5,6 +5,7 @@ use std::hash::{Hash, Hasher};
use std::sync::{Arc, OnceLock};
use serde::Serialize;
use solidcraft_geom::tess::TessPlan;
use solidcraft_geom::{Mesh, Plane, Region2, Vec2, Vec3};
use solidcraft_kernel::{self as kernel, Body, BoolOp};
use solidcraft_sketch::{CurveKind, Profile, SolveReport, find_profiles};
@@ -27,6 +28,9 @@ pub struct ModelBody {
// Timeline snapshots are cloned before meshes are requested. Share the cell itself so
// meshing any snapshot also warms every unchanged copy of this body.
mesh: Arc<OnceLock<Arc<Mesh>>>,
/// The display plan the mesh is evaluated from (none when the body has no plan: a mesh
/// body, or one the planner refuses), shared like the mesh. A GPU viewport draws it.
plan: Arc<OnceLock<Option<Arc<TessPlan>>>>,
/// Identity of this body's content for the evaluation cache: the key of the evaluation
/// that produced it (kept while later features pass it through unchanged).
key: u64,
@@ -51,6 +55,7 @@ impl ModelBody {
body,
feature,
mesh: Arc::new(OnceLock::new()),
plan: Arc::new(OnceLock::new()),
key: 0,
names: Default::default(),
made: Default::default(),
@@ -68,12 +73,34 @@ impl ModelBody {
pub fn mesh(&self) -> Arc<Mesh> {
self.mesh
.get_or_init(|| {
let tol = (self.body.size() * 1e-3).max(1e-3);
Arc::new(self.body.display_mesh_angular(tol, kernel::DISPLAY_ANGLE).unwrap_or_default())
let mesh = match self.display_plan() {
Some(plan) => self.body.display_mesh_of(&plan),
None => self.body.reference_display_mesh_angular(self.display_tol(), kernel::DISPLAY_ANGLE),
};
Arc::new(mesh.unwrap_or_default())
})
.clone()
}
/// The display plan behind [`ModelBody::mesh`] (same tolerance and angle), made once; `None`
/// when the body has no plan and its mesh comes from the reference mesher.
pub fn display_plan(&self) -> Option<Arc<TessPlan>> {
self.plan
.get_or_init(|| match self.body.display_plan_angular(self.display_tol(), kernel::DISPLAY_ANGLE) {
Ok(plan) => Some(Arc::new(plan)),
Err(e) => {
log::debug!("{}: no display plan ({e})", self.name);
None
}
})
.clone()
}
/// The display mesh's chord tolerance: 1/1000 of the body's size.
fn display_tol(&self) -> f64 {
(self.body.size() * 1e-3).max(1e-3)
}
/// Already computed display geometry; saving must not trigger cold tessellation.
pub fn cached_mesh(&self) -> Option<Arc<Mesh>> {
self.mesh.get().cloned()