ui node system

This commit is contained in:
Carter Anderson 2020-01-12 16:51:21 -08:00
parent 480443d35a
commit 6da891be29
13 changed files with 244 additions and 96 deletions

View file

@ -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),
),)],
);
}

View file

@ -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
}

View file

@ -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...");

View file

@ -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};

View file

@ -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::*;

View file

@ -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,

View file

@ -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;

View file

@ -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
View 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
View 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
View 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
View 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
View 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);
}
})
}