From 60c29b81b867b253ed41f4549e0deb6b147e97f8 Mon Sep 17 00:00:00 2001 From: Nicola Papale Date: Thu, 21 Jul 2022 11:54:07 +0200 Subject: [PATCH] Add user control of UI cameras Add fields to UiCameraConfig to control the UI camera scale and position. Fixes #5242. It is again possible to manipulate the ui camera. An alternative design was considered, where instead of having a component that controls all of the UI camera settings, the component would only hold an Entity referencing another camera. That design was abandoned in favor of the current one because the viewport is tightly bound to the "actual" camera the UI camera is attached to. So it would be awkward to maintain independently two different cameras. --- crates/bevy_ui/src/entity.rs | 33 +++++++++++++++++++++++++++++++- crates/bevy_ui/src/lib.rs | 10 +++++++++- crates/bevy_ui/src/render/mod.rs | 22 +++++---------------- crates/bevy_ui/src/update.rs | 19 +++++++++++++++++- 4 files changed, 64 insertions(+), 20 deletions(-) diff --git a/crates/bevy_ui/src/entity.rs b/crates/bevy_ui/src/entity.rs index 57fcc196333ed..fc8636d453cac 100644 --- a/crates/bevy_ui/src/entity.rs +++ b/crates/bevy_ui/src/entity.rs @@ -2,10 +2,12 @@ use crate::{ widget::{Button, ImageMode}, - CalculatedSize, FocusPolicy, Interaction, Node, Style, UiColor, UiImage, + CalculatedSize, FocusPolicy, Interaction, Node, Style, UiColor, UiImage, UI_CAMERA_FAR, }; use bevy_ecs::{bundle::Bundle, prelude::Component}; +use bevy_math::Vec2; use bevy_render::{ + camera::{DepthCalculation, OrthographicProjection, WindowOrigin}, prelude::ComputedVisibility, view::{RenderLayers, Visibility}, }; @@ -186,6 +188,28 @@ pub struct UiCameraConfig { pub show_ui: bool, /// The ui camera layers this camera can see. pub ui_render_layers: RenderLayers, + /// The position of the UI camera in UI space. + pub position: Vec2, + /// The projection data for the UI camera. + /// + /// The code relies on this not being set, + /// please use [`UiCameraConfig::scale_mut`] and [`UiCameraConfig::projection`] + /// instead. + /// This is only public so it is possible to use the struct update syntax. + #[doc(hidden)] + pub projection: OrthographicProjection, +} + +impl UiCameraConfig { + /// Get mutably the scale of the UI camera, useful for zoom effects. + pub fn scale_mut(&mut self) -> &mut f32 { + &mut self.projection.scale + } + + /// The projection data for the UI camera. + pub fn projection(&self) -> &OrthographicProjection { + &self.projection + } } impl Default for UiCameraConfig { @@ -193,6 +217,13 @@ impl Default for UiCameraConfig { Self { show_ui: true, ui_render_layers: Default::default(), + position: Vec2::ZERO, + projection: OrthographicProjection { + far: UI_CAMERA_FAR, + window_origin: WindowOrigin::BottomLeft, + depth_calculation: DepthCalculation::ZDifference, + ..Default::default() + }, } } } diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 3da864c203fb4..3c308dc6562cd 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -30,7 +30,9 @@ use bevy_input::InputSystem; use bevy_render::view::VisibilitySystems; use bevy_transform::TransformSystem; use bevy_window::ModifiesWindows; -use update::{ui_z_system, update_clipping_system, update_layer_visibility}; +use update::{ + ui_z_system, update_clipping_system, update_layer_visibility, update_ui_camera_perspective, +}; /// The basic plugin for Bevy UI #[derive(Default)] @@ -48,6 +50,8 @@ pub enum UiSystem { /// /// [`ComputedVisibility`]: bevy_render::view::ComputedVisibility LayerVisibility, + /// Update Ui camera perspective to fit new viewport logical size. + UpdateUiCameraPerspective, } impl Plugin for UiPlugin { @@ -93,6 +97,10 @@ impl Plugin for UiPlugin { CoreStage::PostUpdate, widget::image_node_system.before(UiSystem::Flex), ) + .add_system_to_stage( + CoreStage::Last, + update_ui_camera_perspective.label(UiSystem::UpdateUiCameraPerspective), + ) .add_system_to_stage( CoreStage::PostUpdate, flex_node_system diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 4cd3c48199a58..8c01c2cd7f7d0 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -12,7 +12,7 @@ use bevy_ecs::prelude::*; use bevy_math::{Mat4, Vec2, Vec3, Vec4Swizzles}; use bevy_reflect::TypeUuid; use bevy_render::{ - camera::{Camera, CameraProjection, DepthCalculation, OrthographicProjection, WindowOrigin}, + camera::{Camera, CameraProjection}, color::Color, render_asset::RenderAssets, render_graph::{RenderGraph, RunGraphOnViewNode, SlotInfo, SlotType}, @@ -219,7 +219,7 @@ pub fn extract_uinodes( /// as ui elements are "stacked on top of each other", they are within the camera's view /// and have room to grow. // TODO: Consider computing this value at runtime based on the maximum z-value. -const UI_CAMERA_FAR: f32 = 1000.0; +pub(crate) const UI_CAMERA_FAR: f32 = 1000.0; // This value is subtracted from the far distance for the camera's z-position to ensure nodes at z == 0.0 are rendered // TODO: Evaluate if we still need this. @@ -251,26 +251,14 @@ pub fn extract_default_ui_camera_view( if !ui_config.show_ui { continue; } - let logical_size = if let Some(logical_size) = camera.logical_viewport_size() { - logical_size - } else { - continue; - }; - let mut projection = OrthographicProjection { - far: UI_CAMERA_FAR, - window_origin: WindowOrigin::BottomLeft, - depth_calculation: DepthCalculation::ZDifference, - ..Default::default() - }; - projection.update(logical_size.x, logical_size.y); if let Some(physical_size) = camera.physical_viewport_size() { let ui_camera = commands .spawn() .insert(ExtractedView { - projection: projection.get_projection_matrix(), + projection: ui_config.projection.get_projection_matrix(), transform: GlobalTransform::from_xyz( - 0.0, - 0.0, + ui_config.position.x, + ui_config.position.y, UI_CAMERA_FAR + UI_CAMERA_TRANSFORM_OFFSET, ), width: physical_size.x, diff --git a/crates/bevy_ui/src/update.rs b/crates/bevy_ui/src/update.rs index 7361ce93c9d93..6cb1f7095592c 100644 --- a/crates/bevy_ui/src/update.rs +++ b/crates/bevy_ui/src/update.rs @@ -5,12 +5,16 @@ use crate::{entity::UiCameraConfig, CalculatedClip, Overflow, Style}; use super::Node; use bevy_ecs::{ entity::Entity, + prelude::{Changed, Or}, query::{With, Without}, system::{Commands, Query}, }; use bevy_hierarchy::{Children, Parent}; use bevy_math::Vec2; -use bevy_render::view::{ComputedVisibility, RenderLayers}; +use bevy_render::{ + camera::{Camera, CameraProjection}, + view::{ComputedVisibility, RenderLayers}, +}; use bevy_sprite::Rect; use bevy_transform::components::{GlobalTransform, Transform}; @@ -107,6 +111,19 @@ pub fn update_clipping_system( } } +pub fn update_ui_camera_perspective( + mut query: Query< + (&Camera, &mut UiCameraConfig), + Or<(Changed, Changed)>, + >, +) { + for (camera, mut ui_config) in query.iter_mut() { + if let Some(logical_size) = camera.logical_viewport_size() { + ui_config.projection.update(logical_size.x, logical_size.y); + } + } +} + fn update_clipping( commands: &mut Commands, children_query: &Query<&Children>,