Search across packages, versions, files, declarations, and comments. Use multiple terms to narrow results (AND), and add @vertex, @fragment, or @compute to filter by shader stage.
1import package::sprite_mesh::{
2 types::{
3 SPRITE_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS,
4 SPRITE_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE,
5 SPRITE_MATERIAL_FLAGS_ALPHA_MODE_MASK,
6 SPRITE_MATERIAL_FLAGS_FLIP_X,
7 SPRITE_MATERIAL_FLAGS_FLIP_Y,
8 SPRITE_MATERIAL_FLAGS_TILE_X,
9 SPRITE_MATERIAL_FLAGS_TILE_Y,
10 },
11 bindings::material,
12};
13@if(BINDLESS)
14import package::sprite_mesh::bindings::material_indices;
15@if(!BINDLESS)
16import package::sprite_mesh::bindings::{texture, texture_sampler};
17import package::mesh2d::bindings::mesh;
18import bevy_render::bindless::{bindless_samplers_filtering, bindless_textures_2d};
20/// Samples the sprite's final color, including the tint and alpha discard, at a given UV.
21fn sample_final_color(uv: vec2<f32>, instance_index: u32) -> vec4<f32> {
22 let sprite_color = sample_sprite_texture(uv, instance_index);
23 return get_final_color(sprite_color, instance_index);
26/// Samples the sprite's texture without tint and alpha discard at a given UV.
27fn sample_sprite_texture(uv: vec2<f32>, instance_index: u32) -> vec4<f32> {
28 let final_uv = get_final_uv(uv, instance_index);
29@if(BINDLESS)
30 let slot = mesh[instance_index].material_bind_group_slot;
31@if(BINDLESS)
32 return textureSample(
33 bindless_textures_2d[material_indices[slot].texture],
34 bindless_samplers_filtering[material_indices[slot].texture_sampler],
35 final_uv
36 );
37@else
38 return textureSample(texture, texture_sampler, final_uv);
41/// Applies tint and alpha discard to the sprite's color.
42fn get_final_color(sprite_color: vec4<f32>, instance_index: u32) -> vec4<f32> {
43 var output_color = apply_tint(sprite_color, instance_index);
44 output_color = alpha_discard(output_color, instance_index);
45 return output_color;
48/// Applies all the necessary transformations to get the final UV that the texture should be sampled from.
49fn get_final_uv(uv: vec2<f32>, instance_index: u32) -> vec2<f32> {
50 var out = uv;
51 out = apply_flip(out, instance_index);
52 out = apply_tiling(out, instance_index);
53 out = apply_slicing(out, instance_index);
54 out = apply_uv_transform(out, instance_index);
55 return out;
58/// Flips the UV based on the sprite's flip X and Y properties.
59fn apply_flip(uv: vec2<f32>, instance_index: u32) -> vec2<f32> {
60@if(BINDLESS)
61 let slot = mesh[instance_index].material_bind_group_slot;
62@if(BINDLESS)
63 let flags = material[material_indices[slot].material].flags;
64@else
65 let flags = material.flags;
67 var out = uv;
68 if (flags & SPRITE_MATERIAL_FLAGS_FLIP_X) != 0u {
69 out.x = 1.0 - out.x;
70 }
71 if (flags & SPRITE_MATERIAL_FLAGS_FLIP_Y) != 0u {
72 out.y = 1.0 - out.y;
73 }
75 return out;
78/// Applies tiling to the UV based on the sprite's tiling properties when `image_mode` is `Tiled`.
79fn apply_tiling(uv: vec2<f32>, instance_index: u32) -> vec2<f32> {
80@if(BINDLESS)
81 let slot = mesh[instance_index].material_bind_group_slot;
82@if(BINDLESS)
83 let flags = material[material_indices[slot].material].flags;
84@else
85 let flags = material.flags;
86@if(BINDLESS)
87 let tile_stretch_value = material[material_indices[slot].material].tile_stretch_value;
88@else
89 let tile_stretch_value = material.tile_stretch_value;
91 var out = uv;
92 if (flags & SPRITE_MATERIAL_FLAGS_TILE_X) != 0u {
93 out.x = (out.x - tile_stretch_value.x * floor(out.x / tile_stretch_value.x)) / tile_stretch_value.x;
94 }
95 if (flags & SPRITE_MATERIAL_FLAGS_TILE_Y) != 0u {
96 out.y = (out.y - tile_stretch_value.y * floor(out.y / tile_stretch_value.y)) / tile_stretch_value.y;
97 }
99 return out;
102/// Applies the sprite's UV transform,
103/// which is used for sampling the correct region from a texture atlas
104/// and scaling the sprite when `image_mode` is `Scaled`.
105fn apply_uv_transform(uv: vec2<f32>, instance_index: u32) -> vec2<f32> {
106@if(BINDLESS)
107 let slot = mesh[instance_index].material_bind_group_slot;
108@if(BINDLESS)
109 let uv_transform = material[material_indices[slot].material].uv_transform;
110@else
111 let uv_transform = material.uv_transform;
113 return (uv_transform * vec3(uv, 1.0)).xy;
116/// Applies UV slicing based on the sprite's slicing properties when `image_mode` is `Sliced`.
117fn apply_slicing(uv: vec2<f32>, instance_index: u32) -> vec2<f32> {
118@if(BINDLESS)
119 let slot = mesh[instance_index].material_bind_group_slot;
120@if(BINDLESS)
121 let scale = material[material_indices[slot].material].scale;
122@else
123 let scale = material.scale;
124@if(BINDLESS)
125 let min_inset = material[material_indices[slot].material].min_inset;
126@else
127 let min_inset = material.min_inset;
128@if(BINDLESS)
129 let max_inset = material[material_indices[slot].material].max_inset;
130@else
131 let max_inset = material.max_inset;
132@if(BINDLESS)
133 let side_stretch_value = material[material_indices[slot].material].side_stretch_value;
134@else
135 let side_stretch_value = material.side_stretch_value;
136@if(BINDLESS)
137 let center_stretch_value = material[material_indices[slot].material].center_stretch_value;
138@else
139 let center_stretch_value = material.center_stretch_value;
141 // using this as a temp check for slicing
142 if scale.x == 0.0 {
143 return uv;
144 }
146 let min_inset_scaled = min_inset / scale;
147 let max_inset_scaled = max_inset / scale;
149 let left = uv.x < min_inset_scaled.x;
150 let right = uv.x > 1.0 - max_inset_scaled.x;
151 let top = uv.y < min_inset_scaled.y;
152 let bottom = uv.y > 1.0 - max_inset_scaled.y;
154 // top-left corner
155 if top && left {
156 return uv * scale;
157 }
159 // top-right corner
160 if top && right {
161 return vec2<f32>(
162 1.0 - (1.0 - uv.x) * scale.x,
163 uv.y * scale.y,
164 );
165 }
167 // bottom-left corner
168 if bottom && left {
169 return vec2<f32>(
170 uv.x * scale.x,
171 1.0 - (1.0 - uv.y) * scale.y
172 );
173 }
175 // bottom-right corner
176 if bottom && right {
177 return vec2<f32>(1.0) - (vec2<f32>(1.0) - uv) * scale;
178 }
180 // top edge
181 if top {
182 return vec2<f32>(
183 tile_or_stretch(uv.x, min_inset_scaled.x, 1.0 - max_inset_scaled.x, min_inset.x, 1.0 - max_inset.x, side_stretch_value.x),
184 uv.y * scale.y
185 );
186 }
188 // bottom edge
189 if bottom {
190 return vec2<f32>(
191 tile_or_stretch(uv.x, min_inset_scaled.x, 1.0 - max_inset_scaled.x, min_inset.x, 1.0 - max_inset.x, side_stretch_value.x),
192 1.0 - (1.0 - uv.y) * scale.y
193 );
194 }
196 // left edge
197 if left {
198 return vec2<f32>(
199 uv.x * scale.x,
200 tile_or_stretch(uv.y, min_inset_scaled.y, 1.0 - max_inset_scaled.y, min_inset.y, 1.0 - max_inset.y, side_stretch_value.y)
201 );
202 }
204 // right edge
205 if right {
206 return vec2<f32>(
207 1.0 - (1.0 - uv.x) * scale.x,
208 tile_or_stretch(uv.y, min_inset_scaled.y, 1.0 - max_inset_scaled.y, min_inset.y, 1.0 - max_inset.y, side_stretch_value.y)
209 );
210 }
212 // center
213 return vec2<f32>(
214 tile_or_stretch(uv.x, min_inset_scaled.x, 1.0 - max_inset_scaled.x, min_inset.x, 1.0 - max_inset.x, center_stretch_value.x),
215 tile_or_stretch(uv.y, min_inset_scaled.y, 1.0 - max_inset_scaled.y, min_inset.y, 1.0 - max_inset.y, center_stretch_value.y)
216 );
219/// Applies the tint from the sprite's `color` property.
220fn apply_tint(sprite_color: vec4<f32>, instance_index: u32) -> vec4<f32> {
221@if(BINDLESS)
222 let slot = mesh[instance_index].material_bind_group_slot;
223@if(BINDLESS)
224 let color = material[material_indices[slot].material].color;
225@else
226 let color = material.color;
228 return sprite_color * color;
231/// Discards fragments based on the sprite's `alpha_cutoff` and `alpha_mode`.
232fn alpha_discard(output_color: vec4<f32>, instance_index: u32) -> vec4<f32> {
233@if(BINDLESS)
234 let slot = mesh[instance_index].material_bind_group_slot;
235@if(BINDLESS)
236 let flags = material[material_indices[slot].material].flags;
237@else
238 let flags = material.flags;
239@if(BINDLESS)
240 let alpha_cutoff = material[material_indices[slot].material].alpha_cutoff;
241@else
242 let alpha_cutoff = material.alpha_cutoff;
244 var color = output_color;
245 let alpha_mode = flags & SPRITE_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
247 if alpha_mode == SPRITE_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
248 // NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
249 color.a = 1.0;
250 }
252 else {
253 @if(MAY_DISCARD)
254 if alpha_mode == SPRITE_MATERIAL_FLAGS_ALPHA_MODE_MASK {
255 if color.a >= alpha_cutoff {
256 // NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
257 color.a = 1.0;
258 } else {
259 // NOTE: output_color.a < alpha_cutoff should not be rendered
260 discard;
261 }
262 }
263 }
265 return color;
268/// Maps a point p from [a, b] to [c, d], tiling it if stretch_value is not 0.
269fn tile_or_stretch(p: f32, a: f32, b: f32, c: f32, d: f32, stretch_value: f32) -> f32 {
270 if stretch_value == 0.0 {
271 return stretch_interval(p, a, b, c, d);
272 }
273 return tile_interval(p, a, b, c, d, stretch_value);
276/// Takes a point p from an interval [a, b] and maps it to a portion of the tile [c, d]
277fn tile_interval(p: f32, a: f32, b: f32, c: f32, d: f32, stretch_value: f32) -> f32 {
278 let value = (p - a) / (b - a);
279 let tile_value = (value - stretch_value * floor(value / stretch_value)) / stretch_value;
280 return tile_value * (d - c) + c;
283/// Takes a point p from an interval [a, b] and translates it to the interval [c, d]
284fn stretch_interval(p: f32, a: f32, b: f32, c: f32, d: f32) -> f32 {
285 return (p - a) / (b - a) * (d - c) + c;
Contents