diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 9c7edd59168eb3..1771bcf5751f46 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -1,5 +1,5 @@ use super::{Camera, DepthCalculation}; -use crate::prelude::Visible; +use crate::{draw::Frustum, prelude::Visible}; use bevy_core::FloatOrd; use bevy_ecs::{Entity, Query, With}; use bevy_reflect::{Reflect, ReflectComponent}; @@ -204,8 +204,8 @@ pub fn visible_entities_system( &mut VisibleEntities, Option<&RenderLayers>, )>, - visible_query: Query<(Entity, &Visible, Option<&RenderLayers>)>, - visible_transform_query: Query<&GlobalTransform, With>, + visible_query: Query<(Entity, &Visible, Option<&RenderLayers>), With>, + visible_transform_query: Query<&GlobalTransform, With>, ) { for (camera, camera_global_transform, mut visible_entities, maybe_camera_mask) in camera_query.iter_mut() diff --git a/crates/bevy_render/src/draw.rs b/crates/bevy_render/src/draw.rs index ab6c6192465bd2..14964005e187ce 100644 --- a/crates/bevy_render/src/draw.rs +++ b/crates/bevy_render/src/draw.rs @@ -63,6 +63,16 @@ impl Default for Visible { } } +#[derive(Debug, Clone, Reflect)] +#[reflect(Component)] +pub struct Frustum; + +impl Default for Frustum { + fn default() -> Self { + Frustum + } +} + /// A component that indicates how to draw an entity. #[derive(Debug, Clone, Reflect)] #[reflect(Component)] diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 8097cfefd68601..059f09bb7cbfad 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -13,7 +13,7 @@ pub mod texture; use bevy_ecs::{IntoExclusiveSystem, IntoSystem, SystemStage}; use bevy_reflect::RegisterTypeBuilder; -use draw::Visible; +use draw::{Frustum, Visible}; pub use once_cell; pub mod prelude { @@ -129,6 +129,7 @@ impl Plugin for RenderPlugin { .register_type::() .register_type::() .register_type::() + .register_type::() .register_type::() .register_type::() .register_type::() diff --git a/crates/bevy_render/src/pipeline/render_pipelines.rs b/crates/bevy_render/src/pipeline/render_pipelines.rs index da2928e5fe4c2b..75248762ecd715 100644 --- a/crates/bevy_render/src/pipeline/render_pipelines.rs +++ b/crates/bevy_render/src/pipeline/render_pipelines.rs @@ -1,12 +1,12 @@ use super::{PipelineDescriptor, PipelineSpecialization}; use crate::{ - draw::{Draw, DrawContext}, + draw::{Draw, DrawContext, Frustum}, mesh::{Indices, Mesh}, prelude::{Msaa, Visible}, renderer::RenderResourceBindings, }; use bevy_asset::{Assets, Handle}; -use bevy_ecs::{Query, Res, ResMut}; +use bevy_ecs::{Query, Res, ResMut, With}; use bevy_reflect::{Reflect, ReflectComponent}; use bevy_utils::HashSet; @@ -82,7 +82,7 @@ pub fn draw_render_pipelines_system( mut render_resource_bindings: ResMut, msaa: Res, meshes: Res>, - mut query: Query<(&mut Draw, &mut RenderPipelines, &Handle, &Visible)>, + mut query: Query<(&mut Draw, &mut RenderPipelines, &Handle, &Visible), With>, ) { for (mut draw, mut render_pipelines, mesh_handle, visible) in query.iter_mut() { if !visible.is_visible { diff --git a/crates/bevy_render/src/render_graph/nodes/pass_node.rs b/crates/bevy_render/src/render_graph/nodes/pass_node.rs index 2db0058ac52b4c..6058f1d2e450e2 100644 --- a/crates/bevy_render/src/render_graph/nodes/pass_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/pass_node.rs @@ -1,6 +1,6 @@ use crate::{ camera::{ActiveCameras, VisibleEntities}, - draw::{Draw, RenderCommand}, + draw::{Draw, Frustum, RenderCommand}, pass::{ClearColor, LoadOp, PassDescriptor, TextureAttachment}, pipeline::{ BindGroupDescriptor, BindType, BindingDescriptor, BindingShaderStage, IndexFormat, @@ -236,6 +236,10 @@ where } else { continue; }; + + if world.get::(visible_entity.entity).is_err() { + continue; + } if let Ok(visible) = world.get::(visible_entity.entity) { if !visible.is_visible { diff --git a/crates/bevy_render/src/shader/shader_defs.rs b/crates/bevy_render/src/shader/shader_defs.rs index 977872462732cc..0612768d314a94 100644 --- a/crates/bevy_render/src/shader/shader_defs.rs +++ b/crates/bevy_render/src/shader/shader_defs.rs @@ -1,8 +1,8 @@ use bevy_asset::{Asset, Assets, Handle}; -use crate::{pipeline::RenderPipelines, Texture}; +use crate::{draw::Frustum, pipeline::RenderPipelines, Texture}; pub use bevy_derive::ShaderDefs; -use bevy_ecs::{Query, Res}; +use bevy_ecs::{Query, Res, With}; /// Something that can either be "defined" or "not defined". This is used to determine if a "shader def" should be considered "defined" pub trait ShaderDef { @@ -60,7 +60,7 @@ impl ShaderDef for Option> { } /// Updates [RenderPipelines] with the latest [ShaderDefs] -pub fn shader_defs_system(mut query: Query<(&T, &mut RenderPipelines)>) +pub fn shader_defs_system(mut query: Query<(&T, &mut RenderPipelines), With>) where T: ShaderDefs + Send + Sync + 'static, { @@ -93,7 +93,7 @@ pub fn clear_shader_defs_system(mut query: Query<&mut RenderPipelines>) { /// Updates [RenderPipelines] with the latest [ShaderDefs] from a given asset type pub fn asset_shader_defs_system( assets: Res>, - mut query: Query<(&Handle, &mut RenderPipelines)>, + mut query: Query<(&Handle, &mut RenderPipelines), With>, ) where T: ShaderDefs + Send + Sync + 'static, { diff --git a/crates/bevy_sprite/Cargo.toml b/crates/bevy_sprite/Cargo.toml index 2ae632f368ac75..516621f8a454c9 100644 --- a/crates/bevy_sprite/Cargo.toml +++ b/crates/bevy_sprite/Cargo.toml @@ -24,6 +24,7 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy" bevy_render = { path = "../bevy_render", version = "0.4.0" } bevy_transform = { path = "../bevy_transform", version = "0.4.0" } bevy_utils = { path = "../bevy_utils", version = "0.4.0" } +bevy_window = { path = "../bevy_window", version = "0.4.0" } # other rectangle-pack = "0.2" diff --git a/crates/bevy_sprite/src/frustum_culling.rs b/crates/bevy_sprite/src/frustum_culling.rs new file mode 100644 index 00000000000000..a7418254d60c8e --- /dev/null +++ b/crates/bevy_sprite/src/frustum_culling.rs @@ -0,0 +1,126 @@ +use bevy_asset::{Assets, Handle}; +use bevy_ecs::{Commands, Entity, Query, Res, With}; +use bevy_math::Vec2; +use bevy_render::{camera::Camera, draw::Frustum}; +use bevy_transform::components::Transform; +use bevy_window::Windows; + +use crate::{Sprite, TextureAtlas, TextureAtlasSprite}; + +struct Rect { + position: Vec2, + size: Vec2, +} + +impl Rect { + #[inline] + pub fn is_overlapping(&self, other: Rect) -> bool { + !(self.position.x > other.end().x + || self.end().x < other.position.x + || self.position.y > other.end().y + || self.end().y < other.position.y) + } + + #[inline] + pub fn end(&self) -> Vec2 { + self.position + self.size + } +} +pub fn frustum_culling_sprites( + commands: &mut Commands, + windows: Res, + cameras: Query<&Transform, With>, + visible: Query<&Frustum, With>, + sprites: Query<(Entity, &Transform, &Sprite)>, +) { + let window = windows.get_primary().unwrap(); + let window_size = Vec2::new(window.width(), window.height()); + + for camera_transform in cameras.iter() { + let camera_position = Vec2::new( + camera_transform.translation.x, + camera_transform.translation.y, + ); + + let camera_size = window_size * camera_transform.scale.truncate(); + + let rect = Rect { + position: camera_position - camera_size / Vec2::new(2.0, 2.0), + size: camera_size, + }; + + for (entity, drawable_transform, sprite) in sprites.iter() { + let sprite_rect = Rect { + position: drawable_transform.translation.truncate() + - sprite.size / Vec2::new(2.0, 2.0), + size: sprite.size, + }; + + if rect.is_overlapping(sprite_rect) { + if visible.get(entity).is_err() { + commands.insert_one(entity, Frustum::default()); + } + } else if visible.get(entity).is_ok() { + commands.remove_one::(entity); + } + } + } +} + +pub fn frustum_culling_atlases( + commands: &mut Commands, + windows: Res, + textures: Res>, + cameras: Query<&Transform, With>, + visible: Query<&Frustum, With>, + sprites: Query<( + Entity, + &Transform, + &TextureAtlasSprite, + &Handle, + )>, +) { + let window = windows.get_primary().unwrap(); + let window_size = Vec2::new(window.width(), window.height()); + + for camera_transform in cameras.iter() { + let camera_position = Vec2::new( + camera_transform.translation.x, + camera_transform.translation.y, + ); + + let camera_size = window_size * camera_transform.scale.truncate(); + + let rect = Rect { + position: camera_position - camera_size / Vec2::new(2.0, 2.0), + size: camera_size, + }; + + for (entity, drawable_transform, sprite, atlas_handle) in sprites.iter() { + let atlas = match textures.get(atlas_handle) { + Some(atlas) => atlas, + None => continue, + }; + + let sprite = match atlas.textures.get(sprite.index as usize) { + Some(sprite) => sprite, + None => continue, + }; + + let size = Vec2::new(sprite.width(), sprite.height()); + + let sprite_rect = Rect { + position: drawable_transform.translation.truncate() - size / Vec2::new(2.0, 2.0), + size, + }; + + if rect.is_overlapping(sprite_rect) { + if visible.get(entity).is_err() { + commands.insert_one(entity, Frustum::default()); + } + } else if visible.get(entity).is_ok() { + commands.remove_one::(entity); + } + } + } +} diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index e0ea01cb2a1f3f..19db2ce77909bf 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -3,6 +3,7 @@ pub mod entity; mod color_material; mod dynamic_texture_atlas_builder; +mod frustum_culling; mod rect; mod render; mod sprite; @@ -48,6 +49,14 @@ impl Plugin for SpritePlugin { .add_asset::() .register_type::() .add_system_to_stage(CoreStage::PostUpdate, sprite_system.system()) + .add_system_to_stage( + CoreStage::PostUpdate, + frustum_culling::frustum_culling_sprites.system(), + ) + .add_system_to_stage( + CoreStage::PostUpdate, + frustum_culling::frustum_culling_atlases.system(), + ) .add_system_to_stage( CoreStage::PostUpdate, asset_shader_defs_system::.system(), diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index f35f712f252257..261ebd7720a550 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -1,9 +1,9 @@ use crate::ColorMaterial; use bevy_asset::{Assets, Handle}; -use bevy_ecs::{Query, Res}; +use bevy_ecs::{Query, Res, With}; use bevy_math::Vec2; use bevy_reflect::{Reflect, ReflectDeserialize, TypeUuid}; -use bevy_render::{renderer::RenderResources, texture::Texture}; +use bevy_render::{draw::Frustum, renderer::RenderResources, texture::Texture}; use serde::{Deserialize, Serialize}; #[derive(Debug, Default, Clone, RenderResources, TypeUuid, Reflect)] @@ -41,7 +41,7 @@ impl Sprite { pub fn sprite_system( materials: Res>, textures: Res>, - mut query: Query<(&mut Sprite, &Handle)>, + mut query: Query<(&mut Sprite, &Handle), With>, ) { for (mut sprite, handle) in query.iter_mut() { match sprite.resize_mode { diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 52b146b8430789..e5cd1632295675 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -2,7 +2,7 @@ use bevy_asset::Assets; use bevy_ecs::{Bundle, Changed, Entity, Local, Query, QuerySet, Res, ResMut, With}; use bevy_math::{Size, Vec3}; use bevy_render::{ - draw::{DrawContext, Drawable}, + draw::{DrawContext, Drawable, Frustum}, mesh::Mesh, prelude::{Draw, Msaa, Texture, Visible}, render_graph::base::MainPass, @@ -69,7 +69,7 @@ pub fn draw_text2d_system( &GlobalTransform, &CalculatedSize, ), - With, + (With, With), >, ) { let font_quad = meshes.get(&QUAD_HANDLE).unwrap(); diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index 97d4850d3fcf6d..c20de7191ac0fb 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -1,9 +1,9 @@ use crate::{Node, Style, Val}; use bevy_asset::Assets; -use bevy_ecs::{Changed, Entity, Local, Or, Query, QuerySet, Res, ResMut}; +use bevy_ecs::{Changed, Entity, Local, Or, Query, QuerySet, Res, ResMut, With}; use bevy_math::Size; use bevy_render::{ - draw::{Draw, DrawContext, Drawable}, + draw::{Draw, DrawContext, Drawable, Frustum}, mesh::Mesh, prelude::{Msaa, Visible}, renderer::RenderResourceBindings, @@ -133,7 +133,7 @@ pub fn draw_text_system( meshes: Res>, mut render_resource_bindings: ResMut, text_pipeline: Res, - mut query: Query<(Entity, &mut Draw, &Visible, &Text, &Node, &GlobalTransform)>, + mut query: Query<(Entity, &mut Draw, &Visible, &Text, &Node, &GlobalTransform), With>, ) { let scale_factor = if let Some(window) = windows.get_primary() { window.scale_factor()