Skip to content

Commit

Permalink
frustum culling for sprites
Browse files Browse the repository at this point in the history
  • Loading branch information
Byteron committed Mar 11, 2021
1 parent b17f8a4 commit 2d8692c
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 24 deletions.
6 changes: 3 additions & 3 deletions crates/bevy_render/src/camera/visible_entities.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{Camera, DepthCalculation};
use crate::prelude::Visible;
use crate::{draw::WithinFrustum, prelude::Visible};
use bevy_core::FloatOrd;
use bevy_ecs::{entity::Entity, query::With, reflect::ReflectComponent, system::Query};
use bevy_reflect::Reflect;
Expand Down Expand Up @@ -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>>,
visible_query: Query<(Entity, &Visible, Option<&RenderLayers>), With<WithinFrustum>>,
visible_transform_query: Query<&GlobalTransform, With<WithinFrustum>>,
) {
for (camera, camera_global_transform, mut visible_entities, maybe_camera_mask) in
camera_query.iter_mut()
Expand Down
6 changes: 6 additions & 0 deletions crates/bevy_render/src/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ impl Default for Visible {
}
}

/// Viewable is used for frustum culling.
/// Any Sprite or AtlasTextureSprite will have this removed if they are outside the camera frustum and thus not be rendered.
#[derive(Debug, Default, Clone, Reflect)]
#[reflect(Component)]
pub struct WithinFrustum;

/// A component that indicates how to draw an entity.
#[derive(Debug, Clone, Reflect)]
#[reflect(Component)]
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use bevy_ecs::{
system::{IntoExclusiveSystem, IntoSystem},
};
use bevy_transform::TransformSystem;
use draw::Visible;
use draw::{Visible, WithinFrustum};

pub use once_cell;

pub mod prelude {
Expand Down Expand Up @@ -137,6 +138,7 @@ impl Plugin for RenderPlugin {
.register_type::<DepthCalculation>()
.register_type::<Draw>()
.register_type::<Visible>()
.register_type::<WithinFrustum>()
.register_type::<RenderPipelines>()
.register_type::<OrthographicProjection>()
.register_type::<PerspectiveProjection>()
Expand Down
8 changes: 6 additions & 2 deletions crates/bevy_render/src/pipeline/render_pipelines.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use super::{PipelineDescriptor, PipelineSpecialization};
use crate::{
draw::{Draw, DrawContext},
draw::{Draw, DrawContext, WithinFrustum},
mesh::{Indices, Mesh},
prelude::{Msaa, Visible},
renderer::RenderResourceBindings,
};
use bevy_asset::{Assets, Handle};
use bevy_ecs::{
query::With,
reflect::ReflectComponent,
system::{Query, Res, ResMut},
};
Expand Down Expand Up @@ -86,7 +87,10 @@ pub fn draw_render_pipelines_system(
mut render_resource_bindings: ResMut<RenderResourceBindings>,
msaa: Res<Msaa>,
meshes: Res<Assets<Mesh>>,
mut query: Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible)>,
mut query: Query<
(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible),
With<WithinFrustum>,
>,
) {
for (mut draw, mut render_pipelines, mesh_handle, visible) in query.iter_mut() {
if !visible.is_visible {
Expand Down
11 changes: 7 additions & 4 deletions crates/bevy_render/src/shader/shader_defs.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use bevy_asset::{Asset, Assets, Handle};

use crate::{pipeline::RenderPipelines, Texture};
use crate::{draw::WithinFrustum, pipeline::RenderPipelines, Texture};
pub use bevy_derive::ShaderDefs;
use bevy_ecs::system::{Query, Res};
use bevy_ecs::{
query::With,
system::{Query, Res},
};

/// Something that can either be "defined" or "not defined". This is used to determine if a "shader
/// def" should be considered "defined"
Expand Down Expand Up @@ -61,7 +64,7 @@ impl ShaderDef for Option<Handle<Texture>> {
}

/// Updates [RenderPipelines] with the latest [ShaderDefs]
pub fn shader_defs_system<T>(mut query: Query<(&T, &mut RenderPipelines)>)
pub fn shader_defs_system<T>(mut query: Query<(&T, &mut RenderPipelines), With<WithinFrustum>>)
where
T: ShaderDefs + Send + Sync + 'static,
{
Expand Down Expand Up @@ -94,7 +97,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<T: Asset>(
assets: Res<Assets<T>>,
mut query: Query<(&Handle<T>, &mut RenderPipelines)>,
mut query: Query<(&Handle<T>, &mut RenderPipelines), With<WithinFrustum>>,
) where
T: ShaderDefs + Send + Sync + 'static,
{
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_sprite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ 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
parking_lot = "0.11.1"
rectangle-pack = "0.2"
thiserror = "1.0"
guillotiere = "0.6.0"
Expand Down
111 changes: 111 additions & 0 deletions crates/bevy_sprite/src/frustum_culling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use bevy_asset::{Assets, Handle};
use bevy_ecs::prelude::{Commands, Entity, Query, Res, With};
use bevy_math::Vec2;
use bevy_render::{camera::Camera, draw::WithinFrustum};
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_intersecting(&self, other: Rect) -> bool {
self.position.distance(other.position) < (self.get_radius() + other.get_radius())
}

#[inline]
pub fn get_radius(&self) -> f32 {
let half_size = self.size / Vec2::splat(2.0);
(half_size.x.powf(2.0) + half_size.y.powf(2.0)).sqrt()
}
}

pub fn sprites(
mut commands: Commands,
windows: Res<Windows>,
cameras: Query<&Transform, With<Camera>>,
visible: Query<&WithinFrustum, With<Sprite>>,
sprites: Query<(Entity, &Transform, &Sprite)>,
) {
let window_size = if let Some(window) = windows.get_primary() {
Vec2::new(window.width(), window.height())
} else {
return;
};

for camera_transform in cameras.iter() {
let camera_size = window_size * camera_transform.scale.truncate();

let rect = Rect {
position: camera_transform.translation.truncate(),
size: camera_size,
};

for (entity, drawable_transform, sprite) in sprites.iter() {
let sprite_rect = Rect {
position: drawable_transform.translation.truncate(),
size: sprite.size,
};

if rect.is_intersecting(sprite_rect) {
if visible.get(entity).is_err() {
commands.insert(entity, WithinFrustum);
}
} else if visible.get(entity).is_ok() {
commands.remove::<WithinFrustum>(entity);
}
}
}
}

pub fn atlases(
mut commands: Commands,
windows: Res<Windows>,
textures: Res<Assets<TextureAtlas>>,
cameras: Query<&Transform, With<Camera>>,
visible: Query<&WithinFrustum, With<TextureAtlasSprite>>,
sprites: Query<(
Entity,
&Transform,
&TextureAtlasSprite,
&Handle<TextureAtlas>,
)>,
) {
let window = windows.get_primary().unwrap();
let window_size = Vec2::new(window.width(), window.height());

for camera_transform in cameras.iter() {
let camera_size = window_size * camera_transform.scale.truncate();

let rect = Rect {
position: camera_transform.translation.truncate(),
size: camera_size,
};

for (entity, drawable_transform, sprite, atlas_handle) in sprites.iter() {
if let Some(atlas) = textures.get(atlas_handle) {
if let Some(sprite) = atlas.textures.get(sprite.index as usize) {
let size = Vec2::new(sprite.width(), sprite.height());

let sprite_rect = Rect {
position: drawable_transform.translation.truncate(),
size,
};

if rect.is_intersecting(sprite_rect) {
if visible.get(entity).is_err() {
commands.insert(entity, WithinFrustum::default());
}
} else if visible.get(entity).is_ok() {
commands.remove::<WithinFrustum>(entity);
}
}
}
}
}
}
30 changes: 23 additions & 7 deletions crates/bevy_sprite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod entity;

mod color_material;
mod dynamic_texture_atlas_builder;
mod frustum_culling;
mod rect;
mod render;
mod sprite;
Expand All @@ -26,10 +27,14 @@ pub use texture_atlas_builder::*;

use bevy_app::prelude::*;
use bevy_asset::{AddAsset, Assets, Handle, HandleUntyped};
use bevy_ecs::system::IntoSystem;
use bevy_ecs::{
component::{ComponentDescriptor, StorageType},
system::IntoSystem,
};
use bevy_math::Vec2;
use bevy_reflect::TypeUuid;
use bevy_render::{
draw::WithinFrustum,
mesh::{shape, Mesh},
pipeline::PipelineDescriptor,
render_graph::RenderGraph,
Expand All @@ -50,21 +55,32 @@ impl Plugin for SpritePlugin {
.register_type::<Sprite>()
.register_type::<SpriteResizeMode>()
.add_system_to_stage(CoreStage::PostUpdate, sprite_system.system())
.add_system_to_stage(CoreStage::PostUpdate, frustum_culling::sprites.system())
.add_system_to_stage(CoreStage::PostUpdate, frustum_culling::atlases.system())
.add_system_to_stage(
CoreStage::PostUpdate,
asset_shader_defs_system::<ColorMaterial>.system(),
);

let world = app.world_mut().cell();
let mut render_graph = world.get_resource_mut::<RenderGraph>().unwrap();
let mut pipelines = world
let world = app.world_mut();
world
.register_component(ComponentDescriptor::new::<WithinFrustum>(
StorageType::SparseSet,
))
.unwrap();

let world_cell = world.cell();
let mut render_graph = world_cell.get_resource_mut::<RenderGraph>().unwrap();
let mut pipelines = world_cell
.get_resource_mut::<Assets<PipelineDescriptor>>()
.unwrap();
let mut shaders = world.get_resource_mut::<Assets<Shader>>().unwrap();
let mut shaders = world_cell.get_resource_mut::<Assets<Shader>>().unwrap();
crate::render::add_sprite_graph(&mut render_graph, &mut pipelines, &mut shaders);

let mut meshes = world.get_resource_mut::<Assets<Mesh>>().unwrap();
let mut color_materials = world.get_resource_mut::<Assets<ColorMaterial>>().unwrap();
let mut meshes = world_cell.get_resource_mut::<Assets<Mesh>>().unwrap();
let mut color_materials = world_cell
.get_resource_mut::<Assets<ColorMaterial>>()
.unwrap();
color_materials.set_untracked(Handle::<ColorMaterial>::default(), ColorMaterial::default());
meshes.set_untracked(
QUAD_HANDLE,
Expand Down
8 changes: 6 additions & 2 deletions crates/bevy_sprite/src/sprite.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use crate::ColorMaterial;
use bevy_asset::{Assets, Handle};
use bevy_core::Bytes;
use bevy_ecs::system::{Query, Res};
use bevy_ecs::{
query::With,
system::{Query, Res},
};
use bevy_math::Vec2;
use bevy_reflect::{Reflect, ReflectDeserialize, TypeUuid};
use bevy_render::{
draw::WithinFrustum,
renderer::{RenderResource, RenderResourceType, RenderResources},
texture::Texture,
};
Expand Down Expand Up @@ -76,7 +80,7 @@ impl Sprite {
pub fn sprite_system(
materials: Res<Assets<ColorMaterial>>,
textures: Res<Assets<Texture>>,
mut query: Query<(&mut Sprite, &Handle<ColorMaterial>)>,
mut query: Query<(&mut Sprite, &Handle<ColorMaterial>), With<WithinFrustum>>,
) {
for (mut sprite, handle) in query.iter_mut() {
match sprite.resize_mode {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_text/src/text2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use bevy_ecs::{
};
use bevy_math::{Size, Vec3};
use bevy_render::{
draw::{DrawContext, Drawable},
draw::{DrawContext, Drawable, WithinFrustum},
mesh::Mesh,
prelude::{Draw, Msaa, Texture, Visible},
render_graph::base::MainPass,
Expand Down Expand Up @@ -72,7 +72,7 @@ pub fn draw_text2d_system(
&GlobalTransform,
&Text2dSize,
),
With<MainPass>,
(With<MainPass>, With<WithinFrustum>),
>,
) {
let font_quad = meshes.get(&QUAD_HANDLE).unwrap();
Expand Down
9 changes: 6 additions & 3 deletions crates/bevy_ui/src/widget/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ use crate::{CalculatedSize, Node, Style, Val};
use bevy_asset::Assets;
use bevy_ecs::{
entity::Entity,
query::{Changed, Or},
query::{Changed, Or, With},
system::{Local, Query, QuerySet, Res, ResMut},
};
use bevy_math::Size;
use bevy_render::{
draw::{Draw, DrawContext, Drawable},
draw::{Draw, DrawContext, Drawable, WithinFrustum},
mesh::Mesh,
prelude::{Msaa, Visible},
renderer::RenderResourceBindings,
Expand Down Expand Up @@ -136,7 +136,10 @@ pub fn draw_text_system(
meshes: Res<Assets<Mesh>>,
mut render_resource_bindings: ResMut<RenderResourceBindings>,
text_pipeline: Res<DefaultTextPipeline>,
mut query: Query<(Entity, &mut Draw, &Visible, &Text, &Node, &GlobalTransform)>,
mut query: Query<
(Entity, &mut Draw, &Visible, &Text, &Node, &GlobalTransform),
With<WithinFrustum>,
>,
) {
let scale_factor = if let Some(window) = windows.get_primary() {
window.scale_factor()
Expand Down

0 comments on commit 2d8692c

Please sign in to comment.