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 camera_uniform: MeshCameraUniform,
329 pub camera_buffer: wgpu::Buffer,
330 pub lighting_params: MeshLightingParams,
331 pub lighting_buffer: wgpu::Buffer,
332 pub bind_group_layout: wgpu::BindGroupLayout,
333 pub config: MeshRenderConfig,
334 pub msaa_texture: Option<wgpu::Texture>,
335 pub msaa_view: Option<wgpu::TextureView>,
336}
337
338impl<'window> MeshRenderer<'window> {
339 pub async fn new(window: &'window Window, config: MeshRenderConfig) -> Result<Self> {
341 let gpu_context = GpuContext::new().await?;
342
343 let surface = gpu_context.instance.create_surface(window)
344 .map_err(|e| Error::Gpu(format!("Failed to create surface: {:?}", e)))?;
345
346 let surface_caps = surface.get_capabilities(&gpu_context.adapter);
347 let surface_format = surface_caps.formats.iter()
348 .copied()
349 .find(|f| f.is_srgb())
350 .unwrap_or(surface_caps.formats[0]);
351
352 let size = window.inner_size();
353 let sample_count = if config.enable_multisampling { 4 } else { 1 };
354
355 let surface_config = wgpu::SurfaceConfiguration {
356 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
357 format: surface_format,
358 width: size.width,
359 height: size.height,
360 present_mode: surface_caps.present_modes[0],
361 alpha_mode: surface_caps.alpha_modes[0],
362 view_formats: vec![],
363 desired_maximum_frame_latency: 2,
364 };
365 surface.configure(&gpu_context.device, &surface_config);
366
367 let (msaa_texture, msaa_view) = if config.enable_multisampling {
369 let msaa_texture = gpu_context.device.create_texture(&wgpu::TextureDescriptor {
370 label: Some("MSAA Texture"),
371 size: wgpu::Extent3d {
372 width: size.width,
373 height: size.height,
374 depth_or_array_layers: 1,
375 },
376 mip_level_count: 1,
377 sample_count,
378 dimension: wgpu::TextureDimension::D2,
379 format: surface_format,
380 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
381 view_formats: &[],
382 });
383 let msaa_view = msaa_texture.create_view(&wgpu::TextureViewDescriptor::default());
384 (Some(msaa_texture), Some(msaa_view))
385 } else {
386 (None, None)
387 };
388
389 let camera_uniform = MeshCameraUniform {
391 view_proj: Matrix4::identity().into(),
392 view_pos: [0.0, 0.0, 0.0],
393 _padding: 0.0,
394 };
395
396 let camera_buffer = gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
397 label: Some("Camera Buffer"),
398 contents: bytemuck::bytes_of(&camera_uniform),
399 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
400 });
401
402 let lighting_params = config.lighting_params;
404 let lighting_buffer = gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
405 label: Some("Lighting Buffer"),
406 contents: bytemuck::bytes_of(&lighting_params),
407 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
408 });
409
410 let bind_group_layout = gpu_context.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
412 entries: &[
413 wgpu::BindGroupLayoutEntry {
414 binding: 0,
415 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
416 ty: wgpu::BindingType::Buffer {
417 ty: wgpu::BufferBindingType::Uniform,
418 has_dynamic_offset: false,
419 min_binding_size: None,
420 },
421 count: None,
422 },
423 wgpu::BindGroupLayoutEntry {
424 binding: 1,
425 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
426 ty: wgpu::BindingType::Buffer {
427 ty: wgpu::BufferBindingType::Uniform,
428 has_dynamic_offset: false,
429 min_binding_size: None,
430 },
431 count: None,
432 },
433 wgpu::BindGroupLayoutEntry {
434 binding: 2,
435 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
436 ty: wgpu::BindingType::Buffer {
437 ty: wgpu::BufferBindingType::Uniform,
438 has_dynamic_offset: false,
439 min_binding_size: None,
440 },
441 count: None,
442 },
443 ],
444 label: Some("mesh_bind_group_layout"),
445 });
446
447 let pbr_shader = gpu_context.device.create_shader_module(wgpu::ShaderModuleDescriptor {
449 label: Some("PBR Mesh Shader"),
450 source: wgpu::ShaderSource::Wgsl(include_str!("shaders/mesh_pbr.wgsl").into()),
451 });
452
453 let pbr_pipeline = Self::create_render_pipeline(
454 &gpu_context.device,
455 &bind_group_layout,
456 &pbr_shader,
457 surface_format,
458 sample_count,
459 &config,
460 "PBR",
461 );
462
463 let flat_shader = gpu_context.device.create_shader_module(wgpu::ShaderModuleDescriptor {
465 label: Some("Flat Mesh Shader"),
466 source: wgpu::ShaderSource::Wgsl(include_str!("shaders/mesh_flat.wgsl").into()),
467 });
468
469 let flat_pipeline = Self::create_render_pipeline(
470 &gpu_context.device,
471 &bind_group_layout,
472 &flat_shader,
473 surface_format,
474 sample_count,
475 &config,
476 "Flat",
477 );
478
479 Ok(Self {
480 gpu_context,
481 surface,
482 surface_config,
483 pbr_pipeline,
484 flat_pipeline,
485 camera_uniform,
486 camera_buffer,
487 lighting_params,
488 lighting_buffer,
489 bind_group_layout,
490 config,
491 msaa_texture,
492 msaa_view,
493 })
494 }
495
496 fn create_render_pipeline(
498 device: &wgpu::Device,
499 bind_group_layout: &wgpu::BindGroupLayout,
500 shader: &wgpu::ShaderModule,
501 surface_format: wgpu::TextureFormat,
502 sample_count: u32,
503 config: &MeshRenderConfig,
504 label: &str,
505 ) -> wgpu::RenderPipeline {
506 let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
507 label: Some(&format!("{} Mesh Render Pipeline Layout", label)),
508 bind_group_layouts: &[bind_group_layout],
509 immediate_size: 0,
510 });
511
512 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
513 label: Some(&format!("{} Mesh Render Pipeline", label)),
514 layout: Some(&render_pipeline_layout),
515 vertex: wgpu::VertexState {
516 module: shader,
517 entry_point: Some("vs_main"),
518 buffers: &[MeshVertex::desc()],
519 compilation_options: wgpu::PipelineCompilationOptions::default(),
520 },
521 fragment: Some(wgpu::FragmentState {
522 module: shader,
523 entry_point: Some("fs_main"),
524 targets: &[Some(wgpu::ColorTargetState {
525 format: surface_format,
526 blend: Some(wgpu::BlendState::REPLACE),
527 write_mask: wgpu::ColorWrites::ALL,
528 })],
529 compilation_options: wgpu::PipelineCompilationOptions::default(),
530 }),
531 primitive: wgpu::PrimitiveState {
532 topology: if config.wireframe_mode {
533 wgpu::PrimitiveTopology::LineList
534 } else {
535 wgpu::PrimitiveTopology::TriangleList
536 },
537 strip_index_format: None,
538 front_face: wgpu::FrontFace::Ccw,
539 cull_mode: if config.enable_backface_culling {
540 Some(wgpu::Face::Back)
541 } else {
542 None
543 },
544 unclipped_depth: false,
545 polygon_mode: wgpu::PolygonMode::Fill,
546 conservative: false,
547 },
548 depth_stencil: if config.enable_depth_test {
549 Some(wgpu::DepthStencilState {
550 format: wgpu::TextureFormat::Depth32Float,
551 depth_write_enabled: true,
552 depth_compare: wgpu::CompareFunction::Less,
553 stencil: wgpu::StencilState::default(),
554 bias: wgpu::DepthBiasState::default(),
555 })
556 } else {
557 None
558 },
559 multisample: wgpu::MultisampleState {
560 count: sample_count,
561 mask: !0,
562 alpha_to_coverage_enabled: false,
563 },
564 multiview_mask: None,
565 cache: None,
566 })
567 }
568
569 pub fn update_camera(&mut self, view_matrix: Matrix4<f32>, proj_matrix: Matrix4<f32>, camera_pos: Vector3<f32>) {
571 self.camera_uniform.view_proj = (proj_matrix * view_matrix).into();
572 self.camera_uniform.view_pos = camera_pos.into();
573
574 self.gpu_context.queue.write_buffer(
575 &self.camera_buffer,
576 0,
577 bytemuck::bytes_of(&self.camera_uniform),
578 );
579 }
580
581 pub fn update_lighting(&mut self, params: MeshLightingParams) {
583 self.lighting_params = params;
584 self.gpu_context.queue.write_buffer(
585 &self.lighting_buffer,
586 0,
587 bytemuck::bytes_of(&self.lighting_params),
588 );
589 }
590
591 pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
593 if new_size.width > 0 && new_size.height > 0 {
594 self.surface_config.width = new_size.width;
595 self.surface_config.height = new_size.height;
596 self.surface.configure(&self.gpu_context.device, &self.surface_config);
597
598 if self.config.enable_multisampling {
600 let msaa_texture = self.gpu_context.device.create_texture(&wgpu::TextureDescriptor {
601 label: Some("MSAA Texture"),
602 size: wgpu::Extent3d {
603 width: new_size.width,
604 height: new_size.height,
605 depth_or_array_layers: 1,
606 },
607 mip_level_count: 1,
608 sample_count: 4,
609 dimension: wgpu::TextureDimension::D2,
610 format: self.surface_config.format,
611 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
612 view_formats: &[],
613 });
614 let msaa_view = msaa_texture.create_view(&wgpu::TextureViewDescriptor::default());
615 self.msaa_texture = Some(msaa_texture);
616 self.msaa_view = Some(msaa_view);
617 }
618 }
619 }
620
621 pub fn create_vertex_buffer(&self, vertices: &[MeshVertex]) -> wgpu::Buffer {
623 self.gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
624 label: Some("Mesh Vertex Buffer"),
625 contents: bytemuck::cast_slice(vertices),
626 usage: wgpu::BufferUsages::VERTEX,
627 })
628 }
629
630 pub fn create_index_buffer(&self, indices: &[u32]) -> wgpu::Buffer {
632 self.gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
633 label: Some("Mesh Index Buffer"),
634 contents: bytemuck::cast_slice(indices),
635 usage: wgpu::BufferUsages::INDEX,
636 })
637 }
638
639 pub fn create_depth_texture(&self) -> wgpu::Texture {
641 let sample_count = if self.config.enable_multisampling { 4 } else { 1 };
642
643 self.gpu_context.device.create_texture(&wgpu::TextureDescriptor {
644 label: Some("Depth Texture"),
645 size: wgpu::Extent3d {
646 width: self.surface_config.width,
647 height: self.surface_config.height,
648 depth_or_array_layers: 1,
649 },
650 mip_level_count: 1,
651 sample_count,
652 dimension: wgpu::TextureDimension::D2,
653 format: wgpu::TextureFormat::Depth32Float,
654 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
655 view_formats: &[],
656 })
657 }
658
659 pub fn render(&self, mesh: &GpuMesh, shading_mode: ShadingMode) -> Result<()> {
661 let vertex_buffer = self.create_vertex_buffer(&mesh.vertices);
662 let index_buffer = self.create_index_buffer(&mesh.indices);
663 let depth_texture = self.create_depth_texture();
664 let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
665
666 let output = self.surface.get_current_texture()
667 .map_err(|e| Error::Gpu(format!("Failed to get surface texture: {:?}", e)))?;
668
669 let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
670
671 let material_buffer = match shading_mode {
673 ShadingMode::Pbr => {
674 self.gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
675 label: Some("PBR Material Buffer"),
676 contents: bytemuck::bytes_of(&mesh.material),
677 usage: wgpu::BufferUsages::UNIFORM,
678 })
679 }
680 ShadingMode::Flat => {
681 let flat_material = FlatMaterial {
682 color: mesh.material.albedo,
683 _padding: 0.0,
684 };
685 self.gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
686 label: Some("Flat Material Buffer"),
687 contents: bytemuck::bytes_of(&flat_material),
688 usage: wgpu::BufferUsages::UNIFORM,
689 })
690 }
691 };
692
693 let bind_group = self.gpu_context.device.create_bind_group(&wgpu::BindGroupDescriptor {
694 layout: &self.bind_group_layout,
695 entries: &[
696 wgpu::BindGroupEntry {
697 binding: 0,
698 resource: self.camera_buffer.as_entire_binding(),
699 },
700 wgpu::BindGroupEntry {
701 binding: 1,
702 resource: material_buffer.as_entire_binding(),
703 },
704 wgpu::BindGroupEntry {
705 binding: 2,
706 resource: self.lighting_buffer.as_entire_binding(),
707 },
708 ],
709 label: Some("mesh_bind_group"),
710 });
711
712 let mut encoder = self.gpu_context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
713 label: Some("Mesh Render Encoder"),
714 });
715
716 let (color_attachment, resolve_target) = if let Some(ref msaa_view) = self.msaa_view {
718 (msaa_view, Some(&view))
719 } else {
720 (&view, None)
721 };
722
723 {
724 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
725 label: Some("Mesh Render Pass"),
726 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
727 view: color_attachment,
728 resolve_target,
729 ops: wgpu::Operations {
730 load: wgpu::LoadOp::Clear(wgpu::Color {
731 r: self.config.background_color[0],
732 g: self.config.background_color[1],
733 b: self.config.background_color[2],
734 a: self.config.background_color[3],
735 }),
736 store: wgpu::StoreOp::Store,
737 },
738 depth_slice: None,
739 })],
740 depth_stencil_attachment: if self.config.enable_depth_test {
741 Some(wgpu::RenderPassDepthStencilAttachment {
742 view: &depth_view,
743 depth_ops: Some(wgpu::Operations {
744 load: wgpu::LoadOp::Clear(1.0),
745 store: wgpu::StoreOp::Store,
746 }),
747 stencil_ops: None,
748 })
749 } else {
750 None
751 },
752 timestamp_writes: None,
753 occlusion_query_set: None,
754 multiview_mask: None,
755 });
756
757 let pipeline = match shading_mode {
758 ShadingMode::Pbr => &self.pbr_pipeline,
759 ShadingMode::Flat => &self.flat_pipeline,
760 };
761
762 render_pass.set_pipeline(pipeline);
763 render_pass.set_bind_group(0, &bind_group, &[]);
764 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
765 render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
766 render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);
767 }
768
769 self.gpu_context.queue.submit(std::iter::once(encoder.finish()));
770 output.present();
771
772 Ok(())
773 }
774}
775
776pub fn mesh_to_gpu_mesh(
778 vertices: &[Point3<f32>],
779 indices: &[u32],
780 normals: Option<&[Vector3<f32>]>,
781 colors: Option<&[[f32; 3]]>,
782 material: Option<PbrMaterial>,
783) -> GpuMesh {
784 let gpu_vertices: Vec<MeshVertex> = vertices
785 .iter()
786 .enumerate()
787 .map(|(i, vertex)| {
788 let normal = normals
789 .and_then(|n| n.get(i))
790 .map(|n| [n.x, n.y, n.z])
791 .unwrap_or([0.0, 0.0, 1.0]);
792
793 let color = colors
794 .and_then(|c| c.get(i))
795 .copied()
796 .unwrap_or([0.8, 0.8, 0.8]);
797
798 MeshVertex::new([vertex.x, vertex.y, vertex.z], normal, [0.0, 0.0], color)
799 })
800 .collect();
801
802 GpuMesh::new(
803 gpu_vertices,
804 indices.to_vec(),
805 material.unwrap_or_default(),
806 )
807}
808
809pub struct LodMesh {
814 pub levels: Vec<GpuMesh>,
816 pub thresholds: Vec<f32>,
819}
820
821impl LodMesh {
822 pub fn from_progressive_mesh(pm: &ProgressiveMesh, num_levels: usize) -> Self {
826 let num_levels = num_levels.max(2);
827
828 let levels: Vec<GpuMesh> = (0..num_levels)
829 .map(|i| {
830 let ratio = i as f32 / (num_levels - 1) as f32;
831 let mesh = pm.reconstruct_at_ratio(ratio);
832 triangle_mesh_to_gpu_mesh(&mesh)
833 })
834 .collect();
835
836 let thresholds: Vec<f32> = (0..num_levels.saturating_sub(1))
838 .map(|i| {
839 let t = (num_levels - 1 - i) as f32 / (num_levels - 1) as f32;
840 t * 100.0
841 })
842 .collect();
843
844 LodMesh { levels, thresholds }
845 }
846
847 pub fn select_level(&self, distance: f32) -> &GpuMesh {
852 for (i, &threshold) in self.thresholds.iter().enumerate() {
853 if distance > threshold {
854 return &self.levels[i];
855 }
856 }
857 self.levels.last().unwrap_or(&self.levels[0])
859 }
860
861 pub fn num_levels(&self) -> usize {
863 self.levels.len()
864 }
865}
866
867fn triangle_mesh_to_gpu_mesh(mesh: &threecrate_core::TriangleMesh) -> GpuMesh {
869 let normals_slice = mesh.normals.as_deref();
870 let colors_f32: Option<Vec<[f32; 3]>> = mesh.colors.as_ref().map(|colors| {
871 colors
872 .iter()
873 .map(|c| [c[0] as f32 / 255.0, c[1] as f32 / 255.0, c[2] as f32 / 255.0])
874 .collect()
875 });
876
877 let indices: Vec<u32> = mesh
878 .faces
879 .iter()
880 .flat_map(|f| [f[0] as u32, f[1] as u32, f[2] as u32])
881 .collect();
882
883 mesh_to_gpu_mesh(
884 &mesh.vertices,
885 &indices,
886 normals_slice,
887 colors_f32.as_deref(),
888 None,
889 )
890}