diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 93e2a98b84279..e7bc5cf6f78cf 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -192,6 +192,8 @@ pub struct ExtractedUiNode { pub image: Handle, pub atlas_size: Option, pub clip: Option, + pub flip_x: bool, + pub flip_y: bool, pub scale_factor: f32, } @@ -225,13 +227,11 @@ pub fn extract_uinodes( if !visibility.is_visible() { continue; } - - let image = if let Some(image) = maybe_image { - image.0.clone_weak() + let (image, flip_x, flip_y) = if let Some(image) = maybe_image { + (image.texture.clone_weak(), image.flip_x, image.flip_y) } else { - DEFAULT_IMAGE_HANDLE.typed().clone_weak() + (DEFAULT_IMAGE_HANDLE.typed().clone_weak(), false, false) }; - // Skip loading images if !images.contains(&image) { continue; @@ -252,6 +252,8 @@ pub fn extract_uinodes( image, atlas_size: None, clip: clip.map(|clip| clip.clip), + flip_x, + flip_y, scale_factor, }); } @@ -378,6 +380,8 @@ pub fn extract_text_uinodes( image: texture, atlas_size, clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, scale_factor, }); } @@ -512,7 +516,7 @@ pub fn prepare_uinodes( } let atlas_extent = extracted_uinode.atlas_size.unwrap_or(uinode_rect.max); - let uvs = [ + let mut uvs = [ Vec2::new( uinode_rect.min.x + positions_diff[0].x * extracted_uinode.scale_factor, uinode_rect.min.y + positions_diff[0].y * extracted_uinode.scale_factor, @@ -532,6 +536,13 @@ pub fn prepare_uinodes( ] .map(|pos| pos / atlas_extent); + if extracted_uinode.flip_x { + uvs = [uvs[1], uvs[0], uvs[3], uvs[2]]; + } + if extracted_uinode.flip_y { + uvs = [uvs[3], uvs[2], uvs[1], uvs[0]]; + } + for i in QUAD_INDICES { ui_meta.vertices.push(UiVertex { position: positions_clipped[i].into(), diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index af7aec01b4e92..af4beff3eb898 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -1,6 +1,5 @@ use crate::{Size, UiRect}; use bevy_asset::Handle; -use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{prelude::Component, reflect::ReflectComponent}; use bevy_math::{Rect, Vec2}; use bevy_reflect::prelude::*; @@ -455,19 +454,39 @@ impl From for BackgroundColor { } /// The 2D texture displayed for this UI node -#[derive(Component, Clone, Debug, Reflect, Deref, DerefMut)] +#[derive(Component, Clone, Debug, Reflect)] #[reflect(Component, Default)] -pub struct UiImage(pub Handle); +pub struct UiImage { + /// Handle to the texture + pub texture: Handle, + /// Whether the image should be flipped along its x-axis + pub flip_x: bool, + /// Whether the image should be flipped along its y-axis + pub flip_y: bool, +} impl Default for UiImage { - fn default() -> Self { - Self(DEFAULT_IMAGE_HANDLE.typed()) + fn default() -> UiImage { + UiImage { + texture: DEFAULT_IMAGE_HANDLE.typed(), + flip_x: false, + flip_y: false, + } + } +} + +impl UiImage { + pub fn new(texture: Handle) -> Self { + Self { + texture, + ..Default::default() + } } } impl From> for UiImage { - fn from(handle: Handle) -> Self { - Self(handle) + fn from(texture: Handle) -> Self { + Self::new(texture) } } diff --git a/crates/bevy_ui/src/widget/image.rs b/crates/bevy_ui/src/widget/image.rs index 2a8d806e59b89..c6e0e5ad23ed1 100644 --- a/crates/bevy_ui/src/widget/image.rs +++ b/crates/bevy_ui/src/widget/image.rs @@ -26,7 +26,7 @@ pub fn image_node_system( mut query: Query<(&mut CalculatedSize, &UiImage), (With, Without)>, ) { for (mut calculated_size, image) in &mut query { - if let Some(texture) = textures.get(image) { + if let Some(texture) = textures.get(&image.texture) { let size = Size { width: Val::Px(texture.texture_descriptor.size.width as f32), height: Val::Px(texture.texture_descriptor.size.height as f32), diff --git a/examples/games/game_menu.rs b/examples/games/game_menu.rs index 9f2b19ab2ffc1..0045ef2da41ff 100644 --- a/examples/games/game_menu.rs +++ b/examples/games/game_menu.rs @@ -90,7 +90,7 @@ mod splash { size: Size::new(Val::Px(200.0), Val::Auto), ..default() }, - image: UiImage(icon), + image: UiImage::new(icon), ..default() }, OnSplashScreen, @@ -462,7 +462,7 @@ mod menu { let icon = asset_server.load("textures/Game Icons/right.png"); parent.spawn(ImageBundle { style: button_icon_style.clone(), - image: UiImage(icon), + image: UiImage::new(icon), ..default() }); parent.spawn(TextBundle::from_section( @@ -483,7 +483,7 @@ mod menu { let icon = asset_server.load("textures/Game Icons/wrench.png"); parent.spawn(ImageBundle { style: button_icon_style.clone(), - image: UiImage(icon), + image: UiImage::new(icon), ..default() }); parent.spawn(TextBundle::from_section( @@ -504,7 +504,7 @@ mod menu { let icon = asset_server.load("textures/Game Icons/exitRight.png"); parent.spawn(ImageBundle { style: button_icon_style, - image: UiImage(icon), + image: UiImage::new(icon), ..default() }); parent.spawn(TextBundle::from_section("Quit", button_text_style));