fix(render): scale solid tessellation

Derive one drawing-space chordal tolerance so large curves stay smooth while small solids avoid excessive detail.
This commit is contained in:
Hakan Seven committed 2026-08-12 12:46:17 +03:00
1 parent 7b4af0aa75
commit ca331fff48
7 files changed
+138 -32

No files matched your search

+9 -13
View File
@@ -23,6 +23,7 @@ pub fn tessellate_sat(
name: String,
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
) -> Option<MeshLodSet> {
let (bodies, loss) = lift(document);
@@ -53,7 +54,10 @@ pub fn tessellate_sat(
} else {
1.0
};
let max_angle = cadkernel::tessellation::angle_for_resolution(resolution);
let max_angle = chordal_deflection.map_or_else(
|| cadkernel::tessellation::angle_for_resolution(resolution),
|_| cadkernel::tessellation::display_angle_for_resolution(resolution),
);
// Positions stay f64 until `finalize_mesh` splits them into the coarse
// and fine pair, so a solid at survey coordinates keeps its millimetres.
@@ -106,11 +110,14 @@ pub fn tessellate_sat(
DEFAULT_FIT_TOLERANCE
};
for (body, placement_scale) in &bodies {
let tolerance = brep::mesh::TessellationTolerance::new(
let mut tolerance = brep::mesh::TessellationTolerance::new(
max_angle,
source_fit * placement_scale,
)
.with_isolines(isolines);
if let Some(deflection) = chordal_deflection {
tolerance = tolerance.with_chordal_deflection(deflection);
}
let tessellation = brep::mesh::tessellate(body, tolerance);
undrawn += tessellation.missing_faces.len();
for face in &tessellation.triangle_faces {
@@ -244,15 +251,4 @@ mod tests {
assert_eq!(moved, [10.0, 20.0, 30.0]);
}
fn sides(max_angle: f64) -> f64 {
std::f64::consts::TAU / max_angle
}
#[test]
fn a_round_surface_gets_the_same_sides_whatever_its_size() {
let angle = cadkernel::tessellation::DEFAULT_ANGLE;
assert!(sides(angle) > 24.0, "{}", sides(angle));
assert!(sides(angle) < 96.0, "{}", sides(angle));
}
}
+26 -2
View File
@@ -8,9 +8,17 @@ fn tessellate_acis(
name: String,
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
) -> Option<MeshLodSet> {
crate::scene::convert::acis_kernel::tessellate_sat(sat, name, color, facet_res, isolines)
crate::scene::convert::acis_kernel::tessellate_sat(
sat,
name,
color,
facet_res,
chordal_deflection,
isolines,
)
}
pub(crate) fn body_transform(
@@ -167,10 +175,18 @@ fn finish(
name: String,
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
acis: &acadrust::entities::AcisData,
) -> Option<MeshLodSet> {
let mut set = tessellate_acis(&sat, name, color, facet_res, isolines)?;
let mut set = tessellate_acis(
&sat,
name,
color,
facet_res,
chordal_deflection,
isolines,
)?;
remap_acis_material_bindings(&mut set, acis);
Some(set)
}
@@ -179,6 +195,7 @@ pub fn tessellate_region(
region: &Region,
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
) -> Option<MeshLodSet> {
let sat = parse_acis(
@@ -191,6 +208,7 @@ pub fn tessellate_region(
region.common.handle.value().to_string(),
color,
facet_res,
chordal_deflection,
isolines,
&region.acis_data,
)
@@ -200,6 +218,7 @@ pub fn tessellate_body(
body: &Body,
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
) -> Option<MeshLodSet> {
let sat = parse_acis(
@@ -212,6 +231,7 @@ pub fn tessellate_body(
body.common.handle.value().to_string(),
color,
facet_res,
chordal_deflection,
isolines,
&body.acis_data,
)
@@ -221,6 +241,7 @@ pub fn tessellate_surface(
surface: &acadrust::entities::Surface,
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
) -> Option<MeshLodSet> {
let sat = parse_acis(
@@ -233,6 +254,7 @@ pub fn tessellate_surface(
surface.common.handle.value().to_string(),
color,
facet_res,
chordal_deflection,
isolines,
&surface.acis_data,
)
@@ -242,6 +264,7 @@ pub fn tessellate_solid3d(
solid: &Solid3D,
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
) -> Option<MeshLodSet> {
let sat = parse_acis(
@@ -254,6 +277,7 @@ pub fn tessellate_solid3d(
solid.common.handle.value().to_string(),
color,
facet_res,
chordal_deflection,
isolines,
&solid.acis_data,
)
+33 -5
View File
@@ -136,6 +136,8 @@ impl Scene {
};
let image_seed = self.image_seed_for(&entity);
let facet_res = self.document.header.facet_resolution;
let chordal_deflection =
crate::entities::solid3d::display_deflection(&self.document.header, facet_res);
let isolines = self.document.header.isolines.max(0) as usize;
let mesh_seed = if matches!(
&entity,
@@ -148,8 +150,14 @@ impl Scene {
| EntityType::PolyfaceMesh(_)
) {
let color = self.render_style(&entity).0;
crate::entities::solid3d::tessellate_volume(&entity, color, facet_res, isolines)
.map(|m| offset_mesh_lod_set(m))
crate::entities::solid3d::tessellate_volume(
&entity,
color,
facet_res,
chordal_deflection,
isolines,
)
.map(offset_mesh_lod_set)
} else {
None
};
@@ -357,6 +365,8 @@ impl Scene {
};
let image_seed = self.image_seed_for(&entity);
let facet_res = self.document.header.facet_resolution;
let chordal_deflection =
crate::entities::solid3d::display_deflection(&self.document.header, facet_res);
let isolines = self.document.header.isolines.max(0) as usize;
let mesh_seed = if matches!(
&entity,
@@ -369,8 +379,14 @@ impl Scene {
| EntityType::PolyfaceMesh(_)
) {
let color = self.render_style(&entity).0;
crate::entities::solid3d::tessellate_volume(&entity, color, facet_res, isolines)
.map(|m| offset_mesh_lod_set(m))
crate::entities::solid3d::tessellate_volume(
&entity,
color,
facet_res,
chordal_deflection,
isolines,
)
.map(offset_mesh_lod_set)
} else {
None
};
@@ -538,6 +554,8 @@ impl Scene {
})
.collect();
let facet_res = self.document.header.facet_resolution;
let chordal_deflection =
crate::entities::solid3d::display_deflection(&self.document.header, facet_res);
let isolines = self.document.header.isolines.max(0) as usize;
use crate::par::prelude::*;
let built: Vec<(Handle, MeshLodSet, bool)> = entries
@@ -547,6 +565,7 @@ impl Scene {
entity.as_ref(),
color,
facet_res,
chordal_deflection,
isolines,
)
.map(|mut mesh| {
@@ -1892,6 +1911,8 @@ impl Scene {
use crate::par::prelude::*;
let facet_res = self.document.header.facet_resolution;
let chordal_deflection =
crate::entities::solid3d::display_deflection(&self.document.header, facet_res);
let isolines = self.document.header.isolines.max(0) as usize;
// Top-level solids: offset into the render frame, drawn flat.
// Block-definition solids: keep block-local coords for per-INSERT
@@ -1899,7 +1920,14 @@ impl Scene {
let built: Vec<(Handle, MeshLodSet, bool)> = entries
.into_par_iter()
.filter_map(|(handle, entity, color, top_level)| {
crate::entities::solid3d::tessellate_volume(&entity, color, facet_res, isolines).map(|mut mesh| {
crate::entities::solid3d::tessellate_volume(
&entity,
color,
facet_res,
chordal_deflection,
isolines,
)
.map(|mut mesh| {
let material = crate::scene::model::material_model::resolve_material_with_base(
&self.document,
&entity,
+12 -6
View File
@@ -763,12 +763,12 @@ fn build_derived_caches_impl(
})
.collect();
// meshes (parallel tessellation). FACETRES (header.facet_resolution)
// scales the per-LOD segment counts so users with finer drawings get
// smoother solids; clamped to AutoCAD's [0.01, 10.0] range inside.
// FACETRES biases one shared chordal tolerance for all solids.
// Top-level (layout-owned) solids are offset into the render frame; block
// definition solids keep block-local coords for per-INSERT instancing. (#123)
let facet_res = doc.header.facet_resolution;
let chordal_deflection =
crate::entities::solid3d::display_deflection(&doc.header, facet_res);
let isolines = doc.header.isolines.max(0) as usize;
// Real layout blocks come from the Layout objects' block_record handles —
// `BlockRecord::is_layout()` is unreliable here (it flags ordinary blocks).
@@ -796,8 +796,14 @@ fn build_derived_caches_impl(
material_base_dir,
);
let top_level = layout_blocks.contains(&e.common().owner_handle);
let result = crate::entities::solid3d::tessellate_volume(e, color, facet_res, isolines)
.map(|mut mesh| {
let result = crate::entities::solid3d::tessellate_volume(
e,
color,
facet_res,
chordal_deflection,
isolines,
)
.map(|mut mesh| {
material.apply_to_with_face_overrides(
&mut mesh,
doc,
@@ -810,7 +816,7 @@ fn build_derived_caches_impl(
);
let mesh = if top_level { offset_mesh_lod_set(mesh) } else { mesh };
(handle, mesh, top_level)
});
});
let done = detail_done.fetch_add(1, Ordering::Relaxed) + 1;
if done & 0xff == 0 || done == detail_total {
report_detail(done);