From 33b239af2d0104c963033d5358f277913fccf9e7 Mon Sep 17 00:00:00 2001 From: Matthew <logicprojectsforfpgas@gmail.com> Date: Thu, 9 Jun 2022 16:02:27 -0600 Subject: [PATCH] Added texture atlas support --- assets/texture_atlas.png | Bin 0 -> 10314 bytes bevy_kayak_ui/src/render/mod.rs | 6 ++ .../src/render/texture_atlas/extract.rs | 80 ++++++++++++++++++ bevy_kayak_ui/src/render/texture_atlas/mod.rs | 2 + kayak_core/src/render_command.rs | 5 ++ kayak_core/src/render_primitive.rs | 13 +++ src/widgets/mod.rs | 2 + src/widgets/texture_atlas.rs | 75 ++++++++++++++++ 8 files changed, 183 insertions(+) create mode 100644 assets/texture_atlas.png create mode 100644 bevy_kayak_ui/src/render/texture_atlas/extract.rs create mode 100644 bevy_kayak_ui/src/render/texture_atlas/mod.rs create mode 100644 src/widgets/texture_atlas.rs diff --git a/assets/texture_atlas.png b/assets/texture_atlas.png new file mode 100644 index 0000000000000000000000000000000000000000..939cb2835adae278f6ac31d8ea82e21ba3f9139a GIT binary patch literal 10314 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 I#Eb*}4+*yQXaE2J literal 0 HcmV?d00001 diff --git a/bevy_kayak_ui/src/render/mod.rs b/bevy_kayak_ui/src/render/mod.rs index 4a60b6e..aa55c2b 100644 --- a/bevy_kayak_ui/src/render/mod.rs +++ b/bevy_kayak_ui/src/render/mod.rs @@ -16,6 +16,7 @@ use kayak_font::KayakFont; pub mod font; pub mod image; mod nine_patch; +mod texture_atlas; mod quad; pub struct BevyKayakUIExtractPlugin; @@ -79,6 +80,11 @@ pub fn extract( nine_patch::extract_nine_patch(&render_primitive, &image_manager, &images, dpi); extracted_quads.extend(nine_patch_quads); } + RenderPrimitive::TextureAtlas { .. } => { + let texture_atlas_quads = + texture_atlas::extract_texture_atlas(&render_primitive, &image_manager, &images, dpi); + extracted_quads.extend(texture_atlas_quads); + } RenderPrimitive::Clip { layout } => { extracted_quads.push(ExtractQuadBundle { extracted_quad: ExtractedQuad { diff --git a/bevy_kayak_ui/src/render/texture_atlas/extract.rs b/bevy_kayak_ui/src/render/texture_atlas/extract.rs new file mode 100644 index 0000000..efe552e --- /dev/null +++ b/bevy_kayak_ui/src/render/texture_atlas/extract.rs @@ -0,0 +1,80 @@ +use crate::ImageManager; +use bevy::{ + math::Vec2, + prelude::{Assets, Res}, + render::{color::Color, texture::Image}, + sprite::Rect, +}; +use bevy_kayak_renderer::{ + render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType}, + Corner, +}; +use kayak_core::render_primitive::RenderPrimitive; + +pub fn extract_texture_atlas( + render_primitive: &RenderPrimitive, + image_manager: &Res<ImageManager>, + images: &Res<Assets<Image>>, + dpi: f32, +) -> Vec<ExtractQuadBundle> { + let mut extracted_quads = Vec::new(); + + let (size, position, layout, handle) = match render_primitive { + RenderPrimitive::TextureAtlas { + size, + position, + layout, + handle, + } => (size, position, layout, handle), + _ => panic!(""), + }; + + let image_handle = image_manager + .get_handle(handle) + .and_then(|a| Some(a.clone_weak())); + + let image = images.get(image_handle.as_ref().unwrap()); + + if image.is_none() { + return vec![]; + } + + let image_size = image + .and_then(|i| { + Some(Vec2::new( + i.texture_descriptor.size.width as f32, + i.texture_descriptor.size.height as f32, + )) + }) + .unwrap() + * dpi; + + let quad = ExtractQuadBundle { + extracted_quad: ExtractedQuad { + rect: Rect { + min: Vec2::new(layout.posx, layout.posy), + max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height), + }, + uv_min: Some(Vec2::new( + position.0 / image_size.x, + 1.0 - ((position.1 + size.1) / image_size.y) + )), + uv_max: Some(Vec2::new( + (position.0 + size.0) / image_size.x, + 1.0 - (position.1 / image_size.y), + )), + color: Color::WHITE, + vertex_index: 0, + char_id: 0, + z_index: layout.z_index, + font_handle: None, + quad_type: UIQuadType::Image, + type_index: 0, + border_radius: Corner::default(), + image: image_handle, + }, + }; + extracted_quads.push(quad); + + extracted_quads +} diff --git a/bevy_kayak_ui/src/render/texture_atlas/mod.rs b/bevy_kayak_ui/src/render/texture_atlas/mod.rs new file mode 100644 index 0000000..bd5825a --- /dev/null +++ b/bevy_kayak_ui/src/render/texture_atlas/mod.rs @@ -0,0 +1,2 @@ +mod extract; +pub use extract::extract_texture_atlas; diff --git a/kayak_core/src/render_command.rs b/kayak_core/src/render_command.rs index 6a7b408..13f9cba 100644 --- a/kayak_core/src/render_command.rs +++ b/kayak_core/src/render_command.rs @@ -13,6 +13,11 @@ pub enum RenderCommand { Image { handle: u16, }, + TextureAtlas { + position: (f32, f32), + size: (f32, f32), + handle: u16, + }, NinePatch { border: Edge<f32>, handle: u16, diff --git a/kayak_core/src/render_primitive.rs b/kayak_core/src/render_primitive.rs index 127c306..11a589c 100644 --- a/kayak_core/src/render_primitive.rs +++ b/kayak_core/src/render_primitive.rs @@ -32,6 +32,12 @@ pub enum RenderPrimitive { layout: Rect, handle: u16, }, + TextureAtlas { + size: (f32, f32), + position: (f32, f32), + layout: Rect, + handle: u16, + }, NinePatch { border: Edge<f32>, layout: Rect, @@ -47,6 +53,7 @@ impl RenderPrimitive { RenderPrimitive::Text { layout, .. } => *layout = new_layout, RenderPrimitive::Image { layout, .. } => *layout = new_layout, RenderPrimitive::NinePatch { layout, .. } => *layout = new_layout, + RenderPrimitive::TextureAtlas { layout, .. } => *layout = new_layout, _ => (), } } @@ -98,6 +105,12 @@ impl From<&Style> for RenderPrimitive { layout: Rect::default(), handle, }, + RenderCommand::TextureAtlas { handle, size, position, } => Self::TextureAtlas { + handle, + layout: Rect::default(), + size, + position, + }, RenderCommand::NinePatch { handle, border } => Self::NinePatch { border, layout: Rect::default(), diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 4073a78..81ae6a1 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -8,6 +8,7 @@ mod if_element; mod image; mod inspector; mod nine_patch; +mod texture_atlas; mod scroll; mod text; mod text_box; @@ -24,6 +25,7 @@ pub use if_element::*; pub use image::*; pub use inspector::*; pub use nine_patch::*; +pub use texture_atlas::*; pub use scroll::*; pub use text::*; pub use text_box::*; diff --git a/src/widgets/texture_atlas.rs b/src/widgets/texture_atlas.rs new file mode 100644 index 0000000..9ea7375 --- /dev/null +++ b/src/widgets/texture_atlas.rs @@ -0,0 +1,75 @@ +use kayak_core::OnLayout; + +use crate::core::{ + render_command::RenderCommand, + rsx, + styles::{Style, StyleProp}, + widget, Children, OnEvent, WidgetProps, +}; + +/// Props used by the [`NinePatch`] widget +#[derive(WidgetProps, Default, Debug, PartialEq, Clone)] +pub struct TextureAtlasProps { + /// The handle to image + pub handle: u16, + /// The position of the tile (in pixels) + pub position: (f32, f32), + /// The size of the tile (in pixels) + pub tile_size: (f32, f32), + #[prop_field(Styles)] + pub styles: Option<Style>, + #[prop_field(Children)] + pub children: Option<Children>, + #[prop_field(OnEvent)] + pub on_event: Option<OnEvent>, + #[prop_field(OnLayout)] + pub on_layout: Option<OnLayout>, + #[prop_field(Focusable)] + pub focusable: Option<bool>, +} + +#[widget] +/// A widget that renders a nine-patch image background +/// +/// A nine-patch is a special type of image that's broken into nine parts: +/// +/// * Edges - Top, Bottom, Left, Right +/// * Corners - Top-Left, Top-Right, Bottom-Left, Bottom-Right +/// * Center +/// +/// Using these parts of an image, we can construct a scalable background and border +/// all from a single image. This is done by: +/// +/// * Stretching the edges (vertically for left/right and horizontally for top/bottom) +/// * Preserving the corners +/// * Scaling the center to fill the remaining space +/// +/// +/// # Props +/// +/// __Type:__ [`NinePatchProps`] +/// +/// | Common Prop | Accepted | +/// | :---------: | :------: | +/// | `children` | ✅ | +/// | `styles` | ✅ | +/// | `on_event` | ✅ | +/// | `on_layout` | ✅ | +/// | `focusable` | ✅ | +/// +pub fn TextureAtlas(props: TextureAtlasProps) { + props.styles = Some(Style { + render_command: StyleProp::Value(RenderCommand::TextureAtlas { + position: props.position, + size: props.tile_size, + handle: props.handle, + }), + ..props.styles.clone().unwrap_or_default() + }); + + rsx! { + <> + {children} + </> + } +} -- GitLab