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