Skip to main content

polyscope_render/
slice_mesh_render.rs

1//! Slice mesh rendering for volume mesh cross-section capping.
2//!
3//! Renders the triangulated slice geometry using the surface mesh shader.
4
5use glam::{Vec3, Vec4};
6use wgpu::util::DeviceExt;
7
8use crate::surface_mesh_render::MeshUniforms;
9
10/// GPU resources for rendering a slice mesh (volume cross-section cap).
11///
12/// Uses the same shader as surface meshes but with simplified geometry.
13pub struct SliceMeshRenderData {
14    /// Position buffer (storage buffer, vec4 for alignment).
15    vertex_buffer: wgpu::Buffer,
16    /// Index buffer (triangle indices).
17    index_buffer: wgpu::Buffer,
18    /// Normal buffer (vertex normals, vec4 for alignment).
19    normal_buffer: wgpu::Buffer,
20    /// Barycentric coordinate buffer for wireframe rendering (kept alive for `bind_group`).
21    _barycentric_buffer: wgpu::Buffer,
22    /// Color buffer (per-vertex colors).
23    color_buffer: wgpu::Buffer,
24    /// Edge is real buffer (kept alive for `bind_group`).
25    _edge_is_real_buffer: wgpu::Buffer,
26    /// Uniform buffer for mesh-specific settings.
27    uniform_buffer: wgpu::Buffer,
28    /// Bind group for this slice mesh.
29    bind_group: wgpu::BindGroup,
30    /// Number of indices (`num_triangles` * 3).
31    num_indices: u32,
32}
33
34impl SliceMeshRenderData {
35    /// Creates new render data from slice mesh geometry.
36    ///
37    /// # Arguments
38    /// * `device` - The wgpu device
39    /// * `bind_group_layout` - The bind group layout for surface meshes
40    /// * `camera_buffer` - The camera uniform buffer
41    /// * `vertices` - Vertex positions (3 per triangle)
42    /// * `normals` - Vertex normals (3 per triangle)
43    /// * `colors` - Vertex colors (3 per triangle)
44    #[must_use]
45    pub fn new(
46        device: &wgpu::Device,
47        bind_group_layout: &wgpu::BindGroupLayout,
48        camera_buffer: &wgpu::Buffer,
49        vertices: &[Vec3],
50        normals: &[Vec3],
51        colors: &[Vec4],
52    ) -> Self {
53        let num_vertices = vertices.len() as u32;
54        let num_indices = num_vertices;
55
56        // Expand vertex data to vec4 format
57        let mut position_data: Vec<f32> = Vec::with_capacity(vertices.len() * 4);
58        let mut normal_data: Vec<f32> = Vec::with_capacity(normals.len() * 4);
59        let mut color_data: Vec<f32> = Vec::with_capacity(colors.len() * 4);
60        let mut barycentric_data: Vec<f32> = Vec::with_capacity(vertices.len() * 4);
61        let mut edge_is_real_data: Vec<f32> = Vec::with_capacity(vertices.len() * 4);
62
63        for (i, v) in vertices.iter().enumerate() {
64            position_data.extend_from_slice(&[v.x, v.y, v.z, 1.0]);
65
66            let n = normals[i];
67            normal_data.extend_from_slice(&[n.x, n.y, n.z, 0.0]);
68
69            let c = colors[i];
70            color_data.extend_from_slice(&c.to_array());
71
72            // Barycentric coordinates for wireframe (cycle through triangle vertices)
73            let bary = match i % 3 {
74                0 => [1.0, 0.0, 0.0],
75                1 => [0.0, 1.0, 0.0],
76                _ => [0.0, 0.0, 1.0],
77            };
78            barycentric_data.extend_from_slice(&[bary[0], bary[1], bary[2], 0.0]);
79
80            // All edges are "real" for slice mesh
81            edge_is_real_data.extend_from_slice(&[1.0, 1.0, 1.0, 0.0]);
82        }
83
84        // Create buffers
85        let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
86            label: Some("slice mesh vertices"),
87            contents: bytemuck::cast_slice(&position_data),
88            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
89        });
90
91        let index_data: Vec<u32> = (0..num_indices).collect();
92        let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
93            label: Some("slice mesh indices"),
94            contents: bytemuck::cast_slice(&index_data),
95            usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
96        });
97
98        let normal_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
99            label: Some("slice mesh normals"),
100            contents: bytemuck::cast_slice(&normal_data),
101            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
102        });
103
104        let barycentric_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
105            label: Some("slice mesh barycentrics"),
106            contents: bytemuck::cast_slice(&barycentric_data),
107            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
108        });
109
110        let color_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
111            label: Some("slice mesh colors"),
112            contents: bytemuck::cast_slice(&color_data),
113            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
114        });
115
116        let edge_is_real_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
117            label: Some("slice mesh edge_is_real"),
118            contents: bytemuck::cast_slice(&edge_is_real_data),
119            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
120        });
121
122        // Create uniform buffer with interior color styling
123        let mut uniforms = MeshUniforms::default();
124        uniforms.shade_style = 1; // Flat shading for slice cap
125        uniforms.show_edges = 0; // No edges by default
126        uniforms.backface_policy = 0; // Identical front/back
127
128        let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
129            label: Some("slice mesh uniforms"),
130            contents: bytemuck::cast_slice(&[uniforms]),
131            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
132        });
133
134        // Create bind group
135        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
136            label: Some("slice mesh bind group"),
137            layout: bind_group_layout,
138            entries: &[
139                wgpu::BindGroupEntry {
140                    binding: 0,
141                    resource: camera_buffer.as_entire_binding(),
142                },
143                wgpu::BindGroupEntry {
144                    binding: 1,
145                    resource: uniform_buffer.as_entire_binding(),
146                },
147                wgpu::BindGroupEntry {
148                    binding: 2,
149                    resource: vertex_buffer.as_entire_binding(),
150                },
151                wgpu::BindGroupEntry {
152                    binding: 3,
153                    resource: normal_buffer.as_entire_binding(),
154                },
155                wgpu::BindGroupEntry {
156                    binding: 4,
157                    resource: barycentric_buffer.as_entire_binding(),
158                },
159                wgpu::BindGroupEntry {
160                    binding: 5,
161                    resource: color_buffer.as_entire_binding(),
162                },
163                wgpu::BindGroupEntry {
164                    binding: 6,
165                    resource: edge_is_real_buffer.as_entire_binding(),
166                },
167            ],
168        });
169
170        Self {
171            vertex_buffer,
172            index_buffer,
173            normal_buffer,
174            _barycentric_buffer: barycentric_buffer,
175            color_buffer,
176            _edge_is_real_buffer: edge_is_real_buffer,
177            uniform_buffer,
178            bind_group,
179            num_indices,
180        }
181    }
182
183    /// Updates the slice mesh geometry.
184    ///
185    /// This recreates the buffers if the geometry size has changed.
186    pub fn update(
187        &mut self,
188        device: &wgpu::Device,
189        queue: &wgpu::Queue,
190        bind_group_layout: &wgpu::BindGroupLayout,
191        camera_buffer: &wgpu::Buffer,
192        vertices: &[Vec3],
193        normals: &[Vec3],
194        colors: &[Vec4],
195    ) {
196        // For simplicity, recreate if size differs (slice geometry can change significantly)
197        let new_num_indices = vertices.len() as u32;
198        if new_num_indices != self.num_indices {
199            *self = Self::new(
200                device,
201                bind_group_layout,
202                camera_buffer,
203                vertices,
204                normals,
205                colors,
206            );
207            return;
208        }
209
210        // Update buffers in place
211        let mut position_data: Vec<f32> = Vec::with_capacity(vertices.len() * 4);
212        let mut normal_data: Vec<f32> = Vec::with_capacity(normals.len() * 4);
213        let mut color_data: Vec<f32> = Vec::with_capacity(colors.len() * 4);
214
215        for (i, v) in vertices.iter().enumerate() {
216            position_data.extend_from_slice(&[v.x, v.y, v.z, 1.0]);
217
218            let n = normals[i];
219            normal_data.extend_from_slice(&[n.x, n.y, n.z, 0.0]);
220
221            let c = colors[i];
222            color_data.extend_from_slice(&c.to_array());
223        }
224
225        queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&position_data));
226        queue.write_buffer(&self.normal_buffer, 0, bytemuck::cast_slice(&normal_data));
227        queue.write_buffer(&self.color_buffer, 0, bytemuck::cast_slice(&color_data));
228    }
229
230    /// Updates the uniform buffer with new settings.
231    pub fn update_uniforms(&self, queue: &wgpu::Queue, color: Vec3) {
232        let mut uniforms = MeshUniforms::default();
233        uniforms.shade_style = 1; // Flat shading
234        uniforms.show_edges = 0;
235        uniforms.surface_color = [color.x, color.y, color.z, 1.0];
236        uniforms.backface_policy = 0;
237
238        queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
239    }
240
241    /// Returns the bind group for rendering.
242    #[must_use]
243    pub fn bind_group(&self) -> &wgpu::BindGroup {
244        &self.bind_group
245    }
246
247    /// Returns the index buffer for rendering.
248    #[must_use]
249    pub fn index_buffer(&self) -> &wgpu::Buffer {
250        &self.index_buffer
251    }
252
253    /// Returns the number of indices to draw.
254    #[must_use]
255    pub fn num_indices(&self) -> u32 {
256        self.num_indices
257    }
258
259    /// Returns true if the slice mesh is empty.
260    #[must_use]
261    pub fn is_empty(&self) -> bool {
262        self.num_indices == 0
263    }
264}