1use wgpu::{Buffer, BufferUsages, VertexBufferLayout, VertexStepMode, VertexAttribute, VertexFormat};
6use bytemuck::{Pod, Zeroable};
7
8use crate::renderer::RenderDevice;
9
10pub trait Vertex: Pod + Zeroable {
23 fn layout() -> VertexBufferLayout<'static>;
25}
26
27#[repr(C)]
50#[derive(Copy, Clone, Debug, Pod, Zeroable)]
51pub struct ColorVertex {
52 pub position: [f32; 3],
54 pub color: [f32; 3],
56}
57
58impl Vertex for ColorVertex {
59 fn layout() -> VertexBufferLayout<'static> {
60 const ATTRIBUTES: &[VertexAttribute] = &[
61 VertexAttribute {
63 offset: 0,
64 shader_location: 0,
65 format: VertexFormat::Float32x3,
66 },
67 VertexAttribute {
69 offset: std::mem::size_of::<[f32; 3]>() as u64,
70 shader_location: 1,
71 format: VertexFormat::Float32x3,
72 },
73 ];
74
75 VertexBufferLayout {
76 array_stride: std::mem::size_of::<ColorVertex>() as u64,
77 step_mode: VertexStepMode::Vertex,
78 attributes: ATTRIBUTES,
79 }
80 }
81}
82
83#[repr(C)]
108#[derive(Copy, Clone, Debug, Pod, Zeroable)]
109pub struct MeshVertex {
110 pub position: [f32; 3],
112 pub normal: [f32; 3],
114 pub texcoord: [f32; 2],
116}
117
118impl Vertex for MeshVertex {
119 fn layout() -> VertexBufferLayout<'static> {
120 const ATTRIBUTES: &[VertexAttribute] = &[
121 VertexAttribute {
122 offset: 0,
123 shader_location: 0,
124 format: VertexFormat::Float32x3,
125 },
126 VertexAttribute {
127 offset: 12,
128 shader_location: 1,
129 format: VertexFormat::Float32x3,
130 },
131 VertexAttribute {
132 offset: 24,
133 shader_location: 2,
134 format: VertexFormat::Float32x2,
135 },
136 ];
137
138 VertexBufferLayout {
139 array_stride: std::mem::size_of::<MeshVertex>() as u64,
140 step_mode: VertexStepMode::Vertex,
141 attributes: ATTRIBUTES,
142 }
143 }
144}
145
146#[repr(C)]
173#[derive(Copy, Clone, Debug, Pod, Zeroable)]
174pub struct PbrVertex {
175 pub position: [f32; 3],
177 pub normal: [f32; 3],
179 pub texcoord: [f32; 2],
181 pub tangent: [f32; 4],
183}
184
185impl Vertex for PbrVertex {
186 fn layout() -> VertexBufferLayout<'static> {
187 const ATTRIBUTES: &[VertexAttribute] = &[
188 VertexAttribute {
189 offset: 0,
190 shader_location: 0,
191 format: VertexFormat::Float32x3,
192 },
193 VertexAttribute {
194 offset: 12,
195 shader_location: 1,
196 format: VertexFormat::Float32x3,
197 },
198 VertexAttribute {
199 offset: 24,
200 shader_location: 2,
201 format: VertexFormat::Float32x2,
202 },
203 VertexAttribute {
204 offset: 32,
205 shader_location: 3,
206 format: VertexFormat::Float32x4,
207 },
208 ];
209
210 VertexBufferLayout {
211 array_stride: std::mem::size_of::<PbrVertex>() as u64,
212 step_mode: VertexStepMode::Vertex,
213 attributes: ATTRIBUTES,
214 }
215 }
216}
217
218#[repr(C)]
234#[derive(Copy, Clone, Debug, Pod, Zeroable)]
235pub struct SkinnedVertex {
236 pub position: [f32; 3],
237 pub normal: [f32; 3],
238 pub texcoord: [f32; 2],
239 pub tangent: [f32; 4],
240 pub joint_indices: [u16; 4],
241 pub joint_weights: [f32; 4],
242}
243
244impl Vertex for SkinnedVertex {
245 fn layout() -> VertexBufferLayout<'static> {
246 const ATTRIBUTES: &[VertexAttribute] = &[
247 VertexAttribute { offset: 0, shader_location: 0, format: VertexFormat::Float32x3 },
248 VertexAttribute { offset: 12, shader_location: 1, format: VertexFormat::Float32x3 },
249 VertexAttribute { offset: 24, shader_location: 2, format: VertexFormat::Float32x2 },
250 VertexAttribute { offset: 32, shader_location: 3, format: VertexFormat::Float32x4 },
251 VertexAttribute { offset: 48, shader_location: 4, format: VertexFormat::Uint16x4 },
252 VertexAttribute { offset: 56, shader_location: 5, format: VertexFormat::Float32x4 },
253 ];
254
255 VertexBufferLayout {
256 array_stride: std::mem::size_of::<SkinnedVertex>() as u64,
257 step_mode: VertexStepMode::Vertex,
258 attributes: ATTRIBUTES,
259 }
260 }
261}
262
263pub fn create_vertex_buffer<V: Vertex>(
289 device: &RenderDevice,
290 label: &str,
291 vertices: &[V],
292) -> Buffer {
293 use wgpu::util::{BufferInitDescriptor, DeviceExt};
294
295 device.device().create_buffer_init(&BufferInitDescriptor {
296 label: Some(label),
297 contents: bytemuck::cast_slice(vertices),
298 usage: BufferUsages::VERTEX,
299 })
300}
301
302pub fn create_index_buffer(
324 device: &RenderDevice,
325 label: &str,
326 indices: &[u16],
327) -> Buffer {
328 use wgpu::util::{BufferInitDescriptor, DeviceExt};
329
330 device.device().create_buffer_init(&BufferInitDescriptor {
331 label: Some(label),
332 contents: bytemuck::cast_slice(indices),
333 usage: BufferUsages::INDEX,
334 })
335}
336
337pub fn create_index_buffer_u32(
353 device: &RenderDevice,
354 label: &str,
355 indices: &[u32],
356) -> Buffer {
357 use wgpu::util::{BufferInitDescriptor, DeviceExt};
358
359 device.device().create_buffer_init(&BufferInitDescriptor {
360 label: Some(label),
361 contents: bytemuck::cast_slice(indices),
362 usage: BufferUsages::INDEX,
363 })
364}
365
366pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
368
369pub const HDR_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float;
371
372pub const SHADOW_MAP_SIZE: u32 = 2048;
374
375pub const MSAA_SAMPLE_COUNT: u32 = 4;
377
378pub fn create_shadow_map(
393 device: &RenderDevice,
394 size: u32,
395 label: &str,
396) -> (wgpu::Texture, wgpu::TextureView) {
397 let texture = device.device().create_texture(&wgpu::TextureDescriptor {
398 label: Some(label),
399 size: wgpu::Extent3d {
400 width: size,
401 height: size,
402 depth_or_array_layers: 1,
403 },
404 mip_level_count: 1,
405 sample_count: 1,
406 dimension: wgpu::TextureDimension::D2,
407 format: DEPTH_FORMAT,
408 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
409 view_formats: &[],
410 });
411 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
412 (texture, view)
413}
414
415pub fn create_shadow_sampler(device: &RenderDevice, label: &str) -> wgpu::Sampler {
430 device.device().create_sampler(&wgpu::SamplerDescriptor {
431 label: Some(label),
432 address_mode_u: wgpu::AddressMode::ClampToEdge,
433 address_mode_v: wgpu::AddressMode::ClampToEdge,
434 mag_filter: wgpu::FilterMode::Linear,
435 min_filter: wgpu::FilterMode::Linear,
436 compare: Some(wgpu::CompareFunction::LessEqual),
437 ..Default::default()
438 })
439}
440
441pub fn create_uniform_buffer(
463 device: &RenderDevice,
464 label: &str,
465 contents: &[u8],
466) -> Buffer {
467 use wgpu::util::{BufferInitDescriptor, DeviceExt};
468
469 device.device().create_buffer_init(&BufferInitDescriptor {
470 label: Some(label),
471 contents,
472 usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
473 })
474}
475
476pub fn create_depth_texture(
502 device: &RenderDevice,
503 width: u32,
504 height: u32,
505 label: &str,
506) -> (wgpu::Texture, wgpu::TextureView) {
507 let texture = device.device().create_texture(&wgpu::TextureDescriptor {
508 label: Some(label),
509 size: wgpu::Extent3d {
510 width,
511 height,
512 depth_or_array_layers: 1,
513 },
514 mip_level_count: 1,
515 sample_count: 1,
516 dimension: wgpu::TextureDimension::D2,
517 format: DEPTH_FORMAT,
518 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
519 view_formats: &[],
520 });
521 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
522 (texture, view)
523}
524
525pub fn create_hdr_render_target(
552 device: &RenderDevice,
553 width: u32,
554 height: u32,
555 label: &str,
556) -> (wgpu::Texture, wgpu::TextureView) {
557 let texture = device.device().create_texture(&wgpu::TextureDescriptor {
558 label: Some(label),
559 size: wgpu::Extent3d {
560 width,
561 height,
562 depth_or_array_layers: 1,
563 },
564 mip_level_count: 1,
565 sample_count: 1,
566 dimension: wgpu::TextureDimension::D2,
567 format: HDR_FORMAT,
568 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
569 view_formats: &[],
570 });
571 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
572 (texture, view)
573}
574
575pub fn create_depth_texture_msaa(
577 device: &RenderDevice,
578 width: u32,
579 height: u32,
580 label: &str,
581) -> (wgpu::Texture, wgpu::TextureView) {
582 let texture = device.device().create_texture(&wgpu::TextureDescriptor {
583 label: Some(label),
584 size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
585 mip_level_count: 1,
586 sample_count: MSAA_SAMPLE_COUNT,
587 dimension: wgpu::TextureDimension::D2,
588 format: DEPTH_FORMAT,
589 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
590 view_formats: &[],
591 });
592 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
593 (texture, view)
594}
595
596pub fn create_hdr_msaa_texture(
600 device: &RenderDevice,
601 width: u32,
602 height: u32,
603 label: &str,
604) -> (wgpu::Texture, wgpu::TextureView) {
605 let texture = device.device().create_texture(&wgpu::TextureDescriptor {
606 label: Some(label),
607 size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
608 mip_level_count: 1,
609 sample_count: MSAA_SAMPLE_COUNT,
610 dimension: wgpu::TextureDimension::D2,
611 format: HDR_FORMAT,
612 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
613 view_formats: &[],
614 });
615 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
616 (texture, view)
617}
618
619pub fn create_texture(
642 device: &RenderDevice,
643 width: u32,
644 height: u32,
645 data: &[u8],
646 label: &str,
647) -> (wgpu::Texture, wgpu::TextureView) {
648 let size = wgpu::Extent3d {
649 width,
650 height,
651 depth_or_array_layers: 1,
652 };
653
654 let texture = device.device().create_texture(&wgpu::TextureDescriptor {
655 label: Some(label),
656 size,
657 mip_level_count: 1,
658 sample_count: 1,
659 dimension: wgpu::TextureDimension::D2,
660 format: wgpu::TextureFormat::Rgba8UnormSrgb,
661 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
662 view_formats: &[],
663 });
664
665 device.queue().write_texture(
666 wgpu::ImageCopyTexture {
667 texture: &texture,
668 mip_level: 0,
669 origin: wgpu::Origin3d::ZERO,
670 aspect: wgpu::TextureAspect::All,
671 },
672 data,
673 wgpu::ImageDataLayout {
674 offset: 0,
675 bytes_per_row: Some(4 * width),
676 rows_per_image: Some(height),
677 },
678 size,
679 );
680
681 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
682 (texture, view)
683}
684
685pub fn create_texture_linear(
702 device: &RenderDevice,
703 width: u32,
704 height: u32,
705 data: &[u8],
706 label: &str,
707) -> (wgpu::Texture, wgpu::TextureView) {
708 let size = wgpu::Extent3d {
709 width,
710 height,
711 depth_or_array_layers: 1,
712 };
713
714 let texture = device.device().create_texture(&wgpu::TextureDescriptor {
715 label: Some(label),
716 size,
717 mip_level_count: 1,
718 sample_count: 1,
719 dimension: wgpu::TextureDimension::D2,
720 format: wgpu::TextureFormat::Rgba8Unorm,
721 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
722 view_formats: &[],
723 });
724
725 device.queue().write_texture(
726 wgpu::ImageCopyTexture {
727 texture: &texture,
728 mip_level: 0,
729 origin: wgpu::Origin3d::ZERO,
730 aspect: wgpu::TextureAspect::All,
731 },
732 data,
733 wgpu::ImageDataLayout {
734 offset: 0,
735 bytes_per_row: Some(4 * width),
736 rows_per_image: Some(height),
737 },
738 size,
739 );
740
741 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
742 (texture, view)
743}
744
745pub fn create_sampler(device: &RenderDevice, label: &str) -> wgpu::Sampler {
758 device.device().create_sampler(&wgpu::SamplerDescriptor {
759 label: Some(label),
760 address_mode_u: wgpu::AddressMode::Repeat,
761 address_mode_v: wgpu::AddressMode::Repeat,
762 address_mode_w: wgpu::AddressMode::Repeat,
763 mag_filter: wgpu::FilterMode::Linear,
764 min_filter: wgpu::FilterMode::Linear,
765 mipmap_filter: wgpu::FilterMode::Nearest,
766 ..Default::default()
767 })
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773
774 #[test]
775 fn test_color_vertex_size() {
776 assert_eq!(std::mem::size_of::<ColorVertex>(), 24); }
778
779 #[test]
780 fn test_color_vertex_layout() {
781 let layout = ColorVertex::layout();
782 assert_eq!(layout.array_stride, 24);
783 assert_eq!(layout.attributes.len(), 2);
784 assert_eq!(layout.attributes[0].offset, 0);
785 assert_eq!(layout.attributes[0].shader_location, 0);
786 assert_eq!(layout.attributes[1].offset, 12);
787 assert_eq!(layout.attributes[1].shader_location, 1);
788 }
789
790 #[test]
791 fn test_color_vertex_creation() {
792 let v = ColorVertex {
793 position: [1.0, 2.0, 3.0],
794 color: [0.5, 0.5, 0.5],
795 };
796 assert_eq!(v.position, [1.0, 2.0, 3.0]);
797 assert_eq!(v.color, [0.5, 0.5, 0.5]);
798 }
799
800 #[test]
801 fn test_color_vertex_bytemuck() {
802 let vertices = [
803 ColorVertex { position: [0.0, 0.5, 0.0], color: [1.0, 0.0, 0.0] },
804 ColorVertex { position: [-0.5, -0.5, 0.0], color: [0.0, 1.0, 0.0] },
805 ];
806 let bytes: &[u8] = bytemuck::cast_slice(&vertices);
807 assert_eq!(bytes.len(), 48); }
809
810 #[test]
811 fn test_depth_format() {
812 assert_eq!(DEPTH_FORMAT, wgpu::TextureFormat::Depth32Float);
813 }
814
815 #[test]
816 fn test_mesh_vertex_size() {
817 assert_eq!(std::mem::size_of::<MeshVertex>(), 32); }
819
820 #[test]
821 fn test_mesh_vertex_layout() {
822 let layout = MeshVertex::layout();
823 assert_eq!(layout.array_stride, 32);
824 assert_eq!(layout.attributes.len(), 3);
825 assert_eq!(layout.attributes[0].offset, 0); assert_eq!(layout.attributes[1].offset, 12); assert_eq!(layout.attributes[2].offset, 24); }
829
830 #[test]
831 fn test_pbr_vertex_size() {
832 assert_eq!(std::mem::size_of::<PbrVertex>(), 48); }
834
835 #[test]
836 fn test_pbr_vertex_layout() {
837 let layout = PbrVertex::layout();
838 assert_eq!(layout.array_stride, 48);
839 assert_eq!(layout.attributes.len(), 4);
840 assert_eq!(layout.attributes[0].offset, 0); assert_eq!(layout.attributes[1].offset, 12); assert_eq!(layout.attributes[2].offset, 24); assert_eq!(layout.attributes[3].offset, 32); }
845}