forked from 1b-agent/opencadstudio-upstream
Wireframe 2D/3D modlarında Solid3D meshlerini edge-only çizmek için LineList topolojili bir mesh wireframe pipeline ve her üçgenin (a,b),(b,c),(c,a) kenarlarına genişletilmiş ayrı bir index buffer eklendi. PolyfaceMesh / PolygonMesh face fill'leri (fill_tris + dolu points) wireframe'de gizleniyor; text greek / MultiLeader gibi 2D fill'ler (points boş) etkilenmiyor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
143 lines
5.0 KiB
Rust
143 lines
5.0 KiB
Rust
// Mesh GPU buffers — TriangleList rendering for solid objects.
|
||
//
|
||
// Vertex layout (40 bytes):
|
||
// position [f32; 3] offset 0 12 B
|
||
// normal [f32; 3] offset 12 12 B
|
||
// color [f32; 4] offset 24 16 B
|
||
// ------
|
||
// 40 B / vertex
|
||
|
||
use crate::scene::mesh_model::{MeshLodSet, MeshModel};
|
||
use iced::wgpu;
|
||
use iced::wgpu::util::DeviceExt;
|
||
|
||
// ── Vertex layout ─────────────────────────────────────────────────────────
|
||
|
||
#[repr(C)]
|
||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||
pub struct MeshVertex {
|
||
pub position: [f32; 3],
|
||
pub normal: [f32; 3],
|
||
pub color: [f32; 4],
|
||
}
|
||
|
||
impl MeshVertex {
|
||
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||
wgpu::VertexBufferLayout {
|
||
array_stride: std::mem::size_of::<MeshVertex>() as u64,
|
||
step_mode: wgpu::VertexStepMode::Vertex,
|
||
attributes: &[
|
||
wgpu::VertexAttribute {
|
||
offset: 0,
|
||
shader_location: 0,
|
||
format: wgpu::VertexFormat::Float32x3,
|
||
},
|
||
wgpu::VertexAttribute {
|
||
offset: 12,
|
||
shader_location: 1,
|
||
format: wgpu::VertexFormat::Float32x3,
|
||
},
|
||
wgpu::VertexAttribute {
|
||
offset: 24,
|
||
shader_location: 2,
|
||
format: wgpu::VertexFormat::Float32x4,
|
||
},
|
||
],
|
||
}
|
||
}
|
||
}
|
||
|
||
// ── GPU handle ────────────────────────────────────────────────────────────
|
||
|
||
pub struct MeshGpu {
|
||
pub vertex_buffer: wgpu::Buffer,
|
||
pub index_buffer: wgpu::Buffer,
|
||
pub index_count: u32,
|
||
/// Line-list index buffer: every triangle `(a, b, c)` from the
|
||
/// solid index buffer is expanded into three segments
|
||
/// `(a,b)(b,c)(c,a)`. Used by the wireframe-mode render path so 3D
|
||
/// solids draw as their triangle edges without needing the
|
||
/// `POLYGON_MODE_LINE` device feature.
|
||
pub wire_index_buffer: wgpu::Buffer,
|
||
pub wire_index_count: u32,
|
||
}
|
||
|
||
/// GPU-side bundle of MeshLodSet — one MeshGpu per available LOD plus
|
||
/// the world-XY AABB needed to pick a level per frame.
|
||
pub struct MeshLodGpu {
|
||
pub lods: Vec<MeshGpu>,
|
||
pub world_aabb: [f32; 4],
|
||
}
|
||
|
||
impl MeshLodGpu {
|
||
pub fn new(device: &wgpu::Device, set: &MeshLodSet) -> Self {
|
||
Self {
|
||
lods: set
|
||
.lods
|
||
.iter()
|
||
.filter(|m| !m.indices.is_empty())
|
||
.map(|m| MeshGpu::new(device, m))
|
||
.collect(),
|
||
world_aabb: set.world_aabb,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl MeshGpu {
|
||
pub fn new(device: &wgpu::Device, mesh: &MeshModel) -> Self {
|
||
let has_normals = mesh.normals.len() == mesh.verts.len();
|
||
let vertices: Vec<MeshVertex> = mesh
|
||
.verts
|
||
.iter()
|
||
.enumerate()
|
||
.map(|(i, &pos)| {
|
||
let normal = if has_normals {
|
||
mesh.normals[i]
|
||
} else {
|
||
[0.0, 1.0, 0.0]
|
||
};
|
||
MeshVertex {
|
||
position: pos,
|
||
normal,
|
||
color: mesh.color,
|
||
}
|
||
})
|
||
.collect();
|
||
|
||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||
label: Some(&format!("mesh.vbuf.{}", mesh.name)),
|
||
contents: bytemuck::cast_slice(&vertices),
|
||
usage: wgpu::BufferUsages::VERTEX,
|
||
});
|
||
|
||
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||
label: Some(&format!("mesh.ibuf.{}", mesh.name)),
|
||
contents: bytemuck::cast_slice(&mesh.indices),
|
||
usage: wgpu::BufferUsages::INDEX,
|
||
});
|
||
|
||
// Wireframe-mode index buffer: expand each triangle into its
|
||
// three edge segments. Allocates ~2× the solid index count but
|
||
// is cheap compared to mesh tessellation and only happens when
|
||
// a new mesh is uploaded.
|
||
let mut wire_indices: Vec<u32> = Vec::with_capacity(mesh.indices.len() * 2);
|
||
for tri in mesh.indices.chunks_exact(3) {
|
||
let (a, b, c) = (tri[0], tri[1], tri[2]);
|
||
wire_indices.extend_from_slice(&[a, b, b, c, c, a]);
|
||
}
|
||
let wire_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||
label: Some(&format!("mesh.wire_ibuf.{}", mesh.name)),
|
||
contents: bytemuck::cast_slice(&wire_indices),
|
||
usage: wgpu::BufferUsages::INDEX,
|
||
});
|
||
|
||
Self {
|
||
vertex_buffer,
|
||
index_buffer,
|
||
index_count: mesh.indices.len() as u32,
|
||
wire_index_buffer,
|
||
wire_index_count: wire_indices.len() as u32,
|
||
}
|
||
}
|
||
}
|