1use super::RenderEngine;
2use crate::ground_plane::GroundPlaneRenderData;
3use crate::slice_plane_render::SlicePlaneRenderData;
4
5const DEFAULT_MATERIAL: &str = "clay";
6
7impl RenderEngine {
8 pub fn default_matcap_bind_group(&self) -> &wgpu::BindGroup {
10 &self.matcap_textures[DEFAULT_MATERIAL].bind_group
11 }
12
13 pub fn matcap_bind_group_for(&self, material_name: &str) -> &wgpu::BindGroup {
16 &self
17 .matcap_textures
18 .get(material_name)
19 .unwrap_or(&self.matcap_textures[DEFAULT_MATERIAL])
20 .bind_group
21 }
22 #[allow(clippy::too_many_arguments)]
36 pub fn render_ground_plane(
37 &mut self,
38 encoder: &mut wgpu::CommandEncoder,
39 surface_view: &wgpu::TextureView,
40 enabled: bool,
41 scene_center: [f32; 3],
42 scene_min_y: f32,
43 length_scale: f32,
44 height_override: Option<f32>,
45 shadow_darkness: f32,
46 shadow_mode: u32,
47 reflection_intensity: f32,
48 ) {
49 let is_orthographic =
51 self.camera.projection_mode == crate::camera::ProjectionMode::Orthographic;
52 if !enabled {
53 return;
54 }
55
56 let view = self.hdr_view.as_ref().unwrap_or(surface_view);
58
59 if self.ground_plane_render_data.is_none() {
61 if let Some(ref shadow_pass) = self.shadow_map_pass {
62 self.ground_plane_render_data = Some(GroundPlaneRenderData::new(
63 &self.device,
64 &self.ground_plane_bind_group_layout,
65 &self.camera_buffer,
66 shadow_pass.light_buffer(),
67 shadow_pass.depth_view(),
68 shadow_pass.comparison_sampler(),
69 ));
70 }
71 }
72
73 let camera_height = self.camera.position.y;
75
76 if let Some(render_data) = &self.ground_plane_render_data {
77 render_data.update(
78 &self.queue,
79 scene_center,
80 scene_min_y,
81 length_scale,
82 camera_height,
83 height_override,
84 shadow_darkness,
85 shadow_mode,
86 is_orthographic,
87 reflection_intensity,
88 );
89
90 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
91 label: Some("Ground Plane Pass"),
92 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
93 view,
94 resolve_target: None,
95 ops: wgpu::Operations {
96 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
98 },
99 depth_slice: None,
100 })],
101 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
102 view: &self.depth_view,
103 depth_ops: Some(wgpu::Operations {
104 load: wgpu::LoadOp::Load,
105 store: wgpu::StoreOp::Store,
106 }),
107 stencil_ops: None,
108 }),
109 ..Default::default()
110 });
111
112 render_pass.set_pipeline(&self.ground_plane_pipeline);
113 render_pass.set_bind_group(0, render_data.bind_group(), &[]);
114 render_pass.draw(0..12, 0..1);
116 }
117 }
118
119 pub fn render_slice_planes(
124 &mut self,
125 encoder: &mut wgpu::CommandEncoder,
126 planes: &[polyscope_core::slice_plane::SlicePlane],
127 length_scale: f32,
128 ) {
129 let Some(view) = &self.hdr_view else {
131 return;
132 };
133
134 while self.slice_plane_render_data.len() < planes.len() {
136 let data = SlicePlaneRenderData::new(
137 &self.device,
138 &self.slice_plane_vis_bind_group_layout,
139 &self.camera_buffer,
140 );
141 self.slice_plane_render_data.push(data);
142 }
143
144 for (i, plane) in planes.iter().enumerate() {
146 if !plane.is_enabled() || !plane.draw_plane() {
147 continue;
148 }
149
150 self.slice_plane_render_data[i].update(&self.queue, plane, length_scale);
152
153 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
155 label: Some("Slice Plane Visualization Pass"),
156 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
157 view,
158 resolve_target: None,
159 ops: wgpu::Operations {
160 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
162 },
163 depth_slice: None,
164 })],
165 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
166 view: &self.depth_view,
167 depth_ops: Some(wgpu::Operations {
168 load: wgpu::LoadOp::Load,
169 store: wgpu::StoreOp::Store,
170 }),
171 stencil_ops: None,
172 }),
173 ..Default::default()
174 });
175
176 render_pass.set_pipeline(&self.slice_plane_vis_pipeline);
177 self.slice_plane_render_data[i].draw(&mut render_pass);
178 }
179 }
180
181 pub fn render_slice_planes_with_clear(
187 &mut self,
188 encoder: &mut wgpu::CommandEncoder,
189 planes: &[polyscope_core::slice_plane::SlicePlane],
190 length_scale: f32,
191 clear_color: [f32; 3],
192 ) {
193 let Some(view) = &self.hdr_view else {
195 return;
196 };
197
198 while self.slice_plane_render_data.len() < planes.len() {
200 let data = SlicePlaneRenderData::new(
201 &self.device,
202 &self.slice_plane_vis_bind_group_layout,
203 &self.camera_buffer,
204 );
205 self.slice_plane_render_data.push(data);
206 }
207
208 let has_visible_planes = planes.iter().any(|p| p.is_enabled() && p.draw_plane());
210
211 {
213 let _clear_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
214 label: Some("Slice Plane Clear Pass"),
215 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
216 view,
217 resolve_target: None,
218 ops: wgpu::Operations {
219 load: wgpu::LoadOp::Clear(wgpu::Color {
220 r: f64::from(clear_color[0]),
221 g: f64::from(clear_color[1]),
222 b: f64::from(clear_color[2]),
223 a: 1.0,
224 }),
225 store: wgpu::StoreOp::Store,
226 },
227 depth_slice: None,
228 })],
229 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
230 view: &self.depth_view,
231 depth_ops: Some(wgpu::Operations {
232 load: wgpu::LoadOp::Clear(1.0),
233 store: wgpu::StoreOp::Store,
234 }),
235 stencil_ops: None,
236 }),
237 ..Default::default()
238 });
239 }
241
242 if !has_visible_planes {
244 return;
245 }
246
247 for (i, plane) in planes.iter().enumerate() {
249 if !plane.is_enabled() || !plane.draw_plane() {
250 continue;
251 }
252
253 self.slice_plane_render_data[i].update(&self.queue, plane, length_scale);
255
256 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
258 label: Some("Slice Plane Visualization Pass"),
259 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
260 view,
261 resolve_target: None,
262 ops: wgpu::Operations {
263 load: wgpu::LoadOp::Load,
264 store: wgpu::StoreOp::Store,
265 },
266 depth_slice: None,
267 })],
268 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
269 view: &self.depth_view,
270 depth_ops: Some(wgpu::Operations {
271 load: wgpu::LoadOp::Load,
272 store: wgpu::StoreOp::Store,
273 }),
274 stencil_ops: None,
275 }),
276 ..Default::default()
277 });
278
279 render_pass.set_pipeline(&self.slice_plane_vis_pipeline);
280 self.slice_plane_render_data[i].draw(&mut render_pass);
281 }
282 }
283
284 #[allow(clippy::too_many_arguments)]
289 pub fn render_stencil_pass(
290 &mut self,
291 encoder: &mut wgpu::CommandEncoder,
292 color_view: &wgpu::TextureView,
293 ground_height: f32,
294 scene_center: [f32; 3],
295 length_scale: f32,
296 ) {
297 let Some(pipeline) = &self.ground_stencil_pipeline else {
298 return;
299 };
300
301 if self.ground_plane_render_data.is_none() {
303 if let Some(ref shadow_pass) = self.shadow_map_pass {
304 self.ground_plane_render_data = Some(GroundPlaneRenderData::new(
305 &self.device,
306 &self.ground_plane_bind_group_layout,
307 &self.camera_buffer,
308 shadow_pass.light_buffer(),
309 shadow_pass.depth_view(),
310 shadow_pass.comparison_sampler(),
311 ));
312 }
313 }
314
315 let Some(render_data) = &self.ground_plane_render_data else {
316 return;
317 };
318
319 let is_orthographic =
321 self.camera.projection_mode == crate::camera::ProjectionMode::Orthographic;
322 let camera_height = self.camera.position.y;
323
324 render_data.update(
326 &self.queue,
327 scene_center,
328 scene_center[1] - length_scale * 0.5, length_scale,
330 camera_height,
331 Some(ground_height),
332 0.0, 0, is_orthographic,
335 0.0, );
337
338 let view = self.hdr_view.as_ref().unwrap_or(color_view);
339
340 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
341 label: Some("Stencil Pass"),
342 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
343 view,
344 resolve_target: None,
345 ops: wgpu::Operations {
346 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
348 },
349 depth_slice: None,
350 })],
351 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
352 view: &self.depth_view,
353 depth_ops: Some(wgpu::Operations {
354 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
356 }),
357 stencil_ops: Some(wgpu::Operations {
358 load: wgpu::LoadOp::Clear(0), store: wgpu::StoreOp::Store,
360 }),
361 }),
362 ..Default::default()
363 });
364
365 render_pass.set_pipeline(pipeline);
366 render_pass.set_bind_group(0, render_data.bind_group(), &[]);
367 render_pass.set_stencil_reference(1); render_pass.draw(0..12, 0..1); }
370
371 pub(crate) fn init_reflection_pass(&mut self) {
373 self.reflection_pass = Some(crate::reflection_pass::ReflectionPass::new(&self.device));
374 }
375
376 pub fn reflection_pass(&self) -> Option<&crate::reflection_pass::ReflectionPass> {
378 self.reflection_pass.as_ref()
379 }
380
381 pub fn update_reflection(
383 &self,
384 reflection_matrix: glam::Mat4,
385 intensity: f32,
386 ground_height: f32,
387 ) {
388 if let Some(reflection) = &self.reflection_pass {
389 reflection.update_uniforms(&self.queue, reflection_matrix, intensity, ground_height);
390 }
391 }
392
393 pub fn create_reflected_mesh_bind_group(
395 &self,
396 mesh_render_data: &crate::surface_mesh_render::SurfaceMeshRenderData,
397 ) -> Option<wgpu::BindGroup> {
398 let layout = self.reflected_mesh_bind_group_layout.as_ref()?;
399
400 Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
401 label: Some("Reflected Mesh Bind Group"),
402 layout,
403 entries: &[
404 wgpu::BindGroupEntry {
405 binding: 0,
406 resource: self.camera_buffer.as_entire_binding(),
407 },
408 wgpu::BindGroupEntry {
409 binding: 1,
410 resource: mesh_render_data.uniform_buffer().as_entire_binding(),
411 },
412 wgpu::BindGroupEntry {
413 binding: 2,
414 resource: mesh_render_data.position_buffer().as_entire_binding(),
415 },
416 wgpu::BindGroupEntry {
417 binding: 3,
418 resource: mesh_render_data.normal_buffer().as_entire_binding(),
419 },
420 wgpu::BindGroupEntry {
421 binding: 4,
422 resource: mesh_render_data.barycentric_buffer().as_entire_binding(),
423 },
424 wgpu::BindGroupEntry {
425 binding: 5,
426 resource: mesh_render_data.color_buffer().as_entire_binding(),
427 },
428 wgpu::BindGroupEntry {
429 binding: 6,
430 resource: mesh_render_data.edge_is_real_buffer().as_entire_binding(),
431 },
432 ],
433 }))
434 }
435
436 pub fn render_reflected_mesh(
440 &self,
441 render_pass: &mut wgpu::RenderPass,
442 mesh_bind_group: &wgpu::BindGroup,
443 vertex_count: u32,
444 material_name: &str,
445 ) {
446 let Some(pipeline) = &self.reflected_mesh_pipeline else {
447 return;
448 };
449 let Some(reflection) = &self.reflection_pass else {
450 return;
451 };
452
453 render_pass.set_pipeline(pipeline);
454 render_pass.set_bind_group(0, mesh_bind_group, &[]);
455 render_pass.set_bind_group(1, reflection.bind_group(), &[]);
456 render_pass.set_bind_group(2, self.matcap_bind_group_for(material_name), &[]);
457 render_pass.set_bind_group(3, &self.slice_plane_bind_group, &[]);
458 render_pass.set_stencil_reference(1); render_pass.draw(0..vertex_count, 0..1);
460 }
461
462 pub fn create_reflected_point_cloud_bind_group(
464 &self,
465 point_render_data: &crate::point_cloud_render::PointCloudRenderData,
466 ) -> Option<wgpu::BindGroup> {
467 let layout = self.reflected_point_cloud_bind_group_layout.as_ref()?;
468
469 Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
470 label: Some("Reflected Point Cloud Bind Group"),
471 layout,
472 entries: &[
473 wgpu::BindGroupEntry {
474 binding: 0,
475 resource: self.camera_buffer.as_entire_binding(),
476 },
477 wgpu::BindGroupEntry {
478 binding: 1,
479 resource: point_render_data.uniform_buffer.as_entire_binding(),
480 },
481 wgpu::BindGroupEntry {
482 binding: 2,
483 resource: point_render_data.position_buffer.as_entire_binding(),
484 },
485 wgpu::BindGroupEntry {
486 binding: 3,
487 resource: point_render_data.color_buffer.as_entire_binding(),
488 },
489 ],
490 }))
491 }
492
493 pub fn render_reflected_point_cloud(
495 &self,
496 render_pass: &mut wgpu::RenderPass,
497 point_bind_group: &wgpu::BindGroup,
498 point_count: u32,
499 material_name: &str,
500 ) {
501 let Some(pipeline) = &self.reflected_point_cloud_pipeline else {
502 return;
503 };
504 let Some(reflection) = &self.reflection_pass else {
505 return;
506 };
507
508 render_pass.set_pipeline(pipeline);
509 render_pass.set_bind_group(0, point_bind_group, &[]);
510 render_pass.set_bind_group(1, reflection.bind_group(), &[]);
511 render_pass.set_bind_group(2, self.matcap_bind_group_for(material_name), &[]);
512 render_pass.set_bind_group(3, &self.slice_plane_bind_group, &[]);
513 render_pass.set_stencil_reference(1);
514 render_pass.draw(0..6, 0..point_count);
516 }
517
518 pub fn create_reflected_curve_network_bind_group(
520 &self,
521 curve_render_data: &crate::curve_network_render::CurveNetworkRenderData,
522 ) -> Option<wgpu::BindGroup> {
523 let layout = self.reflected_curve_network_bind_group_layout.as_ref()?;
524
525 Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
526 label: Some("Reflected Curve Network Bind Group"),
527 layout,
528 entries: &[
529 wgpu::BindGroupEntry {
530 binding: 0,
531 resource: self.camera_buffer.as_entire_binding(),
532 },
533 wgpu::BindGroupEntry {
534 binding: 1,
535 resource: curve_render_data.uniform_buffer.as_entire_binding(),
536 },
537 wgpu::BindGroupEntry {
538 binding: 2,
539 resource: curve_render_data.edge_vertex_buffer.as_entire_binding(),
540 },
541 wgpu::BindGroupEntry {
542 binding: 3,
543 resource: curve_render_data.edge_color_buffer.as_entire_binding(),
544 },
545 ],
546 }))
547 }
548
549 pub fn render_reflected_curve_network(
551 &self,
552 render_pass: &mut wgpu::RenderPass,
553 curve_bind_group: &wgpu::BindGroup,
554 curve_render_data: &crate::curve_network_render::CurveNetworkRenderData,
555 material_name: &str,
556 ) {
557 let Some(pipeline) = &self.reflected_curve_network_pipeline else {
558 return;
559 };
560 let Some(reflection) = &self.reflection_pass else {
561 return;
562 };
563 let Some(tube_vertex_buffer) = &curve_render_data.generated_vertex_buffer else {
564 return;
565 };
566
567 let tube_vertex_count = curve_render_data.num_edges * 36;
569
570 render_pass.set_pipeline(pipeline);
571 render_pass.set_bind_group(0, curve_bind_group, &[]);
572 render_pass.set_bind_group(1, reflection.bind_group(), &[]);
573 render_pass.set_bind_group(2, self.matcap_bind_group_for(material_name), &[]);
574 render_pass.set_bind_group(3, &self.slice_plane_bind_group, &[]);
575 render_pass.set_vertex_buffer(0, tube_vertex_buffer.slice(..));
576 render_pass.set_stencil_reference(1);
577 render_pass.draw(0..tube_vertex_count, 0..1);
578 }
579}