Skip to content
Snippets Groups Projects
Commit bff1e898 authored by StarArawn's avatar StarArawn
Browse files

Added in nine-patch images.

parent ba31e3c7
No related branches found
No related tags found
No related merge requests found
Showing
with 444 additions and 20 deletions
...@@ -138,6 +138,8 @@ pub fn extract_texts( ...@@ -138,6 +138,8 @@ pub fn extract_texts(
type_index: 0, type_index: 0,
border_radius: (0.0, 0.0, 0.0, 0.0), border_radius: (0.0, 0.0, 0.0, 0.0),
image: None, image: None,
uv_max: None,
uv_min: None,
}, },
}); });
......
...@@ -305,7 +305,6 @@ impl FontTextureCache { ...@@ -305,7 +305,6 @@ impl FontTextureCache {
atlas_texture: &GpuImage, atlas_texture: &GpuImage,
size: Vec2, size: Vec2,
) { ) {
dbg!(size);
Self::create_texture( Self::create_texture(
images, images,
font_handle.clone_weak(), font_handle.clone_weak(),
......
...@@ -90,14 +90,11 @@ pub fn set_font_texture( ...@@ -90,14 +90,11 @@ pub fn set_font_texture(
) { ) {
// quick and dirty, run this for all textures anytime a texture is created. // quick and dirty, run this for all textures anytime a texture is created.
for event in texture_events.iter() { for event in texture_events.iter() {
dbg!(&event);
match event { match event {
AssetEvent::Created { handle } => { AssetEvent::Created { handle } => {
let handle_path = asset_server.get_handle_path(handle).unwrap(); let handle_path = asset_server.get_handle_path(handle).unwrap();
dbg!(&handle_path);
if handle_path.path().to_str().unwrap().contains("roboto") { if handle_path.path().to_str().unwrap().contains("roboto") {
if let Some(mut texture) = textures.get_mut(handle) { if let Some(mut texture) = textures.get_mut(handle) {
dbg!("Setting font texture!");
texture.texture_descriptor.format = TextureFormat::Rgba8Unorm; texture.texture_descriptor.format = TextureFormat::Rgba8Unorm;
texture.sampler_descriptor.min_filter = FilterMode::Linear; texture.sampler_descriptor.min_filter = FilterMode::Linear;
texture.sampler_descriptor.mipmap_filter = FilterMode::Linear; texture.sampler_descriptor.mipmap_filter = FilterMode::Linear;
......
...@@ -51,6 +51,8 @@ pub fn extract_images( ...@@ -51,6 +51,8 @@ pub fn extract_images(
image: image_manager image: image_manager
.get_handle(handle) .get_handle(handle)
.and_then(|a| Some(a.clone_weak())), .and_then(|a| Some(a.clone_weak())),
uv_max: None,
uv_min: None,
}, },
}); });
} }
......
...@@ -22,7 +22,7 @@ impl ImageManager { ...@@ -22,7 +22,7 @@ impl ImageManager {
} else { } else {
let id = self.count; let id = self.count;
self.count += 1; self.count += 1;
self.mapping.insert(id, image_handle.clone_weak()); self.mapping.insert(id, image_handle.clone());
self.reverse_mapping.insert(image_handle.clone_weak(), id); self.reverse_mapping.insert(image_handle.clone_weak(), id);
return id; return id;
} }
......
...@@ -13,6 +13,7 @@ use self::pipeline::ImageBindGroups; ...@@ -13,6 +13,7 @@ use self::pipeline::ImageBindGroups;
pub mod font; pub mod font;
pub mod image; pub mod image;
mod nine_patch;
mod pipeline; mod pipeline;
mod quad; mod quad;
...@@ -46,6 +47,7 @@ impl Plugin for UnifiedRenderPlugin { ...@@ -46,6 +47,7 @@ impl Plugin for UnifiedRenderPlugin {
app.add_plugin(font::TextRendererPlugin) app.add_plugin(font::TextRendererPlugin)
.add_plugin(quad::QuadRendererPlugin) .add_plugin(quad::QuadRendererPlugin)
.add_plugin(image::ImageRendererPlugin); .add_plugin(image::ImageRendererPlugin)
.add_plugin(nine_patch::NinePatchRendererPlugin);
} }
} }
use bevy::{
math::Vec2,
prelude::{Assets, Commands, Res},
render2::{color::Color, texture::Image},
sprite2::Rect,
};
use kayak_core::render_primitive::RenderPrimitive;
use crate::{
render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType},
BevyContext, ImageManager,
};
pub fn extract_nine_patch(
mut commands: Commands,
context: Res<BevyContext>,
image_manager: Res<ImageManager>,
images: Res<Assets<Image>>,
) {
let render_commands = if let Ok(context) = context.kayak_context.read() {
context.widget_manager.build_render_primitives()
} else {
vec![]
};
let image_commands: Vec<&RenderPrimitive> = render_commands
.iter()
.filter(|command| matches!(command, RenderPrimitive::NinePatch { .. }))
.collect::<Vec<_>>();
let mut extracted_quads = Vec::new();
for render_primitive in image_commands {
let (layout, handle, border) = match render_primitive {
RenderPrimitive::NinePatch {
layout,
handle,
border,
} => (layout, handle, border),
_ => 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() {
dbg!("Uh oh no image! :(");
continue;
}
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();
let extracted_quad_template = ExtractedQuad {
rect: Rect {
min: Vec2::ZERO,
max: Vec2::ZERO,
},
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: (0.0, 0.0, 0.0, 0.0),
image: image_handle,
uv_max: None,
uv_min: None,
};
// TOP
let top_left_quad = ExtractQuadBundle {
extracted_quad: ExtractedQuad {
rect: Rect {
min: Vec2::new(layout.posx, layout.posy),
max: Vec2::new(layout.posx + border.left, layout.posy + border.top),
},
uv_min: Some(Vec2::new(0.0, border.top / image_size.y)),
uv_max: Some(Vec2::new(border.left / image_size.x, 0.0)),
..extracted_quad_template.clone()
},
};
extracted_quads.push(top_left_quad);
let top_right_pos_x = (layout.posx + layout.width) - border.left;
let top_right_quad = ExtractQuadBundle {
extracted_quad: ExtractedQuad {
rect: Rect {
min: Vec2::new(top_right_pos_x, layout.posy),
max: Vec2::new(top_right_pos_x + border.left, layout.posy + border.top),
},
uv_min: Some(Vec2::new(
(image_size.x - border.left) / image_size.x,
border.top / image_size.y,
)),
uv_max: Some(Vec2::new(1.0, 0.0)),
..extracted_quad_template.clone()
},
};
extracted_quads.push(top_right_quad);
let top_middle_pos_x = layout.posx + border.left;
let top_middle_size_x = layout.width - (border.left + border.right);
let top_middle_quad = ExtractQuadBundle {
extracted_quad: ExtractedQuad {
rect: Rect {
min: Vec2::new(top_middle_pos_x, layout.posy),
max: Vec2::new(
top_middle_pos_x + top_middle_size_x,
layout.posy + border.top,
),
},
uv_min: Some(Vec2::new(
border.left / image_size.x,
border.top / image_size.y,
)),
uv_max: Some(Vec2::new((image_size.x - border.left) / image_size.x, 0.0)),
..extracted_quad_template.clone()
},
};
extracted_quads.push(top_middle_quad);
// Bottom
let bottom_y_pos = layout.posy + (layout.width - border.bottom);
let bottom_left_quad = ExtractQuadBundle {
extracted_quad: ExtractedQuad {
rect: Rect {
min: Vec2::new(layout.posx, bottom_y_pos),
max: Vec2::new(layout.posx + border.left, bottom_y_pos + border.bottom),
},
uv_min: Some(Vec2::new(0.0, 1.0)),
uv_max: Some(Vec2::new(
border.left / image_size.x,
(image_size.y - border.bottom) / image_size.y,
)),
..extracted_quad_template.clone()
},
};
extracted_quads.push(bottom_left_quad);
let bottom_right_pos_x = (layout.posx + layout.width) - border.left;
let bottom_right_quad = ExtractQuadBundle {
extracted_quad: ExtractedQuad {
rect: Rect {
min: Vec2::new(bottom_right_pos_x, bottom_y_pos),
max: Vec2::new(bottom_right_pos_x + border.left, bottom_y_pos + border.top),
},
uv_min: Some(Vec2::new((image_size.x - border.left) / image_size.x, 1.0)),
uv_max: Some(Vec2::new(
1.0,
(image_size.y - border.bottom) / image_size.y,
)),
..extracted_quad_template.clone()
},
};
extracted_quads.push(bottom_right_quad);
let bottom_middle_pos_x = layout.posx + border.left;
let bottom_middle_size_x = layout.width - (border.left + border.right);
let bottom_middle_quad = ExtractQuadBundle {
extracted_quad: ExtractedQuad {
rect: Rect {
min: Vec2::new(bottom_middle_pos_x, bottom_y_pos),
max: Vec2::new(
bottom_middle_pos_x + bottom_middle_size_x,
bottom_y_pos + border.top,
),
},
uv_min: Some(Vec2::new(border.left / image_size.x, 1.0)),
uv_max: Some(Vec2::new(
(image_size.x - border.left) / image_size.x,
(image_size.y - border.bottom) / image_size.y,
)),
..extracted_quad_template.clone()
},
};
extracted_quads.push(bottom_middle_quad);
// Left + Right center
let left_middle_pos_y = layout.posy + border.top;
let left_middle_size_y = layout.height - (border.top + border.bottom);
let left_middle_quad = ExtractQuadBundle {
extracted_quad: ExtractedQuad {
rect: Rect {
min: Vec2::new(layout.posx, left_middle_pos_y),
max: Vec2::new(
layout.posx + border.left,
left_middle_pos_y + left_middle_size_y,
),
},
uv_min: Some(Vec2::new(
0.0,
(image_size.y - border.bottom) / image_size.y,
)),
uv_max: Some(Vec2::new(
border.left / image_size.x,
border.top / image_size.y,
)),
..extracted_quad_template.clone()
},
};
extracted_quads.push(left_middle_quad);
let right_middle_pos_x = layout.posx + (layout.width - border.right);
let right_middle_pos_y = layout.posy + border.top;
let right_middle_size_y = layout.height - (border.top + border.bottom);
let right_middle_quad = ExtractQuadBundle {
extracted_quad: ExtractedQuad {
rect: Rect {
min: Vec2::new(right_middle_pos_x, right_middle_pos_y),
max: Vec2::new(
right_middle_pos_x + border.left,
right_middle_pos_y + right_middle_size_y,
),
},
uv_min: Some(Vec2::new(
(image_size.x - border.left) / image_size.x,
(image_size.y - border.bottom) / image_size.y,
)),
uv_max: Some(Vec2::new(1.0, border.top / image_size.y)),
..extracted_quad_template.clone()
},
};
extracted_quads.push(right_middle_quad);
// Last quad in middle.
let middle_pos_x = layout.posx + border.left;
let middle_pos_y = layout.posy + border.top;
let middle_size_x = layout.width - (border.left + border.right);
let middle_size_y = layout.height - (border.top + border.bottom);
let middle_quad = ExtractQuadBundle {
extracted_quad: ExtractedQuad {
rect: Rect {
min: Vec2::new(middle_pos_x, middle_pos_y),
max: Vec2::new(middle_pos_x + middle_size_x, middle_pos_y + middle_size_y),
},
uv_min: Some(Vec2::new(
border.left / image_size.x,
border.bottom / image_size.y,
)),
uv_max: Some(Vec2::new(
(image_size.x - border.right) / image_size.x,
(image_size.y - border.bottom) / image_size.y,
)),
..extracted_quad_template.clone()
},
};
extracted_quads.push(middle_quad);
}
commands.spawn_batch(extracted_quads);
}
use bevy::{
prelude::Plugin,
render2::{RenderApp, RenderStage},
};
mod extract;
pub struct NinePatchRendererPlugin;
impl Plugin for NinePatchRendererPlugin {
fn build(&self, app: &mut bevy::prelude::App) {
let render_app = app.sub_app(RenderApp);
render_app.add_system_to_stage(RenderStage::Extract, extract::extract_nine_patch);
}
}
...@@ -4,7 +4,7 @@ use bevy::{ ...@@ -4,7 +4,7 @@ use bevy::{
lifetimeless::{Read, SQuery, SRes}, lifetimeless::{Read, SQuery, SRes},
SystemState, SystemState,
}, },
math::{const_vec3, Mat4, Quat, Vec3, Vec4}, math::{const_vec3, Mat4, Quat, Vec2, Vec3, Vec4},
prelude::{Bundle, Component, Entity, FromWorld, Handle, Query, Res, ResMut, World}, prelude::{Bundle, Component, Entity, FromWorld, Handle, Query, Res, ResMut, World},
render2::{ render2::{
color::Color, color::Color,
...@@ -289,7 +289,7 @@ impl FromWorld for UnifiedPipeline { ...@@ -289,7 +289,7 @@ impl FromWorld for UnifiedPipeline {
} }
} }
#[derive(Bundle)] #[derive(Debug, Bundle)]
pub struct ExtractQuadBundle { pub struct ExtractQuadBundle {
pub(crate) extracted_quad: ExtractedQuad, pub(crate) extracted_quad: ExtractedQuad,
} }
...@@ -301,7 +301,7 @@ pub enum UIQuadType { ...@@ -301,7 +301,7 @@ pub enum UIQuadType {
Image, Image,
} }
#[derive(Component)] #[derive(Debug, Component, Clone)]
pub struct ExtractedQuad { pub struct ExtractedQuad {
pub rect: Rect, pub rect: Rect,
pub color: Color, pub color: Color,
...@@ -313,6 +313,8 @@ pub struct ExtractedQuad { ...@@ -313,6 +313,8 @@ pub struct ExtractedQuad {
pub type_index: u32, pub type_index: u32,
pub border_radius: (f32, f32, f32, f32), pub border_radius: (f32, f32, f32, f32),
pub image: Option<Handle<Image>>, pub image: Option<Handle<Image>>,
pub uv_min: Option<Vec2>,
pub uv_max: Option<Vec2>,
} }
#[repr(C)] #[repr(C)]
...@@ -390,27 +392,30 @@ pub fn prepare_quads( ...@@ -390,27 +392,30 @@ pub fn prepare_quads(
UIQuadType::Image => extracted_sprite.type_index = image_type_offset, UIQuadType::Image => extracted_sprite.type_index = image_type_offset,
}; };
let uv_min = extracted_sprite.uv_min.unwrap_or(Vec2::ZERO);
let uv_max = extracted_sprite.uv_max.unwrap_or(Vec2::ONE);
let bottom_left = Vec4::new( let bottom_left = Vec4::new(
0.0, uv_min.x,
1.0, uv_max.y,
extracted_sprite.char_id as f32, extracted_sprite.char_id as f32,
extracted_sprite.border_radius.0, extracted_sprite.border_radius.0,
); );
let top_left = Vec4::new( let top_left = Vec4::new(
0.0, uv_min.x,
0.0, uv_min.y,
extracted_sprite.char_id as f32, extracted_sprite.char_id as f32,
extracted_sprite.border_radius.1, extracted_sprite.border_radius.1,
); );
let top_right = Vec4::new( let top_right = Vec4::new(
1.0, uv_max.x,
0.0, uv_min.y,
extracted_sprite.char_id as f32, extracted_sprite.char_id as f32,
extracted_sprite.border_radius.2, extracted_sprite.border_radius.2,
); );
let bottom_right = Vec4::new( let bottom_right = Vec4::new(
1.0, uv_max.x,
1.0, uv_max.y,
extracted_sprite.char_id as f32, extracted_sprite.char_id as f32,
extracted_sprite.border_radius.3, extracted_sprite.border_radius.3,
); );
......
...@@ -48,6 +48,8 @@ pub fn extract_quads(mut commands: Commands, context: Res<BevyContext>) { ...@@ -48,6 +48,8 @@ pub fn extract_quads(mut commands: Commands, context: Res<BevyContext>) {
type_index: 0, type_index: 0,
border_radius: *border_radius, border_radius: *border_radius,
image: None, image: None,
uv_max: None,
uv_min: None,
}, },
}); });
} }
......
...@@ -38,7 +38,6 @@ fn vertex( ...@@ -38,7 +38,6 @@ fn vertex(
out.uv = vertex_uv.xyz; out.uv = vertex_uv.xyz;
out.size = vertex_pos_size.zw; out.size = vertex_pos_size.zw;
out.border_radius = vertex_uv.w; out.border_radius = vertex_uv.w;
return out; return out;
} }
......
use bevy::{
math::Vec2,
prelude::{App as BevyApp, AssetServer, Commands, Handle, Res, ResMut},
window::{WindowDescriptor, Windows},
PipelinedDefaultPlugins,
};
use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, ImageManager, UICameraBundle};
use kayak_components::NinePatch;
use kayak_core::{
layout_cache::Space,
styles::{Style, StyleProp, Units},
Index,
};
use kayak_ui::components::App;
use kayak_ui::core::rsx;
fn startup(
mut commands: Commands,
windows: Res<Windows>,
asset_server: Res<AssetServer>,
mut image_manager: ResMut<ImageManager>,
) {
commands.spawn_bundle(UICameraBundle::new());
let window_size = if let Some(window) = windows.get_primary() {
Vec2::new(window.width(), window.height())
} else {
panic!("Couldn't find primary window!");
};
let handle: Handle<bevy::render2::texture::Image> = asset_server.load("panel.png");
let ui_image_handle = image_manager.get(&handle);
let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
// Hack to trick the proc macro for right now..
let parent_id: Option<Index> = None;
// The border prop splits up the image into 9 quadrants like so:
// 1----2----3
// | |
// 4 9 5
// | |
// 6----7----8
// The sizes of sprites for a 15 pixel border are as follows:
// TopLeft = (15, 15)
// TopRight = (15, 15)
// LeftCenter = (15, image_height)
// RightCenter = (15, image_height)
// TopCenter = (image_width, 15)
// BottomCenter = (image_width, 15)
// BottomRight = (15, 15)
// BottomLeft = (15, 15)
// Middle = (
// 30 being left border + right border
// image_width - 30
// 30 being top border + bottom border
// image_height - 30
// )
//
let nine_patch_styles = Style {
width: StyleProp::Value(Units::Pixels(512.0)),
height: StyleProp::Value(Units::Pixels(512.0)),
..Style::default()
};
rsx! {
<App styles={Some(styles.clone())}>
<NinePatch
styles={Some(nine_patch_styles)}
border={Space {
left: 15.0,
right: 15.0,
top: 15.0,
bottom: 15.0,
}}
handle={ui_image_handle}
/>
</App>
}
});
commands.insert_resource(context);
}
fn main() {
BevyApp::new()
.insert_resource(WindowDescriptor {
width: 1270.0,
height: 720.0,
title: String::from("UI Example"),
..Default::default()
})
.add_plugins(PipelinedDefaultPlugins)
.add_plugin(BevyKayakUIPlugin)
.add_startup_system(startup)
.run();
}
...@@ -3,6 +3,7 @@ mod background; ...@@ -3,6 +3,7 @@ mod background;
mod button; mod button;
mod clip; mod clip;
mod image; mod image;
mod nine_patch;
mod text; mod text;
mod window; mod window;
...@@ -11,5 +12,6 @@ pub use background::*; ...@@ -11,5 +12,6 @@ pub use background::*;
pub use button::*; pub use button::*;
pub use clip::*; pub use clip::*;
pub use image::*; pub use image::*;
pub use nine_patch::*;
pub use text::*; pub use text::*;
pub use window::*; pub use window::*;
use kayak_core::{
layout_cache::Space,
render_command::RenderCommand,
rsx,
styles::{Style, StyleProp},
widget, Children,
};
#[widget]
pub fn NinePatch(handle: u16, border: Space, children: Children) {
*styles = Some(Style {
render_command: StyleProp::Value(RenderCommand::NinePatch {
handle: *handle,
border: *border,
}),
..styles.clone().unwrap_or_default()
});
rsx! {
<>
{children}
</>
}
}
...@@ -20,7 +20,7 @@ impl Rect { ...@@ -20,7 +20,7 @@ impl Rect {
} }
} }
#[derive(Debug, Default, Clone, Copy)] #[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Space { pub struct Space {
pub left: f32, pub left: f32,
pub right: f32, pub right: f32,
......
use crate::layout_cache::Space;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum RenderCommand { pub enum RenderCommand {
Empty, Empty,
...@@ -12,6 +14,10 @@ pub enum RenderCommand { ...@@ -12,6 +14,10 @@ pub enum RenderCommand {
Image { Image {
handle: u16, handle: u16,
}, },
NinePatch {
border: Space,
handle: u16,
},
} }
impl Default for RenderCommand { impl Default for RenderCommand {
......
use crate::{ use crate::{
color::Color, color::Color,
layout_cache::Rect, layout_cache::{Rect, Space},
render_command::RenderCommand, render_command::RenderCommand,
styles::{Style, StyleProp}, styles::{Style, StyleProp},
}; };
...@@ -27,6 +27,11 @@ pub enum RenderPrimitive { ...@@ -27,6 +27,11 @@ pub enum RenderPrimitive {
layout: Rect, layout: Rect,
handle: u16, handle: u16,
}, },
NinePatch {
border: Space,
layout: Rect,
handle: u16,
},
} }
impl RenderPrimitive { impl RenderPrimitive {
...@@ -36,6 +41,7 @@ impl RenderPrimitive { ...@@ -36,6 +41,7 @@ impl RenderPrimitive {
RenderPrimitive::Quad { layout, .. } => *layout = new_layout, RenderPrimitive::Quad { layout, .. } => *layout = new_layout,
RenderPrimitive::Text { layout, .. } => *layout = new_layout, RenderPrimitive::Text { layout, .. } => *layout = new_layout,
RenderPrimitive::Image { layout, .. } => *layout = new_layout, RenderPrimitive::Image { layout, .. } => *layout = new_layout,
RenderPrimitive::NinePatch { layout, .. } => *layout = new_layout,
_ => (), _ => (),
} }
} }
...@@ -77,6 +83,11 @@ impl From<&Style> for RenderPrimitive { ...@@ -77,6 +83,11 @@ impl From<&Style> for RenderPrimitive {
layout: Rect::default(), layout: Rect::default(),
handle, handle,
}, },
RenderCommand::NinePatch { handle, border } => Self::NinePatch {
border,
layout: Rect::default(),
handle,
},
} }
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment