1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! Shader template for the **masked** (alpha-tested) geometry raster variant.
//!
//! Renders: masked bind groups (augmented group 0 + reused groups 1-3) + the
//! plain geometry **vertex** shader (reused verbatim, non-instanced /
//! uniform-meta) + the masked **fragment** (cutoff `discard` + the same
//! visibility-buffer write as the plain pass).
use askama::Template;
use crate::{
dynamic_materials::ShadingBase,
render_passes::geometry::shader::{
cache_key::ShaderCacheKeyGeometry, masked_cache_key::ShaderCacheKeyGeometryMasked,
template::ShaderTemplateGeometryVertex,
},
shaders::{AwsmShaderError, Result},
};
/// Masked geometry shader template components.
#[derive(Debug)]
pub struct ShaderTemplateGeometryMasked {
pub bind_groups: ShaderTemplateGeometryMaskedBindGroups,
pub vertex: ShaderTemplateGeometryVertex,
pub fragment: ShaderTemplateGeometryMaskedFragment,
}
/// Bind-group template for the masked geometry variant.
#[derive(Template, Debug)]
#[template(path = "masked_wgsl/bind_groups.wgsl", whitespace = "minimize")]
pub struct ShaderTemplateGeometryMaskedBindGroups {
texture_pool_arrays_len: u32,
texture_pool_samplers_len: u32,
}
impl ShaderTemplateGeometryMaskedBindGroups {
/// Builds the masked bind-group template for the given texture-pool lengths.
/// Shared with the combined masked + custom-vertex template, which reuses the
/// identical (augmented group-0) layout verbatim.
pub fn new(texture_pool_arrays_len: u32, texture_pool_samplers_len: u32) -> Self {
Self {
texture_pool_arrays_len,
texture_pool_samplers_len,
}
}
}
/// Fragment template for the masked geometry variant.
#[derive(Template, Debug)]
#[template(path = "masked_wgsl/fragment.wgsl", whitespace = "minimize")]
pub struct ShaderTemplateGeometryMaskedFragment {
texture_pool_arrays_len: u32,
texture_pool_samplers_len: u32,
/// MSAA sample count (0 = single-sampled). When > 1 the fragment emits a
/// `sample_mask` of analytic cutout coverage so the MSAA edge-resolve
/// anti-aliases the cutout boundary; when 0 it uses a binary `discard`.
msaa_sample_count: u32,
/// Built-in shading family (selects the base-color path) or `Custom`
/// (emits the author's alpha-only fragment).
base: ShadingBase,
/// Auto-generated `MaterialData` struct (custom only; empty otherwise).
dynamic_struct_decl: String,
/// Auto-generated `material_data_load` accessor (custom only).
dynamic_loader_decl: String,
/// Auto-generated per-texture `material_sample_<name>` helpers (custom only).
dynamic_texture_helpers: String,
/// The author's alpha-only WGSL fragment body (custom only).
dynamic_alpha_wgsl: String,
/// The shared sprite-sheet cell math (Flipbook only; empty otherwise) —
/// `awsm_renderer_materials::flipbook::FLIPBOOK_CELL_WGSL`, injected so the masked
/// cutout evaluates the SAME cell the shaded material shows.
flipbook_cell_wgsl: String,
}
impl ShaderTemplateGeometryMaskedFragment {
/// Builds the masked fragment template. Shared with the combined masked +
/// custom-vertex template (which suppresses the Custom struct/loader so the
/// vertex hook's single copy is reused).
#[allow(clippy::too_many_arguments)]
pub fn new(
texture_pool_arrays_len: u32,
texture_pool_samplers_len: u32,
msaa_sample_count: u32,
base: ShadingBase,
dynamic_struct_decl: String,
dynamic_loader_decl: String,
dynamic_texture_helpers: String,
dynamic_alpha_wgsl: String,
flipbook_cell_wgsl: String,
) -> Self {
Self {
texture_pool_arrays_len,
texture_pool_samplers_len,
msaa_sample_count,
base,
dynamic_struct_decl,
dynamic_loader_decl,
dynamic_texture_helpers,
dynamic_alpha_wgsl,
flipbook_cell_wgsl,
}
}
}
impl TryFrom<&ShaderCacheKeyGeometryMasked> for ShaderTemplateGeometryMasked {
type Error = AwsmShaderError;
fn try_from(value: &ShaderCacheKeyGeometryMasked) -> Result<Self> {
// Reuse the plain geometry vertex shader: masked meshes always take the
// non-instanced, uniform-meta path, so build the vertex for exactly
// that shape.
let vertex_key = ShaderCacheKeyGeometry {
instancing_transforms: false,
meta_storage_array: false,
msaa_samples: None,
// Masked geometry reuses the plain vertex; custom-vertex masked is a
// CV2 follow-on, so the vertex stays non-custom here.
dynamic_vertex_shader: None,
};
let (struct_decl, loader_decl, texture_helpers, alpha_wgsl) = match &value.dynamic_alpha {
Some(info) => (
info.struct_decl.clone(),
info.loader_decl.clone(),
info.texture_helpers.clone(),
info.alpha_wgsl.clone(),
),
None => (String::new(), String::new(), String::new(), String::new()),
};
Ok(Self {
bind_groups: ShaderTemplateGeometryMaskedBindGroups::new(
value.texture_pool_arrays_len,
value.texture_pool_samplers_len,
),
vertex: ShaderTemplateGeometryVertex::new(&vertex_key),
fragment: ShaderTemplateGeometryMaskedFragment::new(
value.texture_pool_arrays_len,
value.texture_pool_samplers_len,
value.msaa_samples.unwrap_or(0),
value.base,
struct_decl,
loader_decl,
texture_helpers,
alpha_wgsl,
if value.base == ShadingBase::Flipbook {
awsm_renderer_materials::flipbook::FLIPBOOK_CELL_WGSL.to_string()
} else {
String::new()
},
),
})
}
}
impl ShaderTemplateGeometryMasked {
/// Renders the masked geometry shader template into WGSL.
pub fn into_source(self) -> Result<String> {
let bind_groups_source = self.bind_groups.render()?;
let vertex_source = self.vertex.render()?;
let fragment_source = self.fragment.render()?;
Ok(format!(
"{}\n{}\n{}",
bind_groups_source, vertex_source, fragment_source
))
}
/// Returns an optional debug label for shader compilation.
pub fn debug_label(&self) -> Option<&str> {
Some("Geometry Masked")
}
}