feat(solid3d): view-dependent silhouettes (DISPSILH) on curved faces
ISOLINES show a curved solid from any angle, but its outline still cut across whichever fixed longitudinal lines happened to face the eye. DISPSILH draws the true silhouette — the two lines where a cylinder (or cone) turns edge-on to the view — and it moves with the camera, so it cannot be a baked edge. Tessellation stores a world-space generator per cone/cylinder face on the mesh set (axis, radial frame, radius, cone taper, extent), sharing the face-extent extraction with the ISOLINES collector. Each frame `upload_silhouettes` walks those generators, solves `du·cosθ + dv·sinθ = -tanα·da` for the two edge-on angles (`φ ± acos(...)`, `φ ± π/2` for a cylinder), and builds a line list in the mesh vertex format — double-single, so it stays precise at UTM. It draws through the existing wireframe / edge pipelines in the wireframe and shaded-with-edges passes. Gated on the document's DISPSILH header flag, which defaults off — so the per-frame pass is a no-op unless the drawing asks for it. Scoped to top-level solids: a block instance's generators aren't transformed by the INSTANCE pose yet, so they are cleared on the block path (its isolines and feature edges still draw). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0ce150411a
commit
ae5231427b
5 changed files with 329 additions and 51 deletions
|
|
@ -36,6 +36,36 @@ pub struct MeshModel {
|
|||
///
|
||||
/// `lods` holds up to one MeshModel per LOD level (high → low). Empty
|
||||
/// slots fall back to the nearest available LOD at render time.
|
||||
/// A curved face's generator, kept so a view-dependent silhouette (DISPSILH)
|
||||
/// can be computed per frame — the silhouette is where the surface turns away
|
||||
/// from the eye, which no baked edge can capture. World-space, post body
|
||||
/// transform; the base point carries a double-single low half so it stays
|
||||
/// precise at UTM scale like the mesh verts.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CurvedGen {
|
||||
/// Base-circle centre (world), high half of the double-single.
|
||||
pub base: [f32; 3],
|
||||
/// Low residual paired with `base`.
|
||||
pub base_low: [f32; 3],
|
||||
/// Unit axis direction (world).
|
||||
pub axis: [f32; 3],
|
||||
/// Radial frame: `u` is the θ=0 direction, `v = axis × u`.
|
||||
pub u_dir: [f32; 3],
|
||||
pub v_dir: [f32; 3],
|
||||
/// Radius at the base (`h = 0`).
|
||||
pub radius: f32,
|
||||
/// `tan(half-angle)`: radius at height `h` is `radius + h * tan_a`
|
||||
/// (0 for a cylinder, non-zero for a cone).
|
||||
pub tan_a: f32,
|
||||
/// Height span along the axis the face covers.
|
||||
pub h_min: f32,
|
||||
pub h_max: f32,
|
||||
/// Angular span the face covers; `full` = a closed revolution.
|
||||
pub theta_min: f32,
|
||||
pub theta_span: f32,
|
||||
pub full: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MeshLodSet {
|
||||
pub lods: Vec<MeshModel>,
|
||||
|
|
@ -46,6 +76,9 @@ pub struct MeshLodSet {
|
|||
pub edge_verts: Vec<[f32; 3]>,
|
||||
/// Low residual paired with `edge_verts`.
|
||||
pub edge_verts_low: Vec<[f32; 3]>,
|
||||
/// Curved-face generators for per-frame silhouette (DISPSILH). Empty for a
|
||||
/// solid with no curved faces, or when silhouettes aren't wanted.
|
||||
pub curved_gens: Vec<CurvedGen>,
|
||||
/// World XY AABB `[min_x, min_y, max_x, max_y]` of the mesh — used
|
||||
/// by the per-frame LOD selector to compute the projected pixel
|
||||
/// diagonal.
|
||||
|
|
@ -87,6 +120,7 @@ impl MeshLodSet {
|
|||
lods,
|
||||
edge_verts: Vec::new(),
|
||||
edge_verts_low: Vec::new(),
|
||||
curved_gens: Vec::new(),
|
||||
world_aabb,
|
||||
z_aabb,
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue