1use crate::wgpu::BindingEntry;
2
3pub struct MaterialSpec {
4 pub shader_source: &'static str,
5 pub entries: Vec<BindingEntry>,
6 pub cull_mode: Option<wgpu::Face>,
7 pub depth: Option<wgpu::DepthStencilState>,
8 pub color_format: Option<wgpu::TextureFormat>,
9}
10
11impl MaterialSpec {
12 pub fn binding_index(&self, name: &str) -> Option<u32> {
13 self.entries
14 .iter()
15 .position(|e| e.name == name)
16 .map(|i| i as u32)
17 }
18}
19
20impl MaterialSpec {
21 pub fn new(shader_source: &'static str, entries: Vec<BindingEntry>) -> Self {
22 Self {
23 shader_source,
24 entries,
25 cull_mode: Some(wgpu::Face::Back),
26 depth: None,
27 color_format: None,
28 }
29 }
30 pub fn with_cull_mode(mut self, mode: Option<wgpu::Face>) -> Self {
31 self.cull_mode = mode;
32 self
33 }
34 pub fn with_depth(mut self, depth: wgpu::DepthStencilState) -> Self {
35 self.depth = Some(depth);
36 self
37 }
38 pub fn with_color_format(mut self, format: wgpu::TextureFormat) -> Self {
39 self.color_format = Some(format);
40 self
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use std::borrow::Cow;
47
48 use crate::{assets::upload::Asset, wgpu::material::MaterialSpec};
49
50 pub struct GPUMaterial {
51 pub pipeline: wgpu::RenderPipeline,
52 pub layout: wgpu::BindGroupLayout,
53 }
54
55 pub struct Material {
56 pub descriptor: MaterialSpec,
57 }
58
59 impl Asset<wgpu::Device> for GPUMaterial {
60 type Source = Material;
61 type Deps<'a> = ();
62
63 fn upload<'a>(
64 source: &Self::Source,
65 backend: &wgpu::Device,
66 deps: &Self::Deps<'a>,
67 ) -> Option<Self> {
68 let desc = &source.descriptor;
69
70 let entries: Vec<_> = source
71 .descriptor
72 .entries
73 .iter()
74 .enumerate()
75 .map(|(i, e)| e.layout_entry(i as u32))
76 .collect();
77
78 let layout = backend.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
79 label: Some("Bind Group Layout"),
80 entries: &entries,
81 });
82
83 let pipeline_layout = backend.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
84 label: Some("Pipeline Layout"),
85 bind_group_layouts: &[Some(&layout)],
86 immediate_size: 0,
87 });
88
89 let module = backend.create_shader_module(wgpu::ShaderModuleDescriptor {
90 label: Some("Shader Module"),
91 source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(source.descriptor.shader_source)),
92 });
93
94 let pipeline = backend.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
95 label: Some("Render Pipeline"),
96 layout: Some(&pipeline_layout),
97 vertex: wgpu::VertexState {
98 module: &module,
99 entry_point: Some("vs_main"),
100 buffers: &[],
101 compilation_options: Default::default(),
102 },
103 primitive: wgpu::PrimitiveState {
104 topology: wgpu::PrimitiveTopology::TriangleList,
105 strip_index_format: None,
106 front_face: wgpu::FrontFace::Ccw,
107 cull_mode: source.descriptor.cull_mode,
108 unclipped_depth: false,
109 polygon_mode: wgpu::PolygonMode::Fill,
110 conservative: false,
111 },
112 depth_stencil: source.descriptor.depth.clone(),
113 multisample: wgpu::MultisampleState::default(),
114 fragment: Some(wgpu::FragmentState {
115 module: &module,
116 entry_point: Some("fs_main"),
117 targets: &[],
118 compilation_options: Default::default(),
119 }),
120 multiview_mask: None,
121 cache: None,
122 });
123
124 Some(Self { pipeline, layout })
125 }
126 }
127}