Merge pull request #952 from ramox81/codex/fix-sphere-3d-render

Fix sphere edge rendering in 3D views
This commit is contained in:
Hakan Seven authored and GitHub committed 2026-09-01 22:26:02 +03:00
commit 6cc4e0f63d
2 files changed
+45 -55

No files matched your search

+38 -24
View File
@@ -343,11 +343,11 @@ pub struct MeshBatchChunk {
/// erase — the geometry behind them. /// erase — the geometry behind them.
pub transp_index_buffer: wgpu::Buffer, pub transp_index_buffer: wgpu::Buffer,
pub transp_index_count: u32, pub transp_index_count: u32,
/// Triangle-edge line list for plain meshes that carry no B-rep edges. /// Expanded triangle-edge line list for plain meshes that carry no B-rep
/// The dedicated vertex buffer matches the edge pipeline layout. /// edges. Keeping the edge endpoints together avoids coupling this buffer
/// to the surface mesh's indexing and vertex representation.
pub wire_vertex_buffer: wgpu::Buffer, pub wire_vertex_buffer: wgpu::Buffer,
pub wire_index_buffer: wgpu::Buffer, pub wire_vertex_count: u32,
pub wire_index_count: u32,
/// B-rep feature edges of ACIS solids, as a standalone LineList vertex /// B-rep feature edges of ACIS solids, as a standalone LineList vertex
/// buffer (pairs of endpoints), drawn non-indexed. Empty for plain meshes. /// buffer (pairs of endpoints), drawn non-indexed. Empty for plain meshes.
pub edge_vertex_buffer: wgpu::Buffer, pub edge_vertex_buffer: wgpu::Buffer,
@@ -438,7 +438,7 @@ fn make_chunk(
queue.write_buffer(&buffer, 0, bytemuck::cast_slice(data)); queue.write_buffer(&buffer, 0, bytemuck::cast_slice(data));
buffer buffer
}; };
let compact_vertices = !material_has_textures(material) && wire_indices.is_empty(); let compact_vertices = !material_has_textures(material);
let mk_plain_vertex = |data: &[MeshVertex]| { let mk_plain_vertex = |data: &[MeshVertex]| {
if data.is_empty() { if data.is_empty() {
return stubs.vertex.clone(); return stubs.vertex.clone();
@@ -479,18 +479,22 @@ fn make_chunk(
} else { } else {
instances instances
}; };
let wire_vertices: Vec<MeshEdgeVertex> = if wire_indices.is_empty() { let mut wire_vertices = Vec::with_capacity(wire_indices.len());
Vec::new() for line in wire_indices.chunks_exact(2) {
} else { let (Some(start), Some(end)) = (
verts verts.get(line[0] as usize),
.iter() verts.get(line[1] as usize),
.map(|vertex| MeshEdgeVertex { ) else {
continue;
};
for vertex in [start, end] {
wire_vertices.push(MeshEdgeVertex {
position: vertex.position, position: vertex.position,
color: face_color, color: face_color,
position_low: vertex.position_low, position_low: vertex.position_low,
}) });
.collect() }
}; }
MeshBatchChunk { MeshBatchChunk {
vertex_buffer: vertex_buffer_override.unwrap_or_else(|| { vertex_buffer: vertex_buffer_override.unwrap_or_else(|| {
if compact_vertices { if compact_vertices {
@@ -505,8 +509,7 @@ fn make_chunk(
transp_index_buffer: mk_index(transp_indices, "mesh.batch.transp_ibuf"), transp_index_buffer: mk_index(transp_indices, "mesh.batch.transp_ibuf"),
transp_index_count: transp_indices.len() as u32, transp_index_count: transp_indices.len() as u32,
wire_vertex_buffer: mk_edge_vertex(&wire_vertices, "mesh.batch.wire_vbuf"), wire_vertex_buffer: mk_edge_vertex(&wire_vertices, "mesh.batch.wire_vbuf"),
wire_index_buffer: mk_index(wire_indices, "mesh.batch.wire_ibuf"), wire_vertex_count: wire_vertices.len() as u32,
wire_index_count: wire_indices.len() as u32,
edge_vertex_buffer: edge_buffer_override edge_vertex_buffer: edge_buffer_override
.unwrap_or_else(|| mk_edge_vertex(edge_verts, "mesh.batch.edge_vbuf")), .unwrap_or_else(|| mk_edge_vertex(edge_verts, "mesh.batch.edge_vbuf")),
edge_vertex_count: edge_verts.len() as u32, edge_vertex_count: edge_verts.len() as u32,
@@ -1169,8 +1172,7 @@ fn build_instanced_chunk(
let material = first.material; let material = first.material;
let color = first.color; let color = first.color;
let source_handle = source.handle.value(); let source_handle = source.handle.value();
let compact_vertices = !material_has_textures(material) let compact_vertices = !material_has_textures(material);
&& (!first.include_edges || !source.edge_verts.is_empty());
let material_identity = if material_has_textures(material) { let material_identity = if material_has_textures(material) {
material.map_or(0, |material| { material.map_or(0, |material| {
material material
@@ -1582,7 +1584,10 @@ pub fn build_mesh_batch_filtered(
let budget = hard_budget.min(32 * 1024 * 1024); let budget = hard_budget.min(32 * 1024 * 1024);
let vsize = std::mem::size_of::<MeshVertex>(); let vsize = std::mem::size_of::<MeshVertex>();
let max_verts = (budget / vsize).max(3); let max_verts = (budget / vsize).max(3);
let max_tris = (budget / (6 * 4)).max(1); // wire-index buffer: 6 u32 per tri // A triangle contributes three line segments (six standalone edge
// vertices). Bound chunks by the largest buffer produced for wire meshes.
let max_tris =
(budget / (6 * std::mem::size_of::<MeshEdgeVertex>())).max(1);
let stubs = MeshBatchStubs::new(device); let stubs = MeshBatchStubs::new(device);
let mut chunks = Vec::new(); let mut chunks = Vec::new();
@@ -1796,18 +1801,27 @@ pub fn build_mesh_batch_filtered(
.len() .len()
.saturating_mul(std::mem::size_of::<MeshVertex>()) .saturating_mul(std::mem::size_of::<MeshVertex>())
<= hard_budget <= hard_budget
&& part.indices.len().saturating_mul(2 * std::mem::size_of::<u32>()) && part.indices.len().saturating_mul(std::mem::size_of::<u32>())
<= hard_budget <= hard_budget
&& part && part
.set .set
.instance_source .instance_source
.as_ref() .as_ref()
.is_some_and(|source| { .is_some_and(|source| {
source let feature_edges_fit = source
.edge_verts .edge_verts
.len() .len()
.saturating_mul(std::mem::size_of::<MeshEdgeVertex>()) .saturating_mul(std::mem::size_of::<MeshEdgeVertex>())
<= hard_budget <= hard_budget;
let triangle_edges_fit = !part.include_edges
|| !source.edge_verts.is_empty()
|| part
.indices
.len()
.saturating_mul(2)
.saturating_mul(std::mem::size_of::<MeshEdgeVertex>())
<= hard_budget;
feature_edges_fit && triangle_edges_fit
}) })
&& part && part
.indices .indices
@@ -2152,8 +2166,8 @@ pub fn build_mesh_batch_filtered(
continue; continue;
} }
// Flush when adding this mesh would overflow either the vertex buffer // Flush when adding this mesh would overflow either the surface vertex
// or the wire-index buffer. // buffer or the expanded wire-vertex buffer.
if !verts.is_empty() if !verts.is_empty()
&& (verts.len() + mesh.verts.len() > max_verts && (verts.len() + mesh.verts.len() > max_verts
|| wire_indices.len() / 6 + mesh_tris > max_tris) || wire_indices.len() / 6 + mesh_tris > max_tris)
+7 -31
View File
@@ -3750,7 +3750,7 @@ impl Pipeline {
// Four draw paths share this pass: // Four draw paths share this pass:
// - Solid: `mesh_pipeline` + triangle index buf. // - Solid: `mesh_pipeline` + triangle index buf.
// - Wireframe: `mesh_wireframe_pipeline` + the // - Wireframe: `mesh_wireframe_pipeline` + the
// pre-built `wire_index_buffer`. // pre-built expanded wire vertex buffer.
// - HiddenLine: depth prepass (`mesh_depth_pipeline`, // - HiddenLine: depth prepass (`mesh_depth_pipeline`,
// writes Z, no colour) → wire overlay. // writes Z, no colour) → wire overlay.
// - Solid+Edges: `mesh_pipeline` shaded fill → wire // - Solid+Edges: `mesh_pipeline` shaded fill → wire
@@ -3809,17 +3809,9 @@ impl Pipeline {
); );
pass.set_vertex_buffer(1, c.instance_buffer.slice(..)); pass.set_vertex_buffer(1, c.instance_buffer.slice(..));
// Plain-mesh triangulation edges. // Plain-mesh triangulation edges.
if c.wire_index_count != 0 { if c.wire_vertex_count != 0 {
pass.set_vertex_buffer(0, c.wire_vertex_buffer.slice(..)); pass.set_vertex_buffer(0, c.wire_vertex_buffer.slice(..));
pass.set_index_buffer( pass.draw(0..c.wire_vertex_count, 0..c.instance_count);
c.wire_index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
pass.draw_indexed(
0..c.wire_index_count,
0,
0..c.instance_count,
);
} }
// ACIS solid B-rep feature edges (LineList, non-indexed). // ACIS solid B-rep feature edges (LineList, non-indexed).
if c.edge_vertex_count != 0 { if c.edge_vertex_count != 0 {
@@ -3846,17 +3838,9 @@ impl Pipeline {
&[], &[],
); );
pass.set_vertex_buffer(1, c.instance_buffer.slice(..)); pass.set_vertex_buffer(1, c.instance_buffer.slice(..));
if c.wire_index_count != 0 { if c.wire_vertex_count != 0 {
pass.set_vertex_buffer(0, c.wire_vertex_buffer.slice(..)); pass.set_vertex_buffer(0, c.wire_vertex_buffer.slice(..));
pass.set_index_buffer( pass.draw(0..c.wire_vertex_count, 0..c.instance_count);
c.wire_index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
pass.draw_indexed(
0..c.wire_index_count,
0,
0..c.instance_count,
);
} }
if c.edge_vertex_count != 0 { if c.edge_vertex_count != 0 {
pass.set_vertex_buffer(0, c.edge_vertex_buffer.slice(..)); pass.set_vertex_buffer(0, c.edge_vertex_buffer.slice(..));
@@ -4002,17 +3986,9 @@ impl Pipeline {
&[], &[],
); );
pass.set_vertex_buffer(1, c.instance_buffer.slice(..)); pass.set_vertex_buffer(1, c.instance_buffer.slice(..));
if c.wire_index_count != 0 { if c.wire_vertex_count != 0 {
pass.set_vertex_buffer(0, c.wire_vertex_buffer.slice(..)); pass.set_vertex_buffer(0, c.wire_vertex_buffer.slice(..));
pass.set_index_buffer( pass.draw(0..c.wire_vertex_count, 0..c.instance_count);
c.wire_index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
pass.draw_indexed(
0..c.wire_index_count,
0,
0..c.instance_count,
);
} }
if c.edge_vertex_count != 0 { if c.edge_vertex_count != 0 {
pass.set_vertex_buffer(0, c.edge_vertex_buffer.slice(..)); pass.set_vertex_buffer(0, c.edge_vertex_buffer.slice(..));