mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 22:20:20 +00:00
ui node system
This commit is contained in:
parent
480443d35a
commit
6da891be29
13 changed files with 244 additions and 96 deletions
|
@ -2,6 +2,7 @@ use bevy::{
|
|||
asset::{Asset, AssetStorage},
|
||||
math::{Mat4, Vec3},
|
||||
render::*,
|
||||
ui::*,
|
||||
*,
|
||||
};
|
||||
|
||||
|
@ -86,31 +87,41 @@ fn setup(world: &mut World) {
|
|||
|
||||
world.insert(
|
||||
(),
|
||||
vec![(Rect {
|
||||
position: math::vec2(75.0, 75.0),
|
||||
dimensions: math::vec2(100.0, 100.0),
|
||||
anchors: Anchors::new(0.5, 0.5, 0.5, 0.5),
|
||||
color: math::vec4(0.0, 1.0, 0.0, 1.0),
|
||||
},)],
|
||||
vec![(Node::new(
|
||||
math::vec2(0.0, 0.0),
|
||||
Anchors::new(0.0, 0.0, 0.0, 1.0),
|
||||
Margins::new(10.0, 100.0, 10.0, 10.0),
|
||||
math::vec4(0.0, 1.0, 0.0, 1.0),
|
||||
),)],
|
||||
);
|
||||
|
||||
world.insert(
|
||||
(),
|
||||
vec![(Rect {
|
||||
position: math::vec2(50.0, 50.0),
|
||||
dimensions: math::vec2(100.0, 100.0),
|
||||
anchors: Anchors::new(0.5, 0.5, 0.5, 0.5),
|
||||
color: math::vec4(1.0, 0.0, 0.0, 1.0),
|
||||
},)],
|
||||
vec![(Node::new(
|
||||
math::vec2(75.0, 75.0),
|
||||
Anchors::new(0.5, 0.5, 0.5, 0.5),
|
||||
Margins::new(0.0, 100.0, 0.0, 100.0),
|
||||
math::vec4(1.0, 0.1, 0.1, 1.0),
|
||||
),)],
|
||||
);
|
||||
|
||||
world.insert(
|
||||
(),
|
||||
vec![(Rect {
|
||||
position: math::vec2(100.0, 100.0),
|
||||
dimensions: math::vec2(100.0, 100.0),
|
||||
anchors: Anchors::new(0.5, 0.5, 0.5, 0.5),
|
||||
color: math::vec4(0.0, 0.0, 1.0, 1.0),
|
||||
},)],
|
||||
vec![(Node::new(
|
||||
math::vec2(50.0, 50.0),
|
||||
Anchors::new(0.5, 0.5, 0.5, 0.5),
|
||||
Margins::new(0.0, 100.0, 0.0, 100.0),
|
||||
math::vec4(1.0, 0.3, 0.3, 1.0),
|
||||
),)],
|
||||
);
|
||||
|
||||
world.insert(
|
||||
(),
|
||||
vec![(Node::new(
|
||||
math::vec2(100.0, 100.0),
|
||||
Anchors::new(0.5, 0.5, 0.5, 0.5),
|
||||
Margins::new(0.0, 100.0, 0.0, 100.0),
|
||||
math::vec4(1.0, 0.5, 0.5, 1.0),
|
||||
),)],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
schedule::Builder,
|
||||
},
|
||||
render::{passes::*, *},
|
||||
transform_system_bundle, App, Time,
|
||||
transform_system_bundle, ui, App, Time,
|
||||
};
|
||||
|
||||
pub struct AppBuilder {
|
||||
|
@ -82,9 +82,13 @@ impl AppBuilder {
|
|||
}
|
||||
|
||||
pub fn add_default_systems(mut self) -> Self {
|
||||
self.schedule_builder = self
|
||||
.schedule_builder
|
||||
.add_system(ui::update_system::build_ui_update_system());
|
||||
for transform_system in transform_system_bundle::build(&mut self.world).drain(..) {
|
||||
self.schedule_builder = self.schedule_builder.add_system(transform_system);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ use winit::{
|
|||
event,
|
||||
event::WindowEvent,
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
use legion::prelude::*;
|
||||
|
@ -15,7 +14,6 @@ use crate::{render::*, Time};
|
|||
|
||||
pub struct App {
|
||||
pub world: World,
|
||||
pub window: Option<Window>,
|
||||
pub render_graph: RenderGraph,
|
||||
pub swap_chain: Option<wgpu::SwapChain>,
|
||||
pub schedule: Schedule,
|
||||
|
@ -28,7 +26,6 @@ impl App {
|
|||
schedule: schedule,
|
||||
render_graph,
|
||||
swap_chain: None,
|
||||
window: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +99,8 @@ impl App {
|
|||
queue,
|
||||
surface,
|
||||
);
|
||||
self.window = Some(window);
|
||||
|
||||
self.world.resources.insert(window);
|
||||
self.swap_chain = Some(swap_chain);
|
||||
|
||||
log::info!("Entering render loop...");
|
||||
|
|
|
@ -2,6 +2,7 @@ mod app;
|
|||
pub mod asset;
|
||||
mod core;
|
||||
pub mod render;
|
||||
pub mod ui;
|
||||
|
||||
pub use crate::core::*;
|
||||
pub use app::{App, AppBuilder};
|
||||
|
|
|
@ -7,7 +7,6 @@ pub mod shader;
|
|||
|
||||
mod light;
|
||||
mod material;
|
||||
mod rect;
|
||||
mod render_graph;
|
||||
mod vertex;
|
||||
|
||||
|
@ -15,7 +14,6 @@ pub use camera::*;
|
|||
pub use light::*;
|
||||
pub use material::*;
|
||||
pub use mesh::*;
|
||||
pub use rect::*;
|
||||
pub use render_graph::*;
|
||||
pub use shader::*;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::{
|
|||
math,
|
||||
render::mesh::*,
|
||||
render::{instancing::InstanceBufferInfo, *},
|
||||
ui::Node,
|
||||
};
|
||||
use legion::prelude::*;
|
||||
use wgpu::SwapChainOutput;
|
||||
|
@ -13,7 +14,6 @@ use zerocopy::{AsBytes, FromBytes};
|
|||
pub struct RectData {
|
||||
pub position: [f32; 2],
|
||||
pub dimensions: [f32; 2],
|
||||
pub anchors: [f32; 4],
|
||||
pub color: [f32; 4],
|
||||
pub z_index: f32,
|
||||
}
|
||||
|
@ -40,22 +40,21 @@ impl UiPipeline {
|
|||
device: &wgpu::Device,
|
||||
world: &World,
|
||||
) -> Vec<InstanceBufferInfo> {
|
||||
let rect_query = <Read<Rect>>::query();
|
||||
let rect_count = rect_query.iter(world).count();
|
||||
let node_query = <Read<Node>>::query();
|
||||
let node_count = node_query.iter(world).count();
|
||||
|
||||
if rect_count == 0 {
|
||||
if node_count == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut data = Vec::with_capacity(rect_count);
|
||||
let mut data = Vec::with_capacity(node_count);
|
||||
// TODO: this probably isn't the best way to handle z-ordering
|
||||
let mut z = 0.9999;
|
||||
for rect in rect_query.iter(world) {
|
||||
for node in node_query.iter(world) {
|
||||
data.push(RectData {
|
||||
position: rect.position.into(),
|
||||
dimensions: rect.dimensions.into(),
|
||||
color: rect.color.into(),
|
||||
anchors: [rect.anchors.top, rect.anchors.bottom, rect.anchors.left, rect.anchors.right],
|
||||
position: node.global_position.into(),
|
||||
dimensions: node.dimensions.into(),
|
||||
color: node.color.into(),
|
||||
z_index: z,
|
||||
});
|
||||
|
||||
|
@ -73,7 +72,7 @@ impl UiPipeline {
|
|||
instance_buffer_infos.push(InstanceBufferInfo {
|
||||
mesh_id: mesh_id,
|
||||
buffer: buffer,
|
||||
instance_count: rect_count,
|
||||
instance_count: node_count,
|
||||
});
|
||||
|
||||
instance_buffer_infos
|
||||
|
@ -154,15 +153,10 @@ impl Pipeline for UiPipeline {
|
|||
shader_location: 4,
|
||||
},
|
||||
wgpu::VertexAttributeDescriptor {
|
||||
format: wgpu::VertexFormat::Float4,
|
||||
format: wgpu::VertexFormat::Float,
|
||||
offset: 8 * 4,
|
||||
shader_location: 5,
|
||||
},
|
||||
wgpu::VertexAttributeDescriptor {
|
||||
format: wgpu::VertexFormat::Float,
|
||||
offset: 12 * 4,
|
||||
shader_location: 6,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -196,8 +190,8 @@ impl Pipeline for UiPipeline {
|
|||
}],
|
||||
depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {
|
||||
format: self.depth_format,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Less,
|
||||
depth_write_enabled: false,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,
|
||||
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
|
||||
stencil_read_mask: 0,
|
||||
|
|
|
@ -7,9 +7,8 @@ layout(location = 1) in vec4 a_Normal;
|
|||
// instanced attributes (RectData)
|
||||
layout (location = 2) in vec2 a_RectPosition;
|
||||
layout (location = 3) in vec2 a_RectDimensions;
|
||||
layout (location = 4) in vec4 a_RectAnchors;
|
||||
layout (location = 5) in vec4 a_RectColor;
|
||||
layout (location = 6) in float a_RectZIndex;
|
||||
layout (location = 4) in vec4 a_RectColor;
|
||||
layout (location = 5) in float a_RectZIndex;
|
||||
|
||||
layout(location = 0) out vec4 v_Color;
|
||||
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
use crate::math::{Vec2, Vec4};
|
||||
|
||||
pub struct Anchors {
|
||||
pub top: f32,
|
||||
pub bottom: f32,
|
||||
pub left: f32,
|
||||
pub right: f32,
|
||||
|
||||
}
|
||||
|
||||
impl Anchors {
|
||||
pub fn new(top: f32, bottom: f32, left: f32, right: f32) -> Self {
|
||||
Anchors {
|
||||
top,
|
||||
bottom,
|
||||
left,
|
||||
right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Anchors {
|
||||
fn default() -> Self {
|
||||
Anchors {
|
||||
top: 0.5,
|
||||
bottom: 0.5,
|
||||
left: 0.5,
|
||||
right: 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rect {
|
||||
pub position: Vec2,
|
||||
pub dimensions: Vec2,
|
||||
pub anchors: Anchors,
|
||||
pub color: Vec4,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn new(position: Vec2, dimensions: Vec2, anchors: Anchors, color: Vec4) -> Self {
|
||||
Rect {
|
||||
position,
|
||||
dimensions,
|
||||
anchors,
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
28
src/ui/anchors.rs
Normal file
28
src/ui/anchors.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
pub struct Anchors {
|
||||
pub left: f32,
|
||||
pub right: f32,
|
||||
pub bottom: f32,
|
||||
pub top: f32,
|
||||
}
|
||||
|
||||
impl Anchors {
|
||||
pub fn new(left: f32, right: f32, bottom: f32, top: f32) -> Self {
|
||||
Anchors {
|
||||
left,
|
||||
right,
|
||||
bottom,
|
||||
top,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Anchors {
|
||||
fn default() -> Self {
|
||||
Anchors {
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
bottom: 0.0,
|
||||
top: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
28
src/ui/margins.rs
Normal file
28
src/ui/margins.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
pub struct Margins {
|
||||
pub left: f32,
|
||||
pub right: f32,
|
||||
pub bottom: f32,
|
||||
pub top: f32,
|
||||
}
|
||||
|
||||
impl Margins {
|
||||
pub fn new(left: f32, right: f32, bottom: f32, top: f32) -> Self {
|
||||
Margins {
|
||||
left,
|
||||
right,
|
||||
bottom,
|
||||
top,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Margins {
|
||||
fn default() -> Self {
|
||||
Margins {
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
bottom: 0.0,
|
||||
top: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
8
src/ui/mod.rs
Normal file
8
src/ui/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
mod anchors;
|
||||
mod margins;
|
||||
mod node;
|
||||
pub mod update_system;
|
||||
|
||||
pub use anchors::*;
|
||||
pub use margins::*;
|
||||
pub use node::*;
|
113
src/ui/node.rs
Normal file
113
src/ui/node.rs
Normal file
|
@ -0,0 +1,113 @@
|
|||
use crate::{
|
||||
math,
|
||||
math::{Vec2, Vec4},
|
||||
ui::{Anchors, Margins},
|
||||
};
|
||||
|
||||
enum GrowDirection {
|
||||
Negative,
|
||||
Positive,
|
||||
}
|
||||
|
||||
pub struct Node {
|
||||
pub position: Vec2,
|
||||
pub global_position: Vec2,
|
||||
pub dimensions: Vec2,
|
||||
pub parent_dimensions: Vec2,
|
||||
pub anchors: Anchors,
|
||||
pub margins: Margins,
|
||||
pub color: Vec4,
|
||||
}
|
||||
|
||||
impl Default for Node {
|
||||
fn default() -> Self {
|
||||
Node {
|
||||
position: Vec2::default(),
|
||||
global_position: Vec2::default(),
|
||||
dimensions: Vec2::default(),
|
||||
parent_dimensions: Vec2::default(),
|
||||
anchors: Anchors::default(),
|
||||
margins: Margins::default(),
|
||||
color: math::vec4(0.0, 0.0, 0.0, 1.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new(position: Vec2, anchors: Anchors, margins: Margins, color: Vec4) -> Self {
|
||||
Node {
|
||||
position,
|
||||
global_position: Vec2::default(),
|
||||
dimensions: Vec2::default(),
|
||||
parent_dimensions: Vec2::default(),
|
||||
anchors,
|
||||
margins,
|
||||
color,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, parent_dimensions: Vec2) {
|
||||
let (rect_x, rect_width) = Self::compute_dimension_properties(
|
||||
self.position.x(),
|
||||
self.margins.left,
|
||||
self.margins.right,
|
||||
self.anchors.left,
|
||||
self.anchors.right,
|
||||
parent_dimensions.x(),
|
||||
);
|
||||
let (rect_y, rect_height) = Self::compute_dimension_properties(
|
||||
self.position.y(),
|
||||
self.margins.bottom,
|
||||
self.margins.top,
|
||||
self.anchors.bottom,
|
||||
self.anchors.top,
|
||||
parent_dimensions.y(),
|
||||
);
|
||||
|
||||
self.global_position = math::vec2(rect_x, rect_y);
|
||||
self.dimensions = math::vec2(rect_width, rect_height);
|
||||
}
|
||||
|
||||
fn compute_dimension_properties(
|
||||
offset: f32,
|
||||
margin0: f32,
|
||||
margin1: f32,
|
||||
anchor0: f32,
|
||||
anchor1: f32,
|
||||
length: f32,
|
||||
) -> (f32, f32) {
|
||||
let anchor_p0 = anchor0 * length;
|
||||
let anchor_p1 = anchor1 * length;
|
||||
|
||||
let p0_grow_direction = if anchor_p0 <= 0.5 {
|
||||
GrowDirection::Positive
|
||||
} else {
|
||||
GrowDirection::Negative
|
||||
};
|
||||
let p1_grow_direction = if anchor_p1 < 0.5 {
|
||||
GrowDirection::Positive
|
||||
} else {
|
||||
GrowDirection::Negative
|
||||
};
|
||||
|
||||
let p0 = Self::compute_rect_position(offset, margin0, anchor_p0, p0_grow_direction);
|
||||
let p1 = Self::compute_rect_position(offset, margin1, anchor_p1, p1_grow_direction);
|
||||
|
||||
let p = (p0 + p1) / 2.0;
|
||||
let final_width = p1 - p0;
|
||||
|
||||
(p, final_width)
|
||||
}
|
||||
|
||||
fn compute_rect_position(
|
||||
position: f32,
|
||||
margin: f32,
|
||||
anchor_position: f32,
|
||||
grow_direction: GrowDirection,
|
||||
) -> f32 {
|
||||
match grow_direction {
|
||||
GrowDirection::Negative => position + anchor_position - margin,
|
||||
GrowDirection::Positive => position + anchor_position + margin,
|
||||
}
|
||||
}
|
||||
}
|
15
src/ui/update_system.rs
Normal file
15
src/ui/update_system.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use crate::{ui::Node, *};
|
||||
use winit::window::Window;
|
||||
|
||||
pub fn build_ui_update_system() -> Box<dyn Schedulable> {
|
||||
SystemBuilder::new("ui_update_system")
|
||||
.read_resource::<Window>()
|
||||
.with_query(<(Write<Node>,)>::query().filter(!component::<Children>()))
|
||||
.build(move |_, world, window, node_query| {
|
||||
let window_size = window.inner_size();
|
||||
let parent_dimensions = math::vec2(window_size.width as f32, window_size.height as f32);
|
||||
for (mut node,) in node_query.iter_mut(world) {
|
||||
node.update(parent_dimensions);
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue