1use crate::GpuContext;
4use bytemuck::{Pod, Zeroable};
5use nalgebra::{Matrix4, Point3, Vector3};
6use threecrate_core::{Error, Result};
7use threecrate_simplification::ProgressiveMesh;
8use wgpu::util::DeviceExt;
9use winit::window::Window;
10
11#[repr(C)]
13#[derive(Copy, Clone, Debug, Pod, Zeroable)]
14pub struct MeshVertex {
15 pub position: [f32; 3],
16 pub normal: [f32; 3],
17 pub tangent: [f32; 3],
18 pub bitangent: [f32; 3],
19 pub uv: [f32; 2],
20 pub color: [f32; 3],
21 pub _padding: f32,
22}
23
24impl MeshVertex {
25 pub fn new(position: [f32; 3], normal: [f32; 3], uv: [f32; 2], color: [f32; 3]) -> Self {
27 let tangent = if normal[0].abs() > 0.9 {
29 [0.0, 1.0, 0.0]
30 } else {
31 [1.0, 0.0, 0.0]
32 };
33
34 let bitangent = [
35 normal[1] * tangent[2] - normal[2] * tangent[1],
36 normal[2] * tangent[0] - normal[0] * tangent[2],
37 normal[0] * tangent[1] - normal[1] * tangent[0],
38 ];
39
40 Self {
41 position,
42 normal,
43 tangent,
44 bitangent,
45 uv,
46 color,
47 _padding: 0.0,
48 }
49 }
50
51 pub fn from_pos_normal(position: [f32; 3], normal: [f32; 3]) -> Self {
53 Self::new(position, normal, [0.0, 0.0], [0.8, 0.8, 0.8])
54 }
55
56 pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
58 wgpu::VertexBufferLayout {
59 array_stride: std::mem::size_of::<MeshVertex>() as wgpu::BufferAddress,
60 step_mode: wgpu::VertexStepMode::Vertex,
61 attributes: &[
62 wgpu::VertexAttribute {
64 offset: 0,
65 shader_location: 0,
66 format: wgpu::VertexFormat::Float32x3,
67 },
68 wgpu::VertexAttribute {
70 offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
71 shader_location: 1,
72 format: wgpu::VertexFormat::Float32x3,
73 },
74 wgpu::VertexAttribute {
76 offset: std::mem::size_of::<[f32; 6]>() as wgpu::BufferAddress,
77 shader_location: 2,
78 format: wgpu::VertexFormat::Float32x3,
79 },
80 wgpu::VertexAttribute {
82 offset: std::mem::size_of::<[f32; 9]>() as wgpu::BufferAddress,
83 shader_location: 3,
84 format: wgpu::VertexFormat::Float32x3,
85 },
86 wgpu::VertexAttribute {
88 offset: std::mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
89 shader_location: 4,
90 format: wgpu::VertexFormat::Float32x2,
91 },
92 wgpu::VertexAttribute {
94 offset: std::mem::size_of::<[f32; 14]>() as wgpu::BufferAddress,
95 shader_location: 5,
96 format: wgpu::VertexFormat::Float32x3,
97 },
98 ],
99 }
100 }
101}
102
103#[repr(C)]
105#[derive(Copy, Clone, Pod, Zeroable)]
106pub struct MeshCameraUniform {
107 pub view_proj: [[f32; 4]; 4],
108 pub view_pos: [f32; 3],
109 pub _padding: f32,
110}
111
112#[repr(C)]
114#[derive(Copy, Clone, Debug, Pod, Zeroable)]
115pub struct PbrMaterial {
116 pub albedo: [f32; 3], pub metallic: f32, pub roughness: f32, pub ao: f32, pub _padding1: [f32; 2], pub emission: [f32; 3], pub _padding2: f32, }
124
125impl Default for PbrMaterial {
126 fn default() -> Self {
127 Self {
128 albedo: [0.7, 0.7, 0.7],
129 metallic: 0.0,
130 roughness: 0.5,
131 ao: 1.0,
132 _padding1: [0.0, 0.0],
133 emission: [0.0, 0.0, 0.0],
134 _padding2: 0.0,
135 }
136 }
137}
138
139#[repr(C)]
141#[derive(Copy, Clone, Debug, Pod, Zeroable)]
142pub struct FlatMaterial {
143 pub color: [f32; 3],
144 pub _padding: f32,
145}
146
147impl Default for FlatMaterial {
148 fn default() -> Self {
149 Self {
150 color: [0.8, 0.8, 0.8],
151 _padding: 0.0,
152 }
153 }
154}
155
156#[repr(C)]
158#[derive(Copy, Clone, Debug, Pod, Zeroable)]
159pub struct MeshLightingParams {
160 pub light_position: [f32; 3],
161 pub light_intensity: f32,
162 pub light_color: [f32; 3],
163 pub ambient_strength: f32,
164 pub gamma: f32,
165 pub exposure: f32,
166 pub _padding: [f32; 2],
167}
168
169impl Default for MeshLightingParams {
170 fn default() -> Self {
171 Self {
172 light_position: [10.0, 10.0, 10.0],
173 light_intensity: 1.0,
174 light_color: [1.0, 1.0, 1.0],
175 ambient_strength: 0.03,
176 gamma: 2.2,
177 exposure: 1.0,
178 _padding: [0.0, 0.0],
179 }
180 }
181}
182
183#[derive(Debug, Clone)]
185pub struct MeshRenderConfig {
186 pub lighting_params: MeshLightingParams,
187 pub background_color: [f64; 4],
188 pub enable_depth_test: bool,
189 pub enable_backface_culling: bool,
190 pub enable_multisampling: bool,
191 pub wireframe_mode: bool,
192}
193
194impl Default for MeshRenderConfig {
195 fn default() -> Self {
196 Self {
197 lighting_params: MeshLightingParams::default(),
198 background_color: [0.1, 0.1, 0.1, 1.0],
199 enable_depth_test: true,
200 enable_backface_culling: true,
201 enable_multisampling: true,
202 wireframe_mode: false,
203 }
204 }
205}
206
207#[derive(Debug, Clone)]
209pub struct GpuMesh {
210 pub vertices: Vec<MeshVertex>,
211 pub indices: Vec<u32>,
212 pub material: PbrMaterial,
213}
214
215impl GpuMesh {
216 pub fn new(vertices: Vec<MeshVertex>, indices: Vec<u32>, material: PbrMaterial) -> Self {
218 Self {
219 vertices,
220 indices,
221 material,
222 }
223 }
224
225 pub fn triangle() -> Self {
227 let vertices = vec![
228 MeshVertex::new(
229 [-0.5, -0.5, 0.0],
230 [0.0, 0.0, 1.0],
231 [0.0, 0.0],
232 [1.0, 0.0, 0.0],
233 ),
234 MeshVertex::new(
235 [0.5, -0.5, 0.0],
236 [0.0, 0.0, 1.0],
237 [1.0, 0.0],
238 [0.0, 1.0, 0.0],
239 ),
240 MeshVertex::new(
241 [0.0, 0.5, 0.0],
242 [0.0, 0.0, 1.0],
243 [0.5, 1.0],
244 [0.0, 0.0, 1.0],
245 ),
246 ];
247
248 let indices = vec![0, 1, 2];
249
250 Self::new(vertices, indices, PbrMaterial::default())
251 }
252
253 pub fn cube() -> Self {
255 let vertices = vec![
256 MeshVertex::new(
258 [-1.0, -1.0, 1.0],
259 [0.0, 0.0, 1.0],
260 [0.0, 0.0],
261 [0.8, 0.2, 0.2],
262 ),
263 MeshVertex::new(
264 [1.0, -1.0, 1.0],
265 [0.0, 0.0, 1.0],
266 [1.0, 0.0],
267 [0.8, 0.2, 0.2],
268 ),
269 MeshVertex::new(
270 [1.0, 1.0, 1.0],
271 [0.0, 0.0, 1.0],
272 [1.0, 1.0],
273 [0.8, 0.2, 0.2],
274 ),
275 MeshVertex::new(
276 [-1.0, 1.0, 1.0],
277 [0.0, 0.0, 1.0],
278 [0.0, 1.0],
279 [0.8, 0.2, 0.2],
280 ),
281 MeshVertex::new(
283 [1.0, -1.0, -1.0],
284 [0.0, 0.0, -1.0],
285 [0.0, 0.0],
286 [0.2, 0.8, 0.2],
287 ),
288 MeshVertex::new(
289 [-1.0, -1.0, -1.0],
290 [0.0, 0.0, -1.0],
291 [1.0, 0.0],
292 [0.2, 0.8, 0.2],
293 ),
294 MeshVertex::new(
295 [-1.0, 1.0, -1.0],
296 [0.0, 0.0, -1.0],
297 [1.0, 1.0],
298 [0.2, 0.8, 0.2],
299 ),
300 MeshVertex::new(
301 [1.0, 1.0, -1.0],
302 [0.0, 0.0, -1.0],
303 [0.0, 1.0],
304 [0.2, 0.8, 0.2],
305 ),
306 MeshVertex::new(
308 [-1.0, 1.0, 1.0],
309 [0.0, 1.0, 0.0],
310 [0.0, 0.0],
311 [0.2, 0.2, 0.8],
312 ),
313 MeshVertex::new(
314 [1.0, 1.0, 1.0],
315 [0.0, 1.0, 0.0],
316 [1.0, 0.0],
317 [0.2, 0.2, 0.8],
318 ),
319 MeshVertex::new(
320 [1.0, 1.0, -1.0],
321 [0.0, 1.0, 0.0],
322 [1.0, 1.0],
323 [0.2, 0.2, 0.8],
324 ),
325 MeshVertex::new(
326 [-1.0, 1.0, -1.0],
327 [0.0, 1.0, 0.0],
328 [0.0, 1.0],
329 [0.2, 0.2, 0.8],
330 ),
331 MeshVertex::new(
333 [-1.0, -1.0, -1.0],
334 [0.0, -1.0, 0.0],
335 [0.0, 0.0],
336 [0.8, 0.8, 0.2],
337 ),
338 MeshVertex::new(
339 [1.0, -1.0, -1.0],
340 [0.0, -1.0, 0.0],
341 [1.0, 0.0],
342 [0.8, 0.8, 0.2],
343 ),
344 MeshVertex::new(
345 [1.0, -1.0, 1.0],
346 [0.0, -1.0, 0.0],
347 [1.0, 1.0],
348 [0.8, 0.8, 0.2],
349 ),
350 MeshVertex::new(
351 [-1.0, -1.0, 1.0],
352 [0.0, -1.0, 0.0],
353 [0.0, 1.0],
354 [0.8, 0.8, 0.2],
355 ),
356 MeshVertex::new(
358 [1.0, -1.0, 1.0],
359 [1.0, 0.0, 0.0],
360 [0.0, 0.0],
361 [0.8, 0.2, 0.8],
362 ),
363 MeshVertex::new(
364 [1.0, -1.0, -1.0],
365 [1.0, 0.0, 0.0],
366 [1.0, 0.0],
367 [0.8, 0.2, 0.8],
368 ),
369 MeshVertex::new(
370 [1.0, 1.0, -1.0],
371 [1.0, 0.0, 0.0],
372 [1.0, 1.0],
373 [0.8, 0.2, 0.8],
374 ),
375 MeshVertex::new(
376 [1.0, 1.0, 1.0],
377 [1.0, 0.0, 0.0],
378 [0.0, 1.0],
379 [0.8, 0.2, 0.8],
380 ),
381 MeshVertex::new(
383 [-1.0, -1.0, -1.0],
384 [-1.0, 0.0, 0.0],
385 [0.0, 0.0],
386 [0.2, 0.8, 0.8],
387 ),
388 MeshVertex::new(
389 [-1.0, -1.0, 1.0],
390 [-1.0, 0.0, 0.0],
391 [1.0, 0.0],
392 [0.2, 0.8, 0.8],
393 ),
394 MeshVertex::new(
395 [-1.0, 1.0, 1.0],
396 [-1.0, 0.0, 0.0],
397 [1.0, 1.0],
398 [0.2, 0.8, 0.8],
399 ),
400 MeshVertex::new(
401 [-1.0, 1.0, -1.0],
402 [-1.0, 0.0, 0.0],
403 [0.0, 1.0],
404 [0.2, 0.8, 0.8],
405 ),
406 ];
407
408 let indices = vec![
409 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 8, 9, 10, 10, 11, 8, 12, 13, 14, 14, 15, 12, 16, 17, 18, 18, 19, 16, 20, 21, 22, 22, 23, 20,
416 ];
417
418 Self::new(vertices, indices, PbrMaterial::default())
419 }
420
421 pub fn from_point_cloud(points: &[Point3<f32>], color: [f32; 3]) -> Self {
423 let vertices: Vec<MeshVertex> = points
424 .iter()
425 .map(|p| {
426 let normal = [0.0, 0.0, 1.0]; MeshVertex::new([p.x, p.y, p.z], normal, [0.0, 0.0], color)
429 })
430 .collect();
431
432 let indices: Vec<u32> = (0..vertices.len() as u32).collect();
434
435 Self::new(vertices, indices, PbrMaterial::default())
436 }
437}
438
439#[derive(Debug, Clone, Copy, PartialEq)]
441pub enum ShadingMode {
442 Flat,
443 Pbr,
444}
445
446pub struct MeshRenderer<'window> {
448 pub gpu_context: GpuContext,
449 pub surface: wgpu::Surface<'window>,
450 pub surface_config: wgpu::SurfaceConfiguration,
451 pub pbr_pipeline: wgpu::RenderPipeline,
452 pub flat_pipeline: wgpu::RenderPipeline,
453 pub screenshot_pbr_pipeline: wgpu::RenderPipeline,
455 pub screenshot_flat_pipeline: wgpu::RenderPipeline,
457 pub camera_uniform: MeshCameraUniform,
458 pub camera_buffer: wgpu::Buffer,
459 pub lighting_params: MeshLightingParams,
460 pub lighting_buffer: wgpu::Buffer,
461 pub bind_group_layout: wgpu::BindGroupLayout,
462 pub config: MeshRenderConfig,
463 pub msaa_texture: Option<wgpu::Texture>,
464 pub msaa_view: Option<wgpu::TextureView>,
465}
466
467impl<'window> MeshRenderer<'window> {
468 pub async fn new(window: &'window Window, config: MeshRenderConfig) -> Result<Self> {
470 let gpu_context = GpuContext::new().await?;
471
472 let surface = gpu_context
473 .instance
474 .create_surface(window)
475 .map_err(|e| Error::Gpu(format!("Failed to create surface: {:?}", e)))?;
476
477 let surface_caps = surface.get_capabilities(&gpu_context.adapter);
478 let surface_format = surface_caps
479 .formats
480 .iter()
481 .copied()
482 .find(|f| f.is_srgb())
483 .unwrap_or(surface_caps.formats[0]);
484
485 let size = window.inner_size();
486 let sample_count = if config.enable_multisampling { 4 } else { 1 };
487
488 let surface_config = wgpu::SurfaceConfiguration {
489 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
490 format: surface_format,
491 width: size.width,
492 height: size.height,
493 present_mode: surface_caps.present_modes[0],
494 alpha_mode: surface_caps.alpha_modes[0],
495 view_formats: vec![],
496 desired_maximum_frame_latency: 2,
497 };
498 surface.configure(&gpu_context.device, &surface_config);
499
500 let (msaa_texture, msaa_view) = if config.enable_multisampling {
502 let msaa_texture = gpu_context.device.create_texture(&wgpu::TextureDescriptor {
503 label: Some("MSAA Texture"),
504 size: wgpu::Extent3d {
505 width: size.width,
506 height: size.height,
507 depth_or_array_layers: 1,
508 },
509 mip_level_count: 1,
510 sample_count,
511 dimension: wgpu::TextureDimension::D2,
512 format: surface_format,
513 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
514 view_formats: &[],
515 });
516 let msaa_view = msaa_texture.create_view(&wgpu::TextureViewDescriptor::default());
517 (Some(msaa_texture), Some(msaa_view))
518 } else {
519 (None, None)
520 };
521
522 let camera_uniform = MeshCameraUniform {
524 view_proj: Matrix4::identity().into(),
525 view_pos: [0.0, 0.0, 0.0],
526 _padding: 0.0,
527 };
528
529 let camera_buffer =
530 gpu_context
531 .device
532 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
533 label: Some("Camera Buffer"),
534 contents: bytemuck::bytes_of(&camera_uniform),
535 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
536 });
537
538 let lighting_params = config.lighting_params;
540 let lighting_buffer =
541 gpu_context
542 .device
543 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
544 label: Some("Lighting Buffer"),
545 contents: bytemuck::bytes_of(&lighting_params),
546 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
547 });
548
549 let bind_group_layout =
551 gpu_context
552 .device
553 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
554 entries: &[
555 wgpu::BindGroupLayoutEntry {
556 binding: 0,
557 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
558 ty: wgpu::BindingType::Buffer {
559 ty: wgpu::BufferBindingType::Uniform,
560 has_dynamic_offset: false,
561 min_binding_size: None,
562 },
563 count: None,
564 },
565 wgpu::BindGroupLayoutEntry {
566 binding: 1,
567 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
568 ty: wgpu::BindingType::Buffer {
569 ty: wgpu::BufferBindingType::Uniform,
570 has_dynamic_offset: false,
571 min_binding_size: None,
572 },
573 count: None,
574 },
575 wgpu::BindGroupLayoutEntry {
576 binding: 2,
577 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
578 ty: wgpu::BindingType::Buffer {
579 ty: wgpu::BufferBindingType::Uniform,
580 has_dynamic_offset: false,
581 min_binding_size: None,
582 },
583 count: None,
584 },
585 ],
586 label: Some("mesh_bind_group_layout"),
587 });
588
589 let pbr_shader = gpu_context
591 .device
592 .create_shader_module(wgpu::ShaderModuleDescriptor {
593 label: Some("PBR Mesh Shader"),
594 source: wgpu::ShaderSource::Wgsl(include_str!("shaders/mesh_pbr.wgsl").into()),
595 });
596
597 let pbr_pipeline = Self::create_render_pipeline(
598 &gpu_context.device,
599 &bind_group_layout,
600 &pbr_shader,
601 surface_format,
602 sample_count,
603 &config,
604 "PBR",
605 );
606
607 let flat_shader = gpu_context
609 .device
610 .create_shader_module(wgpu::ShaderModuleDescriptor {
611 label: Some("Flat Mesh Shader"),
612 source: wgpu::ShaderSource::Wgsl(include_str!("shaders/mesh_flat.wgsl").into()),
613 });
614
615 let flat_pipeline = Self::create_render_pipeline(
616 &gpu_context.device,
617 &bind_group_layout,
618 &flat_shader,
619 surface_format,
620 sample_count,
621 &config,
622 "Flat",
623 );
624
625 let screenshot_pbr_pipeline = Self::create_render_pipeline(
627 &gpu_context.device,
628 &bind_group_layout,
629 &pbr_shader,
630 surface_format,
631 1,
632 &config,
633 "Screenshot PBR",
634 );
635
636 let screenshot_flat_pipeline = Self::create_render_pipeline(
637 &gpu_context.device,
638 &bind_group_layout,
639 &flat_shader,
640 surface_format,
641 1,
642 &config,
643 "Screenshot Flat",
644 );
645
646 Ok(Self {
647 gpu_context,
648 surface,
649 surface_config,
650 pbr_pipeline,
651 flat_pipeline,
652 screenshot_pbr_pipeline,
653 screenshot_flat_pipeline,
654 camera_uniform,
655 camera_buffer,
656 lighting_params,
657 lighting_buffer,
658 bind_group_layout,
659 config,
660 msaa_texture,
661 msaa_view,
662 })
663 }
664
665 fn create_render_pipeline(
667 device: &wgpu::Device,
668 bind_group_layout: &wgpu::BindGroupLayout,
669 shader: &wgpu::ShaderModule,
670 surface_format: wgpu::TextureFormat,
671 sample_count: u32,
672 config: &MeshRenderConfig,
673 label: &str,
674 ) -> wgpu::RenderPipeline {
675 let render_pipeline_layout =
676 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
677 label: Some(&format!("{} Mesh Render Pipeline Layout", label)),
678 bind_group_layouts: &[Some(bind_group_layout)],
679 immediate_size: 0,
680 });
681
682 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
683 label: Some(&format!("{} Mesh Render Pipeline", label)),
684 layout: Some(&render_pipeline_layout),
685 vertex: wgpu::VertexState {
686 module: shader,
687 entry_point: Some("vs_main"),
688 buffers: &[MeshVertex::desc()],
689 compilation_options: wgpu::PipelineCompilationOptions::default(),
690 },
691 fragment: Some(wgpu::FragmentState {
692 module: shader,
693 entry_point: Some("fs_main"),
694 targets: &[Some(wgpu::ColorTargetState {
695 format: surface_format,
696 blend: Some(wgpu::BlendState::REPLACE),
697 write_mask: wgpu::ColorWrites::ALL,
698 })],
699 compilation_options: wgpu::PipelineCompilationOptions::default(),
700 }),
701 primitive: wgpu::PrimitiveState {
702 topology: if config.wireframe_mode {
703 wgpu::PrimitiveTopology::LineList
704 } else {
705 wgpu::PrimitiveTopology::TriangleList
706 },
707 strip_index_format: None,
708 front_face: wgpu::FrontFace::Ccw,
709 cull_mode: if config.enable_backface_culling {
710 Some(wgpu::Face::Back)
711 } else {
712 None
713 },
714 unclipped_depth: false,
715 polygon_mode: wgpu::PolygonMode::Fill,
716 conservative: false,
717 },
718 depth_stencil: if config.enable_depth_test {
719 Some(wgpu::DepthStencilState {
720 format: wgpu::TextureFormat::Depth32Float,
721 depth_write_enabled: Some(true),
722 depth_compare: Some(wgpu::CompareFunction::Less),
723 stencil: wgpu::StencilState::default(),
724 bias: wgpu::DepthBiasState::default(),
725 })
726 } else {
727 None
728 },
729 multisample: wgpu::MultisampleState {
730 count: sample_count,
731 mask: !0,
732 alpha_to_coverage_enabled: false,
733 },
734 multiview_mask: None,
735 cache: None,
736 })
737 }
738
739 pub fn update_camera(
741 &mut self,
742 view_matrix: Matrix4<f32>,
743 proj_matrix: Matrix4<f32>,
744 camera_pos: Vector3<f32>,
745 ) {
746 self.camera_uniform.view_proj = (proj_matrix * view_matrix).into();
747 self.camera_uniform.view_pos = camera_pos.into();
748
749 self.gpu_context.queue.write_buffer(
750 &self.camera_buffer,
751 0,
752 bytemuck::bytes_of(&self.camera_uniform),
753 );
754 }
755
756 pub fn update_lighting(&mut self, params: MeshLightingParams) {
758 self.lighting_params = params;
759 self.gpu_context.queue.write_buffer(
760 &self.lighting_buffer,
761 0,
762 bytemuck::bytes_of(&self.lighting_params),
763 );
764 }
765
766 pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
768 if new_size.width > 0 && new_size.height > 0 {
769 self.surface_config.width = new_size.width;
770 self.surface_config.height = new_size.height;
771 self.surface
772 .configure(&self.gpu_context.device, &self.surface_config);
773
774 if self.config.enable_multisampling {
776 let msaa_texture =
777 self.gpu_context
778 .device
779 .create_texture(&wgpu::TextureDescriptor {
780 label: Some("MSAA Texture"),
781 size: wgpu::Extent3d {
782 width: new_size.width,
783 height: new_size.height,
784 depth_or_array_layers: 1,
785 },
786 mip_level_count: 1,
787 sample_count: 4,
788 dimension: wgpu::TextureDimension::D2,
789 format: self.surface_config.format,
790 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
791 view_formats: &[],
792 });
793 let msaa_view = msaa_texture.create_view(&wgpu::TextureViewDescriptor::default());
794 self.msaa_texture = Some(msaa_texture);
795 self.msaa_view = Some(msaa_view);
796 }
797 }
798 }
799
800 pub fn create_vertex_buffer(&self, vertices: &[MeshVertex]) -> wgpu::Buffer {
802 self.gpu_context
803 .device
804 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
805 label: Some("Mesh Vertex Buffer"),
806 contents: bytemuck::cast_slice(vertices),
807 usage: wgpu::BufferUsages::VERTEX,
808 })
809 }
810
811 pub fn create_index_buffer(&self, indices: &[u32]) -> wgpu::Buffer {
813 self.gpu_context
814 .device
815 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
816 label: Some("Mesh Index Buffer"),
817 contents: bytemuck::cast_slice(indices),
818 usage: wgpu::BufferUsages::INDEX,
819 })
820 }
821
822 pub fn create_depth_texture(&self) -> wgpu::Texture {
824 let sample_count = if self.config.enable_multisampling {
825 4
826 } else {
827 1
828 };
829
830 self.gpu_context
831 .device
832 .create_texture(&wgpu::TextureDescriptor {
833 label: Some("Depth Texture"),
834 size: wgpu::Extent3d {
835 width: self.surface_config.width,
836 height: self.surface_config.height,
837 depth_or_array_layers: 1,
838 },
839 mip_level_count: 1,
840 sample_count,
841 dimension: wgpu::TextureDimension::D2,
842 format: wgpu::TextureFormat::Depth32Float,
843 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
844 view_formats: &[],
845 })
846 }
847
848 pub fn render(&self, mesh: &GpuMesh, shading_mode: ShadingMode) -> Result<()> {
850 let vertex_buffer = self.create_vertex_buffer(&mesh.vertices);
851 let index_buffer = self.create_index_buffer(&mesh.indices);
852 let depth_texture = self.create_depth_texture();
853 let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
854
855 let output = match self.surface.get_current_texture() {
856 wgpu::CurrentSurfaceTexture::Success(frame) => frame,
857 wgpu::CurrentSurfaceTexture::Suboptimal(frame) => frame,
858 wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded => {
859 return Ok(())
860 }
861 _ => return Err(Error::Gpu("Failed to get surface texture".to_string())),
862 };
863
864 let view = output
865 .texture
866 .create_view(&wgpu::TextureViewDescriptor::default());
867
868 let material_buffer = match shading_mode {
870 ShadingMode::Pbr => {
871 self.gpu_context
872 .device
873 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
874 label: Some("PBR Material Buffer"),
875 contents: bytemuck::bytes_of(&mesh.material),
876 usage: wgpu::BufferUsages::UNIFORM,
877 })
878 }
879 ShadingMode::Flat => {
880 let flat_material = FlatMaterial {
881 color: mesh.material.albedo,
882 _padding: 0.0,
883 };
884 self.gpu_context
885 .device
886 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
887 label: Some("Flat Material Buffer"),
888 contents: bytemuck::bytes_of(&flat_material),
889 usage: wgpu::BufferUsages::UNIFORM,
890 })
891 }
892 };
893
894 let bind_group = self
895 .gpu_context
896 .device
897 .create_bind_group(&wgpu::BindGroupDescriptor {
898 layout: &self.bind_group_layout,
899 entries: &[
900 wgpu::BindGroupEntry {
901 binding: 0,
902 resource: self.camera_buffer.as_entire_binding(),
903 },
904 wgpu::BindGroupEntry {
905 binding: 1,
906 resource: material_buffer.as_entire_binding(),
907 },
908 wgpu::BindGroupEntry {
909 binding: 2,
910 resource: self.lighting_buffer.as_entire_binding(),
911 },
912 ],
913 label: Some("mesh_bind_group"),
914 });
915
916 let mut encoder =
917 self.gpu_context
918 .device
919 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
920 label: Some("Mesh Render Encoder"),
921 });
922
923 let (color_attachment, resolve_target) = if let Some(ref msaa_view) = self.msaa_view {
925 (msaa_view, Some(&view))
926 } else {
927 (&view, None)
928 };
929
930 {
931 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
932 label: Some("Mesh Render Pass"),
933 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
934 view: color_attachment,
935 resolve_target,
936 ops: wgpu::Operations {
937 load: wgpu::LoadOp::Clear(wgpu::Color {
938 r: self.config.background_color[0],
939 g: self.config.background_color[1],
940 b: self.config.background_color[2],
941 a: self.config.background_color[3],
942 }),
943 store: wgpu::StoreOp::Store,
944 },
945 depth_slice: None,
946 })],
947 depth_stencil_attachment: if self.config.enable_depth_test {
948 Some(wgpu::RenderPassDepthStencilAttachment {
949 view: &depth_view,
950 depth_ops: Some(wgpu::Operations {
951 load: wgpu::LoadOp::Clear(1.0),
952 store: wgpu::StoreOp::Store,
953 }),
954 stencil_ops: None,
955 })
956 } else {
957 None
958 },
959 timestamp_writes: None,
960 occlusion_query_set: None,
961 multiview_mask: None,
962 });
963
964 let pipeline = match shading_mode {
965 ShadingMode::Pbr => &self.pbr_pipeline,
966 ShadingMode::Flat => &self.flat_pipeline,
967 };
968
969 render_pass.set_pipeline(pipeline);
970 render_pass.set_bind_group(0, &bind_group, &[]);
971 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
972 render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
973 render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);
974 }
975
976 self.gpu_context
977 .queue
978 .submit(std::iter::once(encoder.finish()));
979 output.present();
980
981 Ok(())
982 }
983
984 pub fn render_to_texture(
989 &self,
990 mesh: &GpuMesh,
991 shading_mode: ShadingMode,
992 ) -> Result<(Vec<u8>, wgpu::TextureFormat, u32, u32)> {
993 let width = self.surface_config.width;
994 let height = self.surface_config.height;
995 let format = self.surface_config.format;
996
997 let render_texture = self
999 .gpu_context
1000 .device
1001 .create_texture(&wgpu::TextureDescriptor {
1002 label: Some("Screenshot Render Texture"),
1003 size: wgpu::Extent3d {
1004 width,
1005 height,
1006 depth_or_array_layers: 1,
1007 },
1008 mip_level_count: 1,
1009 sample_count: 1,
1010 dimension: wgpu::TextureDimension::D2,
1011 format,
1012 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
1013 view_formats: &[],
1014 });
1015 let render_view = render_texture.create_view(&wgpu::TextureViewDescriptor::default());
1016
1017 let depth_texture = self
1018 .gpu_context
1019 .device
1020 .create_texture(&wgpu::TextureDescriptor {
1021 label: Some("Screenshot Depth Texture"),
1022 size: wgpu::Extent3d {
1023 width,
1024 height,
1025 depth_or_array_layers: 1,
1026 },
1027 mip_level_count: 1,
1028 sample_count: 1,
1029 dimension: wgpu::TextureDimension::D2,
1030 format: wgpu::TextureFormat::Depth32Float,
1031 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
1032 view_formats: &[],
1033 });
1034 let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
1035
1036 let vertex_buffer = self.create_vertex_buffer(&mesh.vertices);
1037 let index_buffer = self.create_index_buffer(&mesh.indices);
1038
1039 let material_buffer = match shading_mode {
1040 ShadingMode::Pbr => {
1041 self.gpu_context
1042 .device
1043 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
1044 label: Some("Screenshot PBR Material Buffer"),
1045 contents: bytemuck::bytes_of(&mesh.material),
1046 usage: wgpu::BufferUsages::UNIFORM,
1047 })
1048 }
1049 ShadingMode::Flat => {
1050 let flat_material = FlatMaterial {
1051 color: mesh.material.albedo,
1052 _padding: 0.0,
1053 };
1054 self.gpu_context
1055 .device
1056 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
1057 label: Some("Screenshot Flat Material Buffer"),
1058 contents: bytemuck::bytes_of(&flat_material),
1059 usage: wgpu::BufferUsages::UNIFORM,
1060 })
1061 }
1062 };
1063
1064 let bind_group = self
1065 .gpu_context
1066 .device
1067 .create_bind_group(&wgpu::BindGroupDescriptor {
1068 layout: &self.bind_group_layout,
1069 entries: &[
1070 wgpu::BindGroupEntry {
1071 binding: 0,
1072 resource: self.camera_buffer.as_entire_binding(),
1073 },
1074 wgpu::BindGroupEntry {
1075 binding: 1,
1076 resource: material_buffer.as_entire_binding(),
1077 },
1078 wgpu::BindGroupEntry {
1079 binding: 2,
1080 resource: self.lighting_buffer.as_entire_binding(),
1081 },
1082 ],
1083 label: Some("screenshot_bind_group"),
1084 });
1085
1086 let bytes_per_pixel = 4u32;
1088 let unpadded_bytes_per_row = width * bytes_per_pixel;
1089 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
1090 let padded_bytes_per_row = (unpadded_bytes_per_row + align - 1) / align * align;
1091
1092 let staging_buffer = self
1093 .gpu_context
1094 .device
1095 .create_buffer(&wgpu::BufferDescriptor {
1096 label: Some("Screenshot Staging Buffer"),
1097 size: (padded_bytes_per_row * height) as wgpu::BufferAddress,
1098 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
1099 mapped_at_creation: false,
1100 });
1101
1102 let mut encoder =
1103 self.gpu_context
1104 .device
1105 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
1106 label: Some("Screenshot Encoder"),
1107 });
1108
1109 {
1110 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1111 label: Some("Screenshot Render Pass"),
1112 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1113 view: &render_view,
1114 resolve_target: None,
1115 ops: wgpu::Operations {
1116 load: wgpu::LoadOp::Clear(wgpu::Color {
1117 r: self.config.background_color[0],
1118 g: self.config.background_color[1],
1119 b: self.config.background_color[2],
1120 a: self.config.background_color[3],
1121 }),
1122 store: wgpu::StoreOp::Store,
1123 },
1124 depth_slice: None,
1125 })],
1126 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1127 view: &depth_view,
1128 depth_ops: Some(wgpu::Operations {
1129 load: wgpu::LoadOp::Clear(1.0),
1130 store: wgpu::StoreOp::Store,
1131 }),
1132 stencil_ops: None,
1133 }),
1134 timestamp_writes: None,
1135 occlusion_query_set: None,
1136 multiview_mask: None,
1137 });
1138
1139 let pipeline = match shading_mode {
1140 ShadingMode::Pbr => &self.screenshot_pbr_pipeline,
1141 ShadingMode::Flat => &self.screenshot_flat_pipeline,
1142 };
1143
1144 render_pass.set_pipeline(pipeline);
1145 render_pass.set_bind_group(0, &bind_group, &[]);
1146 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
1147 render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
1148 render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);
1149 }
1150
1151 encoder.copy_texture_to_buffer(
1153 wgpu::TexelCopyTextureInfo {
1154 texture: &render_texture,
1155 mip_level: 0,
1156 origin: wgpu::Origin3d::ZERO,
1157 aspect: wgpu::TextureAspect::All,
1158 },
1159 wgpu::TexelCopyBufferInfo {
1160 buffer: &staging_buffer,
1161 layout: wgpu::TexelCopyBufferLayout {
1162 offset: 0,
1163 bytes_per_row: Some(padded_bytes_per_row),
1164 rows_per_image: None,
1165 },
1166 },
1167 wgpu::Extent3d {
1168 width,
1169 height,
1170 depth_or_array_layers: 1,
1171 },
1172 );
1173
1174 self.gpu_context
1175 .queue
1176 .submit(std::iter::once(encoder.finish()));
1177
1178 let buffer_slice = staging_buffer.slice(..);
1180 let (tx, rx) =
1181 std::sync::mpsc::channel::<std::result::Result<(), wgpu::BufferAsyncError>>();
1182 buffer_slice.map_async(wgpu::MapMode::Read, move |v| {
1183 let _ = tx.send(v);
1184 });
1185 self.gpu_context.device.poll(wgpu::PollType::Wait {
1186 submission_index: None,
1187 timeout: None,
1188 });
1189 rx.recv()
1190 .map_err(|_| Error::Gpu("Screenshot buffer map channel error".to_string()))?
1191 .map_err(|e| Error::Gpu(format!("Screenshot buffer map error: {:?}", e)))?;
1192
1193 let data = buffer_slice.get_mapped_range();
1194
1195 let mut pixels: Vec<u8> = Vec::with_capacity((unpadded_bytes_per_row * height) as usize);
1197 for row in 0..height as usize {
1198 let start = row * padded_bytes_per_row as usize;
1199 let end = start + unpadded_bytes_per_row as usize;
1200 pixels.extend_from_slice(&data[start..end]);
1201 }
1202 drop(data);
1203 staging_buffer.unmap();
1204
1205 Ok((pixels, format, width, height))
1206 }
1207}
1208
1209pub fn mesh_to_gpu_mesh(
1211 vertices: &[Point3<f32>],
1212 indices: &[u32],
1213 normals: Option<&[Vector3<f32>]>,
1214 colors: Option<&[[f32; 3]]>,
1215 material: Option<PbrMaterial>,
1216) -> GpuMesh {
1217 let gpu_vertices: Vec<MeshVertex> = vertices
1218 .iter()
1219 .enumerate()
1220 .map(|(i, vertex)| {
1221 let normal = normals
1222 .and_then(|n| n.get(i))
1223 .map(|n| [n.x, n.y, n.z])
1224 .unwrap_or([0.0, 0.0, 1.0]);
1225
1226 let color = colors
1227 .and_then(|c| c.get(i))
1228 .copied()
1229 .unwrap_or([0.8, 0.8, 0.8]);
1230
1231 MeshVertex::new([vertex.x, vertex.y, vertex.z], normal, [0.0, 0.0], color)
1232 })
1233 .collect();
1234
1235 GpuMesh::new(gpu_vertices, indices.to_vec(), material.unwrap_or_default())
1236}
1237
1238pub struct LodMesh {
1243 pub levels: Vec<GpuMesh>,
1245 pub thresholds: Vec<f32>,
1248}
1249
1250impl LodMesh {
1251 pub fn from_progressive_mesh(pm: &ProgressiveMesh, num_levels: usize) -> Self {
1255 let num_levels = num_levels.max(2);
1256
1257 let levels: Vec<GpuMesh> = (0..num_levels)
1258 .map(|i| {
1259 let ratio = i as f32 / (num_levels - 1) as f32;
1260 let mesh = pm.reconstruct_at_ratio(ratio);
1261 triangle_mesh_to_gpu_mesh(&mesh)
1262 })
1263 .collect();
1264
1265 let thresholds: Vec<f32> = (0..num_levels.saturating_sub(1))
1267 .map(|i| {
1268 let t = (num_levels - 1 - i) as f32 / (num_levels - 1) as f32;
1269 t * 100.0
1270 })
1271 .collect();
1272
1273 LodMesh { levels, thresholds }
1274 }
1275
1276 pub fn select_level(&self, distance: f32) -> &GpuMesh {
1281 for (i, &threshold) in self.thresholds.iter().enumerate() {
1282 if distance > threshold {
1283 return &self.levels[i];
1284 }
1285 }
1286 self.levels.last().unwrap_or(&self.levels[0])
1288 }
1289
1290 pub fn num_levels(&self) -> usize {
1292 self.levels.len()
1293 }
1294}
1295
1296fn triangle_mesh_to_gpu_mesh(mesh: &threecrate_core::TriangleMesh) -> GpuMesh {
1298 let normals_slice = mesh.normals.as_deref();
1299 let colors_f32: Option<Vec<[f32; 3]>> = mesh.colors.as_ref().map(|colors| {
1300 colors
1301 .iter()
1302 .map(|c| {
1303 [
1304 c[0] as f32 / 255.0,
1305 c[1] as f32 / 255.0,
1306 c[2] as f32 / 255.0,
1307 ]
1308 })
1309 .collect()
1310 });
1311
1312 let indices: Vec<u32> = mesh
1313 .faces
1314 .iter()
1315 .flat_map(|f| [f[0] as u32, f[1] as u32, f[2] as u32])
1316 .collect();
1317
1318 mesh_to_gpu_mesh(
1319 &mesh.vertices,
1320 &indices,
1321 normals_slice,
1322 colors_f32.as_deref(),
1323 None,
1324 )
1325}