threecrate_gpu/
mesh.rs

1//! GPU-accelerated mesh rendering with PBR and flat shading
2
3use threecrate_core::{Result, Error};
4use crate::GpuContext;
5use nalgebra::{Matrix4, Vector3, Point3};
6use bytemuck::{Pod, Zeroable};
7use wgpu::util::DeviceExt;
8use winit::window::Window;
9
10/// Vertex data for mesh rendering with full PBR attributes
11#[repr(C)]
12#[derive(Copy, Clone, Debug, Pod, Zeroable)]
13pub struct MeshVertex {
14    pub position: [f32; 3],
15    pub normal: [f32; 3],
16    pub tangent: [f32; 3],
17    pub bitangent: [f32; 3],
18    pub uv: [f32; 2],
19    pub color: [f32; 3],
20    pub _padding: f32,
21}
22
23impl MeshVertex {
24    /// Create a new mesh vertex
25    pub fn new(
26        position: [f32; 3],
27        normal: [f32; 3],
28        uv: [f32; 2],
29        color: [f32; 3],
30    ) -> Self {
31        // Calculate tangent and bitangent vectors (simplified)
32        let tangent = if normal[0].abs() > 0.9 {
33            [0.0, 1.0, 0.0]
34        } else {
35            [1.0, 0.0, 0.0]
36        };
37        
38        let bitangent = [
39            normal[1] * tangent[2] - normal[2] * tangent[1],
40            normal[2] * tangent[0] - normal[0] * tangent[2],
41            normal[0] * tangent[1] - normal[1] * tangent[0],
42        ];
43        
44        Self {
45            position,
46            normal,
47            tangent,
48            bitangent,
49            uv,
50            color,
51            _padding: 0.0,
52        }
53    }
54    
55    /// Create vertex from position and normal with default color
56    pub fn from_pos_normal(position: [f32; 3], normal: [f32; 3]) -> Self {
57        Self::new(position, normal, [0.0, 0.0], [0.8, 0.8, 0.8])
58    }
59    
60    /// Vertex buffer layout descriptor
61    pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
62        wgpu::VertexBufferLayout {
63            array_stride: std::mem::size_of::<MeshVertex>() as wgpu::BufferAddress,
64            step_mode: wgpu::VertexStepMode::Vertex,
65            attributes: &[
66                // Position
67                wgpu::VertexAttribute {
68                    offset: 0,
69                    shader_location: 0,
70                    format: wgpu::VertexFormat::Float32x3,
71                },
72                // Normal
73                wgpu::VertexAttribute {
74                    offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
75                    shader_location: 1,
76                    format: wgpu::VertexFormat::Float32x3,
77                },
78                // Tangent
79                wgpu::VertexAttribute {
80                    offset: std::mem::size_of::<[f32; 6]>() as wgpu::BufferAddress,
81                    shader_location: 2,
82                    format: wgpu::VertexFormat::Float32x3,
83                },
84                // Bitangent
85                wgpu::VertexAttribute {
86                    offset: std::mem::size_of::<[f32; 9]>() as wgpu::BufferAddress,
87                    shader_location: 3,
88                    format: wgpu::VertexFormat::Float32x3,
89                },
90                // UV
91                wgpu::VertexAttribute {
92                    offset: std::mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
93                    shader_location: 4,
94                    format: wgpu::VertexFormat::Float32x2,
95                },
96                // Color
97                wgpu::VertexAttribute {
98                    offset: std::mem::size_of::<[f32; 14]>() as wgpu::BufferAddress,
99                    shader_location: 5,
100                    format: wgpu::VertexFormat::Float32x3,
101                },
102            ],
103        }
104    }
105}
106
107/// Camera uniform data for mesh rendering
108#[repr(C)]
109#[derive(Copy, Clone, Pod, Zeroable)]
110pub struct MeshCameraUniform {
111    pub view_proj: [[f32; 4]; 4],
112    pub view_pos: [f32; 3],
113    pub _padding: f32,
114}
115
116/// PBR material properties
117#[repr(C)]
118#[derive(Copy, Clone, Debug, Pod, Zeroable)]
119pub struct PbrMaterial {
120    pub albedo: [f32; 3],
121    pub metallic: f32,
122    pub roughness: f32,
123    pub ao: f32,
124    pub emission: [f32; 3],
125    pub _padding: f32,
126}
127
128impl Default for PbrMaterial {
129    fn default() -> Self {
130        Self {
131            albedo: [0.7, 0.7, 0.7],
132            metallic: 0.0,
133            roughness: 0.5,
134            ao: 1.0,
135            emission: [0.0, 0.0, 0.0],
136            _padding: 0.0,
137        }
138    }
139}
140
141/// Flat shading material properties
142#[repr(C)]
143#[derive(Copy, Clone, Debug, Pod, Zeroable)]
144pub struct FlatMaterial {
145    pub color: [f32; 3],
146    pub _padding: f32,
147}
148
149impl Default for FlatMaterial {
150    fn default() -> Self {
151        Self {
152            color: [0.8, 0.8, 0.8],
153            _padding: 0.0,
154        }
155    }
156}
157
158/// Lighting parameters for mesh rendering
159#[repr(C)]
160#[derive(Copy, Clone, Debug, Pod, Zeroable)]
161pub struct MeshLightingParams {
162    pub light_position: [f32; 3],
163    pub light_intensity: f32,
164    pub light_color: [f32; 3],
165    pub ambient_strength: f32,
166    pub gamma: f32,
167    pub exposure: f32,
168    pub _padding: [f32; 2],
169}
170
171impl Default for MeshLightingParams {
172    fn default() -> Self {
173        Self {
174            light_position: [10.0, 10.0, 10.0],
175            light_intensity: 1.0,
176            light_color: [1.0, 1.0, 1.0],
177            ambient_strength: 0.03,
178            gamma: 2.2,
179            exposure: 1.0,
180            _padding: [0.0, 0.0],
181        }
182    }
183}
184
185/// Mesh rendering configuration
186#[derive(Debug, Clone)]
187pub struct MeshRenderConfig {
188    pub lighting_params: MeshLightingParams,
189    pub background_color: [f64; 4],
190    pub enable_depth_test: bool,
191    pub enable_backface_culling: bool,
192    pub enable_multisampling: bool,
193    pub wireframe_mode: bool,
194}
195
196impl Default for MeshRenderConfig {
197    fn default() -> Self {
198        Self {
199            lighting_params: MeshLightingParams::default(),
200            background_color: [0.1, 0.1, 0.1, 1.0],
201            enable_depth_test: true,
202            enable_backface_culling: true,
203            enable_multisampling: true,
204            wireframe_mode: false,
205        }
206    }
207}
208
209/// Mesh data structure for GPU rendering
210#[derive(Debug, Clone)]
211pub struct GpuMesh {
212    pub vertices: Vec<MeshVertex>,
213    pub indices: Vec<u32>,
214    pub material: PbrMaterial,
215}
216
217impl GpuMesh {
218    /// Create a new GPU mesh
219    pub fn new(vertices: Vec<MeshVertex>, indices: Vec<u32>, material: PbrMaterial) -> Self {
220        Self {
221            vertices,
222            indices,
223            material,
224        }
225    }
226    
227    /// Create a simple triangle mesh for testing
228    pub fn triangle() -> Self {
229        let vertices = vec![
230            MeshVertex::new([-0.5, -0.5, 0.0], [0.0, 0.0, 1.0], [0.0, 0.0], [1.0, 0.0, 0.0]),
231            MeshVertex::new([0.5, -0.5, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0], [0.0, 1.0, 0.0]),
232            MeshVertex::new([0.0, 0.5, 0.0], [0.0, 0.0, 1.0], [0.5, 1.0], [0.0, 0.0, 1.0]),
233        ];
234        
235        let indices = vec![0, 1, 2];
236        
237        Self::new(vertices, indices, PbrMaterial::default())
238    }
239    
240    /// Create a cube mesh for testing
241    pub fn cube() -> Self {
242        let vertices = vec![
243            // Front face
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]),
245            MeshVertex::new([1.0, -1.0, 1.0], [0.0, 0.0, 1.0], [1.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, 1.0], [0.8, 0.2, 0.2]),
247            MeshVertex::new([-1.0, 1.0, 1.0], [0.0, 0.0, 1.0], [0.0, 1.0], [0.8, 0.2, 0.2]),
248            
249            // Back face
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]),
251            MeshVertex::new([-1.0, -1.0, -1.0], [0.0, 0.0, -1.0], [1.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, 1.0], [0.2, 0.8, 0.2]),
253            MeshVertex::new([1.0, 1.0, -1.0], [0.0, 0.0, -1.0], [0.0, 1.0], [0.2, 0.8, 0.2]),
254            
255            // Top face
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]),
257            MeshVertex::new([1.0, 1.0, 1.0], [0.0, 1.0, 0.0], [1.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, 1.0], [0.2, 0.2, 0.8]),
259            MeshVertex::new([-1.0, 1.0, -1.0], [0.0, 1.0, 0.0], [0.0, 1.0], [0.2, 0.2, 0.8]),
260            
261            // Bottom face
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]),
263            MeshVertex::new([1.0, -1.0, -1.0], [0.0, -1.0, 0.0], [1.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, 1.0], [0.8, 0.8, 0.2]),
265            MeshVertex::new([-1.0, -1.0, 1.0], [0.0, -1.0, 0.0], [0.0, 1.0], [0.8, 0.8, 0.2]),
266            
267            // Right face
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]),
269            MeshVertex::new([1.0, -1.0, -1.0], [1.0, 0.0, 0.0], [1.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, 1.0], [0.8, 0.2, 0.8]),
271            MeshVertex::new([1.0, 1.0, 1.0], [1.0, 0.0, 0.0], [0.0, 1.0], [0.8, 0.2, 0.8]),
272            
273            // Left face
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]),
275            MeshVertex::new([-1.0, -1.0, 1.0], [-1.0, 0.0, 0.0], [1.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, 1.0], [0.2, 0.8, 0.8]),
277            MeshVertex::new([-1.0, 1.0, -1.0], [-1.0, 0.0, 0.0], [0.0, 1.0], [0.2, 0.8, 0.8]),
278        ];
279        
280        let indices = vec![
281            // Front face
282            0, 1, 2, 2, 3, 0,
283            // Back face
284            4, 5, 6, 6, 7, 4,
285            // Top face
286            8, 9, 10, 10, 11, 8,
287            // Bottom face
288            12, 13, 14, 14, 15, 12,
289            // Right face
290            16, 17, 18, 18, 19, 16,
291            // Left face
292            20, 21, 22, 22, 23, 20,
293        ];
294        
295        Self::new(vertices, indices, PbrMaterial::default())
296    }
297    
298    /// Create a mesh from point cloud with estimated normals
299    pub fn from_point_cloud(points: &[Point3<f32>], color: [f32; 3]) -> Self {
300        let vertices: Vec<MeshVertex> = points.iter().map(|p| {
301            // Simple normal estimation (could use the GPU normal computation)
302            let normal = [0.0, 0.0, 1.0]; // Default normal
303            MeshVertex::new([p.x, p.y, p.z], normal, [0.0, 0.0], color)
304        }).collect();
305        
306        // Create indices for point rendering (each point is a degenerate triangle)
307        let indices: Vec<u32> = (0..vertices.len() as u32).collect();
308        
309        Self::new(vertices, indices, PbrMaterial::default())
310    }
311}
312
313/// Shading mode for mesh rendering
314#[derive(Debug, Clone, Copy, PartialEq)]
315pub enum ShadingMode {
316    Flat,
317    Pbr,
318}
319
320/// GPU-accelerated mesh renderer with PBR and flat shading
321pub struct MeshRenderer<'window> {
322    pub gpu_context: GpuContext,
323    pub surface: wgpu::Surface<'window>,
324    pub surface_config: wgpu::SurfaceConfiguration,
325    pub pbr_pipeline: wgpu::RenderPipeline,
326    pub flat_pipeline: wgpu::RenderPipeline,
327    pub camera_uniform: MeshCameraUniform,
328    pub camera_buffer: wgpu::Buffer,
329    pub lighting_params: MeshLightingParams,
330    pub lighting_buffer: wgpu::Buffer,
331    pub bind_group_layout: wgpu::BindGroupLayout,
332    pub config: MeshRenderConfig,
333    pub msaa_texture: Option<wgpu::Texture>,
334    pub msaa_view: Option<wgpu::TextureView>,
335}
336
337impl<'window> MeshRenderer<'window> {
338    /// Create new mesh renderer with PBR and flat shading support
339    pub async fn new(window: &'window Window, config: MeshRenderConfig) -> Result<Self> {
340        let gpu_context = GpuContext::new().await?;
341        
342        let surface = gpu_context.instance.create_surface(window)
343            .map_err(|e| Error::Gpu(format!("Failed to create surface: {:?}", e)))?;
344
345        let surface_caps = surface.get_capabilities(&gpu_context.adapter);
346        let surface_format = surface_caps.formats.iter()
347            .copied()
348            .find(|f| f.is_srgb())
349            .unwrap_or(surface_caps.formats[0]);
350
351        let size = window.inner_size();
352        let sample_count = if config.enable_multisampling { 4 } else { 1 };
353        
354        let surface_config = wgpu::SurfaceConfiguration {
355            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
356            format: surface_format,
357            width: size.width,
358            height: size.height,
359            present_mode: surface_caps.present_modes[0],
360            alpha_mode: surface_caps.alpha_modes[0],
361            view_formats: vec![],
362            desired_maximum_frame_latency: 2,
363        };
364        surface.configure(&gpu_context.device, &surface_config);
365
366        // Create MSAA texture if enabled
367        let (msaa_texture, msaa_view) = if config.enable_multisampling {
368            let msaa_texture = gpu_context.device.create_texture(&wgpu::TextureDescriptor {
369                label: Some("MSAA Texture"),
370                size: wgpu::Extent3d {
371                    width: size.width,
372                    height: size.height,
373                    depth_or_array_layers: 1,
374                },
375                mip_level_count: 1,
376                sample_count,
377                dimension: wgpu::TextureDimension::D2,
378                format: surface_format,
379                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
380                view_formats: &[],
381            });
382            let msaa_view = msaa_texture.create_view(&wgpu::TextureViewDescriptor::default());
383            (Some(msaa_texture), Some(msaa_view))
384        } else {
385            (None, None)
386        };
387
388        // Create camera uniform
389        let camera_uniform = MeshCameraUniform {
390            view_proj: Matrix4::identity().into(),
391            view_pos: [0.0, 0.0, 0.0],
392            _padding: 0.0,
393        };
394
395        let camera_buffer = gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
396            label: Some("Camera Buffer"),
397            contents: bytemuck::bytes_of(&camera_uniform),
398            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
399        });
400
401        // Create lighting parameters buffer
402        let lighting_params = config.lighting_params;
403        let lighting_buffer = gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
404            label: Some("Lighting Buffer"),
405            contents: bytemuck::bytes_of(&lighting_params),
406            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
407        });
408
409        // Create bind group layout
410        let bind_group_layout = gpu_context.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
411            entries: &[
412                wgpu::BindGroupLayoutEntry {
413                    binding: 0,
414                    visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
415                    ty: wgpu::BindingType::Buffer {
416                        ty: wgpu::BufferBindingType::Uniform,
417                        has_dynamic_offset: false,
418                        min_binding_size: None,
419                    },
420                    count: None,
421                },
422                wgpu::BindGroupLayoutEntry {
423                    binding: 1,
424                    visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
425                    ty: wgpu::BindingType::Buffer {
426                        ty: wgpu::BufferBindingType::Uniform,
427                        has_dynamic_offset: false,
428                        min_binding_size: None,
429                    },
430                    count: None,
431                },
432                wgpu::BindGroupLayoutEntry {
433                    binding: 2,
434                    visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
435                    ty: wgpu::BindingType::Buffer {
436                        ty: wgpu::BufferBindingType::Uniform,
437                        has_dynamic_offset: false,
438                        min_binding_size: None,
439                    },
440                    count: None,
441                },
442            ],
443            label: Some("mesh_bind_group_layout"),
444        });
445
446        // Create PBR pipeline
447        let pbr_shader = gpu_context.device.create_shader_module(wgpu::ShaderModuleDescriptor {
448            label: Some("PBR Mesh Shader"),
449            source: wgpu::ShaderSource::Wgsl(include_str!("shaders/mesh_pbr.wgsl").into()),
450        });
451
452        let pbr_pipeline = Self::create_render_pipeline(
453            &gpu_context.device,
454            &bind_group_layout,
455            &pbr_shader,
456            surface_format,
457            sample_count,
458            &config,
459            "PBR",
460        );
461
462        // Create flat pipeline
463        let flat_shader = gpu_context.device.create_shader_module(wgpu::ShaderModuleDescriptor {
464            label: Some("Flat Mesh Shader"),
465            source: wgpu::ShaderSource::Wgsl(include_str!("shaders/mesh_flat.wgsl").into()),
466        });
467
468        let flat_pipeline = Self::create_render_pipeline(
469            &gpu_context.device,
470            &bind_group_layout,
471            &flat_shader,
472            surface_format,
473            sample_count,
474            &config,
475            "Flat",
476        );
477
478        Ok(Self {
479            gpu_context,
480            surface,
481            surface_config,
482            pbr_pipeline,
483            flat_pipeline,
484            camera_uniform,
485            camera_buffer,
486            lighting_params,
487            lighting_buffer,
488            bind_group_layout,
489            config,
490            msaa_texture,
491            msaa_view,
492        })
493    }
494
495    /// Create a render pipeline for mesh rendering
496    fn create_render_pipeline(
497        device: &wgpu::Device,
498        bind_group_layout: &wgpu::BindGroupLayout,
499        shader: &wgpu::ShaderModule,
500        surface_format: wgpu::TextureFormat,
501        sample_count: u32,
502        config: &MeshRenderConfig,
503        label: &str,
504    ) -> wgpu::RenderPipeline {
505        let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
506            label: Some(&format!("{} Mesh Render Pipeline Layout", label)),
507            bind_group_layouts: &[bind_group_layout],
508            push_constant_ranges: &[],
509        });
510
511        device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
512            label: Some(&format!("{} Mesh Render Pipeline", label)),
513            layout: Some(&render_pipeline_layout),
514            vertex: wgpu::VertexState {
515                module: shader,
516                entry_point: "vs_main",
517                buffers: &[MeshVertex::desc()],
518                compilation_options: wgpu::PipelineCompilationOptions::default(),
519            },
520            fragment: Some(wgpu::FragmentState {
521                module: shader,
522                entry_point: "fs_main",
523                targets: &[Some(wgpu::ColorTargetState {
524                    format: surface_format,
525                    blend: Some(wgpu::BlendState::REPLACE),
526                    write_mask: wgpu::ColorWrites::ALL,
527                })],
528                compilation_options: wgpu::PipelineCompilationOptions::default(),
529            }),
530            primitive: wgpu::PrimitiveState {
531                topology: if config.wireframe_mode {
532                    wgpu::PrimitiveTopology::LineList
533                } else {
534                    wgpu::PrimitiveTopology::TriangleList
535                },
536                strip_index_format: None,
537                front_face: wgpu::FrontFace::Ccw,
538                cull_mode: if config.enable_backface_culling {
539                    Some(wgpu::Face::Back)
540                } else {
541                    None
542                },
543                unclipped_depth: false,
544                polygon_mode: wgpu::PolygonMode::Fill,
545                conservative: false,
546            },
547            depth_stencil: if config.enable_depth_test {
548                Some(wgpu::DepthStencilState {
549                    format: wgpu::TextureFormat::Depth32Float,
550                    depth_write_enabled: true,
551                    depth_compare: wgpu::CompareFunction::Less,
552                    stencil: wgpu::StencilState::default(),
553                    bias: wgpu::DepthBiasState::default(),
554                })
555            } else {
556                None
557            },
558            multisample: wgpu::MultisampleState {
559                count: sample_count,
560                mask: !0,
561                alpha_to_coverage_enabled: false,
562            },
563            multiview: None,
564        })
565    }
566
567    /// Update camera matrices and position
568    pub fn update_camera(&mut self, view_matrix: Matrix4<f32>, proj_matrix: Matrix4<f32>, camera_pos: Vector3<f32>) {
569        self.camera_uniform.view_proj = (proj_matrix * view_matrix).into();
570        self.camera_uniform.view_pos = camera_pos.into();
571        
572        self.gpu_context.queue.write_buffer(
573            &self.camera_buffer,
574            0,
575            bytemuck::bytes_of(&self.camera_uniform),
576        );
577    }
578
579    /// Update lighting parameters
580    pub fn update_lighting(&mut self, params: MeshLightingParams) {
581        self.lighting_params = params;
582        self.gpu_context.queue.write_buffer(
583            &self.lighting_buffer,
584            0,
585            bytemuck::bytes_of(&self.lighting_params),
586        );
587    }
588
589    /// Resize renderer
590    pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
591        if new_size.width > 0 && new_size.height > 0 {
592            self.surface_config.width = new_size.width;
593            self.surface_config.height = new_size.height;
594            self.surface.configure(&self.gpu_context.device, &self.surface_config);
595            
596            // Recreate MSAA texture if needed
597            if self.config.enable_multisampling {
598                let msaa_texture = self.gpu_context.device.create_texture(&wgpu::TextureDescriptor {
599                    label: Some("MSAA Texture"),
600                    size: wgpu::Extent3d {
601                        width: new_size.width,
602                        height: new_size.height,
603                        depth_or_array_layers: 1,
604                    },
605                    mip_level_count: 1,
606                    sample_count: 4,
607                    dimension: wgpu::TextureDimension::D2,
608                    format: self.surface_config.format,
609                    usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
610                    view_formats: &[],
611                });
612                let msaa_view = msaa_texture.create_view(&wgpu::TextureViewDescriptor::default());
613                self.msaa_texture = Some(msaa_texture);
614                self.msaa_view = Some(msaa_view);
615            }
616        }
617    }
618
619    /// Create vertex buffer
620    pub fn create_vertex_buffer(&self, vertices: &[MeshVertex]) -> wgpu::Buffer {
621        self.gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
622            label: Some("Mesh Vertex Buffer"),
623            contents: bytemuck::cast_slice(vertices),
624            usage: wgpu::BufferUsages::VERTEX,
625        })
626    }
627
628    /// Create index buffer
629    pub fn create_index_buffer(&self, indices: &[u32]) -> wgpu::Buffer {
630        self.gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
631            label: Some("Mesh Index Buffer"),
632            contents: bytemuck::cast_slice(indices),
633            usage: wgpu::BufferUsages::INDEX,
634        })
635    }
636
637    /// Create depth texture
638    pub fn create_depth_texture(&self) -> wgpu::Texture {
639        let sample_count = if self.config.enable_multisampling { 4 } else { 1 };
640        
641        self.gpu_context.device.create_texture(&wgpu::TextureDescriptor {
642            label: Some("Depth Texture"),
643            size: wgpu::Extent3d {
644                width: self.surface_config.width,
645                height: self.surface_config.height,
646                depth_or_array_layers: 1,
647            },
648            mip_level_count: 1,
649            sample_count,
650            dimension: wgpu::TextureDimension::D2,
651            format: wgpu::TextureFormat::Depth32Float,
652            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
653            view_formats: &[],
654        })
655    }
656
657    /// Render mesh with specified shading mode
658    pub fn render(&self, mesh: &GpuMesh, shading_mode: ShadingMode) -> Result<()> {
659        let vertex_buffer = self.create_vertex_buffer(&mesh.vertices);
660        let index_buffer = self.create_index_buffer(&mesh.indices);
661        let depth_texture = self.create_depth_texture();
662        let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
663
664        let output = self.surface.get_current_texture()
665            .map_err(|e| Error::Gpu(format!("Failed to get surface texture: {:?}", e)))?;
666        
667        let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
668
669        // Create material buffer based on shading mode
670        let material_buffer = match shading_mode {
671            ShadingMode::Pbr => {
672                self.gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
673                    label: Some("PBR Material Buffer"),
674                    contents: bytemuck::bytes_of(&mesh.material),
675                    usage: wgpu::BufferUsages::UNIFORM,
676                })
677            }
678            ShadingMode::Flat => {
679                let flat_material = FlatMaterial {
680                    color: mesh.material.albedo,
681                    _padding: 0.0,
682                };
683                self.gpu_context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
684                    label: Some("Flat Material Buffer"),
685                    contents: bytemuck::bytes_of(&flat_material),
686                    usage: wgpu::BufferUsages::UNIFORM,
687                })
688            }
689        };
690
691        let bind_group = self.gpu_context.device.create_bind_group(&wgpu::BindGroupDescriptor {
692            layout: &self.bind_group_layout,
693            entries: &[
694                wgpu::BindGroupEntry {
695                    binding: 0,
696                    resource: self.camera_buffer.as_entire_binding(),
697                },
698                wgpu::BindGroupEntry {
699                    binding: 1,
700                    resource: material_buffer.as_entire_binding(),
701                },
702                wgpu::BindGroupEntry {
703                    binding: 2,
704                    resource: self.lighting_buffer.as_entire_binding(),
705                },
706            ],
707            label: Some("mesh_bind_group"),
708        });
709
710        let mut encoder = self.gpu_context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
711            label: Some("Mesh Render Encoder"),
712        });
713
714        // Determine render target
715        let (color_attachment, resolve_target) = if let Some(ref msaa_view) = self.msaa_view {
716            (msaa_view, Some(&view))
717        } else {
718            (&view, None)
719        };
720
721        {
722            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
723                label: Some("Mesh Render Pass"),
724                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
725                    view: color_attachment,
726                    resolve_target,
727                    ops: wgpu::Operations {
728                        load: wgpu::LoadOp::Clear(wgpu::Color {
729                            r: self.config.background_color[0],
730                            g: self.config.background_color[1],
731                            b: self.config.background_color[2],
732                            a: self.config.background_color[3],
733                        }),
734                        store: wgpu::StoreOp::Store,
735                    },
736                })],
737                depth_stencil_attachment: if self.config.enable_depth_test {
738                    Some(wgpu::RenderPassDepthStencilAttachment {
739                        view: &depth_view,
740                        depth_ops: Some(wgpu::Operations {
741                            load: wgpu::LoadOp::Clear(1.0),
742                            store: wgpu::StoreOp::Store,
743                        }),
744                        stencil_ops: None,
745                    })
746                } else {
747                    None
748                },
749                timestamp_writes: None,
750                occlusion_query_set: None,
751            });
752
753            let pipeline = match shading_mode {
754                ShadingMode::Pbr => &self.pbr_pipeline,
755                ShadingMode::Flat => &self.flat_pipeline,
756            };
757
758            render_pass.set_pipeline(pipeline);
759            render_pass.set_bind_group(0, &bind_group, &[]);
760            render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
761            render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
762            render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);
763        }
764
765        self.gpu_context.queue.submit(std::iter::once(encoder.finish()));
766        output.present();
767
768        Ok(())
769    }
770}
771
772/// Convert threecrate mesh to GPU mesh format
773pub fn mesh_to_gpu_mesh(
774    vertices: &[Point3<f32>],
775    indices: &[u32],
776    normals: Option<&[Vector3<f32>]>,
777    colors: Option<&[[f32; 3]]>,
778    material: Option<PbrMaterial>,
779) -> GpuMesh {
780    let gpu_vertices: Vec<MeshVertex> = vertices
781        .iter()
782        .enumerate()
783        .map(|(i, vertex)| {
784            let normal = normals
785                .and_then(|n| n.get(i))
786                .map(|n| [n.x, n.y, n.z])
787                .unwrap_or([0.0, 0.0, 1.0]);
788            
789            let color = colors
790                .and_then(|c| c.get(i))
791                .copied()
792                .unwrap_or([0.8, 0.8, 0.8]);
793            
794            MeshVertex::new([vertex.x, vertex.y, vertex.z], normal, [0.0, 0.0], color)
795        })
796        .collect();
797
798    GpuMesh::new(
799        gpu_vertices,
800        indices.to_vec(),
801        material.unwrap_or_default(),
802    )
803}