From 50f6196594a8ebd1cacc61b6581c98eadbcfe9d4 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sat, 18 Jul 2026 15:15:24 +0300 Subject: [PATCH] fix(render): flag 2D overlay fills so they show in every view mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A SOLID's filled quad and a dimension/MultiLeader text background are flat 2-D overlays that should draw in wireframe and shaded alike, but face3d_gpu classified fills by `!fill_tris_low.is_empty()` — and a 2-D fill at UTM scale carries a double-single low residual for precision just like a real 3-D mesh face. So the arrowhead fill and the "5;8" cyan text background were routed to the 3-D buffer and only appeared in shaded mode, vanishing back to nothing on return to wireframe. WireModel now carries an explicit `fill_is_3d`: true only for a real surface (PolyfaceMesh / PolygonMesh face, set in the tessellator), false for every flat overlay fill. face3d_gpu classifies on the flag, so 2-D fills stay in the always-visible buffer while keeping their low residual (and UTM precision). Verified on budweiser: the `_BoxFilled` arrow SOLID and the dim '5;8' MTEXT background both tessellate with fill_is_3d = false. Co-Authored-By: Claude Opus 4.8 --- src/app/update/viewport.rs | 2 ++ src/entities/dimension.rs | 5 +++++ src/entities/leader.rs | 2 ++ src/entities/multileader.rs | 4 ++++ src/entities/table.rs | 1 + src/modules/annotate/aligned_dim.rs | 2 ++ src/modules/annotate/angular_dim.rs | 1 + src/modules/annotate/diameter_dim.rs | 1 + src/modules/annotate/dim_baseline.rs | 1 + src/modules/annotate/dim_continue.rs | 1 + src/modules/annotate/dimjogline.rs | 1 + src/modules/annotate/dimtedit.rs | 1 + src/modules/annotate/leader_cmd.rs | 1 + src/modules/annotate/linear_dim.rs | 1 + src/modules/annotate/mleader_cmd.rs | 1 + src/modules/annotate/mleader_edit.rs | 1 + src/modules/annotate/ordinate_dim.rs | 1 + src/modules/annotate/qdim.rs | 1 + src/modules/annotate/radius_dim.rs | 1 + src/modules/annotate/table_cmd.rs | 1 + src/modules/annotate/tolerance_cmd.rs | 1 + src/modules/draw/draw/attdef.rs | 1 + src/modules/draw/draw/mline.rs | 1 + src/modules/draw/draw/raster_image.rs | 1 + src/modules/draw/draw/ray.rs | 2 ++ src/modules/draw/draw/revcloud.rs | 1 + src/modules/draw/draw/wipeout.rs | 2 ++ src/modules/draw/inquiry/area.rs | 1 + src/modules/draw/inquiry/dist.rs | 1 + src/modules/draw/inquiry/measuregeom.rs | 1 + src/modules/draw/modify/trim.rs | 1 + src/modules/layout/mview.rs | 1 + src/modules/model/primitive_cmd.rs | 1 + src/modules/view/plot_window.rs | 1 + src/modules/view/zoom_window.rs | 1 + src/scene/cache/block_cache.rs | 1 + src/scene/convert/tess.rs | 3 +++ src/scene/convert/tessellate.rs | 25 +++++++++++++++++++++++++ src/scene/model/wire_model.rs | 9 +++++++++ src/scene/pipeline/face3d_gpu.rs | 13 +++++++------ src/scene/text/complex_lt.rs | 2 ++ 41 files changed, 94 insertions(+), 6 deletions(-) diff --git a/src/app/update/viewport.rs b/src/app/update/viewport.rs index abc8737af..269f647b4 100644 --- a/src/app/update/viewport.rs +++ b/src/app/update/viewport.rs @@ -1235,6 +1235,7 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task { let far_pos = base + dir * far; let far_neg = base - dir * far; let guide = crate::scene::WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1273,6 +1274,7 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task { let far_pos = h.base + h.dir * far; let far_neg = h.base - h.dir * far; previews.push(crate::scene::WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/entities/dimension.rs b/src/entities/dimension.rs index 52c8ce841..75059ec36 100644 --- a/src/entities/dimension.rs +++ b/src/entities/dimension.rs @@ -1352,6 +1352,7 @@ fn tessellate_dimension_inner( let (ext1, ext2) = split_ext_lines(&geom.ext_lines); if !ext1.is_empty() { wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1378,6 +1379,7 @@ fn tessellate_dimension_inner( } if !ext2.is_empty() { wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1404,6 +1406,7 @@ fn tessellate_dimension_inner( } } else { wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1431,6 +1434,7 @@ fn tessellate_dimension_inner( } wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1474,6 +1478,7 @@ fn tessellate_dimension_inner( aci_to_rgba(&c) }; wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/entities/leader.rs b/src/entities/leader.rs index 8dc007c3c..936fbad56 100644 --- a/src/entities/leader.rs +++ b/src/entities/leader.rs @@ -590,6 +590,7 @@ impl LeaderTess for Leader { if verts.len() < 2 { return WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -696,6 +697,7 @@ impl LeaderTess for Leader { } WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/entities/multileader.rs b/src/entities/multileader.rs index 1cf733457..c72401eb1 100644 --- a/src/entities/multileader.rs +++ b/src/entities/multileader.rs @@ -1404,6 +1404,7 @@ impl MultiLeaderTess for MultiLeader { // WireModels so the renderer respects per-piece coloring. let mut wires: Vec = Vec::new(); wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1693,6 +1694,7 @@ impl MultiLeaderTess for MultiLeader { xy = xy.max(y); } wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1760,6 +1762,7 @@ impl MultiLeaderTess for MultiLeader { wcs_corners[3], ]; wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1795,6 +1798,7 @@ impl MultiLeaderTess for MultiLeader { wcs_corners[0], ]; wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/entities/table.rs b/src/entities/table.rs index 3e16e902f..7fbb1c62d 100644 --- a/src/entities/table.rs +++ b/src/entities/table.rs @@ -753,6 +753,7 @@ pub fn tessellate_table( let mk = |color: [f32; 4], points: Vec<[f32; 3]>, fill_tris: Vec<[f32; 3]>, lw: f32| -> WireModel { WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/aligned_dim.rs b/src/modules/annotate/aligned_dim.rs index b88d006d3..27901976e 100644 --- a/src/modules/annotate/aligned_dim.rs +++ b/src/modules/annotate/aligned_dim.rs @@ -186,6 +186,7 @@ impl CadCommand for AlignedDimensionCommand { let p1 = p1.as_vec3(); let p2 = p2.as_vec3(); Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -237,6 +238,7 @@ fn preview_aligned(p1: DVec3, p2: DVec3, dim_pt: DVec3) -> WireModel { let d1 = d1.as_vec3(); let d2 = d2.as_vec3(); WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/angular_dim.rs b/src/modules/annotate/angular_dim.rs index 89ac14038..f1e5e3a27 100644 --- a/src/modules/annotate/angular_dim.rs +++ b/src/modules/annotate/angular_dim.rs @@ -130,6 +130,7 @@ fn v3(pt: DVec3) -> Vector3 { fn preview_wire(points: Vec) -> WireModel { WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/diameter_dim.rs b/src/modules/annotate/diameter_dim.rs index 74ede2120..d586c5bf2 100644 --- a/src/modules/annotate/diameter_dim.rs +++ b/src/modules/annotate/diameter_dim.rs @@ -180,6 +180,7 @@ fn v3(p: DVec3) -> Vector3 { fn preview_line(a: Vec3, b: Vec3) -> WireModel { WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/dim_baseline.rs b/src/modules/annotate/dim_baseline.rs index ab0154941..1953b9a69 100644 --- a/src/modules/annotate/dim_baseline.rs +++ b/src/modules/annotate/dim_baseline.rs @@ -179,6 +179,7 @@ impl CadCommand for DimBaselineCommand { let dim_line_pt2 = dim_line_pt2.as_vec3(); let pt = pt.as_vec3(); Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/dim_continue.rs b/src/modules/annotate/dim_continue.rs index e98539bce..4bada2dd1 100644 --- a/src/modules/annotate/dim_continue.rs +++ b/src/modules/annotate/dim_continue.rs @@ -159,6 +159,7 @@ impl CadCommand for DimContinueCommand { let dim_line_pt = p1 + perp * (dim_line_perp - p1.dot(perp)); let dim_line_pt2 = pt + perp * (dim_line_perp - pt.dot(perp)); Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/dimjogline.rs b/src/modules/annotate/dimjogline.rs index 2945ac6b5..7978c314a 100644 --- a/src/modules/annotate/dimjogline.rs +++ b/src/modules/annotate/dimjogline.rs @@ -85,6 +85,7 @@ impl CadCommand for DimJogLineCommand { } let d = 0.3_f32; Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/dimtedit.rs b/src/modules/annotate/dimtedit.rs index 2e32c3142..48fd7ce02 100644 --- a/src/modules/annotate/dimtedit.rs +++ b/src/modules/annotate/dimtedit.rs @@ -100,6 +100,7 @@ impl CadCommand for DimTeditCommand { } let d = 0.2_f32; Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/leader_cmd.rs b/src/modules/annotate/leader_cmd.rs index 5ada24bd4..178ad6526 100644 --- a/src/modules/annotate/leader_cmd.rs +++ b/src/modules/annotate/leader_cmd.rs @@ -163,6 +163,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { points.push([w2.x, w2.y, w2.z]); } WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/linear_dim.rs b/src/modules/annotate/linear_dim.rs index d2465af57..fe7a6e664 100644 --- a/src/modules/annotate/linear_dim.rs +++ b/src/modules/annotate/linear_dim.rs @@ -218,6 +218,7 @@ fn v3(pt: DVec3) -> Vector3 { fn preview_wire(points: Vec) -> WireModel { WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/mleader_cmd.rs b/src/modules/annotate/mleader_cmd.rs index 79539e969..3e08351e7 100644 --- a/src/modules/annotate/mleader_cmd.rs +++ b/src/modules/annotate/mleader_cmd.rs @@ -150,6 +150,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { points.push([w2.x, w2.y, w2.z]); } WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/mleader_edit.rs b/src/modules/annotate/mleader_edit.rs index e9a071110..947dfabd9 100644 --- a/src/modules/annotate/mleader_edit.rs +++ b/src/modules/annotate/mleader_edit.rs @@ -458,6 +458,7 @@ impl CadCommand for MLeaderCollectCommand { fn preview_wire(pts: &[Vec3]) -> WireModel { WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/ordinate_dim.rs b/src/modules/annotate/ordinate_dim.rs index 430964102..5753f0242 100644 --- a/src/modules/annotate/ordinate_dim.rs +++ b/src/modules/annotate/ordinate_dim.rs @@ -121,6 +121,7 @@ fn ordinate_elbow(feature: DVec3, leader: DVec3, is_x: bool) -> DVec3 { fn preview_wire(points: Vec) -> WireModel { WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/qdim.rs b/src/modules/annotate/qdim.rs index 5338d57d5..72ee9fef6 100644 --- a/src/modules/annotate/qdim.rs +++ b/src/modules/annotate/qdim.rs @@ -90,6 +90,7 @@ impl CadCommand for QdimCommand { } let d = 0.5_f32; Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/radius_dim.rs b/src/modules/annotate/radius_dim.rs index 64f9a8340..2b2be7e5c 100644 --- a/src/modules/annotate/radius_dim.rs +++ b/src/modules/annotate/radius_dim.rs @@ -185,6 +185,7 @@ fn v3(pt: DVec3) -> Vector3 { fn preview_wire(points: Vec) -> WireModel { WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/table_cmd.rs b/src/modules/annotate/table_cmd.rs index 999f56cfd..64b05ee76 100644 --- a/src/modules/annotate/table_cmd.rs +++ b/src/modules/annotate/table_cmd.rs @@ -139,6 +139,7 @@ impl CadCommand for TableCommand { let y = pt.y; let z = pt.z; Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/annotate/tolerance_cmd.rs b/src/modules/annotate/tolerance_cmd.rs index 78b278d3d..61e46a896 100644 --- a/src/modules/annotate/tolerance_cmd.rs +++ b/src/modules/annotate/tolerance_cmd.rs @@ -84,6 +84,7 @@ impl CadCommand for ToleranceCommand { } let d = 0.15_f32; Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/draw/draw/attdef.rs b/src/modules/draw/draw/attdef.rs index fe0c417f4..d319cc691 100644 --- a/src/modules/draw/draw/attdef.rs +++ b/src/modules/draw/draw/attdef.rs @@ -160,6 +160,7 @@ impl CadCommand for AttdefCommand { // Show a small cross at the insertion point. let d = 0.15_f32; Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/draw/draw/mline.rs b/src/modules/draw/draw/mline.rs index 62cd8de2b..8c2853572 100644 --- a/src/modules/draw/draw/mline.rs +++ b/src/modules/draw/draw/mline.rs @@ -136,6 +136,7 @@ impl CadCommand for MlineCommand { .collect(); pts.push([pt.x, pt.y, pt.z]); Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/draw/draw/raster_image.rs b/src/modules/draw/draw/raster_image.rs index 1360161cf..6c58e1856 100644 --- a/src/modules/draw/draw/raster_image.rs +++ b/src/modules/draw/draw/raster_image.rs @@ -108,6 +108,7 @@ impl CadCommand for ImageCommand { let p3 = [origin.x, origin.y + world_height, origin.z]; Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/draw/draw/ray.rs b/src/modules/draw/draw/ray.rs index 13373420e..a200e0474 100644 --- a/src/modules/draw/draw/ray.rs +++ b/src/modules/draw/draw/ray.rs @@ -84,6 +84,7 @@ impl CadCommand for RayCommand { let dir = (pt - base).normalize_or_zero(); let far = base + dir * DISPLAY_EXTENT; Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -179,6 +180,7 @@ impl CadCommand for XLineCommand { let far_pos = base + dir * DISPLAY_EXTENT; let far_neg = base - dir * DISPLAY_EXTENT; Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/draw/draw/revcloud.rs b/src/modules/draw/draw/revcloud.rs index 750b3a03e..780bc377c 100644 --- a/src/modules/draw/draw/revcloud.rs +++ b/src/modules/draw/draw/revcloud.rs @@ -89,6 +89,7 @@ impl CadCommand for RevCloudCommand { self.points[0].z as f32, ]); Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/draw/draw/wipeout.rs b/src/modules/draw/draw/wipeout.rs index fc0df1178..f7b51868f 100644 --- a/src/modules/draw/draw/wipeout.rs +++ b/src/modules/draw/draw/wipeout.rs @@ -114,6 +114,7 @@ impl CadCommand for WipeoutCommand { let min = p1.min(pt); let max = p1.max(pt); Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -160,6 +161,7 @@ impl CadCommand for WipeoutCommand { self.poly_pts[0].z as f32, ]); Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/draw/inquiry/area.rs b/src/modules/draw/inquiry/area.rs index 637a86847..6e86cf715 100644 --- a/src/modules/draw/inquiry/area.rs +++ b/src/modules/draw/inquiry/area.rs @@ -69,6 +69,7 @@ impl CadCommand for AreaCommand { pts.push(f(pt)); pts.push(f(self.points[0])); Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/draw/inquiry/dist.rs b/src/modules/draw/inquiry/dist.rs index e5280556f..1ad13550a 100644 --- a/src/modules/draw/inquiry/dist.rs +++ b/src/modules/draw/inquiry/dist.rs @@ -63,6 +63,7 @@ impl CadCommand for DistCommand { let p1 = self.first?; // The preview wire is purely visual, so f32 vertices are fine here. Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/draw/inquiry/measuregeom.rs b/src/modules/draw/inquiry/measuregeom.rs index 3a0314481..dad6f287c 100644 --- a/src/modules/draw/inquiry/measuregeom.rs +++ b/src/modules/draw/inquiry/measuregeom.rs @@ -67,6 +67,7 @@ impl MeasureGeomCommand { /// Build a cyan preview wire connecting the picked points and the cursor. fn preview_wire(name: &str, pts: Vec<[f32; 3]>) -> WireModel { WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/draw/modify/trim.rs b/src/modules/draw/modify/trim.rs index 3df30c9a5..ad3c0c937 100644 --- a/src/modules/draw/modify/trim.rs +++ b/src/modules/draw/modify/trim.rs @@ -3059,6 +3059,7 @@ fn collect_runs(pts: &[[f64; 2]], take: &dyn Fn([f64; 2]) -> bool, out: &mut Vec /// Build a preview wire from a NaN-break point list. fn preview_wire(points: Vec<[f32; 3]>, color: [f32; 4], name: &str) -> WireModel { WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/layout/mview.rs b/src/modules/layout/mview.rs index 0bbe5e2e3..5a1e35acd 100644 --- a/src/modules/layout/mview.rs +++ b/src/modules/layout/mview.rs @@ -81,6 +81,7 @@ impl CadCommand for MviewCommand { let pt = pt.as_vec3(); let c1 = self.corner1?; Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/model/primitive_cmd.rs b/src/modules/model/primitive_cmd.rs index b1f767514..1d4219ef9 100644 --- a/src/modules/model/primitive_cmd.rs +++ b/src/modules/model/primitive_cmd.rs @@ -305,6 +305,7 @@ inventory::submit!(crate::command::CommandRegistration { fn wire(name: &str, points: Vec<[f32; 3]>) -> WireModel { WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/view/plot_window.rs b/src/modules/view/plot_window.rs index 388b29435..697b38f7c 100644 --- a/src/modules/view/plot_window.rs +++ b/src/modules/view/plot_window.rs @@ -48,6 +48,7 @@ impl CadCommand for PlotWindowCommand { let p1 = self.p1?.as_vec3(); // Draw the selection rectangle. Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/modules/view/zoom_window.rs b/src/modules/view/zoom_window.rs index ccba95fbb..f4ff5f43a 100644 --- a/src/modules/view/zoom_window.rs +++ b/src/modules/view/zoom_window.rs @@ -65,6 +65,7 @@ impl CadCommand for ZoomWindowCommand { let max = p1.max(pt); // Draw a rectangle preview Some(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/scene/cache/block_cache.rs b/src/scene/cache/block_cache.rs index 77f5a74c2..eb5549728 100644 --- a/src/scene/cache/block_cache.rs +++ b/src/scene/cache/block_cache.rs @@ -842,6 +842,7 @@ impl Batches { // the cached defn. let color = crate::scene::view::render::adapt_to_bg(b.color, bg_color); WireModel { + fill_is_3d: false, pick_tris: b.pick_tris, pick_tris_low: b.pick_tris_low, dash_from_start: false, diff --git a/src/scene/convert/tess.rs b/src/scene/convert/tess.rs index 57b596577..310b0c385 100644 --- a/src/scene/convert/tess.rs +++ b/src/scene/convert/tess.rs @@ -555,6 +555,7 @@ pub(crate) fn tessellate_entity( (ins.insert_point.z) as f32, ); let marker = WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -934,6 +935,7 @@ fn lod_stub_wire( // LOD boundary. #19. let stored_color = if selected { WireModel::SELECTED } else { color }; WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1020,6 +1022,7 @@ fn lod_stub_wire_3d( } let stored_color = if selected { WireModel::SELECTED } else { color }; WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/scene/convert/tessellate.rs b/src/scene/convert/tessellate.rs index d8db06525..265192e3c 100644 --- a/src/scene/convert/tessellate.rs +++ b/src/scene/convert/tessellate.rs @@ -370,6 +370,7 @@ pub fn tessellate( None }; elem_wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), // MLINE dashes: A-type aligned, but the end-dash length is @@ -652,6 +653,7 @@ pub fn tessellate( ftl.push(lo); } wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -689,6 +691,7 @@ pub fn tessellate( fpl.push(lo); } wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -733,6 +736,7 @@ pub fn tessellate( low.push(ll); } wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -758,6 +762,7 @@ pub fn tessellate( }); } wires.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -813,6 +818,7 @@ pub fn tessellate( (Vec::new(), Vec::new(), Vec::new()) }; out.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -850,6 +856,7 @@ pub fn tessellate( (Vec::new(), Vec::new(), Vec::new()) }; out.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -884,6 +891,7 @@ pub fn tessellate( // are empty → the early-return path above). if !sdf_verts.is_empty() { out.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -911,6 +919,7 @@ pub fn tessellate( if out.is_empty() { out.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -960,6 +969,7 @@ pub fn tessellate( .map(|[kx, ky, kz]| [kx, ky, kz]) .collect(); return vec![WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1008,6 +1018,7 @@ pub fn tessellate( .map(|[x, y, z]| [x, y, z]) .collect(); return vec![WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1047,6 +1058,7 @@ pub fn tessellate( // A wide polyline arrives here: its band is pick geometry. let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris); return vec![WireModel { + fill_is_3d: false, pick_tris, pick_tris_low, dash_from_start: false, @@ -1088,6 +1100,14 @@ pub fn tessellate( .map(|[x, y, z]| [x, y, z]) .collect(); let (fill_tris, fill_tris_low) = points_to_ds(te.fill_tris); + // Only a real 3-D mesh surface (PolyfaceMesh / PolygonMesh) fill + // renders shaded-only with hidden-surface depth; every other + // `fill_tris` here (a SOLID's filled quad — e.g. a `_BoxFilled` + // arrowhead) is a flat 2-D overlay visible in every view mode. + let fill_is_3d = matches!( + entity, + EntityType::PolyfaceMesh(_) | EntityType::PolygonMesh(_) + ); // Thickness walls ride on the wire that carries their edges, not // on a wire of their own: they are pick geometry for that entity, // and `fill_tris` below deliberately splits off into a fill-only @@ -1108,6 +1128,7 @@ pub fn tessellate( (Vec::new(), Vec::new(), Vec::new()) }; out.push(WireModel { + fill_is_3d: false, pick_tris, pick_tris_low, dash_from_start: false, @@ -1166,11 +1187,13 @@ pub fn tessellate( vp_scissor: None, fill_tris, fill_tris_low, + fill_is_3d, }); } if out.is_empty() { out.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1210,6 +1233,7 @@ pub fn tessellate( // A wide polyline with PLINEGEN=0 arrives here. let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris); return vec![WireModel { + fill_is_3d: false, pick_tris, pick_tris_low, dash_from_start: false, @@ -1278,6 +1302,7 @@ pub fn tessellate( let snap_pts: Vec<(glam::DVec3, SnapHint)> = snap_pts.into_iter().map(|(p, h)| (p.as_dvec3(), h)).collect(); vec![WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, diff --git a/src/scene/model/wire_model.rs b/src/scene/model/wire_model.rs index a0f51fd35..7f1328bec 100644 --- a/src/scene/model/wire_model.rs +++ b/src/scene/model/wire_model.rs @@ -122,6 +122,13 @@ pub struct WireModel { /// exactly like `points` — no separate collector pass. The renderer /// gathers these across all wires into the text vertex buffer. pub text_verts: Vec, + /// `true` when [`fill_tris`] is a real 3-D surface (PolyfaceMesh / + /// PolygonMesh face) that must render with hidden-surface depth and only in + /// shaded modes. `false` for a flat 2-D overlay fill (SOLID arrowhead, + /// dimension / MultiLeader text background, greek-LOD text) that draws in + /// every view mode. The render pass can't infer this from `fill_tris_low` + /// alone — a 2-D fill at UTM scale carries a low residual too. + pub fill_is_3d: bool, } impl WireModel { @@ -169,6 +176,7 @@ impl WireModel { /// Create a solid wire (no dash pattern, 1px weight). pub fn solid(name: String, points: Vec<[f32; 3]>, color: [f32; 4], selected: bool) -> Self { Self { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), text_verts: Vec::new(), @@ -401,6 +409,7 @@ impl Default for WireModel { vp_scissor: None, fill_tris: Vec::new(), fill_tris_low: Vec::new(), + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), } diff --git a/src/scene/pipeline/face3d_gpu.rs b/src/scene/pipeline/face3d_gpu.rs index 2f6023d94..79153b522 100644 --- a/src/scene/pipeline/face3d_gpu.rs +++ b/src/scene/pipeline/face3d_gpu.rs @@ -186,12 +186,13 @@ impl Face3DGpu { // (text greek, MultiLeader / dimension backgrounds) deliberately // leave `fill_tris_low` empty and order by draw rank instead. // - // NOTE: classifying by `!points.is_empty()` is wrong here — the - // tessellator emits a mesh's edges and its fill as *separate* - // WireModels (points-only vs fill-only), so the fill model has no - // points and was being misrouted to the 2-D (draw-order-biased) - // buffer, which drew meshes in front of solids. - let is_3d_mesh_face = !wire.fill_tris_low.is_empty(); + // NOTE: classifying by `!points.is_empty()` is wrong (a mesh emits + // its edges and its fill as *separate* WireModels), and so is + // `!fill_tris_low.is_empty()` — a 2-D overlay fill (SOLID arrowhead, + // dimension text background) at UTM scale carries a low residual + // too, and would be misrouted to the 3-D buffer where it only shows + // in shaded modes. The tessellator flags real surfaces explicitly. + let is_3d_mesh_face = wire.fill_is_3d; let [r, g, b, a] = wire.color; if is_3d_mesh_face { if !keep_3d_mesh_fills { diff --git a/src/scene/text/complex_lt.rs b/src/scene/text/complex_lt.rs index 337e092ad..fd389bf6c 100644 --- a/src/scene/text/complex_lt.rs +++ b/src/scene/text/complex_lt.rs @@ -305,6 +305,7 @@ pub fn apply_along( .into_iter() .filter(|s| s.len() >= 2) .map(|pts| WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -344,6 +345,7 @@ pub fn apply_along( xy = xy.max(y); } out.push(WireModel { + fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false,