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)]
28#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
29#[allow(clippy::pub_underscore_fields)]
30pub struct CameraUniforms {
31 pub view: [[f32; 4]; 4],
32 pub proj: [[f32; 4]; 4],
33 pub view_proj: [[f32; 4]; 4],
34 pub inv_proj: [[f32; 4]; 4],
35 pub camera_pos: [f32; 3],
36 pub _padding: f32,
37}
38
39impl Default for CameraUniforms {
40 fn default() -> Self {
41 Self {
42 view: glam::Mat4::IDENTITY.to_cols_array_2d(),
43 proj: glam::Mat4::IDENTITY.to_cols_array_2d(),
44 view_proj: glam::Mat4::IDENTITY.to_cols_array_2d(),
45 inv_proj: glam::Mat4::IDENTITY.to_cols_array_2d(),
46 camera_pos: [0.0, 0.0, 5.0],
47 _padding: 0.0,
48 }
49 }
50}
51
52pub struct RenderEngine {
54 pub instance: wgpu::Instance,
56 pub adapter: wgpu::Adapter,
58 pub device: wgpu::Device,
60 pub queue: wgpu::Queue,
62 pub surface: Option<wgpu::Surface<'static>>,
64 pub surface_config: wgpu::SurfaceConfiguration,
66 pub depth_texture: wgpu::Texture,
68 pub depth_view: wgpu::TextureView,
70 pub(crate) depth_only_view: wgpu::TextureView,
72 pub materials: MaterialRegistry,
74 pub color_maps: ColorMapRegistry,
76 pub matcap_bind_group_layout: wgpu::BindGroupLayout,
78 pub matcap_textures: HashMap<String, MatcapTextureSet>,
80 pub camera: Camera,
82 pub width: u32,
84 pub height: u32,
86 pub point_pipeline: Option<wgpu::RenderPipeline>,
88 pub point_bind_group_layout: Option<wgpu::BindGroupLayout>,
90 pub camera_buffer: wgpu::Buffer,
92 pub slice_plane_buffer: wgpu::Buffer,
94 pub slice_plane_bind_group_layout: wgpu::BindGroupLayout,
96 pub slice_plane_bind_group: wgpu::BindGroup,
98 pub vector_pipeline: Option<wgpu::RenderPipeline>,
100 pub vector_bind_group_layout: Option<wgpu::BindGroupLayout>,
102 pub mesh_pipeline: Option<wgpu::RenderPipeline>,
104 pub mesh_depth_normal_pipeline: Option<wgpu::RenderPipeline>,
106 pub(crate) mesh_bind_group_layout: Option<wgpu::BindGroupLayout>,
108 pub curve_network_edge_pipeline: Option<wgpu::RenderPipeline>,
110 pub(crate) curve_network_edge_bind_group_layout: Option<wgpu::BindGroupLayout>,
112 pub curve_network_tube_pipeline: Option<wgpu::RenderPipeline>,
114 pub curve_network_tube_compute_pipeline: Option<wgpu::ComputePipeline>,
116 pub(crate) curve_network_tube_bind_group_layout: Option<wgpu::BindGroupLayout>,
118 pub(crate) curve_network_tube_compute_bind_group_layout: Option<wgpu::BindGroupLayout>,
120 pub(crate) ground_plane_pipeline: wgpu::RenderPipeline,
122 pub(crate) ground_plane_bind_group_layout: wgpu::BindGroupLayout,
124 pub(crate) ground_plane_render_data: Option<GroundPlaneRenderData>,
126 pub(crate) slice_plane_vis_pipeline: wgpu::RenderPipeline,
128 pub(crate) slice_plane_vis_bind_group_layout: wgpu::BindGroupLayout,
130 pub(crate) slice_plane_render_data: Vec<SlicePlaneRenderData>,
132 pub(crate) screenshot_texture: Option<wgpu::Texture>,
134 pub(crate) screenshot_buffer: Option<wgpu::Buffer>,
136 pub(crate) screenshot_hdr_texture: Option<wgpu::Texture>,
138 pub(crate) screenshot_hdr_view: Option<wgpu::TextureView>,
140 pub(crate) hdr_texture: Option<wgpu::Texture>,
142 pub(crate) hdr_view: Option<wgpu::TextureView>,
144 pub(crate) normal_texture: Option<wgpu::Texture>,
146 pub(crate) normal_view: Option<wgpu::TextureView>,
148 pub(crate) ssao_noise_texture: Option<wgpu::Texture>,
150 pub(crate) ssao_noise_view: Option<wgpu::TextureView>,
152 pub(crate) ssao_pass: Option<crate::ssao_pass::SsaoPass>,
154 pub(crate) ssao_output_texture: Option<wgpu::Texture>,
156 pub(crate) ssao_output_view: Option<wgpu::TextureView>,
158 pub(crate) depth_peel_pass: Option<crate::depth_peel_pass::DepthPeelPass>,
160 pub(crate) tone_map_pass: Option<ToneMapPass>,
162 pub(crate) ssaa_pass: Option<crate::ssaa_pass::SsaaPass>,
164 pub(crate) ssaa_factor: u32,
166 pub(crate) ssaa_intermediate_texture: Option<wgpu::Texture>,
168 pub(crate) ssaa_intermediate_view: Option<wgpu::TextureView>,
170 pub(crate) shadow_map_pass: Option<ShadowMapPass>,
172 pub(crate) shadow_pipeline: Option<wgpu::RenderPipeline>,
174 pub(crate) shadow_bind_group_layout: Option<wgpu::BindGroupLayout>,
176 pub(crate) reflection_pass: Option<crate::reflection_pass::ReflectionPass>,
178 pub(crate) ground_stencil_pipeline: Option<wgpu::RenderPipeline>,
180 pub(crate) reflected_mesh_pipeline: Option<wgpu::RenderPipeline>,
182 pub(crate) reflected_mesh_bind_group_layout: Option<wgpu::BindGroupLayout>,
184 pub(crate) reflected_point_cloud_pipeline: Option<wgpu::RenderPipeline>,
186 pub(crate) reflected_point_cloud_bind_group_layout: Option<wgpu::BindGroupLayout>,
188 pub(crate) reflected_curve_network_pipeline: Option<wgpu::RenderPipeline>,
190 pub(crate) reflected_curve_network_bind_group_layout: Option<wgpu::BindGroupLayout>,
192 pub simple_mesh_pipeline: Option<wgpu::RenderPipeline>,
194 pub(crate) simple_mesh_bind_group_layout: Option<wgpu::BindGroupLayout>,
196 pub gridcube_pipeline: Option<wgpu::RenderPipeline>,
198 pub(crate) gridcube_bind_group_layout: Option<wgpu::BindGroupLayout>,
200
201 pub(crate) pick_ranges: HashMap<(String, String), pick::PickRange>,
204 pub(crate) next_global_index: u32,
206
207 pub(crate) pick_texture: Option<wgpu::Texture>,
210 pub(crate) pick_texture_view: Option<wgpu::TextureView>,
212 pub(crate) pick_depth_texture: Option<wgpu::Texture>,
214 pub(crate) pick_depth_view: Option<wgpu::TextureView>,
216 pub(crate) pick_staging_buffer: Option<wgpu::Buffer>,
218 pub(crate) pick_buffer_size: (u32, u32),
220 pub(crate) point_pick_pipeline: Option<wgpu::RenderPipeline>,
222 pub(crate) curve_network_pick_pipeline: Option<wgpu::RenderPipeline>,
224 pub(crate) curve_network_tube_pick_pipeline: Option<wgpu::RenderPipeline>,
226 pub(crate) curve_network_tube_pick_bind_group_layout: Option<wgpu::BindGroupLayout>,
228 pub(crate) pick_bind_group_layout: Option<wgpu::BindGroupLayout>,
230 pub(crate) mesh_pick_pipeline: Option<wgpu::RenderPipeline>,
232 pub(crate) mesh_pick_bind_group_layout: Option<wgpu::BindGroupLayout>,
234 pub(crate) gridcube_pick_pipeline: Option<wgpu::RenderPipeline>,
236 pub(crate) gridcube_pick_bind_group_layout: Option<wgpu::BindGroupLayout>,
238}
239
240impl RenderEngine {
241 pub async fn new_windowed(window: Arc<winit::window::Window>) -> RenderResult<Self> {
243 let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
244 backends: wgpu::Backends::all(),
245 ..wgpu::InstanceDescriptor::default()
246 });
247
248 let surface = instance.create_surface(window.clone())?;
249
250 let adapter = instance
251 .request_adapter(&wgpu::RequestAdapterOptions {
252 power_preference: wgpu::PowerPreference::HighPerformance,
253 compatible_surface: Some(&surface),
254 force_fallback_adapter: false,
255 })
256 .await
257 .map_err(|_| RenderError::AdapterCreationFailed)?;
258
259 let (device, queue) = adapter
260 .request_device(&wgpu::DeviceDescriptor {
261 label: Some("polyscope device"),
262 required_features: wgpu::Features::empty(),
263 required_limits: wgpu::Limits::default(),
264 memory_hints: wgpu::MemoryHints::default(),
265 trace: wgpu::Trace::default(),
266 experimental_features: wgpu::ExperimentalFeatures::default(),
267 })
268 .await?;
269
270 let size = window.inner_size();
271 let width = size.width.max(1);
272 let height = size.height.max(1);
273
274 let surface_caps = surface.get_capabilities(&adapter);
275 let surface_format = surface_caps
276 .formats
277 .iter()
278 .find(|f| f.is_srgb())
279 .copied()
280 .unwrap_or(surface_caps.formats[0]);
281
282 let surface_config = wgpu::SurfaceConfiguration {
283 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
284 format: surface_format,
285 width,
286 height,
287 present_mode: wgpu::PresentMode::AutoVsync,
288 alpha_mode: surface_caps.alpha_modes[0],
289 view_formats: vec![],
290 desired_maximum_frame_latency: 2,
291 };
292 surface.configure(&device, &surface_config);
293
294 let (depth_texture, depth_view, depth_only_view) =
295 Self::create_depth_texture(&device, width, height);
296
297 let camera = Camera::new(width as f32 / height as f32);
298
299 let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
300 label: Some("camera uniforms"),
301 contents: bytemuck::cast_slice(&[CameraUniforms::default()]),
302 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
303 });
304
305 let slice_planes_data = [SlicePlaneUniforms::default(); MAX_SLICE_PLANES];
307 let slice_plane_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
308 label: Some("Slice Plane Buffer"),
309 contents: bytemuck::cast_slice(&slice_planes_data),
310 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
311 });
312
313 let slice_plane_bind_group_layout =
314 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
315 label: Some("Slice Plane Bind Group Layout"),
316 entries: &[wgpu::BindGroupLayoutEntry {
317 binding: 0,
318 visibility: wgpu::ShaderStages::FRAGMENT,
319 ty: wgpu::BindingType::Buffer {
320 ty: wgpu::BufferBindingType::Uniform,
321 has_dynamic_offset: false,
322 min_binding_size: NonZeroU64::new(128),
323 },
324 count: None,
325 }],
326 });
327
328 let slice_plane_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
329 label: Some("Slice Plane Bind Group"),
330 layout: &slice_plane_bind_group_layout,
331 entries: &[wgpu::BindGroupEntry {
332 binding: 0,
333 resource: slice_plane_buffer.as_entire_binding(),
334 }],
335 });
336
337 let shadow_map_pass = ShadowMapPass::new(&device);
339
340 let ground_plane_bind_group_layout =
342 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
343 label: Some("Ground Plane Bind Group Layout"),
344 entries: &[
345 wgpu::BindGroupLayoutEntry {
347 binding: 0,
348 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
349 ty: wgpu::BindingType::Buffer {
350 ty: wgpu::BufferBindingType::Uniform,
351 has_dynamic_offset: false,
352 min_binding_size: NonZeroU64::new(272),
353 },
354 count: None,
355 },
356 wgpu::BindGroupLayoutEntry {
358 binding: 1,
359 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
360 ty: wgpu::BindingType::Buffer {
361 ty: wgpu::BufferBindingType::Uniform,
362 has_dynamic_offset: false,
363 min_binding_size: NonZeroU64::new(96),
364 },
365 count: None,
366 },
367 wgpu::BindGroupLayoutEntry {
369 binding: 2,
370 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
371 ty: wgpu::BindingType::Buffer {
372 ty: wgpu::BufferBindingType::Uniform,
373 has_dynamic_offset: false,
374 min_binding_size: NonZeroU64::new(80),
375 },
376 count: None,
377 },
378 wgpu::BindGroupLayoutEntry {
380 binding: 3,
381 visibility: wgpu::ShaderStages::FRAGMENT,
382 ty: wgpu::BindingType::Texture {
383 sample_type: wgpu::TextureSampleType::Depth,
384 view_dimension: wgpu::TextureViewDimension::D2,
385 multisampled: false,
386 },
387 count: None,
388 },
389 wgpu::BindGroupLayoutEntry {
391 binding: 4,
392 visibility: wgpu::ShaderStages::FRAGMENT,
393 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
394 count: None,
395 },
396 ],
397 });
398
399 let ground_plane_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
401 label: Some("Ground Plane Shader"),
402 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/ground_plane.wgsl").into()),
403 });
404
405 let ground_plane_pipeline_layout =
407 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
408 label: Some("Ground Plane Pipeline Layout"),
409 bind_group_layouts: &[&ground_plane_bind_group_layout],
410 push_constant_ranges: &[],
411 });
412
413 let ground_plane_pipeline =
415 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
416 label: Some("Ground Plane Pipeline"),
417 layout: Some(&ground_plane_pipeline_layout),
418 vertex: wgpu::VertexState {
419 module: &ground_plane_shader,
420 entry_point: Some("vs_main"),
421 buffers: &[],
422 compilation_options: wgpu::PipelineCompilationOptions::default(),
423 },
424 fragment: Some(wgpu::FragmentState {
425 module: &ground_plane_shader,
426 entry_point: Some("fs_main"),
427 targets: &[Some(wgpu::ColorTargetState {
428 format: wgpu::TextureFormat::Rgba16Float, blend: Some(wgpu::BlendState::ALPHA_BLENDING),
430 write_mask: wgpu::ColorWrites::ALL,
431 })],
432 compilation_options: wgpu::PipelineCompilationOptions::default(),
433 }),
434 primitive: wgpu::PrimitiveState {
435 topology: wgpu::PrimitiveTopology::TriangleList,
436 ..wgpu::PrimitiveState::default()
437 },
438 depth_stencil: Some(wgpu::DepthStencilState {
439 format: wgpu::TextureFormat::Depth24PlusStencil8,
440 depth_write_enabled: true,
441 depth_compare: wgpu::CompareFunction::LessEqual,
442 stencil: wgpu::StencilState::default(),
443 bias: wgpu::DepthBiasState::default(),
444 }),
445 multisample: wgpu::MultisampleState::default(),
446 multiview: None,
447 cache: None,
448 });
449
450 let slice_plane_vis_bind_group_layout =
452 crate::slice_plane_render::create_slice_plane_bind_group_layout(&device);
453 let slice_plane_vis_pipeline = crate::slice_plane_render::create_slice_plane_pipeline(
454 &device,
455 &slice_plane_vis_bind_group_layout,
456 wgpu::TextureFormat::Rgba16Float,
457 wgpu::TextureFormat::Depth24PlusStencil8,
458 );
459
460 let matcap_bind_group_layout = materials::create_matcap_bind_group_layout(&device);
462 let matcap_textures =
463 materials::init_matcap_textures(&device, &queue, &matcap_bind_group_layout);
464
465 let mut engine = Self {
466 instance,
467 adapter,
468 device,
469 queue,
470 surface: Some(surface),
471 surface_config,
472 depth_texture,
473 depth_view,
474 depth_only_view,
475 materials: MaterialRegistry::new(),
476 color_maps: ColorMapRegistry::new(),
477 matcap_bind_group_layout,
478 matcap_textures,
479 camera,
480 width,
481 height,
482 point_pipeline: None,
483 point_bind_group_layout: None,
484 camera_buffer,
485 slice_plane_buffer,
486 slice_plane_bind_group_layout,
487 slice_plane_bind_group,
488 vector_pipeline: None,
489 vector_bind_group_layout: None,
490 mesh_pipeline: None,
491
492 mesh_depth_normal_pipeline: None,
493 mesh_bind_group_layout: None,
494 curve_network_edge_pipeline: None,
495 curve_network_edge_bind_group_layout: None,
496 curve_network_tube_pipeline: None,
497 curve_network_tube_compute_pipeline: None,
498 curve_network_tube_bind_group_layout: None,
499 curve_network_tube_compute_bind_group_layout: None,
500 ground_plane_pipeline,
501 ground_plane_bind_group_layout,
502 ground_plane_render_data: None,
503 slice_plane_vis_pipeline,
504 slice_plane_vis_bind_group_layout,
505 slice_plane_render_data: Vec::new(),
506 screenshot_texture: None,
507 screenshot_buffer: None,
508 screenshot_hdr_texture: None,
509 screenshot_hdr_view: None,
510 hdr_texture: None,
511 hdr_view: None,
512 normal_texture: None,
513 normal_view: None,
514 ssao_noise_texture: None,
515 ssao_noise_view: None,
516 ssao_pass: None,
517 ssao_output_texture: None,
518 ssao_output_view: None,
519 depth_peel_pass: None,
520 tone_map_pass: None,
521 ssaa_pass: None,
522 ssaa_factor: 1,
523 ssaa_intermediate_texture: None,
524 ssaa_intermediate_view: None,
525 shadow_map_pass: Some(shadow_map_pass),
526 shadow_pipeline: None,
527 shadow_bind_group_layout: None,
528 reflection_pass: None,
529 ground_stencil_pipeline: None,
530 reflected_mesh_pipeline: None,
531 reflected_mesh_bind_group_layout: None,
532 reflected_point_cloud_pipeline: None,
533 reflected_point_cloud_bind_group_layout: None,
534 reflected_curve_network_pipeline: None,
535 reflected_curve_network_bind_group_layout: None,
536 simple_mesh_pipeline: None,
537 simple_mesh_bind_group_layout: None,
538 gridcube_pipeline: None,
539 gridcube_bind_group_layout: None,
540 pick_ranges: HashMap::new(),
541 next_global_index: 1, pick_texture: None,
543 pick_texture_view: None,
544 pick_depth_texture: None,
545 pick_depth_view: None,
546 pick_staging_buffer: None,
547 pick_buffer_size: (0, 0),
548 point_pick_pipeline: None,
549 curve_network_pick_pipeline: None,
550 curve_network_tube_pick_pipeline: None,
551 curve_network_tube_pick_bind_group_layout: None,
552 pick_bind_group_layout: None,
553 mesh_pick_pipeline: None,
554 mesh_pick_bind_group_layout: None,
555 gridcube_pick_pipeline: None,
556 gridcube_pick_bind_group_layout: None,
557 };
558
559 engine.init_point_pipeline();
560 engine.init_vector_pipeline();
561 engine.create_mesh_pipeline();
562 engine.create_curve_network_edge_pipeline();
563 engine.create_curve_network_tube_pipelines();
564 engine.create_simple_mesh_pipeline();
565 engine.create_gridcube_pipeline();
566 engine.create_shadow_pipeline();
567 engine.init_tone_mapping();
568 engine.init_ssaa_pass();
569 engine.init_reflection_pass();
570 engine.create_ground_stencil_pipeline();
571 engine.create_reflected_mesh_pipeline();
572 engine.create_reflected_point_cloud_pipeline();
573 engine.create_reflected_curve_network_pipeline();
574 engine.init_pick_pipeline();
575 engine.init_mesh_pick_pipeline();
576
577 Ok(engine)
578 }
579
580 pub async fn new_headless(width: u32, height: u32) -> RenderResult<Self> {
582 let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
583 backends: wgpu::Backends::all(),
584 ..wgpu::InstanceDescriptor::default()
585 });
586
587 let adapter = instance
588 .request_adapter(&wgpu::RequestAdapterOptions {
589 power_preference: wgpu::PowerPreference::HighPerformance,
590 compatible_surface: None,
591 force_fallback_adapter: false,
592 })
593 .await
594 .map_err(|_| RenderError::AdapterCreationFailed)?;
595
596 let (device, queue) = adapter
597 .request_device(&wgpu::DeviceDescriptor {
598 label: Some("polyscope device (headless)"),
599 required_features: wgpu::Features::empty(),
600 required_limits: wgpu::Limits::default(),
601 memory_hints: wgpu::MemoryHints::default(),
602 trace: wgpu::Trace::default(),
603 experimental_features: wgpu::ExperimentalFeatures::default(),
604 })
605 .await?;
606
607 let surface_config = wgpu::SurfaceConfiguration {
608 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
609 format: wgpu::TextureFormat::Rgba8UnormSrgb,
610 width,
611 height,
612 present_mode: wgpu::PresentMode::Fifo,
613 alpha_mode: wgpu::CompositeAlphaMode::Auto,
614 view_formats: vec![],
615 desired_maximum_frame_latency: 2,
616 };
617
618 let (depth_texture, depth_view, depth_only_view) =
619 Self::create_depth_texture(&device, width, height);
620
621 let camera = Camera::new(width as f32 / height as f32);
622
623 let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
624 label: Some("camera uniforms"),
625 contents: bytemuck::cast_slice(&[CameraUniforms::default()]),
626 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
627 });
628
629 let slice_planes_data = [SlicePlaneUniforms::default(); MAX_SLICE_PLANES];
631 let slice_plane_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
632 label: Some("Slice Plane Buffer"),
633 contents: bytemuck::cast_slice(&slice_planes_data),
634 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
635 });
636
637 let slice_plane_bind_group_layout =
638 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
639 label: Some("Slice Plane Bind Group Layout"),
640 entries: &[wgpu::BindGroupLayoutEntry {
641 binding: 0,
642 visibility: wgpu::ShaderStages::FRAGMENT,
643 ty: wgpu::BindingType::Buffer {
644 ty: wgpu::BufferBindingType::Uniform,
645 has_dynamic_offset: false,
646 min_binding_size: NonZeroU64::new(128),
647 },
648 count: None,
649 }],
650 });
651
652 let slice_plane_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
653 label: Some("Slice Plane Bind Group"),
654 layout: &slice_plane_bind_group_layout,
655 entries: &[wgpu::BindGroupEntry {
656 binding: 0,
657 resource: slice_plane_buffer.as_entire_binding(),
658 }],
659 });
660
661 let shadow_map_pass = ShadowMapPass::new(&device);
663
664 let ground_plane_bind_group_layout =
666 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
667 label: Some("Ground Plane Bind Group Layout"),
668 entries: &[
669 wgpu::BindGroupLayoutEntry {
671 binding: 0,
672 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
673 ty: wgpu::BindingType::Buffer {
674 ty: wgpu::BufferBindingType::Uniform,
675 has_dynamic_offset: false,
676 min_binding_size: NonZeroU64::new(272),
677 },
678 count: None,
679 },
680 wgpu::BindGroupLayoutEntry {
682 binding: 1,
683 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
684 ty: wgpu::BindingType::Buffer {
685 ty: wgpu::BufferBindingType::Uniform,
686 has_dynamic_offset: false,
687 min_binding_size: NonZeroU64::new(96),
688 },
689 count: None,
690 },
691 wgpu::BindGroupLayoutEntry {
693 binding: 2,
694 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
695 ty: wgpu::BindingType::Buffer {
696 ty: wgpu::BufferBindingType::Uniform,
697 has_dynamic_offset: false,
698 min_binding_size: NonZeroU64::new(80),
699 },
700 count: None,
701 },
702 wgpu::BindGroupLayoutEntry {
704 binding: 3,
705 visibility: wgpu::ShaderStages::FRAGMENT,
706 ty: wgpu::BindingType::Texture {
707 sample_type: wgpu::TextureSampleType::Depth,
708 view_dimension: wgpu::TextureViewDimension::D2,
709 multisampled: false,
710 },
711 count: None,
712 },
713 wgpu::BindGroupLayoutEntry {
715 binding: 4,
716 visibility: wgpu::ShaderStages::FRAGMENT,
717 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
718 count: None,
719 },
720 ],
721 });
722
723 let ground_plane_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
725 label: Some("Ground Plane Shader"),
726 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/ground_plane.wgsl").into()),
727 });
728
729 let ground_plane_pipeline_layout =
731 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
732 label: Some("Ground Plane Pipeline Layout"),
733 bind_group_layouts: &[&ground_plane_bind_group_layout],
734 push_constant_ranges: &[],
735 });
736
737 let ground_plane_pipeline =
739 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
740 label: Some("Ground Plane Pipeline"),
741 layout: Some(&ground_plane_pipeline_layout),
742 vertex: wgpu::VertexState {
743 module: &ground_plane_shader,
744 entry_point: Some("vs_main"),
745 buffers: &[],
746 compilation_options: wgpu::PipelineCompilationOptions::default(),
747 },
748 fragment: Some(wgpu::FragmentState {
749 module: &ground_plane_shader,
750 entry_point: Some("fs_main"),
751 targets: &[Some(wgpu::ColorTargetState {
752 format: wgpu::TextureFormat::Rgba16Float, blend: Some(wgpu::BlendState::ALPHA_BLENDING),
754 write_mask: wgpu::ColorWrites::ALL,
755 })],
756 compilation_options: wgpu::PipelineCompilationOptions::default(),
757 }),
758 primitive: wgpu::PrimitiveState {
759 topology: wgpu::PrimitiveTopology::TriangleList,
760 ..wgpu::PrimitiveState::default()
761 },
762 depth_stencil: Some(wgpu::DepthStencilState {
763 format: wgpu::TextureFormat::Depth24PlusStencil8,
764 depth_write_enabled: true,
765 depth_compare: wgpu::CompareFunction::LessEqual,
766 stencil: wgpu::StencilState::default(),
767 bias: wgpu::DepthBiasState::default(),
768 }),
769 multisample: wgpu::MultisampleState::default(),
770 multiview: None,
771 cache: None,
772 });
773
774 let slice_plane_vis_bind_group_layout =
776 crate::slice_plane_render::create_slice_plane_bind_group_layout(&device);
777 let slice_plane_vis_pipeline = crate::slice_plane_render::create_slice_plane_pipeline(
778 &device,
779 &slice_plane_vis_bind_group_layout,
780 wgpu::TextureFormat::Rgba16Float,
781 wgpu::TextureFormat::Depth24PlusStencil8,
782 );
783
784 let matcap_bind_group_layout = materials::create_matcap_bind_group_layout(&device);
786 let matcap_textures =
787 materials::init_matcap_textures(&device, &queue, &matcap_bind_group_layout);
788
789 let mut engine = Self {
790 instance,
791 adapter,
792 device,
793 queue,
794 surface: None,
795 surface_config,
796 depth_texture,
797 depth_view,
798 depth_only_view,
799 materials: MaterialRegistry::new(),
800 color_maps: ColorMapRegistry::new(),
801 matcap_bind_group_layout,
802 matcap_textures,
803 camera,
804 width,
805 height,
806 point_pipeline: None,
807 point_bind_group_layout: None,
808 camera_buffer,
809 slice_plane_buffer,
810 slice_plane_bind_group_layout,
811 slice_plane_bind_group,
812 vector_pipeline: None,
813 vector_bind_group_layout: None,
814 mesh_pipeline: None,
815
816 mesh_depth_normal_pipeline: None,
817 mesh_bind_group_layout: None,
818 curve_network_edge_pipeline: None,
819 curve_network_edge_bind_group_layout: None,
820 curve_network_tube_pipeline: None,
821 curve_network_tube_compute_pipeline: None,
822 curve_network_tube_bind_group_layout: None,
823 curve_network_tube_compute_bind_group_layout: None,
824 ground_plane_pipeline,
825 ground_plane_bind_group_layout,
826 ground_plane_render_data: None,
827 slice_plane_vis_pipeline,
828 slice_plane_vis_bind_group_layout,
829 slice_plane_render_data: Vec::new(),
830 screenshot_texture: None,
831 screenshot_buffer: None,
832 screenshot_hdr_texture: None,
833 screenshot_hdr_view: None,
834 hdr_texture: None,
835 hdr_view: None,
836 normal_texture: None,
837 normal_view: None,
838 ssao_noise_texture: None,
839 ssao_noise_view: None,
840 ssao_pass: None,
841 ssao_output_texture: None,
842 ssao_output_view: None,
843 depth_peel_pass: None,
844 tone_map_pass: None,
845 ssaa_pass: None,
846 ssaa_factor: 1,
847 ssaa_intermediate_texture: None,
848 ssaa_intermediate_view: None,
849 shadow_map_pass: Some(shadow_map_pass),
850 shadow_pipeline: None,
851 shadow_bind_group_layout: None,
852 reflection_pass: None,
853 ground_stencil_pipeline: None,
854 reflected_mesh_pipeline: None,
855 reflected_mesh_bind_group_layout: None,
856 reflected_point_cloud_pipeline: None,
857 reflected_point_cloud_bind_group_layout: None,
858 reflected_curve_network_pipeline: None,
859 reflected_curve_network_bind_group_layout: None,
860 simple_mesh_pipeline: None,
861 simple_mesh_bind_group_layout: None,
862 gridcube_pipeline: None,
863 gridcube_bind_group_layout: None,
864 pick_ranges: HashMap::new(),
865 next_global_index: 1, pick_texture: None,
867 pick_texture_view: None,
868 pick_depth_texture: None,
869 pick_depth_view: None,
870 pick_staging_buffer: None,
871 pick_buffer_size: (0, 0),
872 point_pick_pipeline: None,
873 curve_network_pick_pipeline: None,
874 curve_network_tube_pick_pipeline: None,
875 curve_network_tube_pick_bind_group_layout: None,
876 pick_bind_group_layout: None,
877 mesh_pick_pipeline: None,
878 mesh_pick_bind_group_layout: None,
879 gridcube_pick_pipeline: None,
880 gridcube_pick_bind_group_layout: None,
881 };
882
883 engine.init_point_pipeline();
884 engine.init_vector_pipeline();
885 engine.create_mesh_pipeline();
886 engine.create_curve_network_edge_pipeline();
887 engine.create_curve_network_tube_pipelines();
888 engine.create_simple_mesh_pipeline();
889 engine.create_gridcube_pipeline();
890 engine.create_shadow_pipeline();
891 engine.init_tone_mapping();
892 engine.init_ssaa_pass();
893 engine.init_reflection_pass();
894 engine.create_ground_stencil_pipeline();
895 engine.create_reflected_mesh_pipeline();
896 engine.create_reflected_point_cloud_pipeline();
897 engine.create_reflected_curve_network_pipeline();
898 engine.init_pick_pipeline();
899 engine.init_mesh_pick_pipeline();
900
901 Ok(engine)
902 }
903
904 pub fn resize(&mut self, width: u32, height: u32) {
906 if width == 0 || height == 0 {
907 return;
908 }
909
910 self.width = width;
911 self.height = height;
912 self.surface_config.width = width;
913 self.surface_config.height = height;
914
915 if let Some(ref surface) = self.surface {
916 surface.configure(&self.device, &self.surface_config);
917 }
918
919 let ssaa_width = width * self.ssaa_factor;
921 let ssaa_height = height * self.ssaa_factor;
922
923 let (depth_texture, depth_view, depth_only_view) =
924 Self::create_depth_texture(&self.device, ssaa_width, ssaa_height);
925 self.depth_texture = depth_texture;
926 self.depth_view = depth_view;
927 self.depth_only_view = depth_only_view;
928
929 self.create_hdr_texture_with_size(ssaa_width, ssaa_height);
931
932 self.create_normal_texture_with_size(ssaa_width, ssaa_height);
934
935 if let Some(ref mut ssao_pass) = self.ssao_pass {
937 ssao_pass.resize(&self.device, &self.queue, ssaa_width, ssaa_height);
938 }
939 self.create_ssao_output_texture_with_size(ssaa_width, ssaa_height);
940
941 if self.ssaa_factor > 1 {
943 self.create_ssaa_intermediate_texture();
944 }
945
946 self.camera.set_aspect_ratio(width as f32 / height as f32);
947 }
948
949 fn create_depth_texture(
950 device: &wgpu::Device,
951 width: u32,
952 height: u32,
953 ) -> (wgpu::Texture, wgpu::TextureView, wgpu::TextureView) {
954 let texture = device.create_texture(&wgpu::TextureDescriptor {
955 label: Some("depth texture"),
956 size: wgpu::Extent3d {
957 width,
958 height,
959 depth_or_array_layers: 1,
960 },
961 mip_level_count: 1,
962 sample_count: 1,
963 dimension: wgpu::TextureDimension::D2,
964 format: wgpu::TextureFormat::Depth24PlusStencil8,
965 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
966 view_formats: &[],
967 });
968
969 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
970
971 let depth_only_view = texture.create_view(&wgpu::TextureViewDescriptor {
973 label: Some("depth only view"),
974 aspect: wgpu::TextureAspect::DepthOnly,
975 ..Default::default()
976 });
977
978 (texture, view, depth_only_view)
979 }
980
981 pub fn update_camera_uniforms(&self) {
983 let view = self.camera.view_matrix();
984 let proj = self.camera.projection_matrix();
985 let view_proj = proj * view;
986 let inv_proj = proj.inverse();
987
988 let uniforms = CameraUniforms {
989 view: view.to_cols_array_2d(),
990 proj: proj.to_cols_array_2d(),
991 view_proj: view_proj.to_cols_array_2d(),
992 inv_proj: inv_proj.to_cols_array_2d(),
993 camera_pos: self.camera.position.to_array(),
994 _padding: 0.0,
995 };
996
997 self.queue
998 .write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[uniforms]));
999 }
1000
1001 pub fn update_slice_plane_uniforms(&self, planes: impl Iterator<Item = SlicePlaneUniforms>) {
1006 let mut uniforms = [SlicePlaneUniforms::default(); MAX_SLICE_PLANES];
1007 for (i, plane) in planes.take(MAX_SLICE_PLANES).enumerate() {
1008 uniforms[i] = plane;
1009 }
1010
1011 self.queue
1012 .write_buffer(&self.slice_plane_buffer, 0, bytemuck::cast_slice(&uniforms));
1013 }
1014
1015 pub fn camera_buffer(&self) -> &wgpu::Buffer {
1017 &self.camera_buffer
1018 }
1019
1020 pub fn shadow_map_pass(&self) -> Option<&ShadowMapPass> {
1022 self.shadow_map_pass.as_ref()
1023 }
1024
1025 pub fn depth_view(&self) -> &wgpu::TextureView {
1027 &self.depth_view
1028 }
1029
1030 pub fn hdr_texture_view(&self) -> Option<&wgpu::TextureView> {
1032 self.hdr_view.as_ref()
1033 }
1034
1035 #[must_use]
1037 pub fn dimensions(&self) -> (u32, u32) {
1038 (self.width, self.height)
1039 }
1040
1041 #[must_use]
1043 pub fn render_dimensions(&self) -> (u32, u32) {
1044 (
1045 self.width * self.ssaa_factor,
1046 self.height * self.ssaa_factor,
1047 )
1048 }
1049
1050 pub fn load_blendable_material(
1055 &mut self,
1056 name: &str,
1057 filenames: [&str; 4],
1058 ) -> std::result::Result<(), polyscope_core::PolyscopeError> {
1059 use polyscope_core::PolyscopeError;
1060
1061 if self.matcap_textures.contains_key(name) {
1062 return Err(PolyscopeError::MaterialExists(name.to_string()));
1063 }
1064
1065 let channel_labels = ["r", "g", "b", "k"];
1066 let mut views = Vec::with_capacity(4);
1067
1068 for (i, filename) in filenames.iter().enumerate() {
1069 let path = std::path::Path::new(filename);
1070 let (w, h, rgba) = materials::decode_matcap_image_from_file(path)
1071 .map_err(PolyscopeError::MaterialLoadError)?;
1072 let tex = materials::upload_matcap_texture(
1073 &self.device,
1074 &self.queue,
1075 &format!("matcap_{name}_{}", channel_labels[i]),
1076 w,
1077 h,
1078 &rgba,
1079 );
1080 views.push(tex.create_view(&wgpu::TextureViewDescriptor::default()));
1081 }
1082
1083 let sampler = materials::create_matcap_sampler(&self.device);
1084
1085 let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1086 label: Some(&format!("matcap_{name}_bind_group")),
1087 layout: &self.matcap_bind_group_layout,
1088 entries: &[
1089 wgpu::BindGroupEntry {
1090 binding: 0,
1091 resource: wgpu::BindingResource::TextureView(&views[0]),
1092 },
1093 wgpu::BindGroupEntry {
1094 binding: 1,
1095 resource: wgpu::BindingResource::TextureView(&views[1]),
1096 },
1097 wgpu::BindGroupEntry {
1098 binding: 2,
1099 resource: wgpu::BindingResource::TextureView(&views[2]),
1100 },
1101 wgpu::BindGroupEntry {
1102 binding: 3,
1103 resource: wgpu::BindingResource::TextureView(&views[3]),
1104 },
1105 wgpu::BindGroupEntry {
1106 binding: 4,
1107 resource: wgpu::BindingResource::Sampler(&sampler),
1108 },
1109 ],
1110 });
1111
1112 let mut drain = views.into_iter();
1114 let tex_r = drain.next().unwrap();
1115 let tex_g = drain.next().unwrap();
1116 let tex_b = drain.next().unwrap();
1117 let tex_k = drain.next().unwrap();
1118
1119 self.matcap_textures.insert(
1120 name.to_string(),
1121 MatcapTextureSet {
1122 tex_r,
1123 tex_g,
1124 tex_b,
1125 tex_k,
1126 sampler,
1127 bind_group,
1128 },
1129 );
1130
1131 self.materials
1132 .register(Material::blendable(name, 0.2, 0.7, 0.3, 32.0));
1133
1134 Ok(())
1135 }
1136
1137 pub fn load_static_material(
1142 &mut self,
1143 name: &str,
1144 filename: &str,
1145 ) -> std::result::Result<(), polyscope_core::PolyscopeError> {
1146 use polyscope_core::PolyscopeError;
1147
1148 if self.matcap_textures.contains_key(name) {
1149 return Err(PolyscopeError::MaterialExists(name.to_string()));
1150 }
1151
1152 let path = std::path::Path::new(filename);
1153 let (w, h, rgba) = materials::decode_matcap_image_from_file(path)
1154 .map_err(PolyscopeError::MaterialLoadError)?;
1155 let tex = materials::upload_matcap_texture(
1156 &self.device,
1157 &self.queue,
1158 &format!("matcap_{name}"),
1159 w,
1160 h,
1161 &rgba,
1162 );
1163
1164 let view_r = tex.create_view(&wgpu::TextureViewDescriptor::default());
1165 let view_g = tex.create_view(&wgpu::TextureViewDescriptor::default());
1166 let view_b = tex.create_view(&wgpu::TextureViewDescriptor::default());
1167 let view_k = tex.create_view(&wgpu::TextureViewDescriptor::default());
1168
1169 let sampler = materials::create_matcap_sampler(&self.device);
1170
1171 let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1172 label: Some(&format!("matcap_{name}_bind_group")),
1173 layout: &self.matcap_bind_group_layout,
1174 entries: &[
1175 wgpu::BindGroupEntry {
1176 binding: 0,
1177 resource: wgpu::BindingResource::TextureView(&view_r),
1178 },
1179 wgpu::BindGroupEntry {
1180 binding: 1,
1181 resource: wgpu::BindingResource::TextureView(&view_g),
1182 },
1183 wgpu::BindGroupEntry {
1184 binding: 2,
1185 resource: wgpu::BindingResource::TextureView(&view_b),
1186 },
1187 wgpu::BindGroupEntry {
1188 binding: 3,
1189 resource: wgpu::BindingResource::TextureView(&view_k),
1190 },
1191 wgpu::BindGroupEntry {
1192 binding: 4,
1193 resource: wgpu::BindingResource::Sampler(&sampler),
1194 },
1195 ],
1196 });
1197
1198 self.matcap_textures.insert(
1199 name.to_string(),
1200 MatcapTextureSet {
1201 tex_r: tex.create_view(&wgpu::TextureViewDescriptor::default()),
1202 tex_g: tex.create_view(&wgpu::TextureViewDescriptor::default()),
1203 tex_b: tex.create_view(&wgpu::TextureViewDescriptor::default()),
1204 tex_k: tex.create_view(&wgpu::TextureViewDescriptor::default()),
1205 sampler,
1206 bind_group,
1207 },
1208 );
1209
1210 self.materials
1211 .register(Material::static_mat(name, 0.2, 0.7, 0.3, 32.0));
1212
1213 Ok(())
1214 }
1215}