ui: rework so Nodes now use transforms and z-sort happens
This commit is contained in:
		
							parent
							
								
									75429f4639
								
							
						
					
					
						commit
						1ef4fbf005
					
				@ -77,14 +77,12 @@ pub fn run_on_hierarchy_subworld<T>(
 | 
			
		||||
pub fn run_on_hierarchy_subworld_mut<T>(
 | 
			
		||||
    world: &mut SubWorld,
 | 
			
		||||
    entity: Entity,
 | 
			
		||||
    input: T,
 | 
			
		||||
    run: &mut dyn FnMut(&mut SubWorld, Entity, T) -> Option<T>,
 | 
			
		||||
    child_result_action: &mut dyn FnMut(T, T) -> T,
 | 
			
		||||
) -> Option<T>
 | 
			
		||||
where
 | 
			
		||||
    T: Copy,
 | 
			
		||||
{
 | 
			
		||||
    parent_result: Option<&mut T>,
 | 
			
		||||
    mut previous_result: Option<T>,
 | 
			
		||||
    run: &mut dyn FnMut(&mut SubWorld, Entity, Option<&mut T>, Option<T>) -> Option<T>,
 | 
			
		||||
) -> Option<T> where T: Clone {
 | 
			
		||||
    // TODO: not a huge fan of this pattern. are there ways to do recursive updates in legion without allocations?
 | 
			
		||||
    // TODO: the problem above might be resolvable with world splitting
 | 
			
		||||
    let children = match world.get_component::<Children>(entity) {
 | 
			
		||||
        Some(children) => Some(
 | 
			
		||||
            children
 | 
			
		||||
@ -95,21 +93,21 @@ where
 | 
			
		||||
        None => None,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let result = run(world, entity, input);
 | 
			
		||||
 | 
			
		||||
    if let Some(mut result) = result {
 | 
			
		||||
        if let Some(children) = children {
 | 
			
		||||
            for child in children {
 | 
			
		||||
                let child_result =
 | 
			
		||||
                    run_on_hierarchy_subworld_mut(world, child, result, run, child_result_action);
 | 
			
		||||
                if let Some(child_result) = child_result {
 | 
			
		||||
                    result = child_result_action(result, child_result)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
    let mut parent_result = run(world, entity, parent_result, previous_result);
 | 
			
		||||
    previous_result = None;
 | 
			
		||||
    if let Some(children) = children {
 | 
			
		||||
        for child in children {
 | 
			
		||||
            previous_result = run_on_hierarchy_subworld_mut(
 | 
			
		||||
                world,
 | 
			
		||||
                child,
 | 
			
		||||
                parent_result.as_mut(),
 | 
			
		||||
                previous_result,
 | 
			
		||||
                run,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Some(result)
 | 
			
		||||
    } else {
 | 
			
		||||
        None
 | 
			
		||||
        previous_result = parent_result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    previous_result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -65,7 +65,7 @@ impl OrthographicCameraEntity {
 | 
			
		||||
                ..Default::default()
 | 
			
		||||
            },
 | 
			
		||||
            orthographic_projection: OrthographicProjection {
 | 
			
		||||
                window_origin: WindowOrigin::BottomLeft,
 | 
			
		||||
                window_origin: WindowOrigin::Center,
 | 
			
		||||
                ..Default::default()
 | 
			
		||||
            },
 | 
			
		||||
            visible_entities: Default::default(),
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
mod color_material;
 | 
			
		||||
mod dynamic_texture_atlas_builder;
 | 
			
		||||
pub mod entity;
 | 
			
		||||
mod quad;
 | 
			
		||||
mod rect;
 | 
			
		||||
mod render;
 | 
			
		||||
mod sprite;
 | 
			
		||||
@ -10,7 +9,6 @@ mod texture_atlas_builder;
 | 
			
		||||
 | 
			
		||||
pub use color_material::*;
 | 
			
		||||
pub use dynamic_texture_atlas_builder::*;
 | 
			
		||||
pub use quad::*;
 | 
			
		||||
pub use rect::*;
 | 
			
		||||
pub use render::*;
 | 
			
		||||
pub use sprite::*;
 | 
			
		||||
 | 
			
		||||
@ -1,11 +0,0 @@
 | 
			
		||||
use bevy_core::bytes::Bytes;
 | 
			
		||||
use bevy_render::render_resource::{RenderResource, RenderResources};
 | 
			
		||||
use glam::Vec2;
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
#[derive(Default, Clone, Copy, Debug, RenderResources, RenderResource, Bytes)]
 | 
			
		||||
#[render_resources(from_self)]
 | 
			
		||||
pub struct Quad {
 | 
			
		||||
    pub position: Vec2,
 | 
			
		||||
    pub size: Vec2,
 | 
			
		||||
    pub z_index: f32,
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
use crate::{ColorMaterial, Quad, TextureAtlas, TextureAtlasSprite, Sprite};
 | 
			
		||||
use crate::{ColorMaterial, TextureAtlas, TextureAtlasSprite, Sprite};
 | 
			
		||||
use bevy_asset::{Assets, Handle};
 | 
			
		||||
use bevy_render::{
 | 
			
		||||
    base_render_graph,
 | 
			
		||||
@ -110,7 +110,6 @@ pub fn build_sprite_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor
 | 
			
		||||
 | 
			
		||||
pub mod node {
 | 
			
		||||
    pub const COLOR_MATERIAL: &'static str = "color_material";
 | 
			
		||||
    pub const QUAD: &'static str = "quad";
 | 
			
		||||
    pub const SPRITE: &'static str = "sprite";
 | 
			
		||||
    pub const SPRITE_SHEET: &'static str = "sprite_sheet";
 | 
			
		||||
    pub const SPRITE_SHEET_SPRITE: &'static str = "sprite_sheet_sprite";
 | 
			
		||||
@ -129,9 +128,6 @@ impl SpriteRenderGraphBuilder for RenderGraph {
 | 
			
		||||
        self.add_node_edge(node::COLOR_MATERIAL, base_render_graph::node::MAIN_PASS)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        self.add_system_node(node::QUAD, RenderResourcesNode::<Quad>::new(false));
 | 
			
		||||
        self.add_node_edge(node::QUAD, base_render_graph::node::MAIN_PASS)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        self.add_system_node(node::SPRITE, RenderResourcesNode::<Sprite>::new(true));
 | 
			
		||||
        self.add_node_edge(node::SPRITE, base_render_graph::node::MAIN_PASS)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
@ -2,28 +2,52 @@ use super::Node;
 | 
			
		||||
use crate::{render::UI_PIPELINE_HANDLE, widget::Label};
 | 
			
		||||
use bevy_asset::Handle;
 | 
			
		||||
use bevy_derive::EntityArchetype;
 | 
			
		||||
use bevy_render::{draw::Draw, mesh::Mesh, pipeline::RenderPipelines};
 | 
			
		||||
use bevy_sprite::{ColorMaterial, Quad, QUAD_HANDLE};
 | 
			
		||||
use bevy_render::{draw::Draw, mesh::Mesh, pipeline::{PipelineSpecialization, RenderPipelines, DynamicBinding, RenderPipeline}};
 | 
			
		||||
use bevy_sprite::{ColorMaterial, QUAD_HANDLE};
 | 
			
		||||
use bevy_transform::prelude::{Translation, Transform, Rotation, Scale};
 | 
			
		||||
 | 
			
		||||
#[derive(EntityArchetype)]
 | 
			
		||||
pub struct UiEntity {
 | 
			
		||||
    pub node: Node,
 | 
			
		||||
    pub quad: Quad,
 | 
			
		||||
    pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
 | 
			
		||||
    pub material: Handle<ColorMaterial>,
 | 
			
		||||
    pub draw: Draw,
 | 
			
		||||
    pub render_pipelines: RenderPipelines,
 | 
			
		||||
    pub transform: Transform,
 | 
			
		||||
    pub translation: Translation,
 | 
			
		||||
    pub rotation: Rotation,
 | 
			
		||||
    pub scale: Scale,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for UiEntity {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        UiEntity {
 | 
			
		||||
            mesh: QUAD_HANDLE,
 | 
			
		||||
            render_pipelines: RenderPipelines::from_handles(&[UI_PIPELINE_HANDLE]),
 | 
			
		||||
            render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::specialized(
 | 
			
		||||
                UI_PIPELINE_HANDLE,
 | 
			
		||||
                PipelineSpecialization {
 | 
			
		||||
                    dynamic_bindings: vec![
 | 
			
		||||
                        // Transform
 | 
			
		||||
                        DynamicBinding {
 | 
			
		||||
                            bind_group: 1,
 | 
			
		||||
                            binding: 0,
 | 
			
		||||
                        },
 | 
			
		||||
                        // Node_size
 | 
			
		||||
                        DynamicBinding {
 | 
			
		||||
                            bind_group: 1,
 | 
			
		||||
                            binding: 1,
 | 
			
		||||
                        },
 | 
			
		||||
                    ],
 | 
			
		||||
                    ..Default::default()
 | 
			
		||||
                },
 | 
			
		||||
            )]),
 | 
			
		||||
            node: Default::default(),
 | 
			
		||||
            quad: Default::default(),
 | 
			
		||||
            material: Default::default(),
 | 
			
		||||
            draw: Default::default(),
 | 
			
		||||
            transform: Default::default(),
 | 
			
		||||
            translation: Default::default(),
 | 
			
		||||
            rotation: Default::default(),
 | 
			
		||||
            scale: Default::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -31,9 +55,12 @@ impl Default for UiEntity {
 | 
			
		||||
#[derive(EntityArchetype)]
 | 
			
		||||
pub struct LabelEntity {
 | 
			
		||||
    pub node: Node,
 | 
			
		||||
    pub quad: Quad,
 | 
			
		||||
    pub draw: Draw,
 | 
			
		||||
    pub label: Label,
 | 
			
		||||
    pub transform: Transform,
 | 
			
		||||
    pub translation: Translation,
 | 
			
		||||
    pub rotation: Rotation,
 | 
			
		||||
    pub scale: Scale,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for LabelEntity {
 | 
			
		||||
@ -41,8 +68,14 @@ impl Default for LabelEntity {
 | 
			
		||||
        LabelEntity {
 | 
			
		||||
            label: Label::default(),
 | 
			
		||||
            node: Default::default(),
 | 
			
		||||
            quad: Default::default(),
 | 
			
		||||
            draw: Default::default(),
 | 
			
		||||
            draw: Draw {
 | 
			
		||||
                is_transparent: true,
 | 
			
		||||
                ..Default::default()
 | 
			
		||||
            },
 | 
			
		||||
            transform: Default::default(),
 | 
			
		||||
            translation: Default::default(),
 | 
			
		||||
            rotation: Default::default(),
 | 
			
		||||
            scale: Default::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ pub struct UiPlugin;
 | 
			
		||||
 | 
			
		||||
impl AppPlugin for UiPlugin {
 | 
			
		||||
    fn build(&self, app: &mut AppBuilder) {
 | 
			
		||||
        app.add_system_to_stage(stage::POST_UPDATE, ui_update_system())
 | 
			
		||||
        app.add_system_to_stage(stage::POST_UPDATE, ui_update_system.system())
 | 
			
		||||
            .add_system_to_stage(stage::POST_UPDATE, Label::label_system.system())
 | 
			
		||||
            .add_system_to_stage(bevy_render::stage::DRAW, Label::draw_label_system.system());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
use super::{Anchors, Margins};
 | 
			
		||||
use bevy_sprite::Quad;
 | 
			
		||||
use glam::Vec2;
 | 
			
		||||
use bevy_transform::prelude::Translation;
 | 
			
		||||
use bevy_render::render_resource::RenderResources;
 | 
			
		||||
use glam::{Vec2, Vec3};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
enum MarginGrowDirection {
 | 
			
		||||
@ -8,41 +9,37 @@ enum MarginGrowDirection {
 | 
			
		||||
    Positive,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
#[derive(Debug, Clone, Default, RenderResources)]
 | 
			
		||||
pub struct Node {
 | 
			
		||||
    pub size: Vec2,
 | 
			
		||||
    #[render_resources(ignore)]
 | 
			
		||||
    pub position: Vec2,
 | 
			
		||||
    #[render_resources(ignore)]
 | 
			
		||||
    pub anchors: Anchors,
 | 
			
		||||
    #[render_resources(ignore)]
 | 
			
		||||
    pub margins: Margins,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Node {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
impl Node {
 | 
			
		||||
    pub fn new(anchors: Anchors, margins: Margins) -> Self {
 | 
			
		||||
        Node {
 | 
			
		||||
            position: Vec2::default(),
 | 
			
		||||
            anchors: Anchors::default(),
 | 
			
		||||
            margins: Margins::default(),
 | 
			
		||||
            anchors,
 | 
			
		||||
            margins,
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Node {
 | 
			
		||||
    pub fn new(position: Vec2, anchors: Anchors, margins: Margins) -> Self {
 | 
			
		||||
    pub fn positioned(position: Vec2, anchors: Anchors, margins: Margins) -> Self {
 | 
			
		||||
        Node {
 | 
			
		||||
            position,
 | 
			
		||||
            anchors,
 | 
			
		||||
            margins,
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn update(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        quad: &mut Quad,
 | 
			
		||||
        parent_size: Vec2,
 | 
			
		||||
        parent_position: Vec2,
 | 
			
		||||
        z_index: f32,
 | 
			
		||||
    ) {
 | 
			
		||||
    pub fn update(&mut self, translation: &mut Translation, z_offset: f32, parent_size: Vec2) {
 | 
			
		||||
        let (quad_x, quad_width) = Self::compute_dimension_properties(
 | 
			
		||||
            self.position.x(),
 | 
			
		||||
            self.margins.left,
 | 
			
		||||
            self.margins.right,
 | 
			
		||||
            self.anchors.left,
 | 
			
		||||
@ -50,7 +47,6 @@ impl Node {
 | 
			
		||||
            parent_size.x(),
 | 
			
		||||
        );
 | 
			
		||||
        let (quad_y, quad_height) = Self::compute_dimension_properties(
 | 
			
		||||
            self.position.y(),
 | 
			
		||||
            self.margins.bottom,
 | 
			
		||||
            self.margins.top,
 | 
			
		||||
            self.anchors.bottom,
 | 
			
		||||
@ -58,13 +54,11 @@ impl Node {
 | 
			
		||||
            parent_size.y(),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        quad.size = Vec2::new(quad_width, quad_height);
 | 
			
		||||
        quad.position = Vec2::new(quad_x, quad_y) + parent_position;
 | 
			
		||||
        quad.z_index = z_index;
 | 
			
		||||
        self.size = Vec2::new(quad_width, quad_height);
 | 
			
		||||
        translation.0 = self.position.extend(0.0) + Vec3::new(quad_x, quad_y, z_offset) - (parent_size / 2.0).extend(0.0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn compute_dimension_properties(
 | 
			
		||||
        offset: f32,
 | 
			
		||||
        margin0: f32,
 | 
			
		||||
        margin1: f32,
 | 
			
		||||
        anchor0: f32,
 | 
			
		||||
@ -85,23 +79,22 @@ impl Node {
 | 
			
		||||
            MarginGrowDirection::Negative
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let p0 = Self::compute_quad_position(offset, margin0, anchor_p0, p0_grow_direction);
 | 
			
		||||
        let p1 = Self::compute_quad_position(offset, margin1, anchor_p1, p1_grow_direction);
 | 
			
		||||
        let p0 = Self::compute_anchored_position(margin0, anchor_p0, p0_grow_direction);
 | 
			
		||||
        let p1 = Self::compute_anchored_position(margin1, anchor_p1, p1_grow_direction);
 | 
			
		||||
 | 
			
		||||
        let final_width = p1 - p0;
 | 
			
		||||
        let p = (p0 + p1) / 2.0;
 | 
			
		||||
        (p, final_width.abs())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn compute_quad_position(
 | 
			
		||||
        position: f32,
 | 
			
		||||
    fn compute_anchored_position(
 | 
			
		||||
        margin: f32,
 | 
			
		||||
        anchor_position: f32,
 | 
			
		||||
        grow_direction: MarginGrowDirection,
 | 
			
		||||
    ) -> f32 {
 | 
			
		||||
        match grow_direction {
 | 
			
		||||
            MarginGrowDirection::Negative => position + anchor_position - margin,
 | 
			
		||||
            MarginGrowDirection::Positive => position + anchor_position + margin,
 | 
			
		||||
            MarginGrowDirection::Negative => anchor_position - margin,
 | 
			
		||||
            MarginGrowDirection::Positive => anchor_position + margin,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,12 @@ use bevy_asset::{Assets, Handle};
 | 
			
		||||
use bevy_render::{
 | 
			
		||||
    base_render_graph,
 | 
			
		||||
    pipeline::{state_descriptors::*, PipelineDescriptor},
 | 
			
		||||
    render_graph::{nodes::{PassNode, CameraNode}, RenderGraph},
 | 
			
		||||
    render_graph::{nodes::{PassNode, CameraNode, RenderResourcesNode}, RenderGraph},
 | 
			
		||||
    shader::{Shader, ShaderStage, ShaderStages},
 | 
			
		||||
    texture::TextureFormat, ActiveCameras,
 | 
			
		||||
};
 | 
			
		||||
use legion::prelude::Resources;
 | 
			
		||||
use crate::Node;
 | 
			
		||||
 | 
			
		||||
pub const UI_PIPELINE_HANDLE: Handle<PipelineDescriptor> =
 | 
			
		||||
    Handle::from_u128(323432002226399387835192542539754486265);
 | 
			
		||||
@ -58,6 +59,7 @@ pub fn build_ui_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
 | 
			
		||||
 | 
			
		||||
pub mod node {
 | 
			
		||||
    pub const UI_CAMERA: &'static str = "ui_camera";
 | 
			
		||||
    pub const NODE: &'static str = "node";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub mod camera {
 | 
			
		||||
@ -78,6 +80,9 @@ impl UiRenderGraphBuilder for RenderGraph {
 | 
			
		||||
        self.add_system_node(node::UI_CAMERA, CameraNode::new(camera::UI_CAMERA));
 | 
			
		||||
        self.add_node_edge(node::UI_CAMERA, base_render_graph::node::MAIN_PASS)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        self.add_system_node(node::NODE, RenderResourcesNode::<Node>::new(true));
 | 
			
		||||
        self.add_node_edge(node::NODE, base_render_graph::node::MAIN_PASS)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        let mut active_cameras = resources.get_mut::<ActiveCameras>().unwrap();
 | 
			
		||||
        let main_pass_node: &mut PassNode = self.get_node_mut(base_render_graph::node::MAIN_PASS).unwrap();
 | 
			
		||||
        main_pass_node.add_camera(camera::UI_CAMERA);
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,8 @@ layout(set = 2, binding = 0) uniform ColorMaterial_color {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
# ifdef COLORMATERIAL_TEXTURE 
 | 
			
		||||
layout(set = 3, binding = 0) uniform texture2D ColorMaterial_texture;
 | 
			
		||||
layout(set = 3, binding = 1) uniform sampler ColorMaterial_texture_sampler;
 | 
			
		||||
layout(set = 2, binding = 1) uniform texture2D ColorMaterial_texture;
 | 
			
		||||
layout(set = 2, binding = 2) uniform sampler ColorMaterial_texture_sampler;
 | 
			
		||||
# endif
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
 | 
			
		||||
@ -10,15 +10,15 @@ layout(set = 0, binding = 0) uniform UiCamera {
 | 
			
		||||
    mat4 ViewProj;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
layout(set = 1, binding = 0) uniform Quad {
 | 
			
		||||
    vec2 Quad_Position;
 | 
			
		||||
    vec2 Quad_Size;
 | 
			
		||||
    float Quad_ZIndex;
 | 
			
		||||
layout(set = 1, binding = 0) uniform Transform {
 | 
			
		||||
    mat4 Object;
 | 
			
		||||
};
 | 
			
		||||
layout(set = 1, binding = 1) uniform Node_size {
 | 
			
		||||
    vec2 NodeSize;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
    v_Uv = Vertex_Uv;
 | 
			
		||||
    vec3 position = Vertex_Position * vec3(Quad_Size, 0.0);
 | 
			
		||||
    position = position + vec3(Quad_Position, Quad_ZIndex);
 | 
			
		||||
    gl_Position = ViewProj * vec4(position, 1.0);
 | 
			
		||||
    vec3 position = Vertex_Position * vec3(NodeSize, 0.0);
 | 
			
		||||
    gl_Position = ViewProj * Object * vec4(position, 1.0);
 | 
			
		||||
}
 | 
			
		||||
@ -1,74 +1,81 @@
 | 
			
		||||
use super::Node;
 | 
			
		||||
use bevy_core::transform::run_on_hierarchy_subworld_mut;
 | 
			
		||||
use bevy_sprite::Quad;
 | 
			
		||||
use bevy_transform::prelude::{Children, Parent};
 | 
			
		||||
use bevy_transform::prelude::{Children, Parent, Translation};
 | 
			
		||||
use bevy_window::Windows;
 | 
			
		||||
use glam::Vec2;
 | 
			
		||||
use legion::{prelude::*, systems::SubWorld};
 | 
			
		||||
 | 
			
		||||
pub const UI_Z_STEP: f32 = 0.001;
 | 
			
		||||
 | 
			
		||||
pub fn ui_update_system() -> Box<dyn Schedulable> {
 | 
			
		||||
    SystemBuilder::new("ui_update")
 | 
			
		||||
        .read_resource::<Windows>()
 | 
			
		||||
        .with_query(<Read<Node>>::query().filter(!component::<Parent>()))
 | 
			
		||||
        .write_component::<Node>()
 | 
			
		||||
        .write_component::<Quad>()
 | 
			
		||||
        .read_component::<Children>()
 | 
			
		||||
        .build(move |_, world, windows, node_query| {
 | 
			
		||||
            if let Some(window) = windows.get_primary() {
 | 
			
		||||
                let mut window_quad = Quad {
 | 
			
		||||
                    size: Vec2::new(window.width as f32, window.height as f32),
 | 
			
		||||
                    position: Vec2::new(0.0, 0.0),
 | 
			
		||||
                    z_index: 999.0,
 | 
			
		||||
                };
 | 
			
		||||
                for entity in node_query
 | 
			
		||||
                    .iter_entities(world)
 | 
			
		||||
                    .map(|(e, _)| e)
 | 
			
		||||
                    .collect::<Vec<Entity>>()
 | 
			
		||||
                {
 | 
			
		||||
                    let result = run_on_hierarchy_subworld_mut(
 | 
			
		||||
                        world,
 | 
			
		||||
                        entity,
 | 
			
		||||
                        window_quad.clone(),
 | 
			
		||||
                        &mut update_node_entity,
 | 
			
		||||
                        &mut process_child_result,
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    if let Some(result) = result {
 | 
			
		||||
                        window_quad.z_index = result.z_index;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct Rect {
 | 
			
		||||
    pub z: f32,
 | 
			
		||||
    pub size: Vec2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn update_node_entity(world: &mut SubWorld, entity: Entity, parent_quad: Quad) -> Option<Quad> {
 | 
			
		||||
pub fn ui_update_system(
 | 
			
		||||
    world: &mut SubWorld,
 | 
			
		||||
    windows: Res<Windows>,
 | 
			
		||||
    node_query: &mut Query<(Write<Node>, Write<Translation>)>,
 | 
			
		||||
    _parent_query: &mut Query<Read<Parent>>,
 | 
			
		||||
    _children_query: &mut Query<Read<Children>>,
 | 
			
		||||
) {
 | 
			
		||||
    let window_size = if let Some(window) = windows.get_primary() {
 | 
			
		||||
        Vec2::new(window.width as f32, window.height as f32)
 | 
			
		||||
    } else {
 | 
			
		||||
        return;
 | 
			
		||||
    };
 | 
			
		||||
    let orphan_nodes = node_query
 | 
			
		||||
        .iter_entities_mut(world)
 | 
			
		||||
        // TODO: replace this filter with a legion query filter (when SimpleQuery gets support for filters)
 | 
			
		||||
        .filter(|(entity, _)| world.get_component::<Parent>(*entity).is_none())
 | 
			
		||||
        .map(|(e, _)| e)
 | 
			
		||||
        .collect::<Vec<Entity>>();
 | 
			
		||||
    let mut window_rect = Rect {
 | 
			
		||||
        z: 0.0,
 | 
			
		||||
        size: window_size,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let mut previous_sibling_result = Some(Rect {
 | 
			
		||||
        z: 999.0,
 | 
			
		||||
        size: window_size,
 | 
			
		||||
    });
 | 
			
		||||
    for entity in orphan_nodes {
 | 
			
		||||
        previous_sibling_result = run_on_hierarchy_subworld_mut(
 | 
			
		||||
            world,
 | 
			
		||||
            entity,
 | 
			
		||||
            Some(&mut window_rect),
 | 
			
		||||
            previous_sibling_result,
 | 
			
		||||
            &mut update_node_entity,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn update_node_entity(world: &mut SubWorld, entity: Entity, parent_rect: Option<&mut Rect>, previous_rect: Option<Rect>) -> Option<Rect> {
 | 
			
		||||
    // TODO: Somehow remove this unsafe
 | 
			
		||||
    unsafe {
 | 
			
		||||
        if let Some(mut node) = world.get_component_mut_unchecked::<Node>(entity) {
 | 
			
		||||
            if let Some(mut quad) = world.get_component_mut_unchecked::<Quad>(entity) {
 | 
			
		||||
            if let Some(mut translation) = world.get_component_mut_unchecked::<Translation>(entity)
 | 
			
		||||
            {
 | 
			
		||||
                let parent_rect = parent_rect.unwrap();
 | 
			
		||||
                let mut z = parent_rect.z;
 | 
			
		||||
                if let Some(previous_rect) = previous_rect {
 | 
			
		||||
                    z = previous_rect.z
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                z -= UI_Z_STEP;
 | 
			
		||||
                node.update(
 | 
			
		||||
                    &mut quad,
 | 
			
		||||
                    parent_quad.size,
 | 
			
		||||
                    parent_quad.position,
 | 
			
		||||
                    parent_quad.z_index,
 | 
			
		||||
                    &mut translation,
 | 
			
		||||
                    z - parent_rect.z, 
 | 
			
		||||
                    parent_rect.size,
 | 
			
		||||
                );
 | 
			
		||||
                return Some(Quad {
 | 
			
		||||
                    size: quad.size,
 | 
			
		||||
                    position: quad.position - quad.size / 2.0,
 | 
			
		||||
                    z_index: quad.z_index - UI_Z_STEP,
 | 
			
		||||
                return Some(Rect {
 | 
			
		||||
                    size: node.size,
 | 
			
		||||
                    z,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn process_child_result(_parent_result: Quad, child_result: Quad) -> Quad {
 | 
			
		||||
    // "earlier" children are sorted behind "later" children
 | 
			
		||||
    let mut result = child_result.clone();
 | 
			
		||||
    result.z_index -= UI_Z_STEP;
 | 
			
		||||
    result
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
use crate::Node;
 | 
			
		||||
use bevy_asset::{Assets, Handle};
 | 
			
		||||
use bevy_render::{
 | 
			
		||||
    draw::{Draw, DrawContext, Drawable},
 | 
			
		||||
@ -5,9 +6,9 @@ use bevy_render::{
 | 
			
		||||
    texture::Texture,
 | 
			
		||||
    Color,
 | 
			
		||||
};
 | 
			
		||||
use bevy_sprite::{ComMut, Quad, TextureAtlas};
 | 
			
		||||
use bevy_sprite::{ComMut, TextureAtlas};
 | 
			
		||||
use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle};
 | 
			
		||||
use glam::Vec3;
 | 
			
		||||
use bevy_transform::prelude::Transform;
 | 
			
		||||
use legion::prelude::{Com, Res, ResMut};
 | 
			
		||||
 | 
			
		||||
pub struct Label {
 | 
			
		||||
@ -44,7 +45,7 @@ impl Label {
 | 
			
		||||
            });
 | 
			
		||||
        // TODO: this call results in one or more TextureAtlases, whose render resources are created in the RENDER_GRAPH_SYSTEMS
 | 
			
		||||
        // stage. That logic runs _before_ the DRAW stage, which means we cant call add_glyphs_to_atlas in the draw stage
 | 
			
		||||
        // without or render resources being a frame behind. Therefore glyph atlasing either needs its own system or the TextureAtlas
 | 
			
		||||
        // without our render resources being a frame behind. Therefore glyph atlasing either needs its own system or the TextureAtlas
 | 
			
		||||
        // resource generation needs to happen AFTER the render graph systems. maybe draw systems should execute within the
 | 
			
		||||
        // render graph so ordering like this can be taken into account? Maybe the RENDER_GRAPH_SYSTEMS stage should be removed entirely
 | 
			
		||||
        // in favor of node.update()? Regardless, in the immediate short term the current approach is fine.
 | 
			
		||||
@ -66,9 +67,12 @@ impl Label {
 | 
			
		||||
        mut asset_render_resource_bindings: ResMut<AssetRenderResourceBindings>,
 | 
			
		||||
        mut draw: ComMut<Draw>,
 | 
			
		||||
        label: Com<Label>,
 | 
			
		||||
        quad: Com<Quad>,
 | 
			
		||||
        node: Com<Node>,
 | 
			
		||||
        transform: Com<Transform>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let position = quad.position - quad.size / 2.0;
 | 
			
		||||
        // let position = transform.0 - quad.size / 2.0;
 | 
			
		||||
        let position = transform.value.w_axis().truncate() - (node.size / 2.0).extend(0.0);
 | 
			
		||||
 | 
			
		||||
        let mut drawable_text = DrawableText::new(
 | 
			
		||||
            fonts.get(&label.font).unwrap(),
 | 
			
		||||
            font_atlas_sets
 | 
			
		||||
@ -77,7 +81,7 @@ impl Label {
 | 
			
		||||
            &texture_atlases,
 | 
			
		||||
            &mut render_resource_bindings,
 | 
			
		||||
            &mut asset_render_resource_bindings,
 | 
			
		||||
            Vec3::new(position.x(), position.y(), quad.z_index),
 | 
			
		||||
            position,
 | 
			
		||||
            &label.style,
 | 
			
		||||
            &label.text,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -73,7 +73,6 @@ fn setup(
 | 
			
		||||
        // texture
 | 
			
		||||
        .add_entity(LabelEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
                math::vec2(0.0, 0.0),
 | 
			
		||||
                Anchors::TOP_LEFT,
 | 
			
		||||
                Margins::new(0.0, 250.0, 0.0, 60.0),
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,6 @@ fn setup(command_buffer: &mut CommandBuffer, asset_server: Res<AssetServer>) {
 | 
			
		||||
        // texture
 | 
			
		||||
        .add_entity(LabelEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
                math::vec2(0.0, 0.0),
 | 
			
		||||
                Anchors::TOP_LEFT,
 | 
			
		||||
                Margins::new(0.0, 250.0, 0.0, 60.0),
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,6 @@ fn setup(
 | 
			
		||||
        // left vertical fill
 | 
			
		||||
        .add_entity(UiEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
                math::vec2(0.0, 0.0),
 | 
			
		||||
                Anchors::LEFT_FULL,
 | 
			
		||||
                Margins::new(10.0, 200.0, 10.0, 10.0),
 | 
			
		||||
            ),
 | 
			
		||||
@ -70,7 +69,6 @@ fn setup(
 | 
			
		||||
        .add_children(|builder| {
 | 
			
		||||
            builder.add_entity(LabelEntity {
 | 
			
		||||
                node: Node::new(
 | 
			
		||||
                    math::vec2(0.0, 0.0),
 | 
			
		||||
                    Anchors::TOP_LEFT,
 | 
			
		||||
                    Margins::new(10.0, 200.0, 40.0, 10.0),
 | 
			
		||||
                ),
 | 
			
		||||
@ -88,7 +86,6 @@ fn setup(
 | 
			
		||||
        // right vertical fill
 | 
			
		||||
        .add_entity(UiEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
                math::vec2(0.0, 0.0),
 | 
			
		||||
                Anchors::RIGHT_FULL,
 | 
			
		||||
                Margins::new(10.0, 100.0, 100.0, 100.0),
 | 
			
		||||
            ),
 | 
			
		||||
@ -97,7 +94,7 @@ fn setup(
 | 
			
		||||
        })
 | 
			
		||||
        // render order test: reddest in the back, whitest in the front
 | 
			
		||||
        .add_entity(UiEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
            node: Node::positioned(
 | 
			
		||||
                math::vec2(75.0, 60.0),
 | 
			
		||||
                Anchors::CENTER,
 | 
			
		||||
                Margins::new(0.0, 100.0, 0.0, 100.0),
 | 
			
		||||
@ -106,7 +103,7 @@ fn setup(
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        })
 | 
			
		||||
        .add_entity(UiEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
            node: Node::positioned(
 | 
			
		||||
                math::vec2(50.0, 35.0),
 | 
			
		||||
                Anchors::CENTER,
 | 
			
		||||
                Margins::new(0.0, 100.0, 0.0, 100.0),
 | 
			
		||||
@ -115,7 +112,7 @@ fn setup(
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        })
 | 
			
		||||
        .add_entity(UiEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
            node: Node::positioned(
 | 
			
		||||
                math::vec2(100.0, 85.0),
 | 
			
		||||
                Anchors::CENTER,
 | 
			
		||||
                Margins::new(0.0, 100.0, 0.0, 100.0),
 | 
			
		||||
@ -124,7 +121,7 @@ fn setup(
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        })
 | 
			
		||||
        .add_entity(UiEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
            node: Node::positioned(
 | 
			
		||||
                math::vec2(150.0, 135.0),
 | 
			
		||||
                Anchors::CENTER,
 | 
			
		||||
                Margins::new(0.0, 100.0, 0.0, 100.0),
 | 
			
		||||
@ -134,7 +131,7 @@ fn setup(
 | 
			
		||||
        })
 | 
			
		||||
        // parenting
 | 
			
		||||
        .add_entity(UiEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
            node: Node::positioned(
 | 
			
		||||
                math::vec2(210.0, 0.0),
 | 
			
		||||
                Anchors::BOTTOM_LEFT,
 | 
			
		||||
                Margins::new(0.0, 200.0, 10.0, 210.0),
 | 
			
		||||
@ -145,7 +142,6 @@ fn setup(
 | 
			
		||||
        .add_children(|builder| {
 | 
			
		||||
            builder.add_entity(UiEntity {
 | 
			
		||||
                node: Node::new(
 | 
			
		||||
                    math::vec2(0.0, 0.0),
 | 
			
		||||
                    Anchors::FULL,
 | 
			
		||||
                    Margins::new(20.0, 20.0, 20.0, 20.0),
 | 
			
		||||
                ),
 | 
			
		||||
@ -155,7 +151,7 @@ fn setup(
 | 
			
		||||
        })
 | 
			
		||||
        // alpha test
 | 
			
		||||
        .add_entity(UiEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
            node: Node::positioned(
 | 
			
		||||
                math::vec2(200.0, 185.0),
 | 
			
		||||
                Anchors::CENTER,
 | 
			
		||||
                Margins::new(0.0, 100.0, 0.0, 100.0),
 | 
			
		||||
@ -166,7 +162,6 @@ fn setup(
 | 
			
		||||
        // texture
 | 
			
		||||
        .add_entity(UiEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
                math::vec2(0.0, 0.0),
 | 
			
		||||
                Anchors::CENTER_TOP,
 | 
			
		||||
                Margins::new(-250.0, 250.0, 510.0 * aspect, 10.0),
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -29,13 +29,14 @@ fn setup(command_buffer: &mut CommandBuffer, mut materials: ResMut<Assets<ColorM
 | 
			
		||||
    let count = 1000;
 | 
			
		||||
    for i in 0..count {
 | 
			
		||||
        // 2d camera
 | 
			
		||||
        let cur = Vec2::new(1.0, 1.0) * 1.0 + prev;
 | 
			
		||||
        let cur = Vec2::new(1.0, 1.0) + prev;
 | 
			
		||||
        builder.add_entity(UiEntity {
 | 
			
		||||
            node: Node::new(
 | 
			
		||||
                math::vec2(75.0, 75.0) + cur,
 | 
			
		||||
                Anchors::new(0.5, 0.5, 0.5, 0.5),
 | 
			
		||||
                Margins::new(0.0, 100.0, 0.0, 100.0),
 | 
			
		||||
            ),
 | 
			
		||||
            node: Node {
 | 
			
		||||
                position: Vec2::new(75.0, 75.0) + cur,
 | 
			
		||||
                anchors: Anchors::new(0.5, 0.5, 0.5, 0.5),
 | 
			
		||||
                margins: Margins::new(0.0, 100.0, 0.0, 100.0),
 | 
			
		||||
                ..Default::default()
 | 
			
		||||
            },
 | 
			
		||||
            material: materials.add(Color::rgb(0.0 + i as f32 / count as f32, 0.1, 0.1).into()),
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ pub use crate::{
 | 
			
		||||
    scene::{Scene, SceneSpawner},
 | 
			
		||||
    sprite::{
 | 
			
		||||
        entity::{SpriteEntity, SpriteSheetEntity},
 | 
			
		||||
        ColorMaterial, Quad, Sprite, TextureAtlas, TextureAtlasSprite,
 | 
			
		||||
        ColorMaterial, Sprite, TextureAtlas, TextureAtlasSprite,
 | 
			
		||||
    },
 | 
			
		||||
    text::{Font, TextStyle},
 | 
			
		||||
    transform::prelude::*,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user