Skip to main content

polyscope_render/
point_cloud_render.rs

1//! Point cloud GPU rendering resources.
2
3use glam::{Vec3, Vec4};
4use wgpu::util::DeviceExt;
5
6/// GPU resources for rendering a point cloud.
7pub struct PointCloudRenderData {
8    /// Position buffer (storage buffer).
9    pub position_buffer: wgpu::Buffer,
10    /// Color buffer (storage buffer).
11    pub color_buffer: wgpu::Buffer,
12    /// Uniform buffer for point-specific settings.
13    pub uniform_buffer: wgpu::Buffer,
14    /// Bind group for this point cloud.
15    pub bind_group: wgpu::BindGroup,
16    /// Number of points.
17    pub num_points: u32,
18}
19
20/// Uniforms for point cloud rendering.
21#[repr(C)]
22#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
23#[allow(clippy::pub_underscore_fields)]
24pub struct PointUniforms {
25    pub model_matrix: [[f32; 4]; 4],
26    pub point_radius: f32,
27    pub use_per_point_color: u32,
28    pub _padding: [f32; 2],
29    pub base_color: [f32; 4],
30}
31
32impl Default for PointUniforms {
33    fn default() -> Self {
34        Self {
35            model_matrix: [
36                [1.0, 0.0, 0.0, 0.0],
37                [0.0, 1.0, 0.0, 0.0],
38                [0.0, 0.0, 1.0, 0.0],
39                [0.0, 0.0, 0.0, 1.0],
40            ],
41            point_radius: 0.01,
42            use_per_point_color: 0,
43            _padding: [0.0; 2],
44            base_color: [0.2, 0.5, 0.8, 1.0], // Default blue
45        }
46    }
47}
48
49impl PointCloudRenderData {
50    /// Creates new render data from point positions.
51    #[must_use]
52    pub fn new(
53        device: &wgpu::Device,
54        bind_group_layout: &wgpu::BindGroupLayout,
55        camera_buffer: &wgpu::Buffer,
56        positions: &[Vec3],
57        colors: Option<&[Vec4]>,
58    ) -> Self {
59        let num_points = positions.len() as u32;
60
61        // Create position buffer
62        let position_data: Vec<f32> = positions
63            .iter()
64            .flat_map(|p| [p.x, p.y, p.z, 0.0]) // pad to vec4 for alignment
65            .collect();
66        let position_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
67            label: Some("point positions"),
68            contents: bytemuck::cast_slice(&position_data),
69            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
70        });
71
72        // Create color buffer (default white if not provided)
73        let color_data: Vec<f32> = if let Some(colors) = colors {
74            colors.iter().flat_map(glam::Vec4::to_array).collect()
75        } else {
76            vec![1.0; positions.len() * 4]
77        };
78        let color_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
79            label: Some("point colors"),
80            contents: bytemuck::cast_slice(&color_data),
81            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
82        });
83
84        // Create uniform buffer
85        let uniforms = PointUniforms::default();
86        let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
87            label: Some("point uniforms"),
88            contents: bytemuck::cast_slice(&[uniforms]),
89            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
90        });
91
92        // Create bind group
93        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
94            label: Some("point cloud bind group"),
95            layout: bind_group_layout,
96            entries: &[
97                wgpu::BindGroupEntry {
98                    binding: 0,
99                    resource: camera_buffer.as_entire_binding(),
100                },
101                wgpu::BindGroupEntry {
102                    binding: 1,
103                    resource: uniform_buffer.as_entire_binding(),
104                },
105                wgpu::BindGroupEntry {
106                    binding: 2,
107                    resource: position_buffer.as_entire_binding(),
108                },
109                wgpu::BindGroupEntry {
110                    binding: 3,
111                    resource: color_buffer.as_entire_binding(),
112                },
113            ],
114        });
115
116        Self {
117            position_buffer,
118            color_buffer,
119            uniform_buffer,
120            bind_group,
121            num_points,
122        }
123    }
124
125    /// Updates the color buffer.
126    pub fn update_colors(&self, queue: &wgpu::Queue, colors: &[Vec4]) {
127        let color_data: Vec<f32> = colors.iter().flat_map(glam::Vec4::to_array).collect();
128        queue.write_buffer(&self.color_buffer, 0, bytemuck::cast_slice(&color_data));
129    }
130
131    /// Updates uniforms.
132    pub fn update_uniforms(&self, queue: &wgpu::Queue, uniforms: &PointUniforms) {
133        queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[*uniforms]));
134    }
135}