|
| 1 | +#import bevy_render::view::View; |
| 2 | +#import bevy_render::globals::Globals; |
| 3 | + |
| 4 | +@group(0) @binding(0) |
| 5 | +var<uniform> view: View; |
| 6 | +@group(0) @binding(1) |
| 7 | +var<uniform> globals: Globals; |
| 8 | + |
| 9 | +@group(1) @binding(0) var sprite_texture: texture_2d<f32>; |
| 10 | +@group(1) @binding(1) var sprite_sampler: sampler; |
| 11 | + |
| 12 | +struct UiVertexOutput { |
| 13 | + @location(0) uv: vec2<f32>, |
| 14 | + @location(1) color: vec4<f32>, |
| 15 | + |
| 16 | + // Defines the dividing line that are used to split the texture atlas rect into corner, side and center slices |
| 17 | + // The distances are normalized and from the top left corner of the texture atlas rect |
| 18 | + // x = distance of the left vertical dividing line |
| 19 | + // y = distance of the top horizontal dividing line |
| 20 | + // z = distance of the right vertical dividing line |
| 21 | + // w = distance of the bottom horizontal dividing line |
| 22 | + @location(2) @interpolate(flat) texture_slices: vec4<f32>, |
| 23 | + |
| 24 | + // Defines the dividing line that are used to split the render target into into corner, side and center slices |
| 25 | + // The distances are normalized and from the top left corner of the render target |
| 26 | + // x = distance of left vertical dividing line |
| 27 | + // y = distance of top horizontal dividing line |
| 28 | + // z = distance of right vertical dividing line |
| 29 | + // w = distance of bottom horizontal dividing line |
| 30 | + @location(3) @interpolate(flat) target_slices: vec4<f32>, |
| 31 | + |
| 32 | + // The number of times the side or center texture slices should be repeated when mapping them to the border slices |
| 33 | + // x = number of times to repeat along the horizontal axis for the side textures |
| 34 | + // y = number of times to repeat along the vertical axis for the side textures |
| 35 | + // z = number of times to repeat along the horizontal axis for the center texture |
| 36 | + // w = number of times to repeat along the vertical axis for the center texture |
| 37 | + @location(4) @interpolate(flat) repeat: vec4<f32>, |
| 38 | + |
| 39 | + // normalized texture atlas rect coordinates |
| 40 | + // x, y = top, left corner of the atlas rect |
| 41 | + // z, w = bottom, right corner of the atlas rect |
| 42 | + @location(5) @interpolate(flat) atlas_rect: vec4<f32>, |
| 43 | + @builtin(position) position: vec4<f32>, |
| 44 | +} |
| 45 | + |
| 46 | +@vertex |
| 47 | +fn vertex( |
| 48 | + @location(0) vertex_position: vec3<f32>, |
| 49 | + @location(1) vertex_uv: vec2<f32>, |
| 50 | + @location(2) vertex_color: vec4<f32>, |
| 51 | + @location(3) texture_slices: vec4<f32>, |
| 52 | + @location(4) target_slices: vec4<f32>, |
| 53 | + @location(5) repeat: vec4<f32>, |
| 54 | + @location(6) atlas_rect: vec4<f32>, |
| 55 | +) -> UiVertexOutput { |
| 56 | + var out: UiVertexOutput; |
| 57 | + out.uv = vertex_uv; |
| 58 | + out.color = vertex_color; |
| 59 | + out.position = view.clip_from_world * vec4<f32>(vertex_position, 1.0); |
| 60 | + out.texture_slices = texture_slices; |
| 61 | + out.target_slices = target_slices; |
| 62 | + out.repeat = repeat; |
| 63 | + out.atlas_rect = atlas_rect; |
| 64 | + return out; |
| 65 | +} |
| 66 | + |
| 67 | +/// maps a point along the axis of the render target to slice coordinates |
| 68 | +fn map_axis_with_repeat( |
| 69 | + // normalized distance along the axis |
| 70 | + p: f32, |
| 71 | + // target min dividing point |
| 72 | + il: f32, |
| 73 | + // target max dividing point |
| 74 | + ih: f32, |
| 75 | + // slice min dividing point |
| 76 | + tl: f32, |
| 77 | + // slice max dividing point |
| 78 | + th: f32, |
| 79 | + // number of times to repeat the slice for sides and the center |
| 80 | + r: f32, |
| 81 | +) -> f32 { |
| 82 | + if p < il { |
| 83 | + // inside one of the two left (horizontal axis) or top (vertical axis) corners |
| 84 | + return (p / il) * tl; |
| 85 | + } else if ih < p { |
| 86 | + // inside one of the two (horizontal axis) or top (vertical axis) corners |
| 87 | + return th + ((p - ih) / (1 - ih)) * (1 - th); |
| 88 | + } else { |
| 89 | + // not inside a corner, repeat the texture |
| 90 | + return tl + fract((r * (p - il)) / (ih - il)) * (th - tl); |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +fn map_uvs_to_slice( |
| 95 | + uv: vec2<f32>, |
| 96 | + target_slices: vec4<f32>, |
| 97 | + texture_slices: vec4<f32>, |
| 98 | + repeat: vec4<f32>, |
| 99 | +) -> vec2<f32> { |
| 100 | + var r: vec2<f32>; |
| 101 | + if target_slices.x <= uv.x && uv.x <= target_slices.z && target_slices.y <= uv.y && uv.y <= target_slices.w { |
| 102 | + // use the center repeat values if the uv coords are inside the center slice of the target |
| 103 | + r = repeat.zw; |
| 104 | + } else { |
| 105 | + // use the side repeat values if the uv coords are outside the center slice |
| 106 | + r = repeat.xy; |
| 107 | + } |
| 108 | + |
| 109 | + // map horizontal axis |
| 110 | + let x = map_axis_with_repeat(uv.x, target_slices.x, target_slices.z, texture_slices.x, texture_slices.z, r.x); |
| 111 | + |
| 112 | + // map vertical axis |
| 113 | + let y = map_axis_with_repeat(uv.y, target_slices.y, target_slices.w, texture_slices.y, texture_slices.w, r.y); |
| 114 | + |
| 115 | + return vec2(x, y); |
| 116 | +} |
| 117 | + |
| 118 | +@fragment |
| 119 | +fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> { |
| 120 | + // map the target uvs to slice coords |
| 121 | + let uv = map_uvs_to_slice(in.uv, in.target_slices, in.texture_slices, in.repeat); |
| 122 | + |
| 123 | + // map the slice coords to texture coords |
| 124 | + let atlas_uv = in.atlas_rect.xy + uv * (in.atlas_rect.zw - in.atlas_rect.xy); |
| 125 | + |
| 126 | + return in.color * textureSample(sprite_texture, sprite_sampler, atlas_uv); |
| 127 | +} |
0 commit comments