use crate::data::{internal::*, *};
use crate::shape::*;
use crate::texture;
use crate::types::{Color, Scalar};
use crate::util;
use crate::util::Vertex;
use crate::window::{ContextHolder, InnerBareState, InnerGraphicalState};
#[cfg(feature = "saves")]
use serde::{Deserialize, Serialize};
use wgpu::include_wgsl;
use wgpu::util::DeviceExt;
use super::shader::get_shader;
#[derive(Clone)]
#[cfg_attr(feature = "saves", derive(Serialize, Deserialize))]
pub enum PointCloudData {
Scalar(Vec<f32>, ColorMap),
Color(Vec<[f32; 3]>),
}
impl DataSettings for PointCloudData {
fn apply_previous_settings(&mut self, other: Self) {
match (self, other) {
(PointCloudData::Scalar(_, set1), PointCloudData::Scalar(_, set2)) => *set1 = set2,
_ => (),
}
}
fn draw_ui(&mut self, ui: &mut egui::Ui) -> bool {
match self {
PointCloudData::Scalar(_, settings) => settings.draw_ui(ui),
PointCloudData::Color(_) => false,
}
}
}
impl DataUniformBuilder for PointCloudData {
fn build_uniform(&self, device: &wgpu::Device) -> Option<DataUniform> {
match self {
PointCloudData::Scalar(_, colormap) => colormap.get_value().build_uniform(device),
_ => None,
}
}
fn refresh_buffer(&self, queue: &wgpu::Queue, data_uniform: &DataUniform) {
match self {
PointCloudData::Scalar(_, colormap) => {
colormap.get_value().refresh_buffer(queue, data_uniform)
}
_ => (),
}
}
}
impl PointCloudData {
fn sphere_desc<'a>(&self) -> wgpu::VertexBufferLayout<'a> {
match self {
PointCloudData::Color(_) => SphereColorData::desc(),
PointCloudData::Scalar(..) => SphereScalarData::desc(),
}
}
fn build_sphere_data_buffer(&self, device: &wgpu::Device) -> wgpu::Buffer {
match self {
PointCloudData::Scalar(scalars, _) => {
let mut min_d = scalars[0];
let mut max_d = scalars[0];
for data in scalars {
if *data > max_d {
max_d = *data;
}
if *data < min_d {
min_d = *data;
}
}
let gpu_vertices: Vec<_> = scalars
.iter()
.map(|data| {
let t = (data - min_d) / (max_d - min_d);
SphereScalarData { scalar: t }
})
.collect();
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Cloud Sphere Center Buffer"),
contents: bytemuck::cast_slice(&gpu_vertices),
usage: wgpu::BufferUsages::VERTEX,
})
}
PointCloudData::Color(colors) => {
let gpu_vertices: Vec<_> = colors
.iter()
.map(|color| SphereColorData { color: *color })
.collect();
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Cloud Sphere Center Buffer"),
contents: bytemuck::cast_slice(&gpu_vertices),
usage: wgpu::BufferUsages::VERTEX,
})
}
}
}
}
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
#[cfg_attr(feature = "saves", derive(Serialize, Deserialize))]
pub struct PCSettings {
radius: Radius,
color: ColorSettings,
}
impl ShapeSettings for PCSettings {
fn new(name: &str, l: f32) -> Self {
let radius = Radius::new(0.3 * l);
let color = ColorSettings::new(name);
PCSettings { radius, color }
}
fn draw_ui(&mut self, ui: &mut egui::Ui, _property_changed: &mut bool) -> bool {
let mut changed = false;
ui.horizontal(|ui| {
changed |= self.color.draw_ui(ui);
changed |= self.radius.draw_ui(ui);
});
changed
}
}
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct SphereVertex {
position: [f32; 3],
}
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub(crate) struct SphereCenter {
position: [f32; 3],
}
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct SphereColorData {
color: [f32; 3],
}
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct SphereScalarData {
scalar: f32,
}
impl Vertex for SphereVertex {
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
use std::mem;
wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
}],
}
}
}
impl Vertex for SphereCenter {
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
use std::mem;
wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Instance,
attributes: &[wgpu::VertexAttribute {
offset: 0,
shader_location: 1,
format: wgpu::VertexFormat::Float32x3,
}],
}
}
}
impl Vertex for SphereColorData {
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
use std::mem;
wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Instance,
attributes: &[wgpu::VertexAttribute {
offset: 0,
shader_location: 2,
format: wgpu::VertexFormat::Float32x3,
}],
}
}
}
impl Vertex for SphereScalarData {
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
use std::mem;
wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Instance,
attributes: &[wgpu::VertexAttribute {
offset: 0,
shader_location: 2,
format: wgpu::VertexFormat::Float32,
}],
}
}
}
#[derive(Clone)]
#[cfg_attr(feature = "saves", derive(Serialize, Deserialize))]
pub struct PointCloudGeometry {
pub positions: Vec<[f32; 3]>,
avg_edge_length: f32,
}
impl ShapeGeometry for PointCloudGeometry {
type Args = Vec<[f32; 3]>;
fn new(args: Self::Args) -> Self {
let mut min_x = f32::MAX;
let mut min_y = f32::MAX;
let mut min_z = f32::MAX;
let mut max_x = f32::MIN;
let mut max_y = f32::MIN;
let mut max_z = f32::MIN;
for pos in &args {
min_x = min_x.min(pos[0]);
min_y = min_y.min(pos[1]);
min_z = min_z.min(pos[2]);
max_x = max_x.max(pos[0]);
max_y = max_y.max(pos[1]);
max_z = max_z.max(pos[2]);
}
let v = [max_x - min_x, max_y - min_y, max_z - min_z];
let avg_edge_length =
(v[0].powi(2) + v[1].powi(2) + v[2].powi(2)).sqrt() / (args.len() as f32).cbrt() / 3.;
PointCloudGeometry {
positions: args,
avg_edge_length,
}
}
fn get_positions(&self) -> &[[f32; 3]] {
&self.positions
}
fn get_total_elements(&self) -> u32 {
self.positions.len() as u32
}
fn can_be_replaced_by(&self, other: &Self) -> bool {
self.positions.len() == other.positions.len()
}
fn get_vertex_pos(&self, vertex: u32) -> [f32; 3] {
self.positions[vertex as usize]
}
fn move_vertex(
&mut self,
vertex: u32,
pos: [f32; 3],
) -> ((Vec<u32>, Vec<[f32; 3]>), (Vec<u32>, Vec<[f32; 3]>)) {
self.positions[vertex as usize] = pos;
((Vec::new(), Vec::new()), (Vec::new(), Vec::new()))
}
fn get_characteristic_length(&self) -> f32 {
self.avg_edge_length
}
}
pub struct PointCloudFixedRenderer {
positions_len: u32,
vertex_buffer: wgpu::Buffer,
center_buffer: wgpu::Buffer,
}
pub struct PointCloudDataBuffer {
sphere_data_buffer: Option<wgpu::Buffer>,
}
pub struct PointCloudPipeline {
sphere_render_pipeline: wgpu::RenderPipeline,
sphere_picker_render_pipeline: wgpu::RenderPipeline,
}
impl DataBuffer for PointCloudDataBuffer {
type Data = PointCloudData;
type Geometry = PointCloudGeometry;
fn new(device: &wgpu::Device, _geometry: &Self::Geometry, data: Option<&Self::Data>) -> Self {
let sphere_data_buffer = data.map(|d| d.build_sphere_data_buffer(device));
Self { sphere_data_buffer }
}
}
impl FixedRenderer for PointCloudFixedRenderer {
type Geometry = PointCloudGeometry;
fn initialize(device: &wgpu::Device, geometry: &Self::Geometry) -> Self {
let s2 = 2_f32.sqrt();
let positions = [[-s2, -s2, 0.], [s2, -s2, 0.], [-s2, s2, 0.], [s2, s2, 0.]];
let vertices = positions.map(|position| SphereVertex { position });
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("PC Vertex Buffer"),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let mut gpu_vertices = Vec::with_capacity(geometry.positions.len());
for position in geometry.positions.iter() {
let vertex = SphereCenter {
position: *position,
};
gpu_vertices.push(vertex);
}
let center_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Cloud Sphere Center Buffer"),
contents: bytemuck::cast_slice(&gpu_vertices),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
});
Self {
vertex_buffer,
center_buffer,
positions_len: geometry.positions.len() as u32,
}
}
fn update_vertex(&mut self, queue: &wgpu::Queue, vertex: u32, geometry: &Self::Geometry) {
let offset = (size_of::<SphereCenter>() * vertex as usize) as wgpu::BufferAddress;
queue.write_buffer(
&self.center_buffer,
offset,
bytemuck::cast_slice(&[SphereCenter {
position: geometry.positions[vertex as usize],
}]),
);
}
}
impl RenderPipeline for PointCloudPipeline {
type Settings = PCSettings;
type Data = PointCloudData;
type Geometry = PointCloudGeometry;
fn new(
device: &wgpu::Device,
data: Option<&Self::Data>,
_geometry: &Self::Geometry,
_settings: &Self::Settings,
transform_uniform: &DataUniform,
settings_uniform: &DataUniform,
data_uniform: Option<&DataUniform>,
camera_bind_group_layout: &wgpu::BindGroupLayout,
counter_bind_group_layout: &wgpu::BindGroupLayout,
) -> Self {
let data_bind_group_layout = data_uniform.map(|d| &d.bind_group_layout);
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Sphere cloud Render Pipeline Layout"),
bind_group_layouts: &[
Some(camera_bind_group_layout),
Some(&transform_uniform.bind_group_layout),
Some(&settings_uniform.bind_group_layout),
data_bind_group_layout,
],
immediate_size: 0,
});
let shader = wgpu::ShaderModuleDescriptor {
label: Some("sphere cloud shader"),
source: wgpu::ShaderSource::Wgsl(get_shader(data).into()),
};
let sphere_buffer_layout = if let Some(data) = &data {
vec![
SphereVertex::desc(),
SphereCenter::desc(),
data.sphere_desc(),
]
} else {
vec![SphereVertex::desc(), SphereCenter::desc()]
};
let sphere_render_pipeline = util::create_quad_pipeline(
device,
&pipeline_layout,
Some(texture::DEPTH_FORMAT),
&sphere_buffer_layout,
shader,
Some("cloud sphere render"),
);
let picker_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Point Cloud Picker Pipeline Layout"),
bind_group_layouts: &[
Some(camera_bind_group_layout),
Some(counter_bind_group_layout),
Some(&transform_uniform.bind_group_layout),
Some(&settings_uniform.bind_group_layout),
],
immediate_size: 0,
});
let picker_shader = include_wgsl!("picker.wgsl");
let sphere_picker_render_pipeline = util::create_quad_picker_pipeline(
device,
&picker_pipeline_layout,
texture::PICKER_FORMAT,
Some(texture::DEPTH_FORMAT),
&[SphereVertex::desc(), SphereCenter::desc()],
picker_shader,
Some("Point Cloud picker"),
None,
);
PointCloudPipeline {
sphere_render_pipeline,
sphere_picker_render_pipeline,
}
}
fn rebuild(
&mut self,
device: &wgpu::Device,
data: Option<&Self::Data>,
_settings: &Self::Settings,
transform_uniform: &DataUniform,
settings_uniform: &DataUniform,
data_uniform: Option<&DataUniform>,
camera_bind_group_layout: &wgpu::BindGroupLayout,
) {
let data_bind_group_layout = data_uniform.map(|d| &d.bind_group_layout);
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Sphere cloud Render Pipeline Layout"),
bind_group_layouts: &[
Some(camera_bind_group_layout),
Some(&transform_uniform.bind_group_layout),
Some(&settings_uniform.bind_group_layout),
data_bind_group_layout,
],
immediate_size: 0,
});
let shader = wgpu::ShaderModuleDescriptor {
label: Some("sphere cloud shader"),
source: wgpu::ShaderSource::Wgsl(get_shader(data).into()),
};
let sphere_buffer_layout = if let Some(data) = &data {
vec![
SphereVertex::desc(),
SphereCenter::desc(),
data.sphere_desc(),
]
} else {
vec![SphereVertex::desc(), SphereCenter::desc()]
};
let sphere_render_pipeline = util::create_quad_pipeline(
device,
&pipeline_layout,
Some(texture::DEPTH_FORMAT),
&sphere_buffer_layout,
shader,
Some("cloud sphere render"),
);
self.sphere_render_pipeline = sphere_render_pipeline;
}
}
type PointCloudRenderer = Renderer<PointCloudDesc>;
impl RenderAttached for AttachedRenderer<PointCloudDesc> {
fn render_attached<'a, 'b>(&'a self, render_pass: &mut wgpu::RenderPass<'b>)
where
'a: 'b,
{
render_pass.set_bind_group(2, &self.settings_uniform.bind_group, &[]);
render_pass.set_pipeline(&self.pipeline.sphere_render_pipeline);
render_pass.set_vertex_buffer(0, self.fixed.vertex_buffer.slice(..));
render_pass.set_vertex_buffer(1, self.fixed.center_buffer.slice(..));
render_pass.draw(0..4, 0..(self.fixed.positions_len));
}
}
impl Render for PointCloudRenderer {
fn render<'a, 'b>(&'a self, render_pass: &mut wgpu::RenderPass<'b>)
where
'a: 'b,
{
render_pass.set_bind_group(1, &self.transform_uniform.bind_group, &[]);
render_pass.set_bind_group(2, &self.settings_uniform.bind_group, &[]);
if let Some(uniform) = &self.data_uniform {
render_pass.set_bind_group(3, &uniform.bind_group, &[]);
}
render_pass.set_pipeline(&self.pipeline.sphere_render_pipeline);
render_pass.set_vertex_buffer(0, self.fixed.vertex_buffer.slice(..));
render_pass.set_vertex_buffer(1, self.fixed.center_buffer.slice(..));
if let Some(data_buffer) = &self.data_buffer.sphere_data_buffer {
render_pass.set_vertex_buffer(2, data_buffer.slice(..));
}
render_pass.draw(0..4, 0..(self.fixed.positions_len));
}
fn render_picker<'a, 'b>(&'a self, render_pass: &mut wgpu::RenderPass<'b>)
where
'a: 'b,
{
render_pass.set_bind_group(2, &self.transform_uniform.bind_group, &[]);
render_pass.set_bind_group(3, &self.settings_uniform.bind_group, &[]);
render_pass.set_pipeline(&self.pipeline.sphere_picker_render_pipeline);
render_pass.set_vertex_buffer(0, self.fixed.vertex_buffer.slice(..));
render_pass.set_vertex_buffer(1, self.fixed.center_buffer.slice(..));
render_pass.draw(0..4, 0..(self.fixed.positions_len));
}
}
pub struct PointCloudDesc;
impl ShapeDescriptor for PointCloudDesc {
type Data = PointCloudData;
type Geometry = PointCloudGeometry;
type Settings = PCSettings;
type DataBuffer = PointCloudDataBuffer;
type FixedBuffer = PointCloudFixedRenderer;
type Pipeline = PointCloudPipeline;
type Attached<S: ContextHolder> = ();
}
pub type PointCloud<S> = Shape<S, PointCloudDesc>;
pub type UninitedPointCloud = PointCloud<InnerBareState>;
pub type DisplayPointCloud = PointCloud<InnerGraphicalState>;
pub type PointCloudMut<'a, S> = ShapeMut<'a, PointCloud<S>, S>;
impl<S: ContextHolder> PointCloudMut<'_, S> {
pub fn set_radius(&mut self, radius: f32, relative: bool) -> &mut Self {
if relative {
self.inner.settings.radius.set_relative(radius);
} else {
self.inner.settings.radius.set_absolute(radius);
}
self.update_settings(false)
}
pub fn set_color(&mut self, color: [f32; 4]) -> &mut Self {
self.inner.settings.color.color = color;
self.update_settings(false)
}
pub fn add_scalar(
&mut self,
name: impl Into<String>,
datas: impl Scalar,
) -> ColorMapMut<'_, S> {
let datas = datas.into();
assert!(datas.len() == self.geometry().positions.len());
let settings = ColorMap::new(&datas, S::get_settings(&self.context));
self.add_data(name.into(), PointCloudData::Scalar(datas, settings))
.convert(|data| {
if let PointCloudData::Scalar(_, settings) = data {
settings
} else {
panic!()
}
})
}
pub fn add_colors(&mut self, name: impl Into<String>, datas: impl Color) {
let datas = datas.into();
assert!(datas.len() == self.geometry().positions.len());
self.add_data(name.into(), PointCloudData::Color(datas));
}
}