Path: blob/main/crates/bevy_camera/src/visibility/mod.rs
9434 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>,659Option<&Sphere>,660&GlobalTransform,661Has<NoFrustumCulling>,662Has<VisibilityRange>,663Has<NoCpuCulling>,664)>,665visible_entity_ranges: Option<Res<VisibleEntityRanges>>,666) {667let visible_entity_ranges = visible_entity_ranges.as_deref();668669for (view, mut visible_entities, frustum, maybe_view_mask, camera, no_cpu_culling_camera) in670&mut view_query671{672if !camera.is_active {673continue;674}675676let view_mask = maybe_view_mask.unwrap_or_default();677678visible_aabb_query.par_iter_mut().for_each_init(679|| thread_queues.borrow_local_mut(),680|queue, query_item| {681let (682entity,683inherited_visibility,684mut view_visibility,685visibility_class,686maybe_entity_mask,687maybe_model_aabb,688maybe_model_sphere,689transform,690no_frustum_culling,691has_visibility_range,692no_cpu_culling_entity,693) = query_item;694695// Skip computing visibility for entities that are configured to be hidden.696// ViewVisibility has already been reset in `reset_view_visibility`.697if !inherited_visibility.get() {698return;699}700701let entity_mask = maybe_entity_mask.unwrap_or_default();702if !view_mask.intersects(entity_mask) {703return;704}705706// If outside of the visibility range, cull.707if has_visibility_range708&& visible_entity_ranges.is_some_and(|visible_entity_ranges| {709!visible_entity_ranges.entity_is_in_range_of_view(entity, view)710})711{712return;713}714715// If we have an aabb or a bounding sphere, do frustum culling716if !no_frustum_culling && !no_cpu_culling_camera && !no_cpu_culling_entity {717if let Some(model_aabb) = maybe_model_aabb {718let world_from_local = transform.affine();719let model_sphere = Sphere {720center: world_from_local.transform_point3a(model_aabb.center),721radius: transform.radius_vec3a(model_aabb.half_extents),722};723// Do quick sphere-based frustum culling724if !frustum.intersects_sphere(&model_sphere, false) {725return;726}727// Do aabb-based frustum culling728if !frustum.intersects_obb(model_aabb, &world_from_local, true, false) {729return;730}731} else if let Some(model_sphere) = maybe_model_sphere732&& !frustum.intersects_sphere(model_sphere, false)733{734// Do sphere-based frustum culling in this case735return;736}737}738739view_visibility.set_visible();740741// The visibility class may be None here because AABB gizmos can be enabled via742// config without a renderable component being added to the entity. This workaround743// allows view visibility to be set for entities without a renderable component, but744// still need to render gizmos.745if let Some(visibility_class) = visibility_class {746// Add the entity to the queue for all visibility classes the entity is in.747for visibility_class_id in visibility_class.iter() {748queue.entry(*visibility_class_id).or_default().push(entity);749}750}751},752);753754visible_entities.clear_all();755756// Drain all the thread queues into the `visible_entities` list.757for class_queues in thread_queues.iter_mut() {758for (class, entities) in class_queues {759visible_entities.get_mut(*class).append(entities);760}761}762}763}764765/// The last step in the visibility pipeline. Looks at entities that were visible last frame but not766/// marked as visible this frame and marks them as hidden by setting the [`ViewVisibility`]. This767/// process is needed to ensure we only trigger change detection on [`ViewVisibility`] when needed.768fn mark_newly_hidden_entities_invisible(mut view_visibilities: Query<&mut ViewVisibility>) {769view_visibilities770.par_iter_mut()771.for_each(|mut view_visibility| {772if view_visibility.as_ref().was_visible_now_hidden() {773*view_visibility = ViewVisibility::HIDDEN;774}775});776}777778/// A generic component add hook that automatically adds the appropriate779/// [`VisibilityClass`] to an entity.780///781/// This can be handy when creating custom renderable components. To use this782/// hook, add it to your renderable component like this:783///784/// ```ignore785/// #[derive(Component)]786/// #[component(on_add = add_visibility_class::<MyComponent>)]787/// struct MyComponent {788/// ...789/// }790/// ```791pub fn add_visibility_class<C>(792mut world: DeferredWorld<'_>,793HookContext { entity, .. }: HookContext,794) where795C: 'static,796{797if let Some(mut visibility_class) = world.get_mut::<VisibilityClass>(entity) {798visibility_class.push(TypeId::of::<C>());799}800}801802#[cfg(test)]803mod test {804use super::*;805use bevy_app::prelude::*;806807#[test]808fn visibility_propagation() {809let mut app = App::new();810app.add_systems(Update, visibility_propagate_system);811812let root1 = app.world_mut().spawn(Visibility::Hidden).id();813let root1_child1 = app.world_mut().spawn(Visibility::default()).id();814let root1_child2 = app.world_mut().spawn(Visibility::Hidden).id();815let root1_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();816let root1_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();817818app.world_mut()819.entity_mut(root1)820.add_children(&[root1_child1, root1_child2]);821app.world_mut()822.entity_mut(root1_child1)823.add_children(&[root1_child1_grandchild1]);824app.world_mut()825.entity_mut(root1_child2)826.add_children(&[root1_child2_grandchild1]);827828let root2 = app.world_mut().spawn(Visibility::default()).id();829let root2_child1 = app.world_mut().spawn(Visibility::default()).id();830let root2_child2 = app.world_mut().spawn(Visibility::Hidden).id();831let root2_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();832let root2_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();833834app.world_mut()835.entity_mut(root2)836.add_children(&[root2_child1, root2_child2]);837app.world_mut()838.entity_mut(root2_child1)839.add_children(&[root2_child1_grandchild1]);840app.world_mut()841.entity_mut(root2_child2)842.add_children(&[root2_child2_grandchild1]);843844app.update();845846let is_visible = |e: Entity| {847app.world()848.entity(e)849.get::<InheritedVisibility>()850.unwrap()851.get()852};853assert!(854!is_visible(root1),855"invisibility propagates down tree from root"856);857assert!(858!is_visible(root1_child1),859"invisibility propagates down tree from root"860);861assert!(862!is_visible(root1_child2),863"invisibility propagates down tree from root"864);865assert!(866!is_visible(root1_child1_grandchild1),867"invisibility propagates down tree from root"868);869assert!(870!is_visible(root1_child2_grandchild1),871"invisibility propagates down tree from root"872);873874assert!(875is_visible(root2),876"visibility propagates down tree from root"877);878assert!(879is_visible(root2_child1),880"visibility propagates down tree from root"881);882assert!(883!is_visible(root2_child2),884"visibility propagates down tree from root, but local invisibility is preserved"885);886assert!(887is_visible(root2_child1_grandchild1),888"visibility propagates down tree from root"889);890assert!(891!is_visible(root2_child2_grandchild1),892"child's invisibility propagates down to grandchild"893);894}895896#[test]897fn test_visibility_propagation_on_parent_change() {898// Setup the world and schedule899let mut app = App::new();900901app.add_systems(Update, visibility_propagate_system);902903// Create entities with visibility and hierarchy904let parent1 = app.world_mut().spawn((Visibility::Hidden,)).id();905let parent2 = app.world_mut().spawn((Visibility::Visible,)).id();906let child1 = app.world_mut().spawn((Visibility::Inherited,)).id();907let child2 = app.world_mut().spawn((Visibility::Inherited,)).id();908909// Build hierarchy910app.world_mut()911.entity_mut(parent1)912.add_children(&[child1, child2]);913914// Run the system initially to set up visibility915app.update();916917// Change parent visibility to Hidden918app.world_mut()919.entity_mut(parent2)920.insert(Visibility::Visible);921// Simulate a change in the parent component922app.world_mut().entity_mut(child2).insert(ChildOf(parent2)); // example of changing parent923924// Run the system again to propagate changes925app.update();926927let is_visible = |e: Entity| {928app.world()929.entity(e)930.get::<InheritedVisibility>()931.unwrap()932.get()933};934935// Retrieve and assert visibility936937assert!(938!is_visible(child1),939"Child1 should inherit visibility from parent"940);941942assert!(943is_visible(child2),944"Child2 should inherit visibility from parent"945);946}947948#[test]949fn visibility_propagation_unconditional_visible() {950use Visibility::{Hidden, Inherited, Visible};951952let mut app = App::new();953app.add_systems(Update, visibility_propagate_system);954955let root1 = app.world_mut().spawn(Visible).id();956let root1_child1 = app.world_mut().spawn(Inherited).id();957let root1_child2 = app.world_mut().spawn(Hidden).id();958let root1_child1_grandchild1 = app.world_mut().spawn(Visible).id();959let root1_child2_grandchild1 = app.world_mut().spawn(Visible).id();960961let root2 = app.world_mut().spawn(Inherited).id();962let root3 = app.world_mut().spawn(Hidden).id();963964app.world_mut()965.entity_mut(root1)966.add_children(&[root1_child1, root1_child2]);967app.world_mut()968.entity_mut(root1_child1)969.add_children(&[root1_child1_grandchild1]);970app.world_mut()971.entity_mut(root1_child2)972.add_children(&[root1_child2_grandchild1]);973974app.update();975976let is_visible = |e: Entity| {977app.world()978.entity(e)979.get::<InheritedVisibility>()980.unwrap()981.get()982};983assert!(984is_visible(root1),985"an unconditionally visible root is visible"986);987assert!(988is_visible(root1_child1),989"an inheriting child of an unconditionally visible parent is visible"990);991assert!(992!is_visible(root1_child2),993"a hidden child on an unconditionally visible parent is hidden"994);995assert!(996is_visible(root1_child1_grandchild1),997"an unconditionally visible child of an inheriting parent is visible"998);999assert!(1000is_visible(root1_child2_grandchild1),1001"an unconditionally visible child of a hidden parent is visible"1002);1003assert!(is_visible(root2), "an inheriting root is visible");1004assert!(!is_visible(root3), "a hidden root is hidden");1005}10061007#[test]1008fn visibility_propagation_change_detection() {1009let mut world = World::new();1010let mut schedule = Schedule::default();1011schedule.add_systems(visibility_propagate_system);10121013// Set up an entity hierarchy.10141015let id1 = world.spawn(Visibility::default()).id();10161017let id2 = world.spawn(Visibility::default()).id();1018world.entity_mut(id1).add_children(&[id2]);10191020let id3 = world.spawn(Visibility::Hidden).id();1021world.entity_mut(id2).add_children(&[id3]);10221023let id4 = world.spawn(Visibility::default()).id();1024world.entity_mut(id3).add_children(&[id4]);10251026// Test the hierarchy.10271028// Make sure the hierarchy is up-to-date.1029schedule.run(&mut world);1030world.clear_trackers();10311032let mut q = world.query::<Ref<InheritedVisibility>>();10331034assert!(!q.get(&world, id1).unwrap().is_changed());1035assert!(!q.get(&world, id2).unwrap().is_changed());1036assert!(!q.get(&world, id3).unwrap().is_changed());1037assert!(!q.get(&world, id4).unwrap().is_changed());10381039world.clear_trackers();1040world.entity_mut(id1).insert(Visibility::Hidden);1041schedule.run(&mut world);10421043assert!(q.get(&world, id1).unwrap().is_changed());1044assert!(q.get(&world, id2).unwrap().is_changed());1045assert!(!q.get(&world, id3).unwrap().is_changed());1046assert!(!q.get(&world, id4).unwrap().is_changed());10471048world.clear_trackers();1049schedule.run(&mut world);10501051assert!(!q.get(&world, id1).unwrap().is_changed());1052assert!(!q.get(&world, id2).unwrap().is_changed());1053assert!(!q.get(&world, id3).unwrap().is_changed());1054assert!(!q.get(&world, id4).unwrap().is_changed());10551056world.clear_trackers();1057world.entity_mut(id3).insert(Visibility::Inherited);1058schedule.run(&mut world);10591060assert!(!q.get(&world, id1).unwrap().is_changed());1061assert!(!q.get(&world, id2).unwrap().is_changed());1062assert!(!q.get(&world, id3).unwrap().is_changed());1063assert!(!q.get(&world, id4).unwrap().is_changed());10641065world.clear_trackers();1066world.entity_mut(id2).insert(Visibility::Visible);1067schedule.run(&mut world);10681069assert!(!q.get(&world, id1).unwrap().is_changed());1070assert!(q.get(&world, id2).unwrap().is_changed());1071assert!(q.get(&world, id3).unwrap().is_changed());1072assert!(q.get(&world, id4).unwrap().is_changed());10731074world.clear_trackers();1075schedule.run(&mut world);10761077assert!(!q.get(&world, id1).unwrap().is_changed());1078assert!(!q.get(&world, id2).unwrap().is_changed());1079assert!(!q.get(&world, id3).unwrap().is_changed());1080assert!(!q.get(&world, id4).unwrap().is_changed());1081}10821083#[test]1084fn visibility_propagation_with_invalid_parent() {1085let mut world = World::new();1086let mut schedule = Schedule::default();1087schedule.add_systems(visibility_propagate_system);10881089let parent = world.spawn(()).id();1090let child = world.spawn(Visibility::default()).id();1091world.entity_mut(parent).add_children(&[child]);10921093schedule.run(&mut world);1094world.clear_trackers();10951096let child_visible = world.entity(child).get::<InheritedVisibility>().unwrap().0;1097// defaults to same behavior of parent not found: visible = true1098assert!(child_visible);1099}11001101#[test]1102fn ensure_visibility_enum_size() {1103assert_eq!(1, size_of::<Visibility>());1104assert_eq!(1, size_of::<Option<Visibility>>());1105}11061107#[derive(Component, Default, Clone, Reflect)]1108#[require(VisibilityClass)]1109#[reflect(Component, Default, Clone)]1110#[component(on_add = add_visibility_class::<Self>)]1111struct TestVisibilityClassHook;11121113#[test]1114fn test_add_visibility_class_hook() {1115let mut world = World::new();1116let entity = world.spawn(TestVisibilityClassHook).id();1117let entity_clone = world.spawn_empty().id();1118world1119.entity_mut(entity)1120.clone_with_opt_out(entity_clone, |_| {});11211122let entity_visibility_class = world.entity(entity).get::<VisibilityClass>().unwrap();1123assert_eq!(entity_visibility_class.len(), 1);11241125let entity_clone_visibility_class =1126world.entity(entity_clone).get::<VisibilityClass>().unwrap();1127assert_eq!(entity_clone_visibility_class.len(), 1);1128}11291130#[test]1131fn view_visibility_lifecycle() {1132let mut app = App::new();1133app.add_plugins((1134TaskPoolPlugin::default(),1135bevy_asset::AssetPlugin::default(),1136bevy_mesh::MeshPlugin,1137bevy_transform::TransformPlugin,1138VisibilityPlugin,1139));11401141#[derive(Resource, Default)]1142struct ManualMark(bool);1143#[derive(Resource, Default)]1144struct ObservedChanged(bool);1145app.init_resource::<ManualMark>();1146app.init_resource::<ObservedChanged>();11471148app.add_systems(1149PostUpdate,1150(1151(|mut q: Query<&mut ViewVisibility>, mark: Res<ManualMark>| {1152if mark.0 {1153for mut v in &mut q {1154v.set_visible();1155}1156}1157})1158.in_set(VisibilitySystems::CheckVisibility),1159(|q: Query<(), Changed<ViewVisibility>>, mut observed: ResMut<ObservedChanged>| {1160if !q.is_empty() {1161observed.0 = true;1162}1163})1164.after(VisibilitySystems::MarkNewlyHiddenEntitiesInvisible),1165),1166);11671168let entity = app.world_mut().spawn(ViewVisibility::HIDDEN).id();11691170// Advance system ticks and clear spawn change1171app.update();1172app.world_mut().resource_mut::<ObservedChanged>().0 = false;11731174// Frame 1: do nothing1175app.update();1176{1177assert!(1178!app.world()1179.entity(entity)1180.get::<ViewVisibility>()1181.unwrap()1182.get(),1183"Frame 1: should be hidden"1184);1185assert!(1186!app.world().resource::<ObservedChanged>().0,1187"Frame 1: should not be changed"1188);1189}11901191// Frame 2: set entity as visible1192app.world_mut().resource_mut::<ManualMark>().0 = true;1193app.update();1194{1195assert!(1196app.world()1197.entity(entity)1198.get::<ViewVisibility>()1199.unwrap()1200.get(),1201"Frame 2: should be visible"1202);1203assert!(1204app.world().resource::<ObservedChanged>().0,1205"Frame 2: should be changed"1206);1207}12081209// Frame 3: still visible1210app.world_mut().resource_mut::<ManualMark>().0 = true;1211app.world_mut().resource_mut::<ObservedChanged>().0 = false;1212app.update();1213{1214assert!(1215app.world()1216.entity(entity)1217.get::<ViewVisibility>()1218.unwrap()1219.get(),1220"Frame 3: should be visible"1221);1222assert!(1223!app.world().resource::<ObservedChanged>().0,1224"Frame 3: should NOT be changed"1225);1226}12271228// Frame 4: do nothing (becomes hidden)1229app.world_mut().resource_mut::<ManualMark>().0 = false;1230app.world_mut().resource_mut::<ObservedChanged>().0 = false;1231app.update();1232{1233assert!(1234!app.world()1235.entity(entity)1236.get::<ViewVisibility>()1237.unwrap()1238.get(),1239"Frame 4: should be hidden"1240);1241assert!(1242app.world().resource::<ObservedChanged>().0,1243"Frame 4: should be changed"1244);1245}12461247// Frame 5: do nothing1248app.world_mut().resource_mut::<ManualMark>().0 = false;1249app.world_mut().resource_mut::<ObservedChanged>().0 = false;1250app.update();1251{1252assert!(1253!app.world()1254.entity(entity)1255.get::<ViewVisibility>()1256.unwrap()1257.get(),1258"Frame 5: should be hidden"1259);1260assert!(1261!app.world().resource::<ObservedChanged>().0,1262"Frame 5: should NOT be changed"1263);1264}1265}1266}126712681269