Flip UI image (#6292)
# Objective Fixes #3225, Allow for flippable UI Images ## Solution Add flip_x and flip_y fields to UiImage, and swap the UV coordinates accordingly in ui_prepare_nodes. ## Changelog * Changes UiImage to a struct with texture, flip_x, and flip_y fields. * Adds flip_x and flip_y fields to ExtractedUiNode. * Changes extract_uinodes to extract the flip_x and flip_y values from UiImage. * Changes prepare_uinodes to swap the UV coordinates as required. * Changes UiImage derefs to texture field accesses.
This commit is contained in:
parent
3ac06b57e9
commit
5f1261110f
@ -192,6 +192,8 @@ pub struct ExtractedUiNode {
|
|||||||
pub image: Handle<Image>,
|
pub image: Handle<Image>,
|
||||||
pub atlas_size: Option<Vec2>,
|
pub atlas_size: Option<Vec2>,
|
||||||
pub clip: Option<Rect>,
|
pub clip: Option<Rect>,
|
||||||
|
pub flip_x: bool,
|
||||||
|
pub flip_y: bool,
|
||||||
pub scale_factor: f32,
|
pub scale_factor: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,13 +227,11 @@ pub fn extract_uinodes(
|
|||||||
if !visibility.is_visible() {
|
if !visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let (image, flip_x, flip_y) = if let Some(image) = maybe_image {
|
||||||
let image = if let Some(image) = maybe_image {
|
(image.texture.clone_weak(), image.flip_x, image.flip_y)
|
||||||
image.0.clone_weak()
|
|
||||||
} else {
|
} else {
|
||||||
DEFAULT_IMAGE_HANDLE.typed().clone_weak()
|
(DEFAULT_IMAGE_HANDLE.typed().clone_weak(), false, false)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Skip loading images
|
// Skip loading images
|
||||||
if !images.contains(&image) {
|
if !images.contains(&image) {
|
||||||
continue;
|
continue;
|
||||||
@ -252,6 +252,8 @@ pub fn extract_uinodes(
|
|||||||
image,
|
image,
|
||||||
atlas_size: None,
|
atlas_size: None,
|
||||||
clip: clip.map(|clip| clip.clip),
|
clip: clip.map(|clip| clip.clip),
|
||||||
|
flip_x,
|
||||||
|
flip_y,
|
||||||
scale_factor,
|
scale_factor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -378,6 +380,8 @@ pub fn extract_text_uinodes(
|
|||||||
image: texture,
|
image: texture,
|
||||||
atlas_size,
|
atlas_size,
|
||||||
clip: clip.map(|clip| clip.clip),
|
clip: clip.map(|clip| clip.clip),
|
||||||
|
flip_x: false,
|
||||||
|
flip_y: false,
|
||||||
scale_factor,
|
scale_factor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -512,7 +516,7 @@ pub fn prepare_uinodes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let atlas_extent = extracted_uinode.atlas_size.unwrap_or(uinode_rect.max);
|
let atlas_extent = extracted_uinode.atlas_size.unwrap_or(uinode_rect.max);
|
||||||
let uvs = [
|
let mut uvs = [
|
||||||
Vec2::new(
|
Vec2::new(
|
||||||
uinode_rect.min.x + positions_diff[0].x * extracted_uinode.scale_factor,
|
uinode_rect.min.x + positions_diff[0].x * extracted_uinode.scale_factor,
|
||||||
uinode_rect.min.y + positions_diff[0].y * 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);
|
.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 {
|
for i in QUAD_INDICES {
|
||||||
ui_meta.vertices.push(UiVertex {
|
ui_meta.vertices.push(UiVertex {
|
||||||
position: positions_clipped[i].into(),
|
position: positions_clipped[i].into(),
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use crate::{Size, UiRect};
|
use crate::{Size, UiRect};
|
||||||
use bevy_asset::Handle;
|
use bevy_asset::Handle;
|
||||||
use bevy_derive::{Deref, DerefMut};
|
|
||||||
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
|
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
|
||||||
use bevy_math::{Rect, Vec2};
|
use bevy_math::{Rect, Vec2};
|
||||||
use bevy_reflect::prelude::*;
|
use bevy_reflect::prelude::*;
|
||||||
@ -452,19 +451,39 @@ impl From<Color> for BackgroundColor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The 2D texture displayed for this UI node
|
/// The 2D texture displayed for this UI node
|
||||||
#[derive(Component, Clone, Debug, Reflect, Deref, DerefMut)]
|
#[derive(Component, Clone, Debug, Reflect)]
|
||||||
#[reflect(Component, Default)]
|
#[reflect(Component, Default)]
|
||||||
pub struct UiImage(pub Handle<Image>);
|
pub struct UiImage {
|
||||||
|
/// Handle to the texture
|
||||||
|
pub texture: Handle<Image>,
|
||||||
|
/// 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 {
|
impl Default for UiImage {
|
||||||
fn default() -> Self {
|
fn default() -> UiImage {
|
||||||
Self(DEFAULT_IMAGE_HANDLE.typed())
|
UiImage {
|
||||||
|
texture: DEFAULT_IMAGE_HANDLE.typed(),
|
||||||
|
flip_x: false,
|
||||||
|
flip_y: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UiImage {
|
||||||
|
pub fn new(texture: Handle<Image>) -> Self {
|
||||||
|
Self {
|
||||||
|
texture,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Handle<Image>> for UiImage {
|
impl From<Handle<Image>> for UiImage {
|
||||||
fn from(handle: Handle<Image>) -> Self {
|
fn from(texture: Handle<Image>) -> Self {
|
||||||
Self(handle)
|
Self::new(texture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ pub fn image_node_system(
|
|||||||
mut query: Query<(&mut CalculatedSize, &UiImage), (With<ImageMode>, Without<Text>)>,
|
mut query: Query<(&mut CalculatedSize, &UiImage), (With<ImageMode>, Without<Text>)>,
|
||||||
) {
|
) {
|
||||||
for (mut calculated_size, image) in &mut query {
|
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 {
|
let size = Size {
|
||||||
width: Val::Px(texture.texture_descriptor.size.width as f32),
|
width: Val::Px(texture.texture_descriptor.size.width as f32),
|
||||||
height: Val::Px(texture.texture_descriptor.size.height as f32),
|
height: Val::Px(texture.texture_descriptor.size.height as f32),
|
||||||
|
|||||||
@ -90,7 +90,7 @@ mod splash {
|
|||||||
size: Size::new(Val::Px(200.0), Val::Auto),
|
size: Size::new(Val::Px(200.0), Val::Auto),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
image: UiImage(icon),
|
image: UiImage::new(icon),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
OnSplashScreen,
|
OnSplashScreen,
|
||||||
@ -462,7 +462,7 @@ mod menu {
|
|||||||
let icon = asset_server.load("textures/Game Icons/right.png");
|
let icon = asset_server.load("textures/Game Icons/right.png");
|
||||||
parent.spawn(ImageBundle {
|
parent.spawn(ImageBundle {
|
||||||
style: button_icon_style.clone(),
|
style: button_icon_style.clone(),
|
||||||
image: UiImage(icon),
|
image: UiImage::new(icon),
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
parent.spawn(TextBundle::from_section(
|
parent.spawn(TextBundle::from_section(
|
||||||
@ -483,7 +483,7 @@ mod menu {
|
|||||||
let icon = asset_server.load("textures/Game Icons/wrench.png");
|
let icon = asset_server.load("textures/Game Icons/wrench.png");
|
||||||
parent.spawn(ImageBundle {
|
parent.spawn(ImageBundle {
|
||||||
style: button_icon_style.clone(),
|
style: button_icon_style.clone(),
|
||||||
image: UiImage(icon),
|
image: UiImage::new(icon),
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
parent.spawn(TextBundle::from_section(
|
parent.spawn(TextBundle::from_section(
|
||||||
@ -504,7 +504,7 @@ mod menu {
|
|||||||
let icon = asset_server.load("textures/Game Icons/exitRight.png");
|
let icon = asset_server.load("textures/Game Icons/exitRight.png");
|
||||||
parent.spawn(ImageBundle {
|
parent.spawn(ImageBundle {
|
||||||
style: button_icon_style,
|
style: button_icon_style,
|
||||||
image: UiImage(icon),
|
image: UiImage::new(icon),
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
parent.spawn(TextBundle::from_section("Quit", button_text_style));
|
parent.spawn(TextBundle::from_section("Quit", button_text_style));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user