Path: blob/main/crates/bevy_camera/src/visibility/mod.rs
9297 views
mod range;1mod render_layers;23use core::any::TypeId;45use bevy_ecs::entity::EntityHashMap;6use bevy_ecs::lifecycle::HookContext;7use bevy_ecs::world::DeferredWorld;8use bevy_mesh::skinning::{9entity_aabb_from_skinned_mesh_bounds, SkinnedMesh, SkinnedMeshInverseBindposes,10};11use derive_more::derive::{Deref, DerefMut};12pub use range::*;13pub use render_layers::*;1415use bevy_app::{Plugin, PostUpdate, ValidateParentHasComponentPlugin};16use bevy_asset::prelude::AssetChanged;17use bevy_asset::{AssetEventSystems, Assets};18use bevy_ecs::prelude::*;19use bevy_reflect::{std_traits::ReflectDefault, Reflect};20use bevy_transform::{components::GlobalTransform, TransformSystems};21use bevy_utils::{Parallel, TypeIdMap};22use smallvec::SmallVec;2324use crate::{25camera::Camera,26primitives::{Aabb, Frustum, MeshAabb, Sphere},27Projection,28};29use bevy_mesh::{mark_3d_meshes_as_changed_if_their_assets_changed, Mesh, Mesh2d, Mesh3d};3031/// Use this component to opt-out of the built-in CPU frustum culling, see32/// [`Frustum`]. This can be attached to a [`Camera`] or to individual entities.33///34/// It can be used for example:35/// - disabling CPU culling completely for a [`Camera`], using only GPU culling.36/// - when overwriting a [`Mesh`]'s transform on the GPU side (e.g. overwriting `MeshInputUniform`'s37/// `world_from_local`), resulting in stale CPU-side positions.38#[derive(Component, Default)]39pub struct NoCpuCulling;4041/// User indication of whether an entity is visible. Propagates down the entity hierarchy.42///43/// If an entity is hidden in this way, all [`Children`] (and all of their children and so on) who44/// are set to [`Inherited`](Self::Inherited) will also be hidden.45///46/// This is done by the `visibility_propagate_system` which uses the entity hierarchy and47/// `Visibility` to set the values of each entity's [`InheritedVisibility`] component.48#[derive(Component, Clone, Copy, Reflect, Debug, PartialEq, Eq, Default)]49#[reflect(Component, Default, Debug, PartialEq, Clone)]50#[require(InheritedVisibility, ViewVisibility)]51pub enum Visibility {52/// An entity with `Visibility::Inherited` will inherit the Visibility of its [`ChildOf`] target.53///54/// A root-level entity that is set to `Inherited` will be visible.55#[default]56Inherited,57/// An entity with `Visibility::Hidden` will be unconditionally hidden.58Hidden,59/// An entity with `Visibility::Visible` will be unconditionally visible.60///61/// Note that an entity with `Visibility::Visible` will be visible regardless of whether the62/// [`ChildOf`] target entity is hidden.63Visible,64}6566impl Visibility {67/// Toggles between `Visibility::Inherited` and `Visibility::Visible`.68/// If the value is `Visibility::Hidden`, it remains unaffected.69#[inline]70pub fn toggle_inherited_visible(&mut self) {71*self = match *self {72Visibility::Inherited => Visibility::Visible,73Visibility::Visible => Visibility::Inherited,74_ => *self,75};76}77/// Toggles between `Visibility::Inherited` and `Visibility::Hidden`.78/// If the value is `Visibility::Visible`, it remains unaffected.79#[inline]80pub fn toggle_inherited_hidden(&mut self) {81*self = match *self {82Visibility::Inherited => Visibility::Hidden,83Visibility::Hidden => Visibility::Inherited,84_ => *self,85};86}87/// Toggles between `Visibility::Visible` and `Visibility::Hidden`.88/// If the value is `Visibility::Inherited`, it remains unaffected.89#[inline]90pub fn toggle_visible_hidden(&mut self) {91*self = match *self {92Visibility::Visible => Visibility::Hidden,93Visibility::Hidden => Visibility::Visible,94_ => *self,95};96}97}9899// Allows `&Visibility == Visibility`100impl PartialEq<Visibility> for &Visibility {101#[inline]102fn eq(&self, other: &Visibility) -> bool {103// Use the base Visibility == Visibility implementation.104<Visibility as PartialEq<Visibility>>::eq(*self, other)105}106}107108// Allows `Visibility == &Visibility`109impl PartialEq<&Visibility> for Visibility {110#[inline]111fn eq(&self, other: &&Visibility) -> bool {112// Use the base Visibility == Visibility implementation.113<Visibility as PartialEq<Visibility>>::eq(self, *other)114}115}116117/// Whether or not an entity is visible in the hierarchy.118/// This will not be accurate until [`VisibilityPropagate`] runs in the [`PostUpdate`] schedule.119///120/// If this is false, then [`ViewVisibility`] should also be false.121///122/// [`VisibilityPropagate`]: VisibilitySystems::VisibilityPropagate123#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)]124#[reflect(Component, Default, Debug, PartialEq, Clone)]125pub struct InheritedVisibility(bool);126127impl InheritedVisibility {128/// An entity that is invisible in the hierarchy.129pub const HIDDEN: Self = Self(false);130/// An entity that is visible in the hierarchy.131pub const VISIBLE: Self = Self(true);132133/// Returns `true` if the entity is visible in the hierarchy.134/// Otherwise, returns `false`.135#[inline]136pub fn get(self) -> bool {137self.0138}139}140141/// A bucket into which we group entities for the purposes of visibility.142///143/// Bevy's various rendering subsystems (3D, 2D, etc.) want to be able to144/// quickly winnow the set of entities to only those that the subsystem is145/// tasked with rendering, to avoid spending time examining irrelevant entities.146/// At the same time, Bevy wants the [`check_visibility`] system to determine147/// all entities' visibilities at the same time, regardless of what rendering148/// subsystem is responsible for drawing them. Additionally, your application149/// may want to add more types of renderable objects that Bevy determines150/// visibility for just as it does for Bevy's built-in objects.151///152/// The solution to this problem is *visibility classes*. A visibility class is153/// a type, typically the type of a component, that represents the subsystem154/// that renders it: for example, `Mesh3d`, `Mesh2d`, and `Sprite`. The155/// [`VisibilityClass`] component stores the visibility class or classes that156/// the entity belongs to. (Generally, an object will belong to only one157/// visibility class, but in rare cases it may belong to multiple.)158///159/// When adding a new renderable component, you'll typically want to write an160/// add-component hook that adds the type ID of that component to the161/// [`VisibilityClass`] array. See `custom_phase_item` for an example.162///163/// `VisibilityClass` is automatically added by a hook on the `Mesh3d` and164/// `Mesh2d` components. To avoid duplicating the `VisibilityClass` and165/// causing issues when cloning, we use `#[component(clone_behavior=Ignore)]`166//167// Note: This can't be a `ComponentId` because the visibility classes are copied168// into the render world, and component IDs are per-world.169#[derive(Clone, Component, Default, Reflect, Deref, DerefMut)]170#[reflect(Component, Default, Clone)]171#[component(clone_behavior=Ignore)]172pub struct VisibilityClass(pub SmallVec<[TypeId; 1]>);173174/// Algorithmically computed indication of whether an entity is visible and should be extracted for175/// rendering.176///177/// Each frame, this will be reset to `false` during [`VisibilityPropagate`] systems in178/// [`PostUpdate`]. Later in the frame, systems in [`CheckVisibility`] will mark any visible179/// entities using [`ViewVisibility::set`]. Because of this, values of this type will be marked as180/// changed every frame, even when they do not change.181///182/// If you wish to add a custom visibility system that sets this value, be sure to add it to the183/// [`CheckVisibility`] set.184///185/// [`VisibilityPropagate`]: VisibilitySystems::VisibilityPropagate186/// [`CheckVisibility`]: VisibilitySystems::CheckVisibility187#[derive(Component, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)]188#[reflect(Component, Default, Debug, PartialEq, Clone)]189pub struct ViewVisibility(190/// Bit packed booleans to track current and previous view visibility state.191///192/// Previous visibility is used as a scratch space to ensure that [`ViewVisibility`] is only193/// mutated (triggering change detection) when necessary.194///195/// This is needed because an entity might be seen by many views (cameras, lights that cast196/// shadows, etc.), so it is easy to know if an entity is visible to something, but hard to know197/// if it is *globally* non-visible to any view. To solve this, we track the visibility from the198/// previous frame. Then, during the [`VisibilitySystems::CheckVisibility`] system set, systems199/// call [`SetViewVisibility::set_visible`] to mark entities as visible.200///201/// Finally, we can look for entities that were previously visible but are no longer visible202/// and set their current state to hidden, ensuring that we have only triggered change detection203/// when necessary.204u8,205);206207impl ViewVisibility {208/// An entity that cannot be seen from any views.209pub const HIDDEN: Self = Self(0);210211/// Returns `true` if the entity is visible in any view.212/// Otherwise, returns `false`.213#[inline]214pub fn get(self) -> bool {215self.0 & 1 != 0216}217218/// Returns `true` if this entity was visible in the previous frame but is now hidden.219#[inline]220fn was_visible_now_hidden(self) -> bool {221// The first bit is false (current), and the second bit is true (previous).222(self.0 & 0b11) == 0b10223}224225#[inline]226fn update(&mut self) {227// Copy the first bit (current) to the second bit position (previous)228// and clear the first bit (current).229self.0 = (self.0 & 1) << 1;230}231}232233pub trait SetViewVisibility {234/// Sets the visibility to `true` if not already visible, triggering change detection only when235/// needed. This should not be considered reversible for a given frame, as this component tracks236/// if the entity is visible in _any_ view.237///238/// You should only manually set this if you are defining a custom visibility system,239/// in which case the system should be placed in the [`CheckVisibility`] set.240/// For normal user-defined entity visibility, see [`Visibility`].241///242/// [`CheckVisibility`]: VisibilitySystems::CheckVisibility243fn set_visible(&mut self);244}245246impl<'a> SetViewVisibility for Mut<'a, ViewVisibility> {247#[inline]248fn set_visible(&mut self) {249// Only update if it's not already visible.250// This is important because `set_visible` may be called multiple times per frame.251if self.0 & 1 == 0 {252if self.0 & 2 != 0 {253// If it was already visible last frame, we don't want to trigger change detection254// because it's still visible this frame.255self.bypass_change_detection().0 |= 1;256} else {257// If it was NOT visible last frame, this is a transition from hidden to visible.258// We want to trigger change detection here.259self.0 |= 1;260}261}262}263}264265/// Use this component to opt-out of built-in frustum culling for entities, see266/// [`Frustum`].267///268/// It can be used for example:269/// - when a [`Mesh`] is updated but its [`Aabb`] is not, which might happen with animations,270/// - when using some light effects, like wanting a [`Mesh`] out of the [`Frustum`]271/// to appear in the reflection of a [`Mesh`] within.272#[derive(Debug, Component, Default, Reflect, Clone, PartialEq)]273#[reflect(Component, Default, Debug)]274pub struct NoFrustumCulling;275276/// Use this component to enable dynamic skinned mesh bounds. The [`Aabb`]277/// component of the skinned mesh will be automatically updated each frame based278/// on the current joint transforms.279///280/// `DynamicSkinnedMeshBounds` depends on data from `Mesh::skinned_mesh_bounds`281/// and `SkinnedMesh`. The resulting `Aabb` will reliably enclose meshes where282/// vertex positions are only affected by skinning. But the `Aabb` may be larger283/// than is optimal, and doesn't account for morph targets, vertex shaders, and284/// anything else that modifies vertex positions.285#[derive(Debug, Component, Default, Reflect)]286#[reflect(Component, Default, Debug)]287pub struct DynamicSkinnedMeshBounds;288289/// Collection of entities visible from the current view.290///291/// This component contains all entities which are visible from the currently292/// rendered view. The collection is updated automatically by the [`VisibilitySystems::CheckVisibility`]293/// system set. Renderers can use the equivalent `RenderVisibleEntities` to optimize rendering of294/// a particular view, to prevent drawing items not visible from that view.295///296/// This component is intended to be attached to the same entity as the [`Camera`] and297/// the [`Frustum`] defining the view.298#[derive(Clone, Component, Default, Debug, Reflect)]299#[reflect(Component, Default, Debug, Clone)]300pub struct VisibleEntities {301#[reflect(ignore, clone)]302pub entities: TypeIdMap<Vec<Entity>>,303}304305impl VisibleEntities {306pub fn get(&self, type_id: TypeId) -> &[Entity] {307match self.entities.get(&type_id) {308Some(entities) => &entities[..],309None => &[],310}311}312313pub fn get_mut(&mut self, type_id: TypeId) -> &mut Vec<Entity> {314self.entities.entry(type_id).or_default()315}316317pub fn iter(&self, type_id: TypeId) -> impl DoubleEndedIterator<Item = &Entity> {318self.get(type_id).iter()319}320321pub fn len(&self, type_id: TypeId) -> usize {322self.get(type_id).len()323}324325pub fn is_empty(&self, type_id: TypeId) -> bool {326self.get(type_id).is_empty()327}328329pub fn clear(&mut self, type_id: TypeId) {330self.get_mut(type_id).clear();331}332333pub fn clear_all(&mut self) {334// Don't just nuke the hash table; we want to reuse allocations.335for entities in self.entities.values_mut() {336entities.clear();337}338}339340pub fn push(&mut self, entity: Entity, type_id: TypeId) {341self.get_mut(type_id).push(entity);342}343}344345/// Collection of mesh entities visible for 3D lighting.346///347/// This component contains all mesh entities visible from the current light view.348/// The collection is updated automatically by `bevy_pbr::SimulationLightSystems`.349#[derive(Component, Clone, Debug, Default, Reflect, Deref, DerefMut)]350#[reflect(Component, Debug, Default, Clone)]351pub struct VisibleMeshEntities {352#[reflect(ignore, clone)]353pub entities: Vec<Entity>,354}355356#[derive(Component, Clone, Debug, Default, Reflect)]357#[reflect(Component, Debug, Default, Clone)]358pub struct CubemapVisibleEntities {359#[reflect(ignore, clone)]360data: [VisibleMeshEntities; 6],361}362363impl CubemapVisibleEntities {364pub fn get(&self, i: usize) -> &VisibleMeshEntities {365&self.data[i]366}367368pub fn get_mut(&mut self, i: usize) -> &mut VisibleMeshEntities {369&mut self.data[i]370}371372pub fn iter(&self) -> impl DoubleEndedIterator<Item = &VisibleMeshEntities> {373self.data.iter()374}375376pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut VisibleMeshEntities> {377self.data.iter_mut()378}379}380381#[derive(Component, Clone, Debug, Default, Reflect)]382#[reflect(Component, Default, Clone)]383pub struct CascadesVisibleEntities {384/// Map of view entity to the visible entities for each cascade frustum.385#[reflect(ignore, clone)]386pub entities: EntityHashMap<Vec<VisibleMeshEntities>>,387}388389#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]390pub enum VisibilitySystems {391/// Label for the [`calculate_bounds`], `calculate_bounds_2d` and `calculate_bounds_text2d` systems,392/// calculating and inserting an [`Aabb`] to relevant entities.393CalculateBounds,394/// Label for [`update_frusta`] in [`CameraProjectionPlugin`](crate::CameraProjectionPlugin).395UpdateFrusta,396/// Label for the system propagating the [`InheritedVisibility`] in a397/// [`ChildOf`] / [`Children`] hierarchy.398VisibilityPropagate,399/// Label for the [`check_visibility`] system updating [`ViewVisibility`]400/// of each entity and the [`VisibleEntities`] of each view.\401///402/// System order ambiguities between systems in this set are ignored:403/// the order of systems within this set is irrelevant, as [`check_visibility`]404/// assumes that its operations are irreversible during the frame.405CheckVisibility,406/// Label for the `mark_newly_hidden_entities_invisible` system, which sets407/// [`ViewVisibility`] to [`ViewVisibility::HIDDEN`] for entities that no408/// view has marked as visible.409MarkNewlyHiddenEntitiesInvisible,410}411412pub struct VisibilityPlugin;413414impl Plugin for VisibilityPlugin {415fn build(&self, app: &mut bevy_app::App) {416use VisibilitySystems::*;417418app.add_plugins(ValidateParentHasComponentPlugin::<InheritedVisibility>::default())419.register_required_components::<Mesh3d, Visibility>()420.register_required_components::<Mesh3d, VisibilityClass>()421.register_required_components::<Mesh2d, Visibility>()422.register_required_components::<Mesh2d, VisibilityClass>()423.configure_sets(424PostUpdate,425(UpdateFrusta, VisibilityPropagate)426.before(CheckVisibility)427.after(TransformSystems::Propagate),428)429.configure_sets(430PostUpdate,431MarkNewlyHiddenEntitiesInvisible.after(CheckVisibility),432)433.configure_sets(434PostUpdate,435(CalculateBounds)436.before(CheckVisibility)437.after(TransformSystems::Propagate)438.after(AssetEventSystems)439.ambiguous_with(CalculateBounds)440.ambiguous_with(mark_3d_meshes_as_changed_if_their_assets_changed),441)442.add_systems(443PostUpdate,444(445(calculate_bounds, update_skinned_mesh_bounds)446.chain()447.in_set(CalculateBounds),448(visibility_propagate_system, reset_view_visibility)449.in_set(VisibilityPropagate),450check_visibility.in_set(CheckVisibility),451mark_newly_hidden_entities_invisible.in_set(MarkNewlyHiddenEntitiesInvisible),452),453);454app.world_mut()455.register_component_hooks::<Mesh3d>()456.on_add(add_visibility_class::<Mesh3d>);457app.world_mut()458.register_component_hooks::<Mesh2d>()459.on_add(add_visibility_class::<Mesh2d>);460}461}462463/// Add this component to an entity to prevent its `AABB` from being automatically recomputed.464///465/// This is useful if entities are already spawned with a correct `Aabb` component, or you have466/// many entities and want to avoid the cost of table scans searching for entities that need to have467/// their AABB recomputed.468#[derive(Component, Clone, Debug, Default, Reflect)]469pub struct NoAutoAabb;470471/// Computes and adds an [`Aabb`] component to entities with a472/// [`Mesh3d`] component and without a [`NoFrustumCulling`] component.473///474/// This system is used in system set [`VisibilitySystems::CalculateBounds`].475pub fn calculate_bounds(476mut commands: Commands,477meshes: Res<Assets<Mesh>>,478new_aabb: Query<479(Entity, &Mesh3d),480(481Without<Aabb>,482Without<NoFrustumCulling>,483Without<NoAutoAabb>,484),485>,486mut update_aabb: Query<487(&Mesh3d, &mut Aabb),488(489Or<(AssetChanged<Mesh3d>, Changed<Mesh3d>)>,490Without<NoFrustumCulling>,491Without<NoAutoAabb>,492),493>,494) {495for (entity, mesh_handle) in &new_aabb {496if let Some(mesh) = meshes.get(mesh_handle)497&& let Some(aabb) = mesh.compute_aabb()498{499commands.entity(entity).try_insert(aabb);500}501}502503update_aabb504.par_iter_mut()505.for_each(|(mesh_handle, mut old_aabb)| {506if let Some(aabb) = meshes.get(mesh_handle).and_then(MeshAabb::compute_aabb) {507*old_aabb = aabb;508}509});510}511512// Update the `Aabb` component of all skinned mesh entities with a `DynamicSkinnedMeshBounds`513// component.514fn update_skinned_mesh_bounds(515inverse_bindposes_assets: Res<Assets<SkinnedMeshInverseBindposes>>,516mesh_assets: Res<Assets<Mesh>>,517mut mesh_entities: Query<518(&mut Aabb, &Mesh3d, &SkinnedMesh, Option<&GlobalTransform>),519With<DynamicSkinnedMeshBounds>,520>,521joint_entities: Query<&GlobalTransform>,522) {523mesh_entities524.par_iter_mut()525.for_each(|(mut aabb, mesh, skinned_mesh, world_from_entity)| {526if let Some(inverse_bindposes_asset) =527inverse_bindposes_assets.get(&skinned_mesh.inverse_bindposes)528&& let Some(mesh_asset) = mesh_assets.get(mesh)529&& let Ok(skinned_aabb) = entity_aabb_from_skinned_mesh_bounds(530&joint_entities,531mesh_asset,532skinned_mesh,533inverse_bindposes_asset,534world_from_entity,535)536{537*aabb = skinned_aabb.into();538}539});540}541542/// Updates [`Frustum`].543///544/// This system is used in [`CameraProjectionPlugin`](crate::CameraProjectionPlugin).545pub fn update_frusta(546mut views: Query<547(&GlobalTransform, &Projection, &mut Frustum),548Or<(Changed<GlobalTransform>, Changed<Projection>)>,549>,550) {551for (transform, projection, mut frustum) in &mut views {552*frustum = projection.compute_frustum(transform);553}554}555556fn visibility_propagate_system(557changed: Query<558(Entity, &Visibility, Option<&ChildOf>, Option<&Children>),559(560With<InheritedVisibility>,561Or<(Changed<Visibility>, Changed<ChildOf>)>,562),563>,564mut visibility_query: Query<(&Visibility, &mut InheritedVisibility)>,565children_query: Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,566) {567for (entity, visibility, child_of, children) in &changed {568let is_visible = match visibility {569Visibility::Visible => true,570Visibility::Hidden => false,571// fall back to true if no parent is found or parent lacks components572Visibility::Inherited => child_of573.and_then(|c| visibility_query.get(c.parent()).ok())574.is_none_or(|(_, x)| x.get()),575};576let (_, mut inherited_visibility) = visibility_query577.get_mut(entity)578.expect("With<InheritedVisibility> ensures this query will return a value");579580// Only update the visibility if it has changed.581// This will also prevent the visibility from propagating multiple times in the same frame582// if this entity's visibility has been updated recursively by its parent.583if inherited_visibility.get() != is_visible {584inherited_visibility.0 = is_visible;585586// Recursively update the visibility of each child.587for &child in children.into_iter().flatten() {588let _ =589propagate_recursive(is_visible, child, &mut visibility_query, &children_query);590}591}592}593}594595fn propagate_recursive(596parent_is_visible: bool,597entity: Entity,598visibility_query: &mut Query<(&Visibility, &mut InheritedVisibility)>,599children_query: &Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,600// BLOCKED: https://github.com/rust-lang/rust/issues/31436601// We use a result here to use the `?` operator. Ideally we'd use a try block instead602) -> Result<(), ()> {603// Get the visibility components for the current entity.604// If the entity does not have the required components, just return early.605let (visibility, mut inherited_visibility) = visibility_query.get_mut(entity).map_err(drop)?;606607let is_visible = match visibility {608Visibility::Visible => true,609Visibility::Hidden => false,610Visibility::Inherited => parent_is_visible,611};612613// Only update the visibility if it has changed.614if inherited_visibility.get() != is_visible {615inherited_visibility.0 = is_visible;616617// Recursively update the visibility of each child.618for &child in children_query.get(entity).ok().into_iter().flatten() {619let _ = propagate_recursive(is_visible, child, visibility_query, children_query);620}621}622623Ok(())624}625626/// Track entities that were visible last frame, used to granularly update [`ViewVisibility`] this627/// frame without spurious `Change` detecation.628fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) {629query.par_iter_mut().for_each(|mut view_visibility| {630view_visibility.bypass_change_detection().update();631});632}633634/// System updating the visibility of entities each frame.635///636/// The system is part of the [`VisibilitySystems::CheckVisibility`] set. Each637/// frame, it updates the [`ViewVisibility`] of all entities, and for each view638/// also compute the [`VisibleEntities`] for that view.639///640/// To ensure that an entity is checked for visibility, make sure that it has a641/// [`VisibilityClass`] component and that that component is nonempty.642pub fn check_visibility(643mut thread_queues: Local<Parallel<TypeIdMap<Vec<Entity>>>>,644mut view_query: Query<(645Entity,646&mut VisibleEntities,647&Frustum,648Option<&RenderLayers>,649&Camera,650Has<NoCpuCulling>,651)>,652mut visible_aabb_query: Query<(653Entity,654&InheritedVisibility,655&mut ViewVisibility,656Option<&VisibilityClass>,657Option<&RenderLayers>,658Option<&Aabb>,659&GlobalTransform,660Has<NoFrustumCulling>,661Has<VisibilityRange>,662Has<NoCpuCulling>,663)>,664visible_entity_ranges: Option<Res<VisibleEntityRanges>>,665) {666let visible_entity_ranges = visible_entity_ranges.as_deref();667668for (view, mut visible_entities, frustum, maybe_view_mask, camera, no_cpu_culling_camera) in669&mut view_query670{671if !camera.is_active {672continue;673}674675let view_mask = maybe_view_mask.unwrap_or_default();676677visible_aabb_query.par_iter_mut().for_each_init(678|| thread_queues.borrow_local_mut(),679|queue, query_item| {680let (681entity,682inherited_visibility,683mut view_visibility,684visibility_class,685maybe_entity_mask,686maybe_model_aabb,687transform,688no_frustum_culling,689has_visibility_range,690no_cpu_culling_entity,691) = query_item;692693// Skip computing visibility for entities that are configured to be hidden.694// ViewVisibility has already been reset in `reset_view_visibility`.695if !inherited_visibility.get() {696return;697}698699let entity_mask = maybe_entity_mask.unwrap_or_default();700if !view_mask.intersects(entity_mask) {701return;702}703704// If outside of the visibility range, cull.705if has_visibility_range706&& visible_entity_ranges.is_some_and(|visible_entity_ranges| {707!visible_entity_ranges.entity_is_in_range_of_view(entity, view)708})709{710return;711}712713// If we have an aabb, do frustum culling714if !no_frustum_culling715&& !no_cpu_culling_camera716&& !no_cpu_culling_entity717&& let Some(model_aabb) = maybe_model_aabb718{719let world_from_local = transform.affine();720let model_sphere = Sphere {721center: world_from_local.transform_point3a(model_aabb.center),722radius: transform.radius_vec3a(model_aabb.half_extents),723};724// Do quick sphere-based frustum culling725if !frustum.intersects_sphere(&model_sphere, false) {726return;727}728// Do aabb-based frustum culling729if !frustum.intersects_obb(model_aabb, &world_from_local, true, false) {730return;731}732}733734view_visibility.set_visible();735736// The visibility class may be None here because AABB gizmos can be enabled via737// config without a renderable component being added to the entity. This workaround738// allows view visibility to be set for entities without a renderable component, but739// still need to render gizmos.740if let Some(visibility_class) = visibility_class {741// Add the entity to the queue for all visibility classes the entity is in.742for visibility_class_id in visibility_class.iter() {743queue.entry(*visibility_class_id).or_default().push(entity);744}745}746},747);748749visible_entities.clear_all();750751// Drain all the thread queues into the `visible_entities` list.752for class_queues in thread_queues.iter_mut() {753for (class, entities) in class_queues {754visible_entities.get_mut(*class).append(entities);755}756}757}758}759760/// The last step in the visibility pipeline. Looks at entities that were visible last frame but not761/// marked as visible this frame and marks them as hidden by setting the [`ViewVisibility`]. This762/// process is needed to ensure we only trigger change detection on [`ViewVisibility`] when needed.763fn mark_newly_hidden_entities_invisible(mut view_visibilities: Query<&mut ViewVisibility>) {764view_visibilities765.par_iter_mut()766.for_each(|mut view_visibility| {767if view_visibility.as_ref().was_visible_now_hidden() {768*view_visibility = ViewVisibility::HIDDEN;769}770});771}772773/// A generic component add hook that automatically adds the appropriate774/// [`VisibilityClass`] to an entity.775///776/// This can be handy when creating custom renderable components. To use this777/// hook, add it to your renderable component like this:778///779/// ```ignore780/// #[derive(Component)]781/// #[component(on_add = add_visibility_class::<MyComponent>)]782/// struct MyComponent {783/// ...784/// }785/// ```786pub fn add_visibility_class<C>(787mut world: DeferredWorld<'_>,788HookContext { entity, .. }: HookContext,789) where790C: 'static,791{792if let Some(mut visibility_class) = world.get_mut::<VisibilityClass>(entity) {793visibility_class.push(TypeId::of::<C>());794}795}796797#[cfg(test)]798mod test {799use super::*;800use bevy_app::prelude::*;801802#[test]803fn visibility_propagation() {804let mut app = App::new();805app.add_systems(Update, visibility_propagate_system);806807let root1 = app.world_mut().spawn(Visibility::Hidden).id();808let root1_child1 = app.world_mut().spawn(Visibility::default()).id();809let root1_child2 = app.world_mut().spawn(Visibility::Hidden).id();810let root1_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();811let root1_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();812813app.world_mut()814.entity_mut(root1)815.add_children(&[root1_child1, root1_child2]);816app.world_mut()817.entity_mut(root1_child1)818.add_children(&[root1_child1_grandchild1]);819app.world_mut()820.entity_mut(root1_child2)821.add_children(&[root1_child2_grandchild1]);822823let root2 = app.world_mut().spawn(Visibility::default()).id();824let root2_child1 = app.world_mut().spawn(Visibility::default()).id();825let root2_child2 = app.world_mut().spawn(Visibility::Hidden).id();826let root2_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();827let root2_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();828829app.world_mut()830.entity_mut(root2)831.add_children(&[root2_child1, root2_child2]);832app.world_mut()833.entity_mut(root2_child1)834.add_children(&[root2_child1_grandchild1]);835app.world_mut()836.entity_mut(root2_child2)837.add_children(&[root2_child2_grandchild1]);838839app.update();840841let is_visible = |e: Entity| {842app.world()843.entity(e)844.get::<InheritedVisibility>()845.unwrap()846.get()847};848assert!(849!is_visible(root1),850"invisibility propagates down tree from root"851);852assert!(853!is_visible(root1_child1),854"invisibility propagates down tree from root"855);856assert!(857!is_visible(root1_child2),858"invisibility propagates down tree from root"859);860assert!(861!is_visible(root1_child1_grandchild1),862"invisibility propagates down tree from root"863);864assert!(865!is_visible(root1_child2_grandchild1),866"invisibility propagates down tree from root"867);868869assert!(870is_visible(root2),871"visibility propagates down tree from root"872);873assert!(874is_visible(root2_child1),875"visibility propagates down tree from root"876);877assert!(878!is_visible(root2_child2),879"visibility propagates down tree from root, but local invisibility is preserved"880);881assert!(882is_visible(root2_child1_grandchild1),883"visibility propagates down tree from root"884);885assert!(886!is_visible(root2_child2_grandchild1),887"child's invisibility propagates down to grandchild"888);889}890891#[test]892fn test_visibility_propagation_on_parent_change() {893// Setup the world and schedule894let mut app = App::new();895896app.add_systems(Update, visibility_propagate_system);897898// Create entities with visibility and hierarchy899let parent1 = app.world_mut().spawn((Visibility::Hidden,)).id();900let parent2 = app.world_mut().spawn((Visibility::Visible,)).id();901let child1 = app.world_mut().spawn((Visibility::Inherited,)).id();902let child2 = app.world_mut().spawn((Visibility::Inherited,)).id();903904// Build hierarchy905app.world_mut()906.entity_mut(parent1)907.add_children(&[child1, child2]);908909// Run the system initially to set up visibility910app.update();911912// Change parent visibility to Hidden913app.world_mut()914.entity_mut(parent2)915.insert(Visibility::Visible);916// Simulate a change in the parent component917app.world_mut().entity_mut(child2).insert(ChildOf(parent2)); // example of changing parent918919// Run the system again to propagate changes920app.update();921922let is_visible = |e: Entity| {923app.world()924.entity(e)925.get::<InheritedVisibility>()926.unwrap()927.get()928};929930// Retrieve and assert visibility931932assert!(933!is_visible(child1),934"Child1 should inherit visibility from parent"935);936937assert!(938is_visible(child2),939"Child2 should inherit visibility from parent"940);941}942943#[test]944fn visibility_propagation_unconditional_visible() {945use Visibility::{Hidden, Inherited, Visible};946947let mut app = App::new();948app.add_systems(Update, visibility_propagate_system);949950let root1 = app.world_mut().spawn(Visible).id();951let root1_child1 = app.world_mut().spawn(Inherited).id();952let root1_child2 = app.world_mut().spawn(Hidden).id();953let root1_child1_grandchild1 = app.world_mut().spawn(Visible).id();954let root1_child2_grandchild1 = app.world_mut().spawn(Visible).id();955956let root2 = app.world_mut().spawn(Inherited).id();957let root3 = app.world_mut().spawn(Hidden).id();958959app.world_mut()960.entity_mut(root1)961.add_children(&[root1_child1, root1_child2]);962app.world_mut()963.entity_mut(root1_child1)964.add_children(&[root1_child1_grandchild1]);965app.world_mut()966.entity_mut(root1_child2)967.add_children(&[root1_child2_grandchild1]);968969app.update();970971let is_visible = |e: Entity| {972app.world()973.entity(e)974.get::<InheritedVisibility>()975.unwrap()976.get()977};978assert!(979is_visible(root1),980"an unconditionally visible root is visible"981);982assert!(983is_visible(root1_child1),984"an inheriting child of an unconditionally visible parent is visible"985);986assert!(987!is_visible(root1_child2),988"a hidden child on an unconditionally visible parent is hidden"989);990assert!(991is_visible(root1_child1_grandchild1),992"an unconditionally visible child of an inheriting parent is visible"993);994assert!(995is_visible(root1_child2_grandchild1),996"an unconditionally visible child of a hidden parent is visible"997);998assert!(is_visible(root2), "an inheriting root is visible");999assert!(!is_visible(root3), "a hidden root is hidden");1000}10011002#[test]1003fn visibility_propagation_change_detection() {1004let mut world = World::new();1005let mut schedule = Schedule::default();1006schedule.add_systems(visibility_propagate_system);10071008// Set up an entity hierarchy.10091010let id1 = world.spawn(Visibility::default()).id();10111012let id2 = world.spawn(Visibility::default()).id();1013world.entity_mut(id1).add_children(&[id2]);10141015let id3 = world.spawn(Visibility::Hidden).id();1016world.entity_mut(id2).add_children(&[id3]);10171018let id4 = world.spawn(Visibility::default()).id();1019world.entity_mut(id3).add_children(&[id4]);10201021// Test the hierarchy.10221023// Make sure the hierarchy is up-to-date.1024schedule.run(&mut world);1025world.clear_trackers();10261027let mut q = world.query::<Ref<InheritedVisibility>>();10281029assert!(!q.get(&world, id1).unwrap().is_changed());1030assert!(!q.get(&world, id2).unwrap().is_changed());1031assert!(!q.get(&world, id3).unwrap().is_changed());1032assert!(!q.get(&world, id4).unwrap().is_changed());10331034world.clear_trackers();1035world.entity_mut(id1).insert(Visibility::Hidden);1036schedule.run(&mut world);10371038assert!(q.get(&world, id1).unwrap().is_changed());1039assert!(q.get(&world, id2).unwrap().is_changed());1040assert!(!q.get(&world, id3).unwrap().is_changed());1041assert!(!q.get(&world, id4).unwrap().is_changed());10421043world.clear_trackers();1044schedule.run(&mut world);10451046assert!(!q.get(&world, id1).unwrap().is_changed());1047assert!(!q.get(&world, id2).unwrap().is_changed());1048assert!(!q.get(&world, id3).unwrap().is_changed());1049assert!(!q.get(&world, id4).unwrap().is_changed());10501051world.clear_trackers();1052world.entity_mut(id3).insert(Visibility::Inherited);1053schedule.run(&mut world);10541055assert!(!q.get(&world, id1).unwrap().is_changed());1056assert!(!q.get(&world, id2).unwrap().is_changed());1057assert!(!q.get(&world, id3).unwrap().is_changed());1058assert!(!q.get(&world, id4).unwrap().is_changed());10591060world.clear_trackers();1061world.entity_mut(id2).insert(Visibility::Visible);1062schedule.run(&mut world);10631064assert!(!q.get(&world, id1).unwrap().is_changed());1065assert!(q.get(&world, id2).unwrap().is_changed());1066assert!(q.get(&world, id3).unwrap().is_changed());1067assert!(q.get(&world, id4).unwrap().is_changed());10681069world.clear_trackers();1070schedule.run(&mut world);10711072assert!(!q.get(&world, id1).unwrap().is_changed());1073assert!(!q.get(&world, id2).unwrap().is_changed());1074assert!(!q.get(&world, id3).unwrap().is_changed());1075assert!(!q.get(&world, id4).unwrap().is_changed());1076}10771078#[test]1079fn visibility_propagation_with_invalid_parent() {1080let mut world = World::new();1081let mut schedule = Schedule::default();1082schedule.add_systems(visibility_propagate_system);10831084let parent = world.spawn(()).id();1085let child = world.spawn(Visibility::default()).id();1086world.entity_mut(parent).add_children(&[child]);10871088schedule.run(&mut world);1089world.clear_trackers();10901091let child_visible = world.entity(child).get::<InheritedVisibility>().unwrap().0;1092// defaults to same behavior of parent not found: visible = true1093assert!(child_visible);1094}10951096#[test]1097fn ensure_visibility_enum_size() {1098assert_eq!(1, size_of::<Visibility>());1099assert_eq!(1, size_of::<Option<Visibility>>());1100}11011102#[derive(Component, Default, Clone, Reflect)]1103#[require(VisibilityClass)]1104#[reflect(Component, Default, Clone)]1105#[component(on_add = add_visibility_class::<Self>)]1106struct TestVisibilityClassHook;11071108#[test]1109fn test_add_visibility_class_hook() {1110let mut world = World::new();1111let entity = world.spawn(TestVisibilityClassHook).id();1112let entity_clone = world.spawn_empty().id();1113world1114.entity_mut(entity)1115.clone_with_opt_out(entity_clone, |_| {});11161117let entity_visibility_class = world.entity(entity).get::<VisibilityClass>().unwrap();1118assert_eq!(entity_visibility_class.len(), 1);11191120let entity_clone_visibility_class =1121world.entity(entity_clone).get::<VisibilityClass>().unwrap();1122assert_eq!(entity_clone_visibility_class.len(), 1);1123}11241125#[test]1126fn view_visibility_lifecycle() {1127let mut app = App::new();1128app.add_plugins((1129TaskPoolPlugin::default(),1130bevy_asset::AssetPlugin::default(),1131bevy_mesh::MeshPlugin,1132bevy_transform::TransformPlugin,1133VisibilityPlugin,1134));11351136#[derive(Resource, Default)]1137struct ManualMark(bool);1138#[derive(Resource, Default)]1139struct ObservedChanged(bool);1140app.init_resource::<ManualMark>();1141app.init_resource::<ObservedChanged>();11421143app.add_systems(1144PostUpdate,1145(1146(|mut q: Query<&mut ViewVisibility>, mark: Res<ManualMark>| {1147if mark.0 {1148for mut v in &mut q {1149v.set_visible();1150}1151}1152})1153.in_set(VisibilitySystems::CheckVisibility),1154(|q: Query<(), Changed<ViewVisibility>>, mut observed: ResMut<ObservedChanged>| {1155if !q.is_empty() {1156observed.0 = true;1157}1158})1159.after(VisibilitySystems::MarkNewlyHiddenEntitiesInvisible),1160),1161);11621163let entity = app.world_mut().spawn(ViewVisibility::HIDDEN).id();11641165// Advance system ticks and clear spawn change1166app.update();1167app.world_mut().resource_mut::<ObservedChanged>().0 = false;11681169// Frame 1: do nothing1170app.update();1171{1172assert!(1173!app.world()1174.entity(entity)1175.get::<ViewVisibility>()1176.unwrap()1177.get(),1178"Frame 1: should be hidden"1179);1180assert!(1181!app.world().resource::<ObservedChanged>().0,1182"Frame 1: should not be changed"1183);1184}11851186// Frame 2: set entity as visible1187app.world_mut().resource_mut::<ManualMark>().0 = true;1188app.update();1189{1190assert!(1191app.world()1192.entity(entity)1193.get::<ViewVisibility>()1194.unwrap()1195.get(),1196"Frame 2: should be visible"1197);1198assert!(1199app.world().resource::<ObservedChanged>().0,1200"Frame 2: should be changed"1201);1202}12031204// Frame 3: still visible1205app.world_mut().resource_mut::<ManualMark>().0 = true;1206app.world_mut().resource_mut::<ObservedChanged>().0 = false;1207app.update();1208{1209assert!(1210app.world()1211.entity(entity)1212.get::<ViewVisibility>()1213.unwrap()1214.get(),1215"Frame 3: should be visible"1216);1217assert!(1218!app.world().resource::<ObservedChanged>().0,1219"Frame 3: should NOT be changed"1220);1221}12221223// Frame 4: do nothing (becomes hidden)1224app.world_mut().resource_mut::<ManualMark>().0 = false;1225app.world_mut().resource_mut::<ObservedChanged>().0 = false;1226app.update();1227{1228assert!(1229!app.world()1230.entity(entity)1231.get::<ViewVisibility>()1232.unwrap()1233.get(),1234"Frame 4: should be hidden"1235);1236assert!(1237app.world().resource::<ObservedChanged>().0,1238"Frame 4: should be changed"1239);1240}12411242// Frame 5: do nothing1243app.world_mut().resource_mut::<ManualMark>().0 = false;1244app.world_mut().resource_mut::<ObservedChanged>().0 = false;1245app.update();1246{1247assert!(1248!app.world()1249.entity(entity)1250.get::<ViewVisibility>()1251.unwrap()1252.get(),1253"Frame 5: should be hidden"1254);1255assert!(1256!app.world().resource::<ObservedChanged>().0,1257"Frame 5: should NOT be changed"1258);1259}1260}1261}126212631264