mod camera;
mod edges;
mod halo;
mod hit;
mod labels;
mod nodes;
pub use camera::{Camera, CameraUniform};
pub use hit::{contains, pick, HitTarget};
pub use labels::LabelDraw;
use edges::EdgePipeline;
use halo::HaloPipeline;
use labels::Labels;
use nodes::NodePipeline;
use wgpu::util::DeviceExt;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NodeInstanceData {
pub pos: [f32; 2],
pub radius: f32,
pub opacity: f32,
pub shape: u32, pub color: [f32; 4],
}
#[derive(Clone)]
pub struct EdgeLineData {
pub a: [f32; 2],
pub b: [f32; 2],
pub color: [f32; 4],
pub width: f32,
pub opacity: f32,
}
#[derive(Clone)]
pub struct HaloInstanceData {
pub pos: [f32; 2],
pub radius: f32,
pub color: [f32; 4],
pub softness: f32,
pub phase: f32,
}
pub struct Renderer {
device: wgpu::Device,
queue: wgpu::Queue,
camera: Camera,
camera_buf: wgpu::Buffer,
camera_bind_group: wgpu::BindGroup,
nodes: NodePipeline,
node_bounds: Option<([f32; 2], [f32; 2])>,
edges: EdgePipeline,
halos: HaloPipeline,
labels: Labels,
}
impl Renderer {
pub fn new(device: wgpu::Device, queue: wgpu::Queue, format: wgpu::TextureFormat, width: u32, height: u32) -> Self {
let camera = Camera::new(width as f32, height as f32);
let camera_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("camera"),
contents: bytemuck::bytes_of(&camera.uniform()),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});
let camera_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("camera-bgl"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});
let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("camera-bg"),
layout: &camera_bgl,
entries: &[wgpu::BindGroupEntry { binding: 0, resource: camera_buf.as_entire_binding() }],
});
let nodes = NodePipeline::new(&device, format, &camera_bgl);
let edges = EdgePipeline::new(&device, format, &camera_bgl);
let halos = HaloPipeline::new(&device, format, &camera_bgl);
let labels = Labels::new(&device, &queue, format);
Self {
device,
queue,
camera,
camera_buf,
camera_bind_group,
nodes,
node_bounds: None,
edges,
halos,
labels,
}
}
pub fn resize(&mut self, width: u32, height: u32) {
self.camera.viewport = [width as f32, height as f32];
}
pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera }
pub fn camera_ref(&self) -> &Camera { &self.camera }
pub fn set_fade(&mut self, fade: f32) {
self.camera.fade = fade.clamp(0.0, 1.0);
}
pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
surface.configure(&self.device, config);
}
pub fn set_nodes(&mut self, nodes: &[NodeInstanceData]) {
self.nodes.upload(&self.device, &self.queue, nodes);
self.node_bounds = if nodes.is_empty() {
None
} else {
let mut min = [f32::MAX, f32::MAX];
let mut max = [f32::MIN, f32::MIN];
for n in nodes {
min[0] = min[0].min(n.pos[0] - n.radius);
min[1] = min[1].min(n.pos[1] - n.radius);
max[0] = max[0].max(n.pos[0] + n.radius);
max[1] = max[1].max(n.pos[1] + n.radius);
}
Some((min, max))
};
}
pub fn set_labels(&mut self, labels: &[LabelDraw]) {
self.labels.set_labels(labels);
}
pub fn set_edges(&mut self, edges: &[EdgeLineData]) {
self.edges.upload(&self.device, &self.queue, edges);
}
pub fn set_halos(&mut self, halos: &[HaloInstanceData]) {
self.halos.upload(&self.device, &self.queue, halos);
}
pub fn tick(&mut self, dt_ms: f32, dur_ms: f32) {
self.camera.tick(dt_ms, dur_ms);
}
pub fn glide_bounds(&mut self, min: [f32; 2], max: [f32; 2]) {
self.camera.glide_bounds(min, max);
}
pub fn fit_view(&mut self) {
if let Some((min, max)) = self.node_bounds {
self.camera.fit_bounds(min, max);
}
}
fn upload_camera(&self) {
self.queue.write_buffer(&self.camera_buf, 0, bytemuck::bytes_of(&self.camera.uniform()));
}
pub fn render(&mut self, view: &wgpu::TextureView) -> Result<(), String> {
self.upload_camera();
self.labels.prepare(&self.device, &self.queue, &self.camera)?;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("frame") });
let label_res;
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("main-pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.05, g: 0.06, b: 0.09, a: 1.0 }),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
self.edges.draw(&mut rpass, &self.camera_bind_group);
self.halos.draw(&mut rpass, &self.camera_bind_group);
self.nodes.draw(&mut rpass, &self.camera_bind_group);
label_res = self.labels.draw(&mut rpass);
}
label_res?;
self.queue.submit(Some(encoder.finish()));
Ok(())
}
}