diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index a46bba2789f06..bb915965c73a2 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -23,7 +23,7 @@ use bevy_app::{App, Plugin}; use bevy_core::FloatOrd; use bevy_ecs::prelude::*; use bevy_render::{ - camera::{ActiveCameras, CameraPlugin, RenderTarget}, + camera::{ActiveCamera, Camera2d, Camera3d, RenderTarget}, color::Color, render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType}, render_phase::{ @@ -367,23 +367,20 @@ pub fn extract_clear_color( pub fn extract_core_pipeline_camera_phases( mut commands: Commands, - active_cameras: Res, + active_2d: Res>, + active_3d: Res>, ) { - if let Some(camera_2d) = active_cameras.get(CameraPlugin::CAMERA_2D) { - if let Some(entity) = camera_2d.entity { - commands - .get_or_spawn(entity) - .insert(RenderPhase::::default()); - } - } - if let Some(camera_3d) = active_cameras.get(CameraPlugin::CAMERA_3D) { - if let Some(entity) = camera_3d.entity { - commands.get_or_spawn(entity).insert_bundle(( - RenderPhase::::default(), - RenderPhase::::default(), - RenderPhase::::default(), - )); - } + if let Some(entity) = active_2d.get() { + commands + .get_or_spawn(entity) + .insert(RenderPhase::::default()); + } + if let Some(entity) = active_3d.get() { + commands.get_or_spawn(entity).insert_bundle(( + RenderPhase::::default(), + RenderPhase::::default(), + RenderPhase::::default(), + )); } } diff --git a/crates/bevy_core_pipeline/src/main_pass_driver.rs b/crates/bevy_core_pipeline/src/main_pass_driver.rs index bb97ab12bf639..f317b4d0bd16e 100644 --- a/crates/bevy_core_pipeline/src/main_pass_driver.rs +++ b/crates/bevy_core_pipeline/src/main_pass_driver.rs @@ -1,6 +1,6 @@ use bevy_ecs::world::World; use bevy_render::{ - camera::{CameraPlugin, ExtractedCameraNames}, + camera::{ActiveCamera, Camera2d, Camera3d}, render_graph::{Node, NodeRunError, RenderGraphContext, SlotValue}, renderer::RenderContext, }; @@ -14,18 +14,17 @@ impl Node for MainPassDriverNode { _render_context: &mut RenderContext, world: &World, ) -> Result<(), NodeRunError> { - let extracted_cameras = world.resource::(); - if let Some(camera_2d) = extracted_cameras.entities.get(CameraPlugin::CAMERA_2D) { + if let Some(camera_2d) = world.resource::>().get() { graph.run_sub_graph( crate::draw_2d_graph::NAME, - vec![SlotValue::Entity(*camera_2d)], + vec![SlotValue::Entity(camera_2d)], )?; } - if let Some(camera_3d) = extracted_cameras.entities.get(CameraPlugin::CAMERA_3D) { + if let Some(camera_3d) = world.resource::>().get() { graph.run_sub_graph( crate::draw_3d_graph::NAME, - vec![SlotValue::Entity(*camera_3d)], + vec![SlotValue::Entity(camera_3d)], )?; } diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 17dd430e74a28..293e4bece8dd1 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -12,7 +12,7 @@ use bevy_pbr::{ }; use bevy_render::{ camera::{ - Camera, CameraPlugin, CameraProjection, OrthographicProjection, PerspectiveProjection, + Camera, Camera2d, Camera3d, CameraProjection, OrthographicProjection, PerspectiveProjection, }, color::Color, mesh::{Indices, Mesh, VertexAttributeValues}, @@ -494,11 +494,10 @@ fn load_node( }; node.insert(Camera { - name: Some(CameraPlugin::CAMERA_2D.to_owned()), projection_matrix: orthographic_projection.get_projection_matrix(), ..Default::default() }); - node.insert(orthographic_projection); + node.insert(orthographic_projection).insert(Camera2d); } gltf::camera::Projection::Perspective(perspective) => { let mut perspective_projection: PerspectiveProjection = PerspectiveProjection { @@ -513,13 +512,13 @@ fn load_node( perspective_projection.aspect_ratio = aspect_ratio; } node.insert(Camera { - name: Some(CameraPlugin::CAMERA_3D.to_owned()), projection_matrix: perspective_projection.get_projection_matrix(), near: perspective_projection.near, far: perspective_projection.far, ..Default::default() }); node.insert(perspective_projection); + node.insert(Camera3d); } } } diff --git a/crates/bevy_render/src/camera/active_cameras.rs b/crates/bevy_render/src/camera/active_cameras.rs deleted file mode 100644 index 42cebc65a67f2..0000000000000 --- a/crates/bevy_render/src/camera/active_cameras.rs +++ /dev/null @@ -1,73 +0,0 @@ -use super::Camera; -use bevy_ecs::{ - entity::Entity, - system::{Query, ResMut}, -}; -use bevy_utils::HashMap; - -#[derive(Debug, Default)] -pub struct ActiveCamera { - pub name: String, - pub entity: Option, -} - -#[derive(Debug, Default)] -pub struct ActiveCameras { - cameras: HashMap, -} - -impl ActiveCameras { - pub fn add(&mut self, name: &str) { - self.cameras.insert( - name.to_string(), - ActiveCamera { - name: name.to_string(), - ..Default::default() - }, - ); - } - - pub fn get(&self, name: &str) -> Option<&ActiveCamera> { - self.cameras.get(name) - } - - pub fn get_mut(&mut self, name: &str) -> Option<&mut ActiveCamera> { - self.cameras.get_mut(name) - } - - pub fn remove(&mut self, name: &str) -> Option { - self.cameras.remove(name) - } - - pub fn iter(&self) -> impl Iterator { - self.cameras.values() - } - - pub fn iter_mut(&mut self) -> impl Iterator { - self.cameras.values_mut() - } -} - -pub fn active_cameras_system( - mut active_cameras: ResMut, - query: Query<(Entity, &Camera)>, -) { - for (name, active_camera) in &mut active_cameras.cameras { - if active_camera - .entity - .map_or(false, |entity| query.get(entity).is_err()) - { - active_camera.entity = None; - } - - if active_camera.entity.is_none() { - for (camera_entity, camera) in query.iter() { - if let Some(ref current_name) = camera.name { - if current_name == name { - active_camera.entity = Some(camera_entity); - } - } - } - } - } -} diff --git a/crates/bevy_render/src/camera/bundle.rs b/crates/bevy_render/src/camera/bundle.rs index 920a12bb35335..f32aad826c5b6 100644 --- a/crates/bevy_render/src/camera/bundle.rs +++ b/crates/bevy_render/src/camera/bundle.rs @@ -1,36 +1,48 @@ use crate::{ - camera::{ - Camera, CameraPlugin, DepthCalculation, OrthographicProjection, PerspectiveProjection, - ScalingMode, - }, + camera::{Camera, DepthCalculation, OrthographicProjection, PerspectiveProjection}, primitives::Frustum, view::VisibleEntities, }; -use bevy_ecs::bundle::Bundle; +use bevy_ecs::{bundle::Bundle, prelude::Component}; use bevy_math::Vec3; use bevy_transform::components::{GlobalTransform, Transform}; -use super::CameraProjection; +use super::{CameraProjection, ScalingMode}; + +#[derive(Component, Default)] +pub struct Camera3d; + +#[derive(Component, Default)] +pub struct Camera2d; /// Component bundle for camera entities with perspective projection /// /// Use this for 3D rendering. #[derive(Bundle)] -pub struct PerspectiveCameraBundle { +pub struct PerspectiveCameraBundle { pub camera: Camera, pub perspective_projection: PerspectiveProjection, pub visible_entities: VisibleEntities, pub frustum: Frustum, pub transform: Transform, pub global_transform: GlobalTransform, + pub marker: M, +} + +impl Default for PerspectiveCameraBundle { + fn default() -> Self { + PerspectiveCameraBundle::new_3d() + } } -impl PerspectiveCameraBundle { +impl PerspectiveCameraBundle { pub fn new_3d() -> Self { - Default::default() + PerspectiveCameraBundle::new() } +} - pub fn with_name(name: &str) -> Self { +impl PerspectiveCameraBundle { + pub fn new() -> Self { let perspective_projection = PerspectiveProjection::default(); let view_projection = perspective_projection.get_projection_matrix(); let frustum = Frustum::from_view_projection( @@ -41,7 +53,6 @@ impl PerspectiveCameraBundle { ); PerspectiveCameraBundle { camera: Camera { - name: Some(name.to_string()), near: perspective_projection.near, far: perspective_projection.far, ..Default::default() @@ -51,30 +62,56 @@ impl PerspectiveCameraBundle { frustum, transform: Default::default(), global_transform: Default::default(), + marker: M::default(), } } } -impl Default for PerspectiveCameraBundle { - fn default() -> Self { - PerspectiveCameraBundle::with_name(CameraPlugin::CAMERA_3D) - } -} - /// Component bundle for camera entities with orthographic projection /// /// Use this for 2D games, isometric games, CAD-like 3D views. #[derive(Bundle)] -pub struct OrthographicCameraBundle { +pub struct OrthographicCameraBundle { pub camera: Camera, pub orthographic_projection: OrthographicProjection, pub visible_entities: VisibleEntities, pub frustum: Frustum, pub transform: Transform, pub global_transform: GlobalTransform, + pub marker: M, +} + +impl OrthographicCameraBundle { + pub fn new_3d() -> Self { + let orthographic_projection = OrthographicProjection { + scaling_mode: ScalingMode::FixedVertical, + depth_calculation: DepthCalculation::Distance, + ..Default::default() + }; + let view_projection = orthographic_projection.get_projection_matrix(); + let frustum = Frustum::from_view_projection( + &view_projection, + &Vec3::ZERO, + &Vec3::Z, + orthographic_projection.far(), + ); + OrthographicCameraBundle { + camera: Camera { + near: orthographic_projection.near, + far: orthographic_projection.far, + ..Default::default() + }, + orthographic_projection, + visible_entities: VisibleEntities::default(), + frustum, + transform: Default::default(), + global_transform: Default::default(), + marker: Camera3d, + } + } } -impl OrthographicCameraBundle { +impl OrthographicCameraBundle { /// Create an orthographic projection camera to render 2D content. /// /// The projection creates a camera space where X points to the right of the screen, @@ -112,7 +149,6 @@ impl OrthographicCameraBundle { ); OrthographicCameraBundle { camera: Camera { - name: Some(CameraPlugin::CAMERA_2D.to_string()), near: orthographic_projection.near, far: orthographic_projection.far, ..Default::default() @@ -122,58 +158,7 @@ impl OrthographicCameraBundle { frustum, transform, global_transform: Default::default(), - } - } - - pub fn new_3d() -> Self { - let orthographic_projection = OrthographicProjection { - scaling_mode: ScalingMode::FixedVertical, - depth_calculation: DepthCalculation::Distance, - ..Default::default() - }; - let view_projection = orthographic_projection.get_projection_matrix(); - let frustum = Frustum::from_view_projection( - &view_projection, - &Vec3::ZERO, - &Vec3::Z, - orthographic_projection.far(), - ); - OrthographicCameraBundle { - camera: Camera { - name: Some(CameraPlugin::CAMERA_3D.to_string()), - near: orthographic_projection.near, - far: orthographic_projection.far, - ..Default::default() - }, - orthographic_projection, - visible_entities: VisibleEntities::default(), - frustum, - transform: Default::default(), - global_transform: Default::default(), - } - } - - pub fn with_name(name: &str) -> Self { - let orthographic_projection = OrthographicProjection::default(); - let view_projection = orthographic_projection.get_projection_matrix(); - let frustum = Frustum::from_view_projection( - &view_projection, - &Vec3::ZERO, - &Vec3::Z, - orthographic_projection.far(), - ); - OrthographicCameraBundle { - camera: Camera { - name: Some(name.to_string()), - near: orthographic_projection.near, - far: orthographic_projection.far, - ..Default::default() - }, - orthographic_projection, - visible_entities: VisibleEntities::default(), - frustum, - transform: Default::default(), - global_transform: Default::default(), + marker: Camera2d, } } } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 07e748fe8211f..6da11ccb66473 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -1,16 +1,23 @@ +use std::marker::PhantomData; + use crate::{ - camera::CameraProjection, prelude::Image, render_asset::RenderAssets, - render_resource::TextureView, view::ExtractedWindows, + camera::CameraProjection, + prelude::Image, + render_asset::RenderAssets, + render_resource::TextureView, + view::{ExtractedView, ExtractedWindows, VisibleEntities}, + RenderApp, RenderStage, }; +use bevy_app::{App, CoreStage, Plugin, StartupStage}; use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_ecs::{ component::Component, entity::Entity, event::EventReader, - prelude::{DetectChanges, QueryState}, + prelude::{DetectChanges, QueryState, With}, query::Added, reflect::ReflectComponent, - system::{QuerySet, Res}, + system::{Commands, Query, QuerySet, Res, ResMut}, }; use bevy_math::{Mat4, UVec2, Vec2, Vec3}; use bevy_reflect::{Reflect, ReflectDeserialize}; @@ -24,7 +31,6 @@ use wgpu::Extent3d; #[reflect(Component)] pub struct Camera { pub projection_matrix: Mat4, - pub name: Option, #[reflect(ignore)] pub target: RenderTarget, #[reflect(ignore)] @@ -203,3 +209,111 @@ pub fn camera_system( } } } + +pub struct CameraTypePlugin(PhantomData); + +impl Default for CameraTypePlugin { + fn default() -> Self { + Self(Default::default()) + } +} + +impl Plugin for CameraTypePlugin { + fn build(&self, app: &mut App) { + app.init_resource::>() + .add_startup_system_to_stage(StartupStage::PostStartup, set_active_camera::) + .add_system_to_stage(CoreStage::PostUpdate, set_active_camera::); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app.add_system_to_stage(RenderStage::Extract, extract_cameras::); + } + } +} + +/// The canonical source of the "active camera" of the given camera type `T`. +#[derive(Debug)] +pub struct ActiveCamera { + camera: Option, + marker: PhantomData, +} + +impl Default for ActiveCamera { + fn default() -> Self { + Self { + camera: Default::default(), + marker: Default::default(), + } + } +} + +impl Clone for ActiveCamera { + fn clone(&self) -> Self { + Self { + camera: self.camera, + marker: self.marker, + } + } +} + +impl ActiveCamera { + /// Sets the active camera to the given `camera` entity. + pub fn set(&mut self, camera: Entity) { + self.camera = Some(camera); + } + + /// Returns the active camera, if it exists. + pub fn get(&self) -> Option { + self.camera + } +} + +pub fn set_active_camera( + mut active_camera: ResMut>, + cameras: Query>, +) { + if active_camera.get().is_some() { + return; + } + + if let Some(camera) = cameras.iter().next() { + active_camera.camera = Some(camera); + } +} + +#[derive(Component, Debug)] +pub struct ExtractedCamera { + pub target: RenderTarget, + pub physical_size: Option, +} + +pub fn extract_cameras( + mut commands: Commands, + windows: Res, + images: Res>, + active_camera: Res>, + query: Query<(&Camera, &GlobalTransform, &VisibleEntities), With>, +) { + if let Some(entity) = active_camera.get() { + if let Ok((camera, transform, visible_entities)) = query.get(entity) { + if let Some(size) = camera.target.get_physical_size(&windows, &images) { + commands.get_or_spawn(entity).insert_bundle(( + ExtractedCamera { + target: camera.target.clone(), + physical_size: camera.target.get_physical_size(&windows, &images), + }, + ExtractedView { + projection: camera.projection_matrix, + transform: *transform, + width: size.x.max(1), + height: size.y.max(1), + near: camera.near, + far: camera.far, + }, + visible_entities.clone(), + M::default(), + )); + } + } + } + + commands.insert_resource(active_camera.clone()) +} diff --git a/crates/bevy_render/src/camera/mod.rs b/crates/bevy_render/src/camera/mod.rs index 35e3451a4320e..7ebdf2c20c941 100644 --- a/crates/bevy_render/src/camera/mod.rs +++ b/crates/bevy_render/src/camera/mod.rs @@ -1,41 +1,23 @@ -mod active_cameras; mod bundle; #[allow(clippy::module_inception)] mod camera; mod projection; -pub use active_cameras::*; -use bevy_asset::Assets; -use bevy_math::UVec2; -use bevy_transform::components::GlobalTransform; -use bevy_utils::HashMap; -use bevy_window::Windows; pub use bundle::*; pub use camera::*; pub use projection::*; use crate::{ - prelude::Image, primitives::Aabb, - view::{ComputedVisibility, ExtractedView, Visibility, VisibleEntities}, - RenderApp, RenderStage, + view::{ComputedVisibility, Visibility, VisibleEntities}, }; use bevy_app::{App, CoreStage, Plugin}; -use bevy_ecs::prelude::*; #[derive(Default)] pub struct CameraPlugin; -impl CameraPlugin { - pub const CAMERA_2D: &'static str = "camera_2d"; - pub const CAMERA_3D: &'static str = "camera_3d"; -} - impl Plugin for CameraPlugin { fn build(&self, app: &mut App) { - let mut active_cameras = ActiveCameras::default(); - active_cameras.add(Self::CAMERA_2D); - active_cameras.add(Self::CAMERA_3D); app.register_type::() .register_type::() .register_type::() @@ -46,8 +28,6 @@ impl Plugin for CameraPlugin { .register_type::() .register_type::() .register_type::() - .insert_resource(active_cameras) - .add_system_to_stage(CoreStage::PostUpdate, crate::camera::active_cameras_system) .add_system_to_stage( CoreStage::PostUpdate, crate::camera::camera_system::, @@ -55,61 +35,8 @@ impl Plugin for CameraPlugin { .add_system_to_stage( CoreStage::PostUpdate, crate::camera::camera_system::, - ); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { - render_app - .init_resource::() - .add_system_to_stage(RenderStage::Extract, extract_cameras); - } - } -} - -#[derive(Default)] -pub struct ExtractedCameraNames { - pub entities: HashMap, -} - -#[derive(Component, Debug)] -pub struct ExtractedCamera { - pub target: RenderTarget, - pub name: Option, - pub physical_size: Option, -} - -fn extract_cameras( - mut commands: Commands, - active_cameras: Res, - windows: Res, - images: Res>, - query: Query<(Entity, &Camera, &GlobalTransform, &VisibleEntities)>, -) { - let mut entities = HashMap::default(); - for camera in active_cameras.iter() { - let name = &camera.name; - if let Some((entity, camera, transform, visible_entities)) = - camera.entity.and_then(|e| query.get(e).ok()) - { - if let Some(size) = camera.target.get_physical_size(&windows, &images) { - entities.insert(name.clone(), entity); - commands.get_or_spawn(entity).insert_bundle(( - ExtractedCamera { - target: camera.target.clone(), - name: camera.name.clone(), - physical_size: camera.target.get_physical_size(&windows, &images), - }, - ExtractedView { - projection: camera.projection_matrix, - transform: *transform, - width: size.x.max(1), - height: size.y.max(1), - near: camera.near, - far: camera.far, - }, - visible_entities.clone(), - )); - } - } + ) + .add_plugin(CameraTypePlugin::::default()) + .add_plugin(CameraTypePlugin::::default()); } - - commands.insert_resource(ExtractedCameraNames { entities }); } diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 7f31d9458bedb..ba712a31943cd 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -9,7 +9,7 @@ use wgpu::{ pub use window::*; use crate::{ - camera::{ExtractedCamera, ExtractedCameraNames}, + camera::ExtractedCamera, prelude::Image, render_asset::RenderAssets, render_resource::{std140::AsStd140, DynamicUniformVec, Texture, TextureView}, @@ -174,20 +174,14 @@ fn prepare_view_uniforms( #[allow(clippy::too_many_arguments)] fn prepare_view_targets( mut commands: Commands, - camera_names: Res, windows: Res, images: Res>, msaa: Res, render_device: Res, mut texture_cache: ResMut, - cameras: Query<&ExtractedCamera>, + cameras: Query<(Entity, &ExtractedCamera)>, ) { - for entity in camera_names.entities.values().copied() { - let camera = if let Ok(camera) = cameras.get(entity) { - camera - } else { - continue; - }; + for (entity, camera) in cameras.iter() { if let Some(size) = camera.physical_size { if let Some(texture_view) = camera.target.get_texture_view(&windows, &images) { let sampled_target = if msaa.samples > 1 { diff --git a/crates/bevy_ui/src/entity.rs b/crates/bevy_ui/src/entity.rs index 02aac0c6bc8c2..c2a6b218bbc8c 100644 --- a/crates/bevy_ui/src/entity.rs +++ b/crates/bevy_ui/src/entity.rs @@ -2,9 +2,9 @@ use crate::{ widget::{Button, ImageMode}, - CalculatedSize, FocusPolicy, Interaction, Node, Style, UiColor, UiImage, CAMERA_UI, + CalculatedSize, FocusPolicy, Interaction, Node, Style, UiColor, UiImage, }; -use bevy_ecs::bundle::Bundle; +use bevy_ecs::{bundle::Bundle, prelude::Component}; use bevy_render::{ camera::{Camera, DepthCalculation, OrthographicProjection, WindowOrigin}, view::{Visibility, VisibleEntities}, @@ -135,10 +135,12 @@ impl Default for ButtonBundle { } } } +#[derive(Component, Default)] +pub struct CameraUi; /// The camera that is needed to see UI elements #[derive(Bundle, Debug)] -pub struct UiCameraBundle { +pub struct UiCameraBundle { /// The camera component pub camera: Camera, /// The orthographic projection settings @@ -150,16 +152,16 @@ pub struct UiCameraBundle { /// Contains visible entities // FIXME there is no frustrum culling for UI pub visible_entities: VisibleEntities, + pub marker: M, } -impl Default for UiCameraBundle { +impl Default for UiCameraBundle { fn default() -> Self { // we want 0 to be "closest" and +far to be "farthest" in 2d, so we offset // the camera's translation by far and use a right handed coordinate system let far = 1000.0; UiCameraBundle { camera: Camera { - name: Some(CAMERA_UI.to_string()), ..Default::default() }, orthographic_projection: OrthographicProjection { @@ -171,6 +173,7 @@ impl Default for UiCameraBundle { transform: Transform::from_xyz(0.0, 0.0, far - 0.1), global_transform: Default::default(), visible_entities: Default::default(), + marker: CameraUi, } } } diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 2f89f6df9ca85..2a44323966a3d 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -12,6 +12,7 @@ pub mod entity; pub mod update; pub mod widget; +use bevy_render::camera::CameraTypePlugin; pub use flex::*; pub use focus::*; pub use margins::*; @@ -31,6 +32,8 @@ use bevy_math::{Rect, Size}; use bevy_transform::TransformSystem; use update::{ui_z_system, update_clipping_system}; +use crate::prelude::CameraUi; + /// The basic plugin for Bevy UI #[derive(Default)] pub struct UiPlugin; @@ -46,7 +49,8 @@ pub enum UiSystem { impl Plugin for UiPlugin { fn build(&self, app: &mut App) { - app.init_resource::() + app.add_plugin(CameraTypePlugin::::default()) + .init_resource::() .register_type::() .register_type::() .register_type::() diff --git a/crates/bevy_ui/src/render/camera.rs b/crates/bevy_ui/src/render/camera.rs index 2e9ccf29cc549..d03c972951714 100644 --- a/crates/bevy_ui/src/render/camera.rs +++ b/crates/bevy_ui/src/render/camera.rs @@ -1,16 +1,18 @@ use bevy_ecs::prelude::*; -use bevy_render::{camera::ActiveCameras, render_phase::RenderPhase}; +use bevy_render::{camera::ActiveCamera, render_phase::RenderPhase}; -/// The name of the UI camera -pub const CAMERA_UI: &str = "camera_ui"; +use crate::prelude::CameraUi; + +use super::TransparentUi; /// Inserts the [`RenderPhase`] into the UI camera -pub fn extract_ui_camera_phases(mut commands: Commands, active_cameras: Res) { - if let Some(camera_ui) = active_cameras.get(CAMERA_UI) { - if let Some(entity) = camera_ui.entity { - commands - .get_or_spawn(entity) - .insert(RenderPhase::::default()); - } +pub fn extract_ui_camera_phases( + mut commands: Commands, + active_camera: Res>, +) { + if let Some(entity) = active_camera.get() { + commands + .get_or_spawn(entity) + .insert(RenderPhase::::default()); } } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 579a4acc19f02..dff085aa39993 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -6,8 +6,7 @@ pub use camera::*; pub use pipeline::*; pub use render_pass::*; -use std::ops::Range; - +use crate::{CalculatedClip, Node, UiColor, UiImage}; use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, AssetEvent, Assets, Handle, HandleUntyped}; use bevy_core::FloatOrd; @@ -15,7 +14,6 @@ use bevy_ecs::prelude::*; use bevy_math::{const_vec3, Mat4, Vec2, Vec3, Vec4Swizzles}; use bevy_reflect::TypeUuid; use bevy_render::{ - camera::ActiveCameras, color::Color, render_asset::RenderAssets, render_graph::{RenderGraph, SlotInfo, SlotType}, @@ -31,10 +29,8 @@ use bevy_text::{DefaultTextPipeline, Text}; use bevy_transform::components::GlobalTransform; use bevy_utils::HashMap; use bevy_window::{WindowId, Windows}; - use bytemuck::{Pod, Zeroable}; - -use crate::{CalculatedClip, Node, UiColor, UiImage}; +use std::ops::Range; pub mod node { pub const UI_PASS_DRIVER: &str = "ui_pass_driver"; @@ -61,9 +57,6 @@ pub enum RenderUiSystem { pub fn build_ui_render(app: &mut App) { load_internal_asset!(app, UI_SHADER_HANDLE, "ui.wgsl", Shader::from_wgsl); - let mut active_cameras = app.world.resource_mut::(); - active_cameras.add(CAMERA_UI); - let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, Err(_) => return, diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 005963f04f5d8..23d1577e6a3bc 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -4,7 +4,7 @@ use bevy_ecs::{ system::{lifetimeless::*, SystemParamItem}, }; use bevy_render::{ - camera::ExtractedCameraNames, + camera::ActiveCamera, render_graph::*, render_phase::*, render_resource::{ @@ -14,20 +14,21 @@ use bevy_render::{ view::*, }; -use super::{draw_ui_graph, UiBatch, UiImageBindGroups, UiMeta, CAMERA_UI}; +use crate::prelude::CameraUi; + +use super::{draw_ui_graph, UiBatch, UiImageBindGroups, UiMeta}; pub struct UiPassDriverNode; -impl bevy_render::render_graph::Node for UiPassDriverNode { +impl Node for UiPassDriverNode { fn run( &self, graph: &mut RenderGraphContext, _render_context: &mut RenderContext, world: &World, ) -> Result<(), NodeRunError> { - let extracted_cameras = world.resource::(); - if let Some(camera_ui) = extracted_cameras.entities.get(CAMERA_UI) { - graph.run_sub_graph(draw_ui_graph::NAME, vec![SlotValue::Entity(*camera_ui)])?; + if let Some(camera_ui) = world.resource::>().get() { + graph.run_sub_graph(draw_ui_graph::NAME, vec![SlotValue::Entity(camera_ui)])?; } Ok(()) @@ -49,7 +50,7 @@ impl UiPassNode { } } -impl bevy_render::render_graph::Node for UiPassNode { +impl Node for UiPassNode { fn input(&self) -> Vec { vec![SlotInfo::new(UiPassNode::IN_VIEW, SlotType::Entity)] } diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index abaaf73a3e4e8..64d403b0c4631 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -5,8 +5,8 @@ use bevy::{ prelude::*, reflect::TypeUuid, render::{ - camera::{ActiveCameras, Camera, ExtractedCameraNames, RenderTarget}, - render_graph::{NodeRunError, RenderGraph, RenderGraphContext, SlotValue}, + camera::{ActiveCamera, Camera, CameraTypePlugin, RenderTarget}, + render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext, SlotValue}, render_phase::RenderPhase, render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, @@ -17,6 +17,9 @@ use bevy::{ }, }; +#[derive(Component, Default)] +pub struct FirstPassCamera; + // This handle will point at the texture to which we will render in the first pass. pub const RENDER_IMAGE_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Image::TYPE_UUID, 13378939762009864029); @@ -24,26 +27,24 @@ pub const RENDER_IMAGE_HANDLE: HandleUntyped = // The name of the final node of the first pass. pub const FIRST_PASS_DRIVER: &str = "first_pass_driver"; -// The name of the camera that determines the view rendered in the first pass. -pub const FIRST_PASS_CAMERA: &str = "first_pass_camera"; - fn main() { let mut app = App::new(); app.insert_resource(Msaa { samples: 4 }) // Use 4x MSAA .add_plugins(DefaultPlugins) + .add_plugin(CameraTypePlugin::::default()) .add_startup_system(setup) .add_system(cube_rotator_system) .add_system(rotator_system); let render_app = app.sub_app_mut(RenderApp); - + let driver = FirstPassCameraDriver::new(&mut render_app.world); // This will add 3D render phases for the new camera. render_app.add_system_to_stage(RenderStage::Extract, extract_first_pass_camera_phases); let mut graph = render_app.world.resource_mut::(); // Add a node for the first pass. - graph.add_node(FIRST_PASS_DRIVER, FirstPassCameraDriver); + graph.add_node(FIRST_PASS_DRIVER, driver); // The first pass's dependencies include those of the main pass. graph @@ -61,30 +62,44 @@ fn main() { } // Add 3D render phases for FIRST_PASS_CAMERA. -fn extract_first_pass_camera_phases(mut commands: Commands, active_cameras: Res) { - if let Some(camera) = active_cameras.get(FIRST_PASS_CAMERA) { - if let Some(entity) = camera.entity { - commands.get_or_spawn(entity).insert_bundle(( - RenderPhase::::default(), - RenderPhase::::default(), - RenderPhase::::default(), - )); - } +fn extract_first_pass_camera_phases( + mut commands: Commands, + active: Res>, +) { + if let Some(entity) = active.get() { + commands.get_or_spawn(entity).insert_bundle(( + RenderPhase::::default(), + RenderPhase::::default(), + RenderPhase::::default(), + )); } } // A node for the first pass camera that runs draw_3d_graph with this camera. -struct FirstPassCameraDriver; -impl bevy::render::render_graph::Node for FirstPassCameraDriver { +struct FirstPassCameraDriver { + query: QueryState>, +} + +impl FirstPassCameraDriver { + pub fn new(render_world: &mut World) -> Self { + Self { + query: QueryState::new(render_world), + } + } +} +impl Node for FirstPassCameraDriver { + fn update(&mut self, world: &mut World) { + self.query.update_archetypes(world); + } + fn run( &self, graph: &mut RenderGraphContext, _render_context: &mut RenderContext, world: &World, ) -> Result<(), NodeRunError> { - let extracted_cameras = world.resource::(); - if let Some(camera_3d) = extracted_cameras.entities.get(FIRST_PASS_CAMERA) { - graph.run_sub_graph(draw_3d_graph::NAME, vec![SlotValue::Entity(*camera_3d)])?; + for camera in self.query.iter_manual(world) { + graph.run_sub_graph(draw_3d_graph::NAME, vec![SlotValue::Entity(camera)])?; } Ok(()) } @@ -102,7 +117,6 @@ fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, - mut active_cameras: ResMut, mut images: ResMut>, mut clear_colors: ResMut, ) { @@ -165,17 +179,15 @@ fn setup( // First pass camera let render_target = RenderTarget::Image(image_handle); clear_colors.insert(render_target.clone(), Color::WHITE); - active_cameras.add(FIRST_PASS_CAMERA); commands - .spawn_bundle(PerspectiveCameraBundle { + .spawn_bundle(PerspectiveCameraBundle:: { camera: Camera { - name: Some(FIRST_PASS_CAMERA.to_string()), target: render_target, ..default() }, transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) .looking_at(Vec3::default(), Vec3::Y), - ..default() + ..PerspectiveCameraBundle::new() }) .insert(first_pass_layer); // NOTE: omitting the RenderLayers component for this camera may cause a validation error: diff --git a/examples/game/alien_cake_addict.rs b/examples/game/alien_cake_addict.rs index 43a1acdeb96c0..699c68eeccd71 100644 --- a/examples/game/alien_cake_addict.rs +++ b/examples/game/alien_cake_addict.rs @@ -1,6 +1,4 @@ -use bevy::{ - core::FixedTimestep, ecs::schedule::SystemSet, prelude::*, render::camera::CameraPlugin, -}; +use bevy::{core::FixedTimestep, ecs::schedule::SystemSet, prelude::*, render::camera::Camera3d}; use rand::Rng; #[derive(Clone, Eq, PartialEq, Debug, Hash)] @@ -257,7 +255,7 @@ fn focus_camera( time: Res