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