1mod pick;
4mod pipelines;
5mod postprocessing;
6mod rendering;
7mod textures;
8
9use std::collections::HashMap;
10use std::num::NonZeroU64;
11use std::sync::Arc;
12
13use wgpu::util::DeviceExt;
14
15use polyscope_core::slice_plane::{MAX_SLICE_PLANES, SlicePlaneUniforms};
16
17use crate::camera::Camera;
18use crate::color_maps::ColorMapRegistry;
19use crate::error::{RenderError, RenderResult};
20use crate::ground_plane::GroundPlaneRenderData;
21use crate::materials::{self, MatcapTextureSet, Material, MaterialRegistry};
22use crate::shadow_map::ShadowMapPass;
23use crate::slice_plane_render::SlicePlaneRenderData;
24use crate::tone_mapping::ToneMapPass;
25
26#[repr(C)]
33#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
34pub struct CameraUniforms {
35 pub view: [[f32; 4]; 4],
36 pub proj: [[f32; 4]; 4],
37 pub view_proj: [[f32; 4]; 4],
38 pub inv_proj: [[f32; 4]; 4],
39 pub camera_pos: [f32; 3],
40 pub is_orthographic: f32,
41}
42
43impl Default for CameraUniforms {
44 fn default() -> Self {
45 Self {
46 view: glam::Mat4::IDENTITY.to_cols_array_2d(),
47 proj: glam::Mat4::IDENTITY.to_cols_array_2d(),
48 view_proj: glam::Mat4::IDENTITY.to_cols_array_2d(),
49 inv_proj: glam::Mat4::IDENTITY.to_cols_array_2d(),
50 camera_pos: [0.0, 0.0, 5.0],
51 is_orthographic: 0.0,
52 }
53 }
54}
55
56impl CameraUniforms {
57 #[must_use]
61 pub fn from_camera(camera: &crate::camera::Camera) -> Self {
62 let view = camera.view_matrix();
63 let proj = camera.projection_matrix();
64 let view_proj = proj * view;
65 let inv_proj = proj.inverse();
66 let is_orthographic = match camera.projection_mode {
67 crate::camera::ProjectionMode::Orthographic => 1.0,
68 crate::camera::ProjectionMode::Perspective => 0.0,
69 };
70 Self {
71 view: view.to_cols_array_2d(),
72 proj: proj.to_cols_array_2d(),
73 view_proj: view_proj.to_cols_array_2d(),
74 inv_proj: inv_proj.to_cols_array_2d(),
75 camera_pos: camera.position.to_array(),
76 is_orthographic,
77 }
78 }
79}
80
81pub struct RenderEngine {
83 pub instance: wgpu::Instance,
85 pub adapter: wgpu::Adapter,
87 pub device: wgpu::Device,
89 pub queue: wgpu::Queue,
91 pub surface: Option<wgpu::Surface<'static>>,
93 pub surface_config: wgpu::SurfaceConfiguration,
95 pub depth_texture: wgpu::Texture,
97 pub depth_view: wgpu::TextureView,
99 pub(crate) depth_only_view: wgpu::TextureView,
101 pub materials: MaterialRegistry,
103 pub color_maps: ColorMapRegistry,
105 pub matcap_bind_group_layout: wgpu::BindGroupLayout,
107 pub matcap_textures: HashMap<String, MatcapTextureSet>,
109 pub camera: Camera,
111 pub width: u32,
113 pub height: u32,
115 pub point_pipeline: Option<wgpu::RenderPipeline>,
117 pub point_bind_group_layout: Option<wgpu::BindGroupLayout>,
119 pub camera_buffer: wgpu::Buffer,
121 pub slice_plane_buffer: wgpu::Buffer,
123 pub slice_plane_bind_group_layout: wgpu::BindGroupLayout,
125 pub slice_plane_bind_group: wgpu::BindGroup,
127 pub vector_pipeline: Option<wgpu::RenderPipeline>,
129 pub vector_bind_group_layout: Option<wgpu::BindGroupLayout>,
131 pub mesh_pipeline: Option<wgpu::RenderPipeline>,
133 pub mesh_depth_normal_pipeline: Option<wgpu::RenderPipeline>,
135 pub(crate) mesh_bind_group_layout: Option<wgpu::BindGroupLayout>,
137 pub curve_network_edge_pipeline: Option<wgpu::RenderPipeline>,
139 pub(crate) curve_network_edge_bind_group_layout: Option<wgpu::BindGroupLayout>,
141 pub curve_network_tube_pipeline: Option<wgpu::RenderPipeline>,
143 pub curve_network_tube_compute_pipeline: Option<wgpu::ComputePipeline>,
145 pub(crate) curve_network_tube_bind_group_layout: Option<wgpu::BindGroupLayout>,
147 pub(crate) curve_network_tube_compute_bind_group_layout: Option<wgpu::BindGroupLayout>,
149 pub(crate) ground_plane_pipeline: wgpu::RenderPipeline,
151 pub(crate) ground_plane_bind_group_layout: wgpu::BindGroupLayout,
153 pub(crate) ground_plane_render_data: Option<GroundPlaneRenderData>,
155 pub(crate) slice_plane_vis_pipeline: wgpu::RenderPipeline,
157 pub(crate) slice_plane_vis_bind_group_layout: wgpu::BindGroupLayout,
159 pub(crate) slice_plane_render_data: Vec<SlicePlaneRenderData>,
161 pub(crate) screenshot_texture: Option<wgpu::Texture>,
163 pub(crate) screenshot_buffer: Option<wgpu::Buffer>,
165 pub(crate) screenshot_hdr_texture: Option<wgpu::Texture>,
167 pub(crate) screenshot_hdr_view: Option<wgpu::TextureView>,
169 pub(crate) hdr_texture: Option<wgpu::Texture>,
171 pub(crate) hdr_view: Option<wgpu::TextureView>,
173 pub(crate) normal_texture: Option<wgpu::Texture>,
175 pub(crate) normal_view: Option<wgpu::TextureView>,
177 pub(crate) ssao_noise_texture: Option<wgpu::Texture>,
179 pub(crate) ssao_noise_view: Option<wgpu::TextureView>,
181 pub(crate) ssao_pass: Option<crate::ssao_pass::SsaoPass>,
183 pub(crate) ssao_output_texture: Option<wgpu::Texture>,
185 pub(crate) ssao_output_view: Option<wgpu::TextureView>,
187 pub(crate) depth_peel_pass: Option<crate::depth_peel_pass::DepthPeelPass>,
189 pub(crate) tone_map_pass: Option<ToneMapPass>,
191 pub(crate) ssaa_pass: Option<crate::ssaa_pass::SsaaPass>,
193 pub(crate) ssaa_factor: u32,
195 pub(crate) ssaa_intermediate_texture: Option<wgpu::Texture>,
197 pub(crate) ssaa_intermediate_view: Option<wgpu::TextureView>,
199 pub(crate) shadow_map_pass: Option<ShadowMapPass>,
201 pub(crate) shadow_pipeline: Option<wgpu::RenderPipeline>,
203 pub(crate) shadow_bind_group_layout: Option<wgpu::BindGroupLayout>,
205 pub(crate) reflection_pass: Option<crate::reflection_pass::ReflectionPass>,
207 pub(crate) ground_stencil_pipeline: Option<wgpu::RenderPipeline>,
209 pub(crate) reflected_mesh_pipeline: Option<wgpu::RenderPipeline>,
211 pub(crate) reflected_mesh_bind_group_layout: Option<wgpu::BindGroupLayout>,
213 pub(crate) reflected_point_cloud_pipeline: Option<wgpu::RenderPipeline>,
215 pub(crate) reflected_point_cloud_bind_group_layout: Option<wgpu::BindGroupLayout>,
217 pub(crate) reflected_curve_network_pipeline: Option<wgpu::RenderPipeline>,
219 pub(crate) reflected_curve_network_bind_group_layout: Option<wgpu::BindGroupLayout>,
221 pub simple_mesh_pipeline: Option<wgpu::RenderPipeline>,
223 pub(crate) simple_mesh_bind_group_layout: Option<wgpu::BindGroupLayout>,
225 pub gridcube_pipeline: Option<wgpu::RenderPipeline>,
227 pub(crate) gridcube_bind_group_layout: Option<wgpu::BindGroupLayout>,
229
230 pub(crate) pick_ranges: HashMap<(String, String), pick::PickRange>,
233 pub(crate) next_global_index: u32,
235
236 pub(crate) pick_texture: Option<wgpu::Texture>,
239 pub(crate) pick_texture_view: Option<wgpu::TextureView>,
241 pub(crate) pick_depth_texture: Option<wgpu::Texture>,
243 pub(crate) pick_depth_view: Option<wgpu::TextureView>,
245 pub(crate) pick_staging_buffer: Option<wgpu::Buffer>,
247 pub(crate) pick_buffer_size: (u32, u32),
249 pub(crate) point_pick_pipeline: Option<wgpu::RenderPipeline>,
251 pub(crate) curve_network_pick_pipeline: Option<wgpu::RenderPipeline>,
253 pub(crate) curve_network_tube_pick_pipeline: Option<wgpu::RenderPipeline>,
255 pub(crate) curve_network_tube_pick_bind_group_layout: Option<wgpu::BindGroupLayout>,
257 pub(crate) pick_bind_group_layout: Option<wgpu::BindGroupLayout>,
259 pub(crate) mesh_pick_pipeline: Option<wgpu::RenderPipeline>,
261 pub(crate) mesh_pick_bind_group_layout: Option<wgpu::BindGroupLayout>,
263 pub(crate) gridcube_pick_pipeline: Option<wgpu::RenderPipeline>,
265 pub(crate) gridcube_pick_bind_group_layout: Option<wgpu::BindGroupLayout>,
267}
268
269impl RenderEngine {
270 pub async fn new_windowed(window: Arc<winit::window::Window>) -> RenderResult<Self> {
272 let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
273 backends: wgpu::Backends::all(),
274 ..wgpu::InstanceDescriptor::default()
275 });
276
277 let surface = instance.create_surface(window.clone())?;
278
279 let adapter = instance
280 .request_adapter(&wgpu::RequestAdapterOptions {
281 power_preference: wgpu::PowerPreference::HighPerformance,
282 compatible_surface: Some(&surface),
283 force_fallback_adapter: false,
284 })
285 .await
286 .map_err(|_| RenderError::AdapterCreationFailed)?;
287
288 let (device, queue) = adapter
289 .request_device(&wgpu::DeviceDescriptor {
290 label: Some("polyscope device"),
291 required_features: wgpu::Features::empty(),
292 required_limits: wgpu::Limits::default(),
293 memory_hints: wgpu::MemoryHints::default(),
294 trace: wgpu::Trace::default(),
295 experimental_features: wgpu::ExperimentalFeatures::default(),
296 })
297 .await?;
298
299 let size = window.inner_size();
300 let width = size.width.max(1);
301 let height = size.height.max(1);
302
303 let surface_caps = surface.get_capabilities(&adapter);
304 let surface_format = surface_caps
305 .formats
306 .iter()
307 .find(|f| f.is_srgb())
308 .copied()
309 .unwrap_or(surface_caps.formats[0]);
310
311 let surface_config = wgpu::SurfaceConfiguration {
312 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
313 format: surface_format,
314 width,
315 height,
316 present_mode: wgpu::PresentMode::AutoVsync,
317 alpha_mode: surface_caps.alpha_modes[0],
318 view_formats: vec![],
319 desired_maximum_frame_latency: 2,
320 };
321 surface.configure(&device, &surface_config);
322
323 let (depth_texture, depth_view, depth_only_view) =
324 Self::create_depth_texture(&device, width, height);
325
326 let camera = Camera::new(width as f32 / height as f32);
327
328 let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
329 label: Some("camera uniforms"),
330 contents: bytemuck::cast_slice(&[CameraUniforms::default()]),
331 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
332 });
333
334 let slice_planes_data = [SlicePlaneUniforms::default(); MAX_SLICE_PLANES];
336 let slice_plane_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
337 label: Some("Slice Plane Buffer"),
338 contents: bytemuck::cast_slice(&slice_planes_data),
339 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
340 });
341
342 let slice_plane_bind_group_layout =
343 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
344 label: Some("Slice Plane Bind Group Layout"),
345 entries: &[wgpu::BindGroupLayoutEntry {
346 binding: 0,
347 visibility: wgpu::ShaderStages::FRAGMENT,
348 ty: wgpu::BindingType::Buffer {
349 ty: wgpu::BufferBindingType::Uniform,
350 has_dynamic_offset: false,
351 min_binding_size: NonZeroU64::new(128),
352 },
353 count: None,
354 }],
355 });
356
357 let slice_plane_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
358 label: Some("Slice Plane Bind Group"),
359 layout: &slice_plane_bind_group_layout,
360 entries: &[wgpu::BindGroupEntry {
361 binding: 0,
362 resource: slice_plane_buffer.as_entire_binding(),
363 }],
364 });
365
366 let shadow_map_pass = ShadowMapPass::new(&device);
368
369 let ground_plane_bind_group_layout =
371 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
372 label: Some("Ground Plane Bind Group Layout"),
373 entries: &[
374 wgpu::BindGroupLayoutEntry {
376 binding: 0,
377 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
378 ty: wgpu::BindingType::Buffer {
379 ty: wgpu::BufferBindingType::Uniform,
380 has_dynamic_offset: false,
381 min_binding_size: NonZeroU64::new(272),
382 },
383 count: None,
384 },
385 wgpu::BindGroupLayoutEntry {
387 binding: 1,
388 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
389 ty: wgpu::BindingType::Buffer {
390 ty: wgpu::BufferBindingType::Uniform,
391 has_dynamic_offset: false,
392 min_binding_size: NonZeroU64::new(96),
393 },
394 count: None,
395 },
396 wgpu::BindGroupLayoutEntry {
398 binding: 2,
399 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
400 ty: wgpu::BindingType::Buffer {
401 ty: wgpu::BufferBindingType::Uniform,
402 has_dynamic_offset: false,
403 min_binding_size: NonZeroU64::new(80),
404 },
405 count: None,
406 },
407 wgpu::BindGroupLayoutEntry {
409 binding: 3,
410 visibility: wgpu::ShaderStages::FRAGMENT,
411 ty: wgpu::BindingType::Texture {
412 sample_type: wgpu::TextureSampleType::Depth,
413 view_dimension: wgpu::TextureViewDimension::D2,
414 multisampled: false,
415 },
416 count: None,
417 },
418 wgpu::BindGroupLayoutEntry {
420 binding: 4,
421 visibility: wgpu::ShaderStages::FRAGMENT,
422 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
423 count: None,
424 },
425 ],
426 });
427
428 let ground_plane_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
430 label: Some("Ground Plane Shader"),
431 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/ground_plane.wgsl").into()),
432 });
433
434 let ground_plane_pipeline_layout =
436 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
437 label: Some("Ground Plane Pipeline Layout"),
438 bind_group_layouts: &[&ground_plane_bind_group_layout],
439 push_constant_ranges: &[],
440 });
441
442 let ground_plane_pipeline =
444 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
445 label: Some("Ground Plane Pipeline"),
446 layout: Some(&ground_plane_pipeline_layout),
447 vertex: wgpu::VertexState {
448 module: &ground_plane_shader,
449 entry_point: Some("vs_main"),
450 buffers: &[],
451 compilation_options: wgpu::PipelineCompilationOptions::default(),
452 },
453 fragment: Some(wgpu::FragmentState {
454 module: &ground_plane_shader,
455 entry_point: Some("fs_main"),
456 targets: &[Some(wgpu::ColorTargetState {
457 format: wgpu::TextureFormat::Rgba16Float, blend: Some(wgpu::BlendState::ALPHA_BLENDING),
459 write_mask: wgpu::ColorWrites::ALL,
460 })],
461 compilation_options: wgpu::PipelineCompilationOptions::default(),
462 }),
463 primitive: wgpu::PrimitiveState {
464 topology: wgpu::PrimitiveTopology::TriangleList,
465 ..wgpu::PrimitiveState::default()
466 },
467 depth_stencil: Some(wgpu::DepthStencilState {
468 format: wgpu::TextureFormat::Depth24PlusStencil8,
469 depth_write_enabled: true,
470 depth_compare: wgpu::CompareFunction::LessEqual,
471 stencil: wgpu::StencilState::default(),
472 bias: wgpu::DepthBiasState::default(),
473 }),
474 multisample: wgpu::MultisampleState::default(),
475 multiview: None,
476 cache: None,
477 });
478
479 let slice_plane_vis_bind_group_layout =
481 crate::slice_plane_render::create_slice_plane_bind_group_layout(&device);
482 let slice_plane_vis_pipeline = crate::slice_plane_render::create_slice_plane_pipeline(
483 &device,
484 &slice_plane_vis_bind_group_layout,
485 wgpu::TextureFormat::Rgba16Float,
486 wgpu::TextureFormat::Depth24PlusStencil8,
487 );
488
489 let matcap_bind_group_layout = materials::create_matcap_bind_group_layout(&device);
491 let matcap_textures =
492 materials::init_matcap_textures(&device, &queue, &matcap_bind_group_layout);
493
494 let mut engine = Self {
495 instance,
496 adapter,
497 device,
498 queue,
499 surface: Some(surface),
500 surface_config,
501 depth_texture,
502 depth_view,
503 depth_only_view,
504 materials: MaterialRegistry::new(),
505 color_maps: ColorMapRegistry::new(),
506 matcap_bind_group_layout,
507 matcap_textures,
508 camera,
509 width,
510 height,
511 point_pipeline: None,
512 point_bind_group_layout: None,
513 camera_buffer,
514 slice_plane_buffer,
515 slice_plane_bind_group_layout,
516 slice_plane_bind_group,
517 vector_pipeline: None,
518 vector_bind_group_layout: None,
519 mesh_pipeline: None,
520
521 mesh_depth_normal_pipeline: None,
522 mesh_bind_group_layout: None,
523 curve_network_edge_pipeline: None,
524 curve_network_edge_bind_group_layout: None,
525 curve_network_tube_pipeline: None,
526 curve_network_tube_compute_pipeline: None,
527 curve_network_tube_bind_group_layout: None,
528 curve_network_tube_compute_bind_group_layout: None,
529 ground_plane_pipeline,
530 ground_plane_bind_group_layout,
531 ground_plane_render_data: None,
532 slice_plane_vis_pipeline,
533 slice_plane_vis_bind_group_layout,
534 slice_plane_render_data: Vec::new(),
535 screenshot_texture: None,
536 screenshot_buffer: None,
537 screenshot_hdr_texture: None,
538 screenshot_hdr_view: None,
539 hdr_texture: None,
540 hdr_view: None,
541 normal_texture: None,
542 normal_view: None,
543 ssao_noise_texture: None,
544 ssao_noise_view: None,
545 ssao_pass: None,
546 ssao_output_texture: None,
547 ssao_output_view: None,
548 depth_peel_pass: None,
549 tone_map_pass: None,
550 ssaa_pass: None,
551 ssaa_factor: 1,
552 ssaa_intermediate_texture: None,
553 ssaa_intermediate_view: None,
554 shadow_map_pass: Some(shadow_map_pass),
555 shadow_pipeline: None,
556 shadow_bind_group_layout: None,
557 reflection_pass: None,
558 ground_stencil_pipeline: None,
559 reflected_mesh_pipeline: None,
560 reflected_mesh_bind_group_layout: None,
561 reflected_point_cloud_pipeline: None,
562 reflected_point_cloud_bind_group_layout: None,
563 reflected_curve_network_pipeline: None,
564 reflected_curve_network_bind_group_layout: None,
565 simple_mesh_pipeline: None,
566 simple_mesh_bind_group_layout: None,
567 gridcube_pipeline: None,
568 gridcube_bind_group_layout: None,
569 pick_ranges: HashMap::new(),
570 next_global_index: 1, pick_texture: None,
572 pick_texture_view: None,
573 pick_depth_texture: None,
574 pick_depth_view: None,
575 pick_staging_buffer: None,
576 pick_buffer_size: (0, 0),
577 point_pick_pipeline: None,
578 curve_network_pick_pipeline: None,
579 curve_network_tube_pick_pipeline: None,
580 curve_network_tube_pick_bind_group_layout: None,
581 pick_bind_group_layout: None,
582 mesh_pick_pipeline: None,
583 mesh_pick_bind_group_layout: None,
584 gridcube_pick_pipeline: None,
585 gridcube_pick_bind_group_layout: None,
586 };
587
588 engine.init_point_pipeline();
589 engine.init_vector_pipeline();
590 engine.create_mesh_pipeline();
591 engine.create_curve_network_edge_pipeline();
592 engine.create_curve_network_tube_pipelines();
593 engine.create_simple_mesh_pipeline();
594 engine.create_gridcube_pipeline();
595 engine.create_shadow_pipeline();
596 engine.init_tone_mapping();
597 engine.init_ssaa_pass();
598 engine.init_reflection_pass();
599 engine.create_ground_stencil_pipeline();
600 engine.create_reflected_mesh_pipeline();
601 engine.create_reflected_point_cloud_pipeline();
602 engine.create_reflected_curve_network_pipeline();
603 engine.init_pick_pipeline();
604 engine.init_mesh_pick_pipeline();
605
606 Ok(engine)
607 }
608
609 pub async fn new_headless(width: u32, height: u32) -> RenderResult<Self> {
611 let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
612 backends: wgpu::Backends::all(),
613 ..wgpu::InstanceDescriptor::default()
614 });
615
616 let adapter = instance
617 .request_adapter(&wgpu::RequestAdapterOptions {
618 power_preference: wgpu::PowerPreference::HighPerformance,
619 compatible_surface: None,
620 force_fallback_adapter: false,
621 })
622 .await
623 .map_err(|_| RenderError::AdapterCreationFailed)?;
624
625 let (device, queue) = adapter
626 .request_device(&wgpu::DeviceDescriptor {
627 label: Some("polyscope device (headless)"),
628 required_features: wgpu::Features::empty(),
629 required_limits: wgpu::Limits::default(),
630 memory_hints: wgpu::MemoryHints::default(),
631 trace: wgpu::Trace::default(),
632 experimental_features: wgpu::ExperimentalFeatures::default(),
633 })
634 .await?;
635
636 let surface_config = wgpu::SurfaceConfiguration {
637 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
638 format: wgpu::TextureFormat::Rgba8UnormSrgb,
639 width,
640 height,
641 present_mode: wgpu::PresentMode::Fifo,
642 alpha_mode: wgpu::CompositeAlphaMode::Auto,
643 view_formats: vec![],
644 desired_maximum_frame_latency: 2,
645 };
646
647 let (depth_texture, depth_view, depth_only_view) =
648 Self::create_depth_texture(&device, width, height);
649
650 let camera = Camera::new(width as f32 / height as f32);
651
652 let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
653 label: Some("camera uniforms"),
654 contents: bytemuck::cast_slice(&[CameraUniforms::default()]),
655 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
656 });
657
658 let slice_planes_data = [SlicePlaneUniforms::default(); MAX_SLICE_PLANES];
660 let slice_plane_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
661 label: Some("Slice Plane Buffer"),
662 contents: bytemuck::cast_slice(&slice_planes_data),
663 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
664 });
665
666 let slice_plane_bind_group_layout =
667 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
668 label: Some("Slice Plane Bind Group Layout"),
669 entries: &[wgpu::BindGroupLayoutEntry {
670 binding: 0,
671 visibility: wgpu::ShaderStages::FRAGMENT,
672 ty: wgpu::BindingType::Buffer {
673 ty: wgpu::BufferBindingType::Uniform,
674 has_dynamic_offset: false,
675 min_binding_size: NonZeroU64::new(128),
676 },
677 count: None,
678 }],
679 });
680
681 let slice_plane_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
682 label: Some("Slice Plane Bind Group"),
683 layout: &slice_plane_bind_group_layout,
684 entries: &[wgpu::BindGroupEntry {
685 binding: 0,
686 resource: slice_plane_buffer.as_entire_binding(),
687 }],
688 });
689
690 let shadow_map_pass = ShadowMapPass::new(&device);
692
693 let ground_plane_bind_group_layout =
695 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
696 label: Some("Ground Plane Bind Group Layout"),
697 entries: &[
698 wgpu::BindGroupLayoutEntry {
700 binding: 0,
701 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
702 ty: wgpu::BindingType::Buffer {
703 ty: wgpu::BufferBindingType::Uniform,
704 has_dynamic_offset: false,
705 min_binding_size: NonZeroU64::new(272),
706 },
707 count: None,
708 },
709 wgpu::BindGroupLayoutEntry {
711 binding: 1,
712 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
713 ty: wgpu::BindingType::Buffer {
714 ty: wgpu::BufferBindingType::Uniform,
715 has_dynamic_offset: false,
716 min_binding_size: NonZeroU64::new(96),
717 },
718 count: None,
719 },
720 wgpu::BindGroupLayoutEntry {
722 binding: 2,
723 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
724 ty: wgpu::BindingType::Buffer {
725 ty: wgpu::BufferBindingType::Uniform,
726 has_dynamic_offset: false,
727 min_binding_size: NonZeroU64::new(80),
728 },
729 count: None,
730 },
731 wgpu::BindGroupLayoutEntry {
733 binding: 3,
734 visibility: wgpu::ShaderStages::FRAGMENT,
735 ty: wgpu::BindingType::Texture {
736 sample_type: wgpu::TextureSampleType::Depth,
737 view_dimension: wgpu::TextureViewDimension::D2,
738 multisampled: false,
739 },
740 count: None,
741 },
742 wgpu::BindGroupLayoutEntry {
744 binding: 4,
745 visibility: wgpu::ShaderStages::FRAGMENT,
746 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
747 count: None,
748 },
749 ],
750 });
751
752 let ground_plane_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
754 label: Some("Ground Plane Shader"),
755 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/ground_plane.wgsl").into()),
756 });
757
758 let ground_plane_pipeline_layout =
760 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
761 label: Some("Ground Plane Pipeline Layout"),
762 bind_group_layouts: &[&ground_plane_bind_group_layout],
763 push_constant_ranges: &[],
764 });
765
766 let ground_plane_pipeline =
768 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
769 label: Some("Ground Plane Pipeline"),
770 layout: Some(&ground_plane_pipeline_layout),
771 vertex: wgpu::VertexState {
772 module: &ground_plane_shader,
773 entry_point: Some("vs_main"),
774 buffers: &[],
775 compilation_options: wgpu::PipelineCompilationOptions::default(),
776 },
777 fragment: Some(wgpu::FragmentState {
778 module: &ground_plane_shader,
779 entry_point: Some("fs_main"),
780 targets: &[Some(wgpu::ColorTargetState {
781 format: wgpu::TextureFormat::Rgba16Float, blend: Some(wgpu::BlendState::ALPHA_BLENDING),
783 write_mask: wgpu::ColorWrites::ALL,
784 })],
785 compilation_options: wgpu::PipelineCompilationOptions::default(),
786 }),
787 primitive: wgpu::PrimitiveState {
788 topology: wgpu::PrimitiveTopology::TriangleList,
789 ..wgpu::PrimitiveState::default()
790 },
791 depth_stencil: Some(wgpu::DepthStencilState {
792 format: wgpu::TextureFormat::Depth24PlusStencil8,
793 depth_write_enabled: true,
794 depth_compare: wgpu::CompareFunction::LessEqual,
795 stencil: wgpu::StencilState::default(),
796 bias: wgpu::DepthBiasState::default(),
797 }),
798 multisample: wgpu::MultisampleState::default(),
799 multiview: None,
800 cache: None,
801 });
802
803 let slice_plane_vis_bind_group_layout =
805 crate::slice_plane_render::create_slice_plane_bind_group_layout(&device);
806 let slice_plane_vis_pipeline = crate::slice_plane_render::create_slice_plane_pipeline(
807 &device,
808 &slice_plane_vis_bind_group_layout,
809 wgpu::TextureFormat::Rgba16Float,
810 wgpu::TextureFormat::Depth24PlusStencil8,
811 );
812
813 let matcap_bind_group_layout = materials::create_matcap_bind_group_layout(&device);
815 let matcap_textures =
816 materials::init_matcap_textures(&device, &queue, &matcap_bind_group_layout);
817
818 let mut engine = Self {
819 instance,
820 adapter,
821 device,
822 queue,
823 surface: None,
824 surface_config,
825 depth_texture,
826 depth_view,
827 depth_only_view,
828 materials: MaterialRegistry::new(),
829 color_maps: ColorMapRegistry::new(),
830 matcap_bind_group_layout,
831 matcap_textures,
832 camera,
833 width,
834 height,
835 point_pipeline: None,
836 point_bind_group_layout: None,
837 camera_buffer,
838 slice_plane_buffer,
839 slice_plane_bind_group_layout,
840 slice_plane_bind_group,
841 vector_pipeline: None,
842 vector_bind_group_layout: None,
843 mesh_pipeline: None,
844
845 mesh_depth_normal_pipeline: None,
846 mesh_bind_group_layout: None,
847 curve_network_edge_pipeline: None,
848 curve_network_edge_bind_group_layout: None,
849 curve_network_tube_pipeline: None,
850 curve_network_tube_compute_pipeline: None,
851 curve_network_tube_bind_group_layout: None,
852 curve_network_tube_compute_bind_group_layout: None,
853 ground_plane_pipeline,
854 ground_plane_bind_group_layout,
855 ground_plane_render_data: None,
856 slice_plane_vis_pipeline,
857 slice_plane_vis_bind_group_layout,
858 slice_plane_render_data: Vec::new(),
859 screenshot_texture: None,
860 screenshot_buffer: None,
861 screenshot_hdr_texture: None,
862 screenshot_hdr_view: None,
863 hdr_texture: None,
864 hdr_view: None,
865 normal_texture: None,
866 normal_view: None,
867 ssao_noise_texture: None,
868 ssao_noise_view: None,
869 ssao_pass: None,
870 ssao_output_texture: None,
871 ssao_output_view: None,
872 depth_peel_pass: None,
873 tone_map_pass: None,
874 ssaa_pass: None,
875 ssaa_factor: 1,
876 ssaa_intermediate_texture: None,
877 ssaa_intermediate_view: None,
878 shadow_map_pass: Some(shadow_map_pass),
879 shadow_pipeline: None,
880 shadow_bind_group_layout: None,
881 reflection_pass: None,
882 ground_stencil_pipeline: None,
883 reflected_mesh_pipeline: None,
884 reflected_mesh_bind_group_layout: None,
885 reflected_point_cloud_pipeline: None,
886 reflected_point_cloud_bind_group_layout: None,
887 reflected_curve_network_pipeline: None,
888 reflected_curve_network_bind_group_layout: None,
889 simple_mesh_pipeline: None,
890 simple_mesh_bind_group_layout: None,
891 gridcube_pipeline: None,
892 gridcube_bind_group_layout: None,
893 pick_ranges: HashMap::new(),
894 next_global_index: 1, pick_texture: None,
896 pick_texture_view: None,
897 pick_depth_texture: None,
898 pick_depth_view: None,
899 pick_staging_buffer: None,
900 pick_buffer_size: (0, 0),
901 point_pick_pipeline: None,
902 curve_network_pick_pipeline: None,
903 curve_network_tube_pick_pipeline: None,
904 curve_network_tube_pick_bind_group_layout: None,
905 pick_bind_group_layout: None,
906 mesh_pick_pipeline: None,
907 mesh_pick_bind_group_layout: None,
908 gridcube_pick_pipeline: None,
909 gridcube_pick_bind_group_layout: None,
910 };
911
912 engine.init_point_pipeline();
913 engine.init_vector_pipeline();
914 engine.create_mesh_pipeline();
915 engine.create_curve_network_edge_pipeline();
916 engine.create_curve_network_tube_pipelines();
917 engine.create_simple_mesh_pipeline();
918 engine.create_gridcube_pipeline();
919 engine.create_shadow_pipeline();
920 engine.init_tone_mapping();
921 engine.init_ssaa_pass();
922 engine.init_reflection_pass();
923 engine.create_ground_stencil_pipeline();
924 engine.create_reflected_mesh_pipeline();
925 engine.create_reflected_point_cloud_pipeline();
926 engine.create_reflected_curve_network_pipeline();
927 engine.init_pick_pipeline();
928 engine.init_mesh_pick_pipeline();
929
930 Ok(engine)
931 }
932
933 pub fn resize(&mut self, width: u32, height: u32) {
935 if width == 0 || height == 0 {
936 return;
937 }
938
939 self.width = width;
940 self.height = height;
941 self.surface_config.width = width;
942 self.surface_config.height = height;
943
944 if let Some(ref surface) = self.surface {
945 surface.configure(&self.device, &self.surface_config);
946 }
947
948 let ssaa_width = width * self.ssaa_factor;
950 let ssaa_height = height * self.ssaa_factor;
951
952 let (depth_texture, depth_view, depth_only_view) =
953 Self::create_depth_texture(&self.device, ssaa_width, ssaa_height);
954 self.depth_texture = depth_texture;
955 self.depth_view = depth_view;
956 self.depth_only_view = depth_only_view;
957
958 self.create_hdr_texture_with_size(ssaa_width, ssaa_height);
960
961 self.create_normal_texture_with_size(ssaa_width, ssaa_height);
963
964 if let Some(ref mut ssao_pass) = self.ssao_pass {
966 ssao_pass.resize(&self.device, &self.queue, ssaa_width, ssaa_height);
967 }
968 self.create_ssao_output_texture_with_size(ssaa_width, ssaa_height);
969
970 if self.ssaa_factor > 1 {
972 self.create_ssaa_intermediate_texture();
973 }
974
975 self.camera.set_aspect_ratio(width as f32 / height as f32);
976 }
977
978 fn create_depth_texture(
979 device: &wgpu::Device,
980 width: u32,
981 height: u32,
982 ) -> (wgpu::Texture, wgpu::TextureView, wgpu::TextureView) {
983 let texture = device.create_texture(&wgpu::TextureDescriptor {
984 label: Some("depth texture"),
985 size: wgpu::Extent3d {
986 width,
987 height,
988 depth_or_array_layers: 1,
989 },
990 mip_level_count: 1,
991 sample_count: 1,
992 dimension: wgpu::TextureDimension::D2,
993 format: wgpu::TextureFormat::Depth24PlusStencil8,
994 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
995 view_formats: &[],
996 });
997
998 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
999
1000 let depth_only_view = texture.create_view(&wgpu::TextureViewDescriptor {
1002 label: Some("depth only view"),
1003 aspect: wgpu::TextureAspect::DepthOnly,
1004 ..Default::default()
1005 });
1006
1007 (texture, view, depth_only_view)
1008 }
1009
1010 pub fn update_camera_uniforms(&self) {
1012 let uniforms = CameraUniforms::from_camera(&self.camera);
1013 self.queue
1014 .write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[uniforms]));
1015 }
1016
1017 pub fn update_slice_plane_uniforms(&self, planes: impl Iterator<Item = SlicePlaneUniforms>) {
1022 let mut uniforms = [SlicePlaneUniforms::default(); MAX_SLICE_PLANES];
1023 for (i, plane) in planes.take(MAX_SLICE_PLANES).enumerate() {
1024 uniforms[i] = plane;
1025 }
1026
1027 self.queue
1028 .write_buffer(&self.slice_plane_buffer, 0, bytemuck::cast_slice(&uniforms));
1029 }
1030
1031 pub fn camera_buffer(&self) -> &wgpu::Buffer {
1033 &self.camera_buffer
1034 }
1035
1036 pub fn shadow_map_pass(&self) -> Option<&ShadowMapPass> {
1038 self.shadow_map_pass.as_ref()
1039 }
1040
1041 pub fn depth_view(&self) -> &wgpu::TextureView {
1043 &self.depth_view
1044 }
1045
1046 pub fn hdr_texture_view(&self) -> Option<&wgpu::TextureView> {
1048 self.hdr_view.as_ref()
1049 }
1050
1051 #[must_use]
1053 pub fn dimensions(&self) -> (u32, u32) {
1054 (self.width, self.height)
1055 }
1056
1057 #[must_use]
1059 pub fn render_dimensions(&self) -> (u32, u32) {
1060 (
1061 self.width * self.ssaa_factor,
1062 self.height * self.ssaa_factor,
1063 )
1064 }
1065
1066 pub fn load_blendable_material(
1071 &mut self,
1072 name: &str,
1073 filenames: [&str; 4],
1074 ) -> std::result::Result<(), polyscope_core::PolyscopeError> {
1075 use polyscope_core::PolyscopeError;
1076
1077 if self.matcap_textures.contains_key(name) {
1078 return Err(PolyscopeError::MaterialExists(name.to_string()));
1079 }
1080
1081 let channel_labels = ["r", "g", "b", "k"];
1082 let mut views = Vec::with_capacity(4);
1083
1084 for (i, filename) in filenames.iter().enumerate() {
1085 let path = std::path::Path::new(filename);
1086 let (w, h, rgba) = materials::decode_matcap_image_from_file(path)
1087 .map_err(PolyscopeError::MaterialLoadError)?;
1088 let tex = materials::upload_matcap_texture(
1089 &self.device,
1090 &self.queue,
1091 &format!("matcap_{name}_{}", channel_labels[i]),
1092 w,
1093 h,
1094 &rgba,
1095 );
1096 views.push(tex.create_view(&wgpu::TextureViewDescriptor::default()));
1097 }
1098
1099 let sampler = materials::create_matcap_sampler(&self.device);
1100
1101 let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1102 label: Some(&format!("matcap_{name}_bind_group")),
1103 layout: &self.matcap_bind_group_layout,
1104 entries: &[
1105 wgpu::BindGroupEntry {
1106 binding: 0,
1107 resource: wgpu::BindingResource::TextureView(&views[0]),
1108 },
1109 wgpu::BindGroupEntry {
1110 binding: 1,
1111 resource: wgpu::BindingResource::TextureView(&views[1]),
1112 },
1113 wgpu::BindGroupEntry {
1114 binding: 2,
1115 resource: wgpu::BindingResource::TextureView(&views[2]),
1116 },
1117 wgpu::BindGroupEntry {
1118 binding: 3,
1119 resource: wgpu::BindingResource::TextureView(&views[3]),
1120 },
1121 wgpu::BindGroupEntry {
1122 binding: 4,
1123 resource: wgpu::BindingResource::Sampler(&sampler),
1124 },
1125 ],
1126 });
1127
1128 let mut drain = views.into_iter();
1130 let tex_r = drain.next().unwrap();
1131 let tex_g = drain.next().unwrap();
1132 let tex_b = drain.next().unwrap();
1133 let tex_k = drain.next().unwrap();
1134
1135 self.matcap_textures.insert(
1136 name.to_string(),
1137 MatcapTextureSet {
1138 tex_r,
1139 tex_g,
1140 tex_b,
1141 tex_k,
1142 sampler,
1143 bind_group,
1144 },
1145 );
1146
1147 self.materials
1148 .register(Material::blendable(name, 0.2, 0.7, 0.3, 32.0));
1149
1150 Ok(())
1151 }
1152
1153 pub fn load_static_material(
1158 &mut self,
1159 name: &str,
1160 filename: &str,
1161 ) -> std::result::Result<(), polyscope_core::PolyscopeError> {
1162 use polyscope_core::PolyscopeError;
1163
1164 if self.matcap_textures.contains_key(name) {
1165 return Err(PolyscopeError::MaterialExists(name.to_string()));
1166 }
1167
1168 let path = std::path::Path::new(filename);
1169 let (w, h, rgba) = materials::decode_matcap_image_from_file(path)
1170 .map_err(PolyscopeError::MaterialLoadError)?;
1171 let tex = materials::upload_matcap_texture(
1172 &self.device,
1173 &self.queue,
1174 &format!("matcap_{name}"),
1175 w,
1176 h,
1177 &rgba,
1178 );
1179
1180 let view_r = tex.create_view(&wgpu::TextureViewDescriptor::default());
1181 let view_g = tex.create_view(&wgpu::TextureViewDescriptor::default());
1182 let view_b = tex.create_view(&wgpu::TextureViewDescriptor::default());
1183 let view_k = tex.create_view(&wgpu::TextureViewDescriptor::default());
1184
1185 let sampler = materials::create_matcap_sampler(&self.device);
1186
1187 let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1188 label: Some(&format!("matcap_{name}_bind_group")),
1189 layout: &self.matcap_bind_group_layout,
1190 entries: &[
1191 wgpu::BindGroupEntry {
1192 binding: 0,
1193 resource: wgpu::BindingResource::TextureView(&view_r),
1194 },
1195 wgpu::BindGroupEntry {
1196 binding: 1,
1197 resource: wgpu::BindingResource::TextureView(&view_g),
1198 },
1199 wgpu::BindGroupEntry {
1200 binding: 2,
1201 resource: wgpu::BindingResource::TextureView(&view_b),
1202 },
1203 wgpu::BindGroupEntry {
1204 binding: 3,
1205 resource: wgpu::BindingResource::TextureView(&view_k),
1206 },
1207 wgpu::BindGroupEntry {
1208 binding: 4,
1209 resource: wgpu::BindingResource::Sampler(&sampler),
1210 },
1211 ],
1212 });
1213
1214 self.matcap_textures.insert(
1215 name.to_string(),
1216 MatcapTextureSet {
1217 tex_r: tex.create_view(&wgpu::TextureViewDescriptor::default()),
1218 tex_g: tex.create_view(&wgpu::TextureViewDescriptor::default()),
1219 tex_b: tex.create_view(&wgpu::TextureViewDescriptor::default()),
1220 tex_k: tex.create_view(&wgpu::TextureViewDescriptor::default()),
1221 sampler,
1222 bind_group,
1223 },
1224 );
1225
1226 self.materials
1227 .register(Material::static_mat(name, 0.2, 0.7, 0.3, 32.0));
1228
1229 Ok(())
1230 }
1231}