diff --git a/crates/bevy_ui/src/entity.rs b/crates/bevy_ui/src/entity.rs index 43cc8f41425ce8..14fef8c9d17423 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}, }; @@ -152,6 +154,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 { @@ -159,6 +183,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 d72c1b83495d16..301392896d514d 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -29,7 +29,7 @@ use bevy_ecs::schedule::{ParallelSystemDescriptorCoercion, SystemLabel}; use bevy_input::InputSystem; use bevy_transform::TransformSystem; use bevy_window::ModifiesWindows; -use update::{ui_z_system, update_clipping_system}; +use update::{ui_z_system, update_clipping_system, update_ui_camera_perspective}; /// The basic plugin for Bevy UI #[derive(Default)] @@ -42,6 +42,8 @@ pub enum UiSystem { Flex, /// After this label, input interactions with UI entities have been updated for this frame Focus, + /// Update Ui camera perspective to fit new viewport logical size. + UpdateUiCameraPerspective, } impl Plugin for UiPlugin { @@ -87,6 +89,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 4b911c937e2b86..7fd9048f62093c 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}, @@ -218,7 +218,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. @@ -240,26 +240,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 05628555f1980b..04b1523412c61f 100644 --- a/crates/bevy_ui/src/update.rs +++ b/crates/bevy_ui/src/update.rs @@ -1,15 +1,17 @@ //! This module contains systems that update the UI when something changes -use crate::{CalculatedClip, Overflow, Style}; +use crate::{prelude::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::camera::{Camera, CameraProjection}; use bevy_sprite::Rect; use bevy_transform::components::{GlobalTransform, Transform}; @@ -82,6 +84,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>,