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

bevy_ui: Add UiImage::tiling_mode for image tiling #8762

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mattzque
Copy link

@mattzque mattzque commented Jun 5, 2023

Objective

In bevy_ui, provide a way to tile/repeat a UiImage.

I was using the flex layout features of bevy_ui to create a dynamically sized box with images as borders (like top-left/right,top/top-right/etc. all separate image textures with some embellished frame). For the box to be dynamically sized I want the top/bottom/left/right images to tile horizontally/vertically, but this is currently not possible with bevy_ui.

Solution

Adjust the UV coordinates of the image node so the image repeats. This means (1, 1) becomes (2, 2) in order to repeat the image once along the x/y axis. In order to do this we need to modify the bevy_ui systems internals, since bevy_ui does not expose any mesh or other interface we could use to adjust the UV coordinates from client code.

This also requires the user to adjust the address_mode_u/v of SamplerDescriptor globally or per image. So the address_mode_u/v/w (I'm not entirely sure what w is meaning in this context) is set to AddressMode::Repeat.

I also found this other PR: #7406 that does something similar for 3d textures, but since we don't have control over the UVs for the UI rendering pipeline in bevy_ui, I had to modify the internals to make it work.


Changelog

  • Add a new tiling_mode to UiImage that indicates if the image should not tile, tile horizontally, tile vertically or tile in both directions.
  • Add a new TilingMode component that indicates if the UiImage should not tile, tile horizontally, tile vertically or tile in both directions.
  • Add a new TiledImageBundle bundle that has no calculated_size and image_size to prevent the image size affecting the node sizing.
  • During extraction, store the original image dimensions and the tiling mode in ExtractedUiNode.
  • During render, use the tiling mode, the original image dimensions and the calculated node dimensions to calculate a tiling factor. This happens in the new function get_tiling_uv_factor. Finally multiply the uv coordinates by this tiling factor.
  • Added example ui_image_tiling to demonstrate this feature.

@github-actions
Copy link
Contributor

github-actions bot commented Jun 5, 2023

Welcome, new contributor!

Please make sure you've read our contributing guide and we look forward to reviewing your pull request shortly ✨

@mattzque mattzque force-pushed the ui_image_tiling branch 2 times, most recently from 8f606fc to 25bb7d3 Compare June 5, 2023 22:51
@github-actions
Copy link
Contributor

github-actions bot commented Jun 5, 2023

You added a new example but didn't update the readme. Please run cargo run -p build-templated-pages -- update examples to update it, and commit the file change.

1 similar comment
@github-actions
Copy link
Contributor

github-actions bot commented Jun 5, 2023

You added a new example but didn't update the readme. Please run cargo run -p build-templated-pages -- update examples to update it, and commit the file change.

Copy link
Contributor

@ickshonpe ickshonpe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only skimmed this as I need to go to bed but it looks like this probably needs a separate widget bundle as you don't want any sort of intrinsic sizing behaviour (I think?).

Something like deriving Component for TilingMode and adding a TiledImageBundle with the same components as ImageBundle except without a ContentSize and with TilingMode.

@mattzque
Copy link
Author

mattzque commented Jun 6, 2023

looks like this probably needs a separate widget bundle as you don't want any sort of intrinsic sizing behaviour (I think?).

I'm not quite sure what you mean, the sizing stays exactly the same as before, the api already allows you to control how/if the image should stretch etc. (by default the align_items and align_content of the Style is set to stretch). So now its just that instead of always stretching the image you can optionally tile the image when setting a different tiling_mode.

@ickshonpe
Copy link
Contributor

ickshonpe commented Jun 6, 2023

An ImageBundle node's size is determined by a MeasureFunc that attempts to find a size for the node that respects the image's aspect ratio given the space allowed to it within the UI layout. The space allowed to it is derived from the style constraints so it can seem like you have direct control.

There are still a few bugs with MeasureFuncs as well and you can see some weird results I think just by giving ImageBundle nodes some margins.

Provide a way to tile/repeat a UiImage

Adjust the UV coordinates of the image node so the
image repeats. This means (1, 1) becomes (2, 2) in order
to repeat the image once along the x/y axis. In order
to do this we need to modify the bevy_ui systems
internals, since bevy_ui does not expose any mesh or
other interface we could use to adjust the UV coordinates
from client code.
This also requires the user to adjust the address_mode_u/v
of SamplerDescriptor globally or per image.

- Add a new TilingMode component that indicates
   if the UiImage should not tile, tile horizontally,
   tile vertically or tile in both directions.
- Add a new TiledImageBundle bundle that has no
    calculated_size and image_size to prevent the
    image size affecting the node sizing.
- During extraction, store the original image dimensions
   and the tiling mode in ExtractedUiNode.
- During render, use the tiling mode, the original
   image dimensions and the calculated node dimensions
   to calculate a tiling factor. This happens in the new
   function `get_tiling_uv_factor`.
   Finally multiply the uv coordinates by this tiling factor.
- Added example ui_image_tiling to demonstrate this feature.
@mattzque
Copy link
Author

mattzque commented Jun 7, 2023

An ImageBundle node's size is determined by a MeasureFunc that attempts to find a size for the node that respects the image's aspect ratio given the space allowed to it within the UI layout. The space allowed to it is derived from the style constraints so it can seem like you have direct control.

Thanks that makes sense! I updated the PR to follow your recommendation of using a separate component for TilingMode and added a TiledImageBundle. I also added some more complex examples:

20230607162418

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-UI Graphical user interfaces, styles, layouts, and widgets labels Jun 11, 2023
@Anti-Alias
Copy link
Contributor

Anti-Alias commented Aug 15, 2023

Looking forward to this. Been hoping for this feature for a while.
This should make ninepatches possible, which opens the door for a lot of other widgets.

Looking at this further, I see that it's doing this at the sampler level. To get my coveted ninepatches, I'd need to use nine textures rather than nine texture regions. Graphics APIs cannot wrap texture coordinates across a region in a texture.

Do we think that this sort of feature will be redundant with what 5213 is attempting to do? Doing wrapping logic in-code rather than in the shader/sampler is, of course, more complex.

But it encourages groups of widgets to all share the same TextureAtlas instead of each widget using its own texture.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-UI Graphical user interfaces, styles, layouts, and widgets C-Feature A new feature, making something new possible
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants