mirror of
https://github.com/bevyengine/bevy
synced 2025-02-18 06:58:34 +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},
|
asset::{Asset, AssetStorage},
|
||||||
math::{Mat4, Vec3},
|
math::{Mat4, Vec3},
|
||||||
render::*,
|
render::*,
|
||||||
|
ui::*,
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,31 +87,41 @@ fn setup(world: &mut World) {
|
||||||
|
|
||||||
world.insert(
|
world.insert(
|
||||||
(),
|
(),
|
||||||
vec![(Rect {
|
vec![(Node::new(
|
||||||
position: math::vec2(75.0, 75.0),
|
math::vec2(0.0, 0.0),
|
||||||
dimensions: math::vec2(100.0, 100.0),
|
Anchors::new(0.0, 0.0, 0.0, 1.0),
|
||||||
anchors: Anchors::new(0.5, 0.5, 0.5, 0.5),
|
Margins::new(10.0, 100.0, 10.0, 10.0),
|
||||||
color: math::vec4(0.0, 1.0, 0.0, 1.0),
|
math::vec4(0.0, 1.0, 0.0, 1.0),
|
||||||
},)],
|
),)],
|
||||||
);
|
);
|
||||||
|
|
||||||
world.insert(
|
world.insert(
|
||||||
(),
|
(),
|
||||||
vec![(Rect {
|
vec![(Node::new(
|
||||||
position: math::vec2(50.0, 50.0),
|
math::vec2(75.0, 75.0),
|
||||||
dimensions: math::vec2(100.0, 100.0),
|
Anchors::new(0.5, 0.5, 0.5, 0.5),
|
||||||
anchors: Anchors::new(0.5, 0.5, 0.5, 0.5),
|
Margins::new(0.0, 100.0, 0.0, 100.0),
|
||||||
color: math::vec4(1.0, 0.0, 0.0, 1.0),
|
math::vec4(1.0, 0.1, 0.1, 1.0),
|
||||||
},)],
|
),)],
|
||||||
);
|
);
|
||||||
|
|
||||||
world.insert(
|
world.insert(
|
||||||
(),
|
(),
|
||||||
vec![(Rect {
|
vec![(Node::new(
|
||||||
position: math::vec2(100.0, 100.0),
|
math::vec2(50.0, 50.0),
|
||||||
dimensions: math::vec2(100.0, 100.0),
|
Anchors::new(0.5, 0.5, 0.5, 0.5),
|
||||||
anchors: Anchors::new(0.5, 0.5, 0.5, 0.5),
|
Margins::new(0.0, 100.0, 0.0, 100.0),
|
||||||
color: math::vec4(0.0, 0.0, 1.0, 1.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,
|
schedule::Builder,
|
||||||
},
|
},
|
||||||
render::{passes::*, *},
|
render::{passes::*, *},
|
||||||
transform_system_bundle, App, Time,
|
transform_system_bundle, ui, App, Time,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct AppBuilder {
|
pub struct AppBuilder {
|
||||||
|
@ -82,9 +82,13 @@ impl AppBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_default_systems(mut self) -> Self {
|
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(..) {
|
for transform_system in transform_system_bundle::build(&mut self.world).drain(..) {
|
||||||
self.schedule_builder = self.schedule_builder.add_system(transform_system);
|
self.schedule_builder = self.schedule_builder.add_system(transform_system);
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ use winit::{
|
||||||
event,
|
event,
|
||||||
event::WindowEvent,
|
event::WindowEvent,
|
||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
window::Window,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use legion::prelude::*;
|
use legion::prelude::*;
|
||||||
|
@ -15,7 +14,6 @@ use crate::{render::*, Time};
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub world: World,
|
pub world: World,
|
||||||
pub window: Option<Window>,
|
|
||||||
pub render_graph: RenderGraph,
|
pub render_graph: RenderGraph,
|
||||||
pub swap_chain: Option<wgpu::SwapChain>,
|
pub swap_chain: Option<wgpu::SwapChain>,
|
||||||
pub schedule: Schedule,
|
pub schedule: Schedule,
|
||||||
|
@ -28,7 +26,6 @@ impl App {
|
||||||
schedule: schedule,
|
schedule: schedule,
|
||||||
render_graph,
|
render_graph,
|
||||||
swap_chain: None,
|
swap_chain: None,
|
||||||
window: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +99,8 @@ impl App {
|
||||||
queue,
|
queue,
|
||||||
surface,
|
surface,
|
||||||
);
|
);
|
||||||
self.window = Some(window);
|
|
||||||
|
self.world.resources.insert(window);
|
||||||
self.swap_chain = Some(swap_chain);
|
self.swap_chain = Some(swap_chain);
|
||||||
|
|
||||||
log::info!("Entering render loop...");
|
log::info!("Entering render loop...");
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod app;
|
||||||
pub mod asset;
|
pub mod asset;
|
||||||
mod core;
|
mod core;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
|
pub mod ui;
|
||||||
|
|
||||||
pub use crate::core::*;
|
pub use crate::core::*;
|
||||||
pub use app::{App, AppBuilder};
|
pub use app::{App, AppBuilder};
|
||||||
|
|
|
@ -7,7 +7,6 @@ pub mod shader;
|
||||||
|
|
||||||
mod light;
|
mod light;
|
||||||
mod material;
|
mod material;
|
||||||
mod rect;
|
|
||||||
mod render_graph;
|
mod render_graph;
|
||||||
mod vertex;
|
mod vertex;
|
||||||
|
|
||||||
|
@ -15,7 +14,6 @@ pub use camera::*;
|
||||||
pub use light::*;
|
pub use light::*;
|
||||||
pub use material::*;
|
pub use material::*;
|
||||||
pub use mesh::*;
|
pub use mesh::*;
|
||||||
pub use rect::*;
|
|
||||||
pub use render_graph::*;
|
pub use render_graph::*;
|
||||||
pub use shader::*;
|
pub use shader::*;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::{
|
||||||
math,
|
math,
|
||||||
render::mesh::*,
|
render::mesh::*,
|
||||||
render::{instancing::InstanceBufferInfo, *},
|
render::{instancing::InstanceBufferInfo, *},
|
||||||
|
ui::Node,
|
||||||
};
|
};
|
||||||
use legion::prelude::*;
|
use legion::prelude::*;
|
||||||
use wgpu::SwapChainOutput;
|
use wgpu::SwapChainOutput;
|
||||||
|
@ -13,7 +14,6 @@ use zerocopy::{AsBytes, FromBytes};
|
||||||
pub struct RectData {
|
pub struct RectData {
|
||||||
pub position: [f32; 2],
|
pub position: [f32; 2],
|
||||||
pub dimensions: [f32; 2],
|
pub dimensions: [f32; 2],
|
||||||
pub anchors: [f32; 4],
|
|
||||||
pub color: [f32; 4],
|
pub color: [f32; 4],
|
||||||
pub z_index: f32,
|
pub z_index: f32,
|
||||||
}
|
}
|
||||||
|
@ -40,22 +40,21 @@ impl UiPipeline {
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
world: &World,
|
world: &World,
|
||||||
) -> Vec<InstanceBufferInfo> {
|
) -> Vec<InstanceBufferInfo> {
|
||||||
let rect_query = <Read<Rect>>::query();
|
let node_query = <Read<Node>>::query();
|
||||||
let rect_count = rect_query.iter(world).count();
|
let node_count = node_query.iter(world).count();
|
||||||
|
|
||||||
if rect_count == 0 {
|
if node_count == 0 {
|
||||||
return Vec::new();
|
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
|
// TODO: this probably isn't the best way to handle z-ordering
|
||||||
let mut z = 0.9999;
|
let mut z = 0.9999;
|
||||||
for rect in rect_query.iter(world) {
|
for node in node_query.iter(world) {
|
||||||
data.push(RectData {
|
data.push(RectData {
|
||||||
position: rect.position.into(),
|
position: node.global_position.into(),
|
||||||
dimensions: rect.dimensions.into(),
|
dimensions: node.dimensions.into(),
|
||||||
color: rect.color.into(),
|
color: node.color.into(),
|
||||||
anchors: [rect.anchors.top, rect.anchors.bottom, rect.anchors.left, rect.anchors.right],
|
|
||||||
z_index: z,
|
z_index: z,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -73,7 +72,7 @@ impl UiPipeline {
|
||||||
instance_buffer_infos.push(InstanceBufferInfo {
|
instance_buffer_infos.push(InstanceBufferInfo {
|
||||||
mesh_id: mesh_id,
|
mesh_id: mesh_id,
|
||||||
buffer: buffer,
|
buffer: buffer,
|
||||||
instance_count: rect_count,
|
instance_count: node_count,
|
||||||
});
|
});
|
||||||
|
|
||||||
instance_buffer_infos
|
instance_buffer_infos
|
||||||
|
@ -154,15 +153,10 @@ impl Pipeline for UiPipeline {
|
||||||
shader_location: 4,
|
shader_location: 4,
|
||||||
},
|
},
|
||||||
wgpu::VertexAttributeDescriptor {
|
wgpu::VertexAttributeDescriptor {
|
||||||
format: wgpu::VertexFormat::Float4,
|
format: wgpu::VertexFormat::Float,
|
||||||
offset: 8 * 4,
|
offset: 8 * 4,
|
||||||
shader_location: 5,
|
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 {
|
depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {
|
||||||
format: self.depth_format,
|
format: self.depth_format,
|
||||||
depth_write_enabled: true,
|
depth_write_enabled: false,
|
||||||
depth_compare: wgpu::CompareFunction::Less,
|
depth_compare: wgpu::CompareFunction::Always,
|
||||||
stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,
|
stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,
|
||||||
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
|
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
|
||||||
stencil_read_mask: 0,
|
stencil_read_mask: 0,
|
||||||
|
|
|
@ -7,9 +7,8 @@ layout(location = 1) in vec4 a_Normal;
|
||||||
// instanced attributes (RectData)
|
// instanced attributes (RectData)
|
||||||
layout (location = 2) in vec2 a_RectPosition;
|
layout (location = 2) in vec2 a_RectPosition;
|
||||||
layout (location = 3) in vec2 a_RectDimensions;
|
layout (location = 3) in vec2 a_RectDimensions;
|
||||||
layout (location = 4) in vec4 a_RectAnchors;
|
layout (location = 4) in vec4 a_RectColor;
|
||||||
layout (location = 5) in vec4 a_RectColor;
|
layout (location = 5) in float a_RectZIndex;
|
||||||
layout (location = 6) in float a_RectZIndex;
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 v_Color;
|
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…
Add table
Reference in a new issue