feat(render): draw ACIS solids as B-rep feature edges, meshes as tri edges

Solid wireframes rendered every triangle edge of the tessellation, so a
curved solid read as a dense mesh instead of its real outline. Now each ACIS
solid's B-rep face-boundary edges are collected from the SAT `edge` records
(straight edges as segments, ellipse/circle edges sampled along their bounded
arc), body-transformed and split into the double-single pair, and stored on
the MeshLodSet. The block-instance and top-level offset paths transform these
edges alongside the mesh vertices.

The batch builder routes a solid with feature edges into a dedicated LineList
vertex buffer and skips its triangle wireframe; a mesh with no B-rep edges
keeps its triangulation edges. Edge colour follows the display mode: the flat
`fs_edge` shader draws the entity colour in the lines-only modes (wireframe,
hidden-line) and a `fs_edge_black` twin forces black for the edge overlay in
filled render modes.

Verified: the Clinic storefront arch bars now yield ~14-16 clean edge
segments (vs the full triangulation) bounded to the solid's true extent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-04 17:57:00 +03:00
1 parent a08cf477c1
commit 1065bc12b2
6 files changed
+285 -63

No files matched your search

+32
View File
@@ -567,6 +567,21 @@ fn offset_mesh_lod_set(mut set: MeshLodSet) -> MeshLodSet {
if hy > max_y { max_y = hy; }
}
}
// Re-split the feature edges the same way so they track the mesh at scale.
{
let n = set.edge_verts.len();
if set.edge_verts_low.len() != n {
set.edge_verts_low = vec![[0.0; 3]; n];
}
for (v, vl) in set.edge_verts.iter_mut().zip(set.edge_verts_low.iter_mut()) {
let ax = v[0] as f64 + vl[0] as f64;
let ay = v[1] as f64 + vl[1] as f64;
let az = v[2] as f64 + vl[2] as f64;
let (hx, hy, hz) = (ax as f32, ay as f32, az as f32);
*v = [hx, hy, hz];
*vl = [(ax - hx as f64) as f32, (ay - hy as f64) as f32, (az - hz as f64) as f32];
}
}
if min_x.is_finite() {
set.world_aabb = [min_x, min_y, max_x, max_y];
}
@@ -624,6 +639,23 @@ fn transform_block_mesh_lod_set(
}
}
}
// Apply the same INSERT transform to the feature edges.
{
let n = out.edge_verts.len();
if out.edge_verts_low.len() != n {
out.edge_verts_low = vec![[0.0; 3]; n];
}
for (v, vl) in out.edge_verts.iter_mut().zip(out.edge_verts_low.iter_mut()) {
let w = xform.apply(Vector3::new(
v[0] as f64 + vl[0] as f64,
v[1] as f64 + vl[1] as f64,
v[2] as f64 + vl[2] as f64,
));
let (hx, hy, hz) = (w.x as f32, w.y as f32, w.z as f32);
*v = [hx, hy, hz];
*vl = [(w.x - hx as f64) as f32, (w.y - hy as f64) as f32, (w.z - hz as f64) as f32];
}
}
if min_x.is_finite() {
out.world_aabb = [min_x, min_y, max_x, max_y];
}