From 77c19851f7f23e9005da6b6526f91bd4e4b322ef Mon Sep 17 00:00:00 2001 From: Gabriel Bourgeois Date: Tue, 13 Sep 2022 19:55:13 -0400 Subject: [PATCH] Add UI camera support to UI rendering This is not complete. It has some temporary code while issues are being worked on. The main issue being that UI cameras seem to clear each other or at least prevent lower-priority UI nodes from showing somehow. --- crates/bevy_ui/src/render/mod.rs | 55 +++++++++++++++++++++--- crates/bevy_ui/src/render/render_pass.rs | 3 ++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 4c3f602f54b2dd..f21f616afc2837 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -2,10 +2,14 @@ mod pipeline; mod render_pass; use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d}; +use bevy_hierarchy::Parent; pub use pipeline::*; pub use render_pass::*; -use crate::{prelude::UiCameraConfig, CalculatedClip, Node, UiColor, UiImage}; +use crate::{ + prelude::{UiCameraConfig, UiRootCamera}, + CalculatedClip, Node, UiColor, UiImage, +}; use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, AssetEvent, Assets, Handle, HandleUntyped}; use bevy_ecs::prelude::*; @@ -166,6 +170,7 @@ pub struct ExtractedUiNode { pub image: Handle, pub atlas_size: Option, pub clip: Option, + pub camera_entity: Entity, } #[derive(Resource, Default)] @@ -173,11 +178,24 @@ pub struct ExtractedUiNodes { pub uinodes: Vec, } +// TODO: replace this with a `UiRoot` component or UiStack loop. +fn get_uinode_root(entity: Entity, parent_query: &Extract>>) -> Entity { + let mut entity = entity; + loop { + if let Ok(parent) = parent_query.get(entity) { + entity = parent.get(); + } else { + return entity; + } + } +} + pub fn extract_uinodes( mut extracted_uinodes: ResMut, images: Extract>>, uinode_query: Extract< Query<( + Entity, &Node, &GlobalTransform, &UiColor, @@ -186,9 +204,11 @@ pub fn extract_uinodes( Option<&CalculatedClip>, )>, >, + parent_query: Extract>>, + root_camera_query: Extract>, ) { extracted_uinodes.uinodes.clear(); - for (uinode, transform, color, image, visibility, clip) in uinode_query.iter() { + for (entity, uinode, transform, color, image, visibility, clip) in uinode_query.iter() { if !visibility.is_visible() { continue; } @@ -201,6 +221,11 @@ pub fn extract_uinodes( if color.0.a() == 0.0 { continue; } + // TODO: Get a default camera. This will be done elsewhere and the camera should already be available. + let camera_entity = root_camera_query + .get(get_uinode_root(entity, &parent_query)) + .unwrap() + .0; extracted_uinodes.uinodes.push(ExtractedUiNode { transform: transform.compute_matrix(), color: color.0, @@ -211,6 +236,7 @@ pub fn extract_uinodes( image, atlas_size: None, clip: clip.map(|clip| clip.clip), + camera_entity, }); } } @@ -284,6 +310,8 @@ pub fn extract_text_uinodes( Option<&CalculatedClip>, )>, >, + parent_query: Extract>>, + root_camera_query: Extract>, ) { let scale_factor = windows.scale_factor(WindowId::primary()) as f32; for (entity, uinode, global_transform, text, visibility, clip) in uinode_query.iter() { @@ -322,7 +350,11 @@ pub fn extract_text_uinodes( * Mat4::from_translation( alignment_offset * scale_factor + text_glyph.position.extend(0.), ); - + // TODO: Get a default camera. This will be done elsewhere and the camera should already be available. + let camera_entity = root_camera_query + .get(get_uinode_root(entity, &parent_query)) + .unwrap() + .0; extracted_uinodes.uinodes.push(ExtractedUiNode { transform: extracted_transform, color, @@ -330,6 +362,7 @@ pub fn extract_text_uinodes( image: texture, atlas_size, clip: clip.map(|clip| clip.clip), + camera_entity, }); } } @@ -373,6 +406,7 @@ pub struct UiBatch { pub range: Range, pub image: Handle, pub z: f32, + pub view: Entity, } pub fn prepare_uinodes( @@ -392,18 +426,23 @@ pub fn prepare_uinodes( let mut start = 0; let mut end = 0; let mut current_batch_handle = Default::default(); + let mut current_camera_entity = Entity::from_raw(0); let mut last_z = 0.0; for extracted_uinode in &extracted_uinodes.uinodes { - if current_batch_handle != extracted_uinode.image { + if current_batch_handle != extracted_uinode.image + || current_camera_entity != extracted_uinode.camera_entity + { if start != end { commands.spawn_bundle((UiBatch { range: start..end, image: current_batch_handle, + view: current_camera_entity, z: last_z, },)); start = end; } current_batch_handle = extracted_uinode.image.clone_weak(); + current_camera_entity = extracted_uinode.camera_entity; } let uinode_rect = extracted_uinode.rect; @@ -501,6 +540,7 @@ pub fn prepare_uinodes( commands.spawn_bundle((UiBatch { range: start..end, image: current_batch_handle, + view: current_camera_entity, z: last_z, },)); } @@ -525,7 +565,7 @@ pub fn queue_uinodes( mut image_bind_groups: ResMut, gpu_images: Res>, ui_batches: Query<(Entity, &UiBatch)>, - mut views: Query<&mut RenderPhase>, + mut views: Query<(Entity, &mut RenderPhase)>, events: Res, ) { // If an image has changed, the GpuImage has (probably) changed @@ -549,8 +589,11 @@ pub fn queue_uinodes( })); let draw_ui_function = draw_functions.read().get_id::().unwrap(); let pipeline = pipelines.specialize(&mut pipeline_cache, &ui_pipeline, UiPipelineKey {}); - for mut transparent_phase in &mut views { + for (view_entity, mut transparent_phase) in &mut views { for (entity, batch) in &ui_batches { + if batch.view != view_entity { + continue; + } image_bind_groups .values .entry(batch.image.clone_weak()) diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 66445c92a17c3b..f4bfee96a32c4f 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -100,6 +100,9 @@ impl Node for UiPassNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); + if let Some(viewport) = camera.viewport.as_ref() { + tracked_pass.set_camera_viewport(viewport); + } for item in &transparent_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); draw_function.draw(world, &mut tracked_pass, view_entity, item);