forked from 1b-agent/opencadstudio-upstream
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
This commit is contained in:
1 parent
ca9ec8f47d
commit
048bb41aef
61 files changed
+151
-21
No files matched your search
@@ -33,6 +33,9 @@ pub struct TruckEntity {
|
||||
pub snap_pts: Vec<(Vec3, SnapHint)>,
|
||||
pub tangent_geoms: Vec<TangentGeom>,
|
||||
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<TruckEntity> {
|
||||
|
||||
@@ -204,6 +204,7 @@ pub fn apply_along(
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -3505,6 +3505,7 @@ fn tessellate_entity(
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
});
|
||||
return wires;
|
||||
}
|
||||
|
||||
@@ -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<Face3DVertex> = 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<Face3DVertex> = 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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+15
-1
@@ -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![],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user