Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI texture atlas slice shader #14990

Merged
merged 45 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
804b4de
initial commit, mostly boiler plate
ickshonpe Aug 24, 2024
6e31fe6
new pipeline works, slicer shader can render textures.
ickshonpe Aug 25, 2024
ddaedd8
basic nine patching implemented
ickshonpe Aug 26, 2024
3ce9c03
renamings
ickshonpe Aug 26, 2024
13c379a
added repeat attributes
ickshonpe Aug 26, 2024
b54afb6
rename insets to border
ickshonpe Aug 26, 2024
d14a778
changed sampling to nearest filtering (all hacks)
ickshonpe Aug 26, 2024
bb74656
geometry fix
ickshonpe Aug 26, 2024
0981e94
* Renamings, `*Slicer*` names to `*TextureSlicer*` names.
ickshonpe Aug 27, 2024
e79784d
reenabled texture filtering
ickshonpe Aug 27, 2024
b050f90
make map_uvs center inclusive of border
ickshonpe Aug 27, 2024
ae95585
tiling working
ickshonpe Aug 27, 2024
c6faba3
clean up, implement side tiling
ickshonpe Aug 27, 2024
a329a4d
Removed unused import
ickshonpe Aug 30, 2024
86ab91e
Removed unused marker component
ickshonpe Aug 30, 2024
abccc0b
Removed more unused imports
ickshonpe Aug 30, 2024
82f8fae
Merge branch 'main' into texture-slice-shader
ickshonpe Aug 30, 2024
44b7fbb
Removed unused qualification and unused variable.
ickshonpe Aug 30, 2024
db2c5c5
Reverted changes to `ui_texture_slice`
ickshonpe Aug 30, 2024
8b08979
Changed ui image extraction function to filter for without `ImageScal…
ickshonpe Aug 30, 2024
31386d5
Reverted changes to `ui` example
ickshonpe Aug 30, 2024
2d153b7
removed unused shader functions
ickshonpe Aug 30, 2024
dfbfb8e
implementing support for max_corner_scale
ickshonpe Aug 30, 2024
79ece1f
max_corner_scale seems correct
ickshonpe Aug 30, 2024
551d097
removed println
ickshonpe Aug 30, 2024
45d8c15
Cleanup, removed texture_slice module
ickshonpe Aug 30, 2024
3289c5b
fixed bind group labels
ickshonpe Aug 30, 2024
2885f92
added vertex parameter to texture slice shader for the atlas rect
ickshonpe Aug 30, 2024
7447173
atlas slicing implementation
ickshonpe Aug 30, 2024
1410810
fixed atlas coords
ickshonpe Aug 30, 2024
48b30be
Fixed atlas coords swizzle in fragment shader
ickshonpe Aug 31, 2024
dcf18f4
Removed unused
ickshonpe Aug 31, 2024
98128aa
allow too many arguments for `prepare_ui_slices`
ickshonpe Aug 31, 2024
fd75d94
Merge branch 'main' into texture-slice-shader
ickshonpe Aug 31, 2024
ff2afd8
Fixed system ambiguities count
ickshonpe Sep 2, 2024
d470548
Merge branch 'texture-slice-shader' of https://github.com/ickshonpe/b…
ickshonpe Sep 2, 2024
c2d68f9
updated ambiguity count
ickshonpe Sep 2, 2024
73cf7cc
Document shader
ickshonpe Sep 2, 2024
c08f86c
fixed wrong identifier
ickshonpe Sep 2, 2024
c1cbc8a
fixed parameter order
ickshonpe Sep 2, 2024
c1c3fc4
improved comments
ickshonpe Sep 2, 2024
59ef4e4
improved vertex atrribute descriptions
ickshonpe Sep 2, 2024
ca073e7
Removed test texture.
ickshonpe Sep 2, 2024
a29599e
Merge branch 'main' into texture-slice-shader
ickshonpe Sep 2, 2024
3db75c4
Update ambiguity_detection.rs
ickshonpe Sep 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/textures/texture-slice-test-texture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 0 additions & 6 deletions crates/bevy_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ mod geometry;
mod layout;
mod render;
mod stack;
mod texture_slice;
mod ui_node;

pub use focus::*;
Expand Down Expand Up @@ -191,11 +190,6 @@ impl Plugin for UiPlugin {
.in_set(UiSystem::Prepare)
.in_set(AmbiguousWithTextSystem)
.in_set(AmbiguousWithUpdateText2DLayout),
(
texture_slice::compute_slices_on_asset_event,
texture_slice::compute_slices_on_image_change,
)
.in_set(UiSystem::PostLayout),
),
);

Expand Down
49 changes: 22 additions & 27 deletions crates/bevy_ui/src/render/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod pipeline;
mod render_pass;
mod ui_material_pipeline;
pub mod ui_texture_slice_pipeline;

use bevy_color::{Alpha, ColorToComponents, LinearRgba};
use bevy_core_pipeline::core_2d::graph::{Core2d, Node2d};
Expand All @@ -15,16 +16,16 @@ use bevy_render::{
view::ViewVisibility,
ExtractSchedule, Render,
};
use bevy_sprite::{SpriteAssetEvents, TextureAtlas};
use bevy_sprite::{ImageScaleMode, SpriteAssetEvents, TextureAtlas};
pub use pipeline::*;
pub use render_pass::*;
pub use ui_material_pipeline::*;
use ui_texture_slice_pipeline::UiTextureSlicerPlugin;

use crate::graph::{NodeUi, SubGraphUi};
use crate::{
texture_slice::ComputedTextureSlices, BackgroundColor, BorderColor, BorderRadius,
CalculatedClip, ContentSize, DefaultUiCamera, Node, Outline, Style, TargetCamera, UiImage,
UiScale, Val,
BackgroundColor, BorderColor, BorderRadius, CalculatedClip, ContentSize, DefaultUiCamera, Node,
Outline, Style, TargetCamera, UiImage, UiScale, Val,
};

use bevy_app::prelude::*;
Expand Down Expand Up @@ -140,6 +141,8 @@ pub fn build_ui_render(app: &mut App) {
graph_3d.add_node_edge(Node3d::EndMainPassPostProcessing, NodeUi::UiPass);
graph_3d.add_node_edge(NodeUi::UiPass, Node3d::Upscaling);
}

app.add_plugins(UiTextureSlicerPlugin);
}

fn get_ui_graph(render_app: &mut SubApp) -> RenderGraph {
Expand Down Expand Up @@ -299,19 +302,21 @@ pub fn extract_uinode_images(
ui_scale: Extract<Res<UiScale>>,
default_ui_camera: Extract<DefaultUiCamera>,
uinode_query: Extract<
Query<(
&Node,
&GlobalTransform,
&ViewVisibility,
Option<&CalculatedClip>,
Option<&TargetCamera>,
&UiImage,
Option<&TextureAtlas>,
Option<&ComputedTextureSlices>,
Option<&BorderRadius>,
Option<&Parent>,
&Style,
)>,
Query<
(
&Node,
&GlobalTransform,
&ViewVisibility,
Option<&CalculatedClip>,
Option<&TargetCamera>,
&UiImage,
Option<&TextureAtlas>,
Option<&BorderRadius>,
Option<&Parent>,
&Style,
),
Without<ImageScaleMode>,
>,
>,
node_query: Extract<Query<&Node>>,
) {
Expand All @@ -323,7 +328,6 @@ pub fn extract_uinode_images(
camera,
image,
atlas,
slices,
border_radius,
parent,
style,
Expand All @@ -342,15 +346,6 @@ pub fn extract_uinode_images(
continue;
}

if let Some(slices) = slices {
extracted_uinodes.uinodes.extend(
slices
.extract_ui_nodes(transform, uinode, image, clip, camera_entity)
.map(|e| (commands.spawn_empty().id(), e)),
);
continue;
}

let (rect, atlas_scaling) = match atlas {
Some(atlas) => {
let Some(layout) = texture_atlases.get(&atlas.layout) else {
Expand Down
127 changes: 127 additions & 0 deletions crates/bevy_ui/src/render/ui_texture_slice.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#import bevy_render::view::View;
#import bevy_render::globals::Globals;

@group(0) @binding(0)
var<uniform> view: View;
@group(0) @binding(1)
var<uniform> globals: Globals;

@group(1) @binding(0) var sprite_texture: texture_2d<f32>;
@group(1) @binding(1) var sprite_sampler: sampler;

struct UiVertexOutput {
@location(0) uv: vec2<f32>,
@location(1) color: vec4<f32>,

// Defines the dividing line that are used to split the texture atlas rect into corner, side and center slices
// The distances are normalized and from the top left corner of the texture atlas rect
// x = distance of the left vertical dividing line
// y = distance of the top horizontal dividing line
// z = distance of the right vertical dividing line
// w = distance of the bottom horizontal dividing line
@location(2) @interpolate(flat) texture_slices: vec4<f32>,

// Defines the dividing line that are used to split the render target into into corner, side and center slices
// The distances are normalized and from the top left corner of the render target
// x = distance of left vertical dividing line
// y = distance of top horizontal dividing line
// z = distance of right vertical dividing line
// w = distance of bottom horizontal dividing line
@location(3) @interpolate(flat) target_slices: vec4<f32>,

// The number of times the side or center texture slices should be repeated when mapping them to the border slices
// x = number of times to repeat along the horizontal axis for the side textures
// y = number of times to repeat along the vertical axis for the side textures
// z = number of times to repeat along the horizontal axis for the center texture
// w = number of times to repeat along the vertical axis for the center texture
@location(4) @interpolate(flat) repeat: vec4<f32>,

// normalized texture atlas rect coordinates
// x, y = top, left corner of the atlas rect
// z, w = bottom, right corner of the atlas rect
@location(5) @interpolate(flat) atlas_rect: vec4<f32>,
@builtin(position) position: vec4<f32>,
}

@vertex
fn vertex(
@location(0) vertex_position: vec3<f32>,
@location(1) vertex_uv: vec2<f32>,
@location(2) vertex_color: vec4<f32>,
@location(3) texture_slices: vec4<f32>,
@location(4) target_slices: vec4<f32>,
@location(5) repeat: vec4<f32>,
@location(6) atlas_rect: vec4<f32>,
) -> UiVertexOutput {
var out: UiVertexOutput;
out.uv = vertex_uv;
out.color = vertex_color;
out.position = view.clip_from_world * vec4<f32>(vertex_position, 1.0);
out.texture_slices = texture_slices;
out.target_slices = target_slices;
out.repeat = repeat;
out.atlas_rect = atlas_rect;
return out;
}

/// maps a point along the axis of the render target to slice coordinates
fn map_axis_with_repeat(
// normalized distance along the axis
p: f32,
// target min dividing point
il: f32,
// target max dividing point
ih: f32,
// slice min dividing point
tl: f32,
// slice max dividing point
th: f32,
// number of times to repeat the slice for sides and the center
r: f32,
) -> f32 {
if p < il {
// inside one of the two left (horizontal axis) or top (vertical axis) corners
return (p / il) * tl;
} else if ih < p {
// inside one of the two (horizontal axis) or top (vertical axis) corners
return th + ((p - ih) / (1 - ih)) * (1 - th);
} else {
// not inside a corner, repeat the texture
return tl + fract((r * (p - il)) / (ih - il)) * (th - tl);
}
}

fn map_uvs_to_slice(
uv: vec2<f32>,
target_slices: vec4<f32>,
texture_slices: vec4<f32>,
repeat: vec4<f32>,
) -> vec2<f32> {
var r: vec2<f32>;
if target_slices.x <= uv.x && uv.x <= target_slices.z && target_slices.y <= uv.y && uv.y <= target_slices.w {
// use the center repeat values if the uv coords are inside the center slice of the target
r = repeat.zw;
} else {
// use the side repeat values if the uv coords are outside the center slice
r = repeat.xy;
}

// map horizontal axis
let x = map_axis_with_repeat(uv.x, target_slices.x, target_slices.z, texture_slices.x, texture_slices.z, r.x);

// map vertical axis
let y = map_axis_with_repeat(uv.y, target_slices.y, target_slices.w, texture_slices.y, texture_slices.w, r.y);

return vec2(x, y);
}

@fragment
fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> {
// map the target uvs to slice coords
let uv = map_uvs_to_slice(in.uv, in.target_slices, in.texture_slices, in.repeat);

// map the slice coords to texture coords
let atlas_uv = in.atlas_rect.xy + uv * (in.atlas_rect.zw - in.atlas_rect.xy);

return in.color * textureSample(sprite_texture, sprite_sampler, atlas_uv);
}
Loading
Loading