From 048bb41aefcf6a5a34347a4406fe360d5c8b56c4 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Wed, 6 May 2026 23:24:26 +0300 Subject: [PATCH] feat(render): solid fill for PolyfaceMesh and PolygonMesh entities PolyfaceMesh and PolygonMesh were wireframe-only. They now render with the same flat-color solid fill as Face3D entities. - Add fill_tris: Vec<[f32;3]> to TruckEntity and WireModel - PolyfaceMesh: fan-triangulate each face into fill_tris - PolygonMesh: triangulate each grid quad into fill_tris - tessellate.rs Lines branch: apply world_offset to fill_tris - Face3DGpu::from_wires: collect fill_tris from all wires in addition to Face3D key_vertices quads - upload_face3d: pass all_wires so fill_tris are batched into one draw call --- src/app/update.rs | 1 + src/entities/arc.rs | 2 ++ src/entities/attribute.rs | 2 ++ src/entities/circle.rs | 2 ++ src/entities/ellipse.rs | 2 ++ src/entities/leader.rs | 1 + src/entities/line.rs | 2 ++ src/entities/lwpolyline.rs | 5 ++++- src/entities/mesh.rs | 30 ++++++++++++++++++++++--- src/entities/mline.rs | 1 + src/entities/mtext.rs | 1 + src/entities/multileader.rs | 1 + src/entities/ole2frame.rs | 2 ++ src/entities/point.rs | 1 + src/entities/polyline.rs | 6 ++++- src/entities/raster_image.rs | 2 ++ src/entities/ray.rs | 2 ++ src/entities/shape.rs | 1 + src/entities/solid.rs | 1 + src/entities/spline.rs | 2 ++ src/entities/table.rs | 1 + src/entities/text.rs | 1 + src/entities/tolerance.rs | 1 + src/entities/underlay.rs | 2 ++ 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/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/home/draw/attdef.rs | 1 + src/modules/home/draw/ellipse.rs | 1 + src/modules/home/draw/line.rs | 1 + src/modules/home/draw/mline.rs | 1 + src/modules/home/draw/raster_image.rs | 1 + src/modules/home/draw/ray.rs | 2 ++ src/modules/home/draw/revcloud.rs | 1 + src/modules/home/draw/wipeout.rs | 2 ++ src/modules/home/inquiry/area.rs | 1 + src/modules/home/inquiry/dist.rs | 1 + src/modules/home/modify/stretch.rs | 1 + src/modules/layout/mview.rs | 1 + src/modules/view/plot_window.rs | 1 + src/modules/view/zoom_window.rs | 1 + src/scene/acad_to_truck.rs | 3 +++ src/scene/complex_lt.rs | 1 + src/scene/mod.rs | 1 + src/scene/pipeline/face3d_gpu.rs | 32 ++++++++++++++++++--------- src/scene/pipeline/mod.rs | 8 ++++--- src/scene/render.rs | 2 +- src/scene/tessellate.rs | 16 +++++++++++++- src/scene/wire_model.rs | 5 +++++ 61 files changed, 151 insertions(+), 21 deletions(-) diff --git a/src/app/update.rs b/src/app/update.rs index 7b251547d..159b01d3b 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -1376,6 +1376,7 @@ impl H7CAD { aabb: crate::scene::WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }; previews.push(guide); } diff --git a/src/entities/arc.rs b/src/entities/arc.rs index d6dd483f6..e84c4118a 100644 --- a/src/entities/arc.rs +++ b/src/entities/arc.rs @@ -87,6 +87,7 @@ fn to_truck(arc: &Arc) -> TruckEntity { snap_pts: vec![(cv, SnapHint::Center)], tangent_geoms: vec![tangent], key_vertices: vec![], + fill_tris: vec![], }; } @@ -101,6 +102,7 @@ fn to_truck(arc: &Arc) -> TruckEntity { snap_pts: vec![(cv, SnapHint::Center)], tangent_geoms: vec![tangent], key_vertices: vec![], + fill_tris: vec![], } } diff --git a/src/entities/attribute.rs b/src/entities/attribute.rs index e7894a841..8f279fe23 100644 --- a/src/entities/attribute.rs +++ b/src/entities/attribute.rs @@ -42,6 +42,7 @@ impl TruckConvertible for AttributeDefinition { snap_pts: vec![(snap_pt, SnapHint::Insertion)], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], }) } } @@ -146,6 +147,7 @@ impl TruckConvertible for AttributeEntity { snap_pts: vec![(snap_pt, SnapHint::Insertion)], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], }) } } diff --git a/src/entities/circle.rs b/src/entities/circle.rs index 33c28a59d..fe822aa61 100644 --- a/src/entities/circle.rs +++ b/src/entities/circle.rs @@ -91,6 +91,7 @@ fn to_truck(circle: &Circle) -> TruckEntity { snap_pts, tangent_geoms: vec![tangent], key_vertices: vec![], + fill_tris: vec![], }; } @@ -104,6 +105,7 @@ fn to_truck(circle: &Circle) -> TruckEntity { snap_pts, tangent_geoms: vec![tangent], key_vertices: vec![], + fill_tris: vec![], } } diff --git a/src/entities/ellipse.rs b/src/entities/ellipse.rs index 07130024d..b31ec5586 100644 --- a/src/entities/ellipse.rs +++ b/src/entities/ellipse.rs @@ -102,6 +102,7 @@ fn to_truck(ell: &Ellipse) -> TruckEntity { snap_pts, tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], } } else { let n = 32usize; @@ -133,6 +134,7 @@ fn to_truck(ell: &Ellipse) -> TruckEntity { snap_pts: vec![(center_v3, SnapHint::Center)], tangent_geoms: vec![], key_vertices, + fill_tris: vec![], } } } diff --git a/src/entities/leader.rs b/src/entities/leader.rs index b18c94a16..4af33a5c0 100644 --- a/src/entities/leader.rs +++ b/src/entities/leader.rs @@ -64,6 +64,7 @@ fn to_truck(leader: &Leader) -> TruckEntity { snap_pts: vec![], tangent_geoms: tangents, key_vertices: key_verts, + fill_tris: vec![], } } diff --git a/src/entities/line.rs b/src/entities/line.rs index b1d624aa8..28da6fb1f 100644 --- a/src/entities/line.rs +++ b/src/entities/line.rs @@ -50,6 +50,7 @@ fn to_truck(line: &Line) -> TruckEntity { snap_pts: vec![], tangent_geoms: vec![tangent], key_vertices: kv, + fill_tris: vec![], }; } @@ -61,6 +62,7 @@ fn to_truck(line: &Line) -> TruckEntity { snap_pts: vec![], tangent_geoms: vec![tangent], key_vertices: kv, + fill_tris: vec![], } } diff --git a/src/entities/lwpolyline.rs b/src/entities/lwpolyline.rs index d31a428ff..a5beb8b59 100644 --- a/src/entities/lwpolyline.rs +++ b/src/entities/lwpolyline.rs @@ -106,7 +106,7 @@ fn thick_segments( pts.push(off(last)); } } - TruckEntity { object: TruckObject::Lines(pts), snap_pts: vec![], tangent_geoms: tangents, key_vertices: key_verts } + TruckEntity { object: TruckObject::Lines(pts), snap_pts: vec![], tangent_geoms: tangents, key_vertices: key_verts, fill_tris: vec![] } } fn to_truck(pline: &LwPolyline) -> TruckEntity { @@ -117,6 +117,7 @@ fn to_truck(pline: &LwPolyline) -> TruckEntity { snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], }; } @@ -237,6 +238,7 @@ fn to_truck(pline: &LwPolyline) -> TruckEntity { snap_pts: vec![], tangent_geoms: tgs, key_vertices: kv, + fill_tris: vec![], }; } @@ -305,6 +307,7 @@ fn to_truck(pline: &LwPolyline) -> TruckEntity { snap_pts: vec![], tangent_geoms: tangents, key_vertices: key_verts, + fill_tris: vec![], } } diff --git a/src/entities/mesh.rs b/src/entities/mesh.rs index 82549afca..0810b2a1c 100644 --- a/src/entities/mesh.rs +++ b/src/entities/mesh.rs @@ -64,6 +64,7 @@ impl TruckConvertible for Face3D { ], tangent_geoms: vec![], key_vertices: vec![p0, p1, p2, p3], + fill_tris: vec![], }) } } @@ -182,9 +183,9 @@ impl TruckConvertible for PolygonMesh { }; let mut pts: Vec<[f32; 3]> = Vec::new(); + let mut fill_tris: Vec<[f32; 3]> = Vec::new(); // Rows (M direction). - let m_end = if closed_m { m } else { m - 1 }; for i in 0..m { pts.push([f32::NAN; 3]); for j in 0..n { @@ -196,7 +197,6 @@ impl TruckConvertible for PolygonMesh { } // Columns (N direction). - let n_end = if closed_n { n } else { n - 1 }; for j in 0..n { pts.push([f32::NAN; 3]); for i in 0..m { @@ -206,13 +206,26 @@ impl TruckConvertible for PolygonMesh { pts.push(pt(0, j)); } } - let _ = (m_end, n_end); // suppress warnings + + // Fill: triangulate each grid quad (two triangles per cell). + let mi = if closed_m { m } else { m - 1 }; + let ni = if closed_n { n } else { n - 1 }; + for i in 0..mi { + for j in 0..ni { + let p00 = pt(i, j); + let p10 = pt((i + 1) % m, j); + let p01 = pt(i, (j + 1) % n); + let p11 = pt((i + 1) % m, (j + 1) % n); + fill_tris.extend_from_slice(&[p00, p10, p11, p00, p11, p01]); + } + } Some(TruckEntity { object: TruckObject::Lines(pts), snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], + fill_tris, }) } } @@ -294,6 +307,7 @@ impl TruckConvertible for PolyfaceMesh { }; let mut pts: Vec<[f32; 3]> = Vec::new(); + let mut fill_tris: Vec<[f32; 3]> = Vec::new(); for face in &self.faces { // Indices: 0 means unused. Negative = invisible edge (still render for wireframe). @@ -313,6 +327,15 @@ impl TruckConvertible for PolyfaceMesh { } // Close the face polygon. pts.push(verts[0]); + + // Fan-triangulate the face for solid fill. + if verts.len() >= 3 { + for i in 1..(verts.len() - 1) { + fill_tris.push(verts[0]); + fill_tris.push(verts[i]); + fill_tris.push(verts[i + 1]); + } + } } Some(TruckEntity { @@ -320,6 +343,7 @@ impl TruckConvertible for PolyfaceMesh { snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], + fill_tris, }) } } diff --git a/src/entities/mline.rs b/src/entities/mline.rs index 2f97c160d..7716f68fc 100644 --- a/src/entities/mline.rs +++ b/src/entities/mline.rs @@ -109,6 +109,7 @@ impl TruckConvertible for MLine { snap_pts, tangent_geoms: vec![], key_vertices: key_verts, + fill_tris: vec![], }) } } diff --git a/src/entities/mtext.rs b/src/entities/mtext.rs index 1eba2e0b5..f2de20b73 100644 --- a/src/entities/mtext.rs +++ b/src/entities/mtext.rs @@ -177,6 +177,7 @@ fn to_truck(t: &MText, document: &acadrust::CadDocument) -> TruckEntity { snap_pts: vec![(insertion, SnapHint::Insertion)], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], } } diff --git a/src/entities/multileader.rs b/src/entities/multileader.rs index 20f9d8304..b52cc4125 100644 --- a/src/entities/multileader.rs +++ b/src/entities/multileader.rs @@ -156,6 +156,7 @@ fn to_truck(ml: &MultiLeader, document: &acadrust::CadDocument) -> Option TruckEntity { snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], }; } @@ -42,6 +43,7 @@ fn to_truck(ole: &Ole2Frame) -> TruckEntity { snap_pts: vec![(center, SnapHint::Center)], tangent_geoms: vec![], key_vertices: vec![[x0, y0, z], [x1, y1, z]], + fill_tris: vec![], } } diff --git a/src/entities/point.rs b/src/entities/point.rs index 18b317420..bee80fcc4 100644 --- a/src/entities/point.rs +++ b/src/entities/point.rs @@ -22,6 +22,7 @@ fn to_truck(pt: &Point) -> TruckEntity { snap_pts: vec![(snap, SnapHint::Node)], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], } } diff --git a/src/entities/polyline.rs b/src/entities/polyline.rs index 06920c547..7f169b174 100644 --- a/src/entities/polyline.rs +++ b/src/entities/polyline.rs @@ -31,6 +31,7 @@ fn tessellate_polyline(pl: &Polyline) -> TruckEntity { snap_pts: vec![], tangent_geoms: vec![], key_vertices: key_verts, + fill_tris: vec![], } } @@ -127,6 +128,7 @@ fn tessellate_polyline2d(pl: &Polyline2D) -> TruckEntity { snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], }; } @@ -204,7 +206,7 @@ fn tessellate_polyline2d(pl: &Polyline2D) -> TruckEntity { if i + 1 < kv.len() { pts.push([f32::NAN; 3]); } } } - return TruckEntity { object: TruckObject::Lines(pts), snap_pts: vec![], tangent_geoms: tgs, key_vertices: kv }; + return TruckEntity { object: TruckObject::Lines(pts), snap_pts: vec![], tangent_geoms: tgs, key_vertices: kv, fill_tris: vec![] }; } for i in 0..seg_count { @@ -271,6 +273,7 @@ fn tessellate_polyline2d(pl: &Polyline2D) -> TruckEntity { snap_pts: vec![], tangent_geoms: tangents, key_vertices: key_verts, + fill_tris: vec![], } } @@ -397,6 +400,7 @@ fn tessellate_polyline3d(pl: &Polyline3D) -> TruckEntity { snap_pts: vec![], tangent_geoms: vec![], key_vertices: key_verts, + fill_tris: vec![], } } diff --git a/src/entities/raster_image.rs b/src/entities/raster_image.rs index 7d96cac38..7ba43939c 100644 --- a/src/entities/raster_image.rs +++ b/src/entities/raster_image.rs @@ -129,6 +129,7 @@ impl TruckConvertible for RasterImage { snap_pts: vec![], tangent_geoms: vec![], key_vertices: corners.to_vec(), + fill_tris: vec![], }) } } @@ -289,6 +290,7 @@ impl TruckConvertible for Wipeout { snap_pts: vec![], tangent_geoms: vec![], key_vertices: corners.to_vec(), + fill_tris: vec![], }) } } diff --git a/src/entities/ray.rs b/src/entities/ray.rs index 77960aae6..c6f177bd6 100644 --- a/src/entities/ray.rs +++ b/src/entities/ray.rs @@ -34,6 +34,7 @@ impl TruckConvertible for Ray { snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![start], + fill_tris: vec![], }) } } @@ -165,6 +166,7 @@ impl TruckConvertible for XLine { snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![[bp.x as f32, bp.y as f32, bp.z as f32]], + fill_tris: vec![], }) } } diff --git a/src/entities/shape.rs b/src/entities/shape.rs index fc0a8fffa..6b54b8626 100644 --- a/src/entities/shape.rs +++ b/src/entities/shape.rs @@ -47,6 +47,7 @@ impl TruckConvertible for Shape { snap_pts: vec![(snap_pt, SnapHint::Insertion)], tangent_geoms: vec![], key_vertices: vec![[ox, oy, oz]], + fill_tris: vec![], }) } } diff --git a/src/entities/solid.rs b/src/entities/solid.rs index 7402815cb..b1afd5748 100644 --- a/src/entities/solid.rs +++ b/src/entities/solid.rs @@ -47,6 +47,7 @@ impl TruckConvertible for Solid { snap_pts: snap, tangent_geoms: vec![], key_vertices: vec![p0, p1, p2, p3], + fill_tris: vec![], }) } } diff --git a/src/entities/spline.rs b/src/entities/spline.rs index 1dfbd6c70..50b08ba2a 100644 --- a/src/entities/spline.rs +++ b/src/entities/spline.rs @@ -19,6 +19,7 @@ fn to_truck(spl: &Spline) -> TruckEntity { snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], }; } @@ -92,6 +93,7 @@ fn to_truck(spl: &Spline) -> TruckEntity { snap_pts: vec![], tangent_geoms: vec![], key_vertices, + fill_tris: vec![], } } diff --git a/src/entities/table.rs b/src/entities/table.rs index e4767a0ad..ef5d75630 100644 --- a/src/entities/table.rs +++ b/src/entities/table.rs @@ -137,6 +137,7 @@ impl TruckConvertible for Table { snap_pts: vec![(origin, SnapHint::Insertion)], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], }) } } diff --git a/src/entities/text.rs b/src/entities/text.rs index ccd8973b3..177a71d51 100644 --- a/src/entities/text.rs +++ b/src/entities/text.rs @@ -125,6 +125,7 @@ fn to_truck(t: &Text, document: &acadrust::CadDocument) -> TruckEntity { snap_pts: vec![(snap_pt, SnapHint::Insertion)], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], } } diff --git a/src/entities/tolerance.rs b/src/entities/tolerance.rs index cc8c445ef..4951b671c 100644 --- a/src/entities/tolerance.rs +++ b/src/entities/tolerance.rs @@ -236,6 +236,7 @@ impl TruckConvertible for Tolerance { snap_pts: vec![(snap_pt, SnapHint::Insertion)], tangent_geoms: vec![], key_vertices: vec![], + fill_tris: vec![], }) } } diff --git a/src/entities/underlay.rs b/src/entities/underlay.rs index d1fd2a7eb..94c418ecb 100644 --- a/src/entities/underlay.rs +++ b/src/entities/underlay.rs @@ -56,6 +56,7 @@ impl TruckConvertible for Underlay { snap_pts: vec![(Vec3::from(origin), SnapHint::Node)], tangent_geoms: vec![], key_vertices: key, + fill_tris: vec![], }) } else { // No clip boundary: draw a cross at insertion point. @@ -65,6 +66,7 @@ impl TruckConvertible for Underlay { snap_pts: vec![(Vec3::from(origin), SnapHint::Node)], tangent_geoms: vec![], key_vertices: vec![origin], + fill_tris: vec![], }) } } diff --git a/src/modules/annotate/aligned_dim.rs b/src/modules/annotate/aligned_dim.rs index 8f965ca00..24cae2472 100644 --- a/src/modules/annotate/aligned_dim.rs +++ b/src/modules/annotate/aligned_dim.rs @@ -104,6 +104,7 @@ impl CadCommand for AlignedDimensionCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } @@ -140,5 +141,6 @@ fn preview_aligned(p1: Vec3, p2: Vec3, dim_pt: Vec3) -> WireModel { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } diff --git a/src/modules/annotate/angular_dim.rs b/src/modules/annotate/angular_dim.rs index 158bf76bf..5b9759839 100644 --- a/src/modules/annotate/angular_dim.rs +++ b/src/modules/annotate/angular_dim.rs @@ -127,6 +127,7 @@ fn preview_wire(points: Vec) -> WireModel { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } diff --git a/src/modules/annotate/diameter_dim.rs b/src/modules/annotate/diameter_dim.rs index 36b09de8d..ac952a33b 100644 --- a/src/modules/annotate/diameter_dim.rs +++ b/src/modules/annotate/diameter_dim.rs @@ -103,5 +103,6 @@ fn preview_line(a: Vec3, b: Vec3) -> WireModel { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } diff --git a/src/modules/annotate/dim_baseline.rs b/src/modules/annotate/dim_baseline.rs index 7fc1acd26..f1c8e6dbb 100644 --- a/src/modules/annotate/dim_baseline.rs +++ b/src/modules/annotate/dim_baseline.rs @@ -145,6 +145,7 @@ impl CadCommand for DimBaselineCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/annotate/dim_continue.rs b/src/modules/annotate/dim_continue.rs index 73e0755f2..9dbdf1e9e 100644 --- a/src/modules/annotate/dim_continue.rs +++ b/src/modules/annotate/dim_continue.rs @@ -142,6 +142,7 @@ impl CadCommand for DimContinueCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/annotate/dimjogline.rs b/src/modules/annotate/dimjogline.rs index af47cf13a..661a5aa6b 100644 --- a/src/modules/annotate/dimjogline.rs +++ b/src/modules/annotate/dimjogline.rs @@ -96,6 +96,7 @@ impl CadCommand for DimJogLineCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/annotate/dimtedit.rs b/src/modules/annotate/dimtedit.rs index 17f4ce036..f8f769f2f 100644 --- a/src/modules/annotate/dimtedit.rs +++ b/src/modules/annotate/dimtedit.rs @@ -104,6 +104,7 @@ impl CadCommand for DimTeditCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } diff --git a/src/modules/annotate/leader_cmd.rs b/src/modules/annotate/leader_cmd.rs index 95754e1ff..87e1891dd 100644 --- a/src/modules/annotate/leader_cmd.rs +++ b/src/modules/annotate/leader_cmd.rs @@ -204,6 +204,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } diff --git a/src/modules/annotate/linear_dim.rs b/src/modules/annotate/linear_dim.rs index f326ecd1d..025080daa 100644 --- a/src/modules/annotate/linear_dim.rs +++ b/src/modules/annotate/linear_dim.rs @@ -115,6 +115,7 @@ fn preview_wire(points: Vec) -> WireModel { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } diff --git a/src/modules/annotate/mleader_cmd.rs b/src/modules/annotate/mleader_cmd.rs index 69455d26d..d145a360a 100644 --- a/src/modules/annotate/mleader_cmd.rs +++ b/src/modules/annotate/mleader_cmd.rs @@ -157,6 +157,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } diff --git a/src/modules/annotate/mleader_edit.rs b/src/modules/annotate/mleader_edit.rs index 2583c2dd6..2acb93afd 100644 --- a/src/modules/annotate/mleader_edit.rs +++ b/src/modules/annotate/mleader_edit.rs @@ -368,6 +368,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } diff --git a/src/modules/annotate/qdim.rs b/src/modules/annotate/qdim.rs index 672526560..a482122ff 100644 --- a/src/modules/annotate/qdim.rs +++ b/src/modules/annotate/qdim.rs @@ -101,6 +101,7 @@ impl CadCommand for QdimCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/annotate/radius_dim.rs b/src/modules/annotate/radius_dim.rs index ab901a0bc..7ed96a5ca 100644 --- a/src/modules/annotate/radius_dim.rs +++ b/src/modules/annotate/radius_dim.rs @@ -114,5 +114,6 @@ fn preview_wire(points: Vec) -> WireModel { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } diff --git a/src/modules/annotate/table_cmd.rs b/src/modules/annotate/table_cmd.rs index dc9bc2db8..108c2d1bf 100644 --- a/src/modules/annotate/table_cmd.rs +++ b/src/modules/annotate/table_cmd.rs @@ -147,6 +147,7 @@ impl CadCommand for TableCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } else { None diff --git a/src/modules/annotate/tolerance_cmd.rs b/src/modules/annotate/tolerance_cmd.rs index d66d05d48..17f8d1193 100644 --- a/src/modules/annotate/tolerance_cmd.rs +++ b/src/modules/annotate/tolerance_cmd.rs @@ -100,6 +100,7 @@ impl CadCommand for ToleranceCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/home/draw/attdef.rs b/src/modules/home/draw/attdef.rs index 91bb4eb08..85954c7ca 100644 --- a/src/modules/home/draw/attdef.rs +++ b/src/modules/home/draw/attdef.rs @@ -136,6 +136,7 @@ impl CadCommand for AttdefCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/home/draw/ellipse.rs b/src/modules/home/draw/ellipse.rs index 5911329df..6e506fb63 100644 --- a/src/modules/home/draw/ellipse.rs +++ b/src/modules/home/draw/ellipse.rs @@ -529,5 +529,6 @@ fn line_wire(from: Vec3, to: Vec3) -> WireModel { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } diff --git a/src/modules/home/draw/line.rs b/src/modules/home/draw/line.rs index 8f8d09b9f..922076db8 100644 --- a/src/modules/home/draw/line.rs +++ b/src/modules/home/draw/line.rs @@ -90,6 +90,7 @@ impl CadCommand for LineCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/home/draw/mline.rs b/src/modules/home/draw/mline.rs index b8352681a..fb27ec866 100644 --- a/src/modules/home/draw/mline.rs +++ b/src/modules/home/draw/mline.rs @@ -115,6 +115,7 @@ impl CadCommand for MlineCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/home/draw/raster_image.rs b/src/modules/home/draw/raster_image.rs index 921ec00b7..334170b65 100644 --- a/src/modules/home/draw/raster_image.rs +++ b/src/modules/home/draw/raster_image.rs @@ -114,6 +114,7 @@ impl CadCommand for ImageCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/home/draw/ray.rs b/src/modules/home/draw/ray.rs index e76712751..c44a5ae62 100644 --- a/src/modules/home/draw/ray.rs +++ b/src/modules/home/draw/ray.rs @@ -88,6 +88,7 @@ impl CadCommand for RayCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } @@ -168,6 +169,7 @@ impl CadCommand for XLineCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/home/draw/revcloud.rs b/src/modules/home/draw/revcloud.rs index 34db9af82..36dfb0804 100644 --- a/src/modules/home/draw/revcloud.rs +++ b/src/modules/home/draw/revcloud.rs @@ -85,6 +85,7 @@ impl CadCommand for RevCloudCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/home/draw/wipeout.rs b/src/modules/home/draw/wipeout.rs index 7f2ccd937..04b9dc189 100644 --- a/src/modules/home/draw/wipeout.rs +++ b/src/modules/home/draw/wipeout.rs @@ -121,6 +121,7 @@ impl CadCommand for WipeoutCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } WipeoutMode::Polygonal => { @@ -144,6 +145,7 @@ impl CadCommand for WipeoutCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/home/inquiry/area.rs b/src/modules/home/inquiry/area.rs index ccd578d69..b9c36db60 100644 --- a/src/modules/home/inquiry/area.rs +++ b/src/modules/home/inquiry/area.rs @@ -78,6 +78,7 @@ impl CadCommand for AreaCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/home/inquiry/dist.rs b/src/modules/home/inquiry/dist.rs index 4f527577c..83b410e71 100644 --- a/src/modules/home/inquiry/dist.rs +++ b/src/modules/home/inquiry/dist.rs @@ -73,6 +73,7 @@ impl CadCommand for DistCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/home/modify/stretch.rs b/src/modules/home/modify/stretch.rs index 6d27f3705..9cc8dacee 100644 --- a/src/modules/home/modify/stretch.rs +++ b/src/modules/home/modify/stretch.rs @@ -136,6 +136,7 @@ impl CadCommand for StretchCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } _ => None, diff --git a/src/modules/layout/mview.rs b/src/modules/layout/mview.rs index 94b2c42d2..72b146db5 100644 --- a/src/modules/layout/mview.rs +++ b/src/modules/layout/mview.rs @@ -100,6 +100,7 @@ impl CadCommand for MviewCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/view/plot_window.rs b/src/modules/view/plot_window.rs index 34dfe5f98..a29a38bb6 100644 --- a/src/modules/view/plot_window.rs +++ b/src/modules/view/plot_window.rs @@ -65,6 +65,7 @@ impl CadCommand for PlotWindowCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/modules/view/zoom_window.rs b/src/modules/view/zoom_window.rs index a68d3ebac..0cb2d510b 100644 --- a/src/modules/view/zoom_window.rs +++ b/src/modules/view/zoom_window.rs @@ -71,6 +71,7 @@ impl CadCommand for ZoomWindowCommand { aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) } } diff --git a/src/scene/acad_to_truck.rs b/src/scene/acad_to_truck.rs index 6ba326644..832a333c7 100644 --- a/src/scene/acad_to_truck.rs +++ b/src/scene/acad_to_truck.rs @@ -33,6 +33,9 @@ pub struct TruckEntity { pub snap_pts: Vec<(Vec3, SnapHint)>, pub tangent_geoms: Vec, pub key_vertices: Vec<[f32; 3]>, + /// Pre-triangulated fill geometry: flat list of [f32;3] vertices, 3 per triangle. + /// Non-empty for mesh-like entities (PolyfaceMesh, PolygonMesh) that need solid fill. + pub fill_tris: Vec<[f32; 3]>, } pub fn convert(entity: &EntityType, document: &CadDocument) -> Option { diff --git a/src/scene/complex_lt.rs b/src/scene/complex_lt.rs index f8bdc216b..5d99b338f 100644 --- a/src/scene/complex_lt.rs +++ b/src/scene/complex_lt.rs @@ -204,6 +204,7 @@ pub fn apply_along( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }) .collect() } diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 377b3aa2a..e98ffb7e4 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -3505,6 +3505,7 @@ fn tessellate_entity( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }); return wires; } diff --git a/src/scene/pipeline/face3d_gpu.rs b/src/scene/pipeline/face3d_gpu.rs index 2695fdd9d..bf2e67165 100644 --- a/src/scene/pipeline/face3d_gpu.rs +++ b/src/scene/pipeline/face3d_gpu.rs @@ -52,16 +52,17 @@ pub struct Face3DGpu { } impl Face3DGpu { - /// Build a batched GPU buffer from Face3D wire models. + /// Build a batched GPU buffer from Face3D wire models and mesh fill_tris. /// - /// Each WireModel's `key_vertices` holds the 4 corners in local space - /// (world_offset already applied by tessellate.rs). Two triangles are - /// emitted per face: (p0,p1,p2) and (p0,p2,p3). - pub fn from_wires(device: &wgpu::Device, wires: &[WireModel]) -> Self { - let mut vertices: Vec = Vec::with_capacity(wires.len() * 6); + /// - `face3d_wires`: Face3D entities — `key_vertices` holds 4 quad corners; + /// emits 2 triangles per face. + /// - `all_wires`: all entity wires — `fill_tris` holds pre-triangulated + /// fill data for PolyfaceMesh / PolygonMesh. + pub fn from_wires(device: &wgpu::Device, face3d_wires: &[WireModel], all_wires: &[WireModel]) -> Self { + let mut vertices: Vec = Vec::with_capacity(face3d_wires.len() * 6); - for wire in wires { - // key_vertices has exactly 4 entries for Face3D (p0..p3). + // Face3D quads (4 key_vertices → 2 triangles). + for wire in face3d_wires { if wire.key_vertices.len() < 4 { continue; } @@ -69,17 +70,26 @@ impl Face3DGpu { let fill_color = [r * 0.45, g * 0.45, b * 0.45, a]; let p = &wire.key_vertices; let v = |i: usize| Face3DVertex { position: p[i], color: fill_color }; - - // Triangle 1: p0, p1, p2 vertices.push(v(0)); vertices.push(v(1)); vertices.push(v(2)); - // Triangle 2: p0, p2, p3 vertices.push(v(0)); vertices.push(v(2)); vertices.push(v(3)); } + // PolyfaceMesh / PolygonMesh pre-triangulated fills. + for wire in all_wires { + if wire.fill_tris.is_empty() { + continue; + } + let [r, g, b, a] = wire.color; + let fill_color = [r * 0.45, g * 0.45, b * 0.45, a]; + for &position in &wire.fill_tris { + vertices.push(Face3DVertex { position, color: fill_color }); + } + } + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("face3d.vbuf"), contents: bytemuck::cast_slice(&vertices), diff --git a/src/scene/pipeline/mod.rs b/src/scene/pipeline/mod.rs index 9c6a0420c..bbb404a83 100644 --- a/src/scene/pipeline/mod.rs +++ b/src/scene/pipeline/mod.rs @@ -637,13 +637,15 @@ impl Pipeline { /// Upload all 3DFACE entities as two batched GPU objects: /// - `gpu_face3d_fill`: filled triangles (1 buffer, 1 draw call) /// - `gpu_face3d_edges`: merged edge wires (1 buffer, 1 draw call) - pub fn upload_face3d(&mut self, device: &wgpu::Device, face3d_wires: &[WireModel]) { - if face3d_wires.is_empty() { + pub fn upload_face3d(&mut self, device: &wgpu::Device, face3d_wires: &[WireModel], all_wires: &[WireModel]) { + let has_fills = !face3d_wires.is_empty() + || all_wires.iter().any(|w| !w.fill_tris.is_empty()); + if !has_fills { self.gpu_face3d_fill = None; self.gpu_face3d_edges = vec![]; return; } - self.gpu_face3d_fill = Some(Face3DGpu::from_wires(device, face3d_wires)); + self.gpu_face3d_fill = Some(Face3DGpu::from_wires(device, face3d_wires, all_wires)); self.gpu_face3d_edges = WireGpu::from_batch(device, face3d_wires); } diff --git a/src/scene/render.rs b/src/scene/render.rs index a038d7d97..6c4dcb62e 100644 --- a/src/scene/render.rs +++ b/src/scene/render.rs @@ -128,7 +128,7 @@ impl shader::Primitive for Primitive { pipeline.upload_images(device, queue, &self.images[..]); pipeline.upload_meshes(device, &self.meshes[..]); pipeline.upload_wires(device, &self.wires[..]); - pipeline.upload_face3d(device, &self.face3d_wires[..]); + pipeline.upload_face3d(device, &self.face3d_wires[..], &self.wires[..]); pipeline.cached_epoch = self.geometry_epoch; } pipeline.compute_wire_scissors(self.uniforms.view_proj, clip_size.width, clip_size.height); diff --git a/src/scene/tessellate.rs b/src/scene/tessellate.rs index 945df6f41..4f5ac94de 100644 --- a/src/scene/tessellate.rs +++ b/src/scene/tessellate.rs @@ -147,6 +147,7 @@ pub fn tessellate( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }; } @@ -177,6 +178,7 @@ pub fn tessellate( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }; } _ => {} @@ -205,6 +207,7 @@ pub fn tessellate( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }; } } @@ -231,6 +234,7 @@ pub fn tessellate( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }; } } @@ -248,6 +252,9 @@ pub fn tessellate( let key_vertices: Vec<[f32; 3]> = te.key_vertices.into_iter() .map(|[x, y, z]| [x - ox as f32, y - oy as f32, z - oz as f32]) .collect(); + let fill_tris: Vec<[f32; 3]> = te.fill_tris.into_iter() + .map(|[x, y, z]| [x - ox as f32, y - oy as f32, z - oz as f32]) + .collect(); return WireModel { name, points: local_pts, @@ -262,7 +269,8 @@ pub fn tessellate( key_vertices, aabb: WireModel::UNBOUNDED_AABB, plinegen: true, - vp_scissor: None, + vp_scissor: None, + fill_tris, }; } @@ -291,6 +299,7 @@ pub fn tessellate( plinegen: false, vp_scissor: None, aabb: WireModel::UNBOUNDED_AABB, + fill_tris: vec![], }; } @@ -339,6 +348,7 @@ pub fn tessellate( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } @@ -397,6 +407,7 @@ pub fn tessellate_dimension( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }]; if let Some(text) = dimension_text_entity(dim) { @@ -454,6 +465,7 @@ fn tessellate_leader_single( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], }; } @@ -508,6 +520,7 @@ fn tessellate_leader_single( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } @@ -637,6 +650,7 @@ fn tessellate_multileader_single( aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } diff --git a/src/scene/wire_model.rs b/src/scene/wire_model.rs index 86ddba8ec..5605003cc 100644 --- a/src/scene/wire_model.rs +++ b/src/scene/wire_model.rs @@ -66,6 +66,9 @@ pub struct WireModel { /// Paper-space bounding box [x0, y0, x1, y1] for GPU scissor clipping. /// Set only for viewport-projected wires in paper-space layouts. pub vp_scissor: Option<[f32; 4]>, + /// Pre-triangulated solid fill: flat vertex list, 3 per triangle (world-offset applied). + /// Non-empty only for PolyfaceMesh / PolygonMesh entities. + pub fill_tris: Vec<[f32; 3]>, } impl WireModel { @@ -93,6 +96,7 @@ impl WireModel { aabb: Self::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: vec![], } } @@ -194,6 +198,7 @@ impl Default for WireModel { aabb: Self::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, + fill_tris: Vec::new(), } } }