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 Feb 23, 2021
1 parent bc4fe9b commit 9112f48
Show file tree
Hide file tree
Showing 12 changed files with 171 additions and 20 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::Frustum, prelude::Visible};
use bevy_core::FloatOrd;
use bevy_ecs::{Entity, Query, With};
use bevy_reflect::{Reflect, ReflectComponent};
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<Frustum>>,
visible_transform_query: Query<&GlobalTransform, With<Frustum>>,
) {
for (camera, camera_global_transform, mut visible_entities, maybe_camera_mask) in
camera_query.iter_mut()
Expand Down
10 changes: 10 additions & 0 deletions crates/bevy_render/src/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -129,6 +129,7 @@ impl Plugin for RenderPlugin {
.register_type::<Camera>()
.register_type::<Draw>()
.register_type::<Visible>()
.register_type::<Frustum>()
.register_type::<RenderPipelines>()
.register_type::<OrthographicProjection>()
.register_type::<PerspectiveProjection>()
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_render/src/pipeline/render_pipelines.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -82,7 +82,7 @@ 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<Frustum>>,
) {
for (mut draw, mut render_pipelines, mesh_handle, visible) in query.iter_mut() {
if !visible.is_visible {
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_render/src/render_graph/nodes/pass_node.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -236,6 +236,10 @@ where
} else {
continue;
};

if world.get::<Frustum>(visible_entity.entity).is_err() {
continue;
}

if let Ok(visible) = world.get::<Visible>(visible_entity.entity) {
if !visible.is_visible {
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_render/src/shader/shader_defs.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -60,7 +60,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<Frustum>>)
where
T: ShaderDefs + Send + Sync + 'static,
{
Expand Down Expand Up @@ -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<T: Asset>(
assets: Res<Assets<T>>,
mut query: Query<(&Handle<T>, &mut RenderPipelines)>,
mut query: Query<(&Handle<T>, &mut RenderPipelines), With<Frustum>>,
) where
T: ShaderDefs + Send + Sync + 'static,
{
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_sprite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
126 changes: 126 additions & 0 deletions crates/bevy_sprite/src/frustum_culling.rs
Original file line number Diff line number Diff line change
@@ -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<Windows>,
cameras: Query<&Transform, With<Camera>>,
visible: Query<&Frustum, With<Sprite>>,
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::<Frustum>(entity);
}
}
}
}

pub fn frustum_culling_atlases(
commands: &mut Commands,
windows: Res<Windows>,
textures: Res<Assets<TextureAtlas>>,
cameras: Query<&Transform, With<Camera>>,
visible: Query<&Frustum, 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_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::<Frustum>(entity);
}
}
}
}
9 changes: 9 additions & 0 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 Down Expand Up @@ -48,6 +49,14 @@ impl Plugin for SpritePlugin {
.add_asset::<TextureAtlas>()
.register_type::<Sprite>()
.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::<ColorMaterial>.system(),
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_sprite/src/sprite.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -41,7 +41,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<Frustum>>,
) {
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 @@ -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,
Expand Down Expand Up @@ -69,7 +69,7 @@ pub fn draw_text2d_system(
&GlobalTransform,
&CalculatedSize,
),
With<MainPass>,
(With<MainPass>, With<Frustum>),
>,
) {
let font_quad = meshes.get(&QUAD_HANDLE).unwrap();
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ui/src/widget/text.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -133,7 +133,7 @@ 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<Frustum>>,
) {
let scale_factor = if let Some(window) = windows.get_primary() {
window.scale_factor()
Expand Down

0 comments on commit 9112f48

Please sign in to comment.