bevy_camera/visibility/
mod.rs

1mod range;
2mod render_layers;
3
4use core::any::TypeId;
5
6use bevy_ecs::entity::{EntityHashMap, EntityHashSet};
7use bevy_ecs::lifecycle::HookContext;
8use bevy_ecs::world::DeferredWorld;
9use derive_more::derive::{Deref, DerefMut};
10pub use range::*;
11pub use render_layers::*;
12
13use bevy_app::{Plugin, PostUpdate};
14use bevy_asset::Assets;
15use bevy_ecs::{hierarchy::validate_parent_has_component, prelude::*};
16use bevy_reflect::{std_traits::ReflectDefault, Reflect};
17use bevy_transform::{components::GlobalTransform, TransformSystems};
18use bevy_utils::{Parallel, TypeIdMap};
19use smallvec::SmallVec;
20
21use crate::{
22    camera::Camera,
23    primitives::{Aabb, Frustum, MeshAabb, Sphere},
24    Projection,
25};
26use bevy_mesh::{mark_3d_meshes_as_changed_if_their_assets_changed, Mesh, Mesh2d, Mesh3d};
27
28#[derive(Component, Default)]
29pub struct NoCpuCulling;
30
31/// User indication of whether an entity is visible. Propagates down the entity hierarchy.
32///
33/// If an entity is hidden in this way, all [`Children`] (and all of their children and so on) who
34/// are set to [`Inherited`](Self::Inherited) will also be hidden.
35///
36/// This is done by the `visibility_propagate_system` which uses the entity hierarchy and
37/// `Visibility` to set the values of each entity's [`InheritedVisibility`] component.
38#[derive(Component, Clone, Copy, Reflect, Debug, PartialEq, Eq, Default)]
39#[reflect(Component, Default, Debug, PartialEq, Clone)]
40#[require(InheritedVisibility, ViewVisibility)]
41pub enum Visibility {
42    /// An entity with `Visibility::Inherited` will inherit the Visibility of its [`ChildOf`] target.
43    ///
44    /// A root-level entity that is set to `Inherited` will be visible.
45    #[default]
46    Inherited,
47    /// An entity with `Visibility::Hidden` will be unconditionally hidden.
48    Hidden,
49    /// An entity with `Visibility::Visible` will be unconditionally visible.
50    ///
51    /// Note that an entity with `Visibility::Visible` will be visible regardless of whether the
52    /// [`ChildOf`] target entity is hidden.
53    Visible,
54}
55
56impl Visibility {
57    /// Toggles between `Visibility::Inherited` and `Visibility::Visible`.
58    /// If the value is `Visibility::Hidden`, it remains unaffected.
59    #[inline]
60    pub fn toggle_inherited_visible(&mut self) {
61        *self = match *self {
62            Visibility::Inherited => Visibility::Visible,
63            Visibility::Visible => Visibility::Inherited,
64            _ => *self,
65        };
66    }
67    /// Toggles between `Visibility::Inherited` and `Visibility::Hidden`.
68    /// If the value is `Visibility::Visible`, it remains unaffected.
69    #[inline]
70    pub fn toggle_inherited_hidden(&mut self) {
71        *self = match *self {
72            Visibility::Inherited => Visibility::Hidden,
73            Visibility::Hidden => Visibility::Inherited,
74            _ => *self,
75        };
76    }
77    /// Toggles between `Visibility::Visible` and `Visibility::Hidden`.
78    /// If the value is `Visibility::Inherited`, it remains unaffected.
79    #[inline]
80    pub fn toggle_visible_hidden(&mut self) {
81        *self = match *self {
82            Visibility::Visible => Visibility::Hidden,
83            Visibility::Hidden => Visibility::Visible,
84            _ => *self,
85        };
86    }
87}
88
89// Allows `&Visibility == Visibility`
90impl PartialEq<Visibility> for &Visibility {
91    #[inline]
92    fn eq(&self, other: &Visibility) -> bool {
93        // Use the base Visibility == Visibility implementation.
94        <Visibility as PartialEq<Visibility>>::eq(*self, other)
95    }
96}
97
98// Allows `Visibility == &Visibility`
99impl PartialEq<&Visibility> for Visibility {
100    #[inline]
101    fn eq(&self, other: &&Visibility) -> bool {
102        // Use the base Visibility == Visibility implementation.
103        <Visibility as PartialEq<Visibility>>::eq(self, *other)
104    }
105}
106
107/// Whether or not an entity is visible in the hierarchy.
108/// This will not be accurate until [`VisibilityPropagate`] runs in the [`PostUpdate`] schedule.
109///
110/// If this is false, then [`ViewVisibility`] should also be false.
111///
112/// [`VisibilityPropagate`]: VisibilitySystems::VisibilityPropagate
113#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)]
114#[reflect(Component, Default, Debug, PartialEq, Clone)]
115#[component(on_insert = validate_parent_has_component::<Self>)]
116pub struct InheritedVisibility(bool);
117
118impl InheritedVisibility {
119    /// An entity that is invisible in the hierarchy.
120    pub const HIDDEN: Self = Self(false);
121    /// An entity that is visible in the hierarchy.
122    pub const VISIBLE: Self = Self(true);
123
124    /// Returns `true` if the entity is visible in the hierarchy.
125    /// Otherwise, returns `false`.
126    #[inline]
127    pub fn get(self) -> bool {
128        self.0
129    }
130}
131
132/// A bucket into which we group entities for the purposes of visibility.
133///
134/// Bevy's various rendering subsystems (3D, 2D, etc.) want to be able to
135/// quickly winnow the set of entities to only those that the subsystem is
136/// tasked with rendering, to avoid spending time examining irrelevant entities.
137/// At the same time, Bevy wants the [`check_visibility`] system to determine
138/// all entities' visibilities at the same time, regardless of what rendering
139/// subsystem is responsible for drawing them. Additionally, your application
140/// may want to add more types of renderable objects that Bevy determines
141/// visibility for just as it does for Bevy's built-in objects.
142///
143/// The solution to this problem is *visibility classes*. A visibility class is
144/// a type, typically the type of a component, that represents the subsystem
145/// that renders it: for example, `Mesh3d`, `Mesh2d`, and `Sprite`. The
146/// [`VisibilityClass`] component stores the visibility class or classes that
147/// the entity belongs to. (Generally, an object will belong to only one
148/// visibility class, but in rare cases it may belong to multiple.)
149///
150/// When adding a new renderable component, you'll typically want to write an
151/// add-component hook that adds the type ID of that component to the
152/// [`VisibilityClass`] array. See `custom_phase_item` for an example.
153///
154/// `VisibilityClass` is automatically added by a hook on the `Mesh3d` and
155/// `Mesh2d` components. To avoid duplicating the `VisibilityClass` and
156/// causing issues when cloning, we use `#[component(clone_behavior=Ignore)]`
157//
158// Note: This can't be a `ComponentId` because the visibility classes are copied
159// into the render world, and component IDs are per-world.
160#[derive(Clone, Component, Default, Reflect, Deref, DerefMut)]
161#[reflect(Component, Default, Clone)]
162#[component(clone_behavior=Ignore)]
163pub struct VisibilityClass(pub SmallVec<[TypeId; 1]>);
164
165/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering.
166///
167/// Each frame, this will be reset to `false` during [`VisibilityPropagate`] systems in [`PostUpdate`].
168/// Later in the frame, systems in [`CheckVisibility`] will mark any visible entities using [`ViewVisibility::set`].
169/// Because of this, values of this type will be marked as changed every frame, even when they do not change.
170///
171/// If you wish to add custom visibility system that sets this value, make sure you add it to the [`CheckVisibility`] set.
172///
173/// [`VisibilityPropagate`]: VisibilitySystems::VisibilityPropagate
174/// [`CheckVisibility`]: VisibilitySystems::CheckVisibility
175#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)]
176#[reflect(Component, Default, Debug, PartialEq, Clone)]
177pub struct ViewVisibility(bool);
178
179impl ViewVisibility {
180    /// An entity that cannot be seen from any views.
181    pub const HIDDEN: Self = Self(false);
182
183    /// Returns `true` if the entity is visible in any view.
184    /// Otherwise, returns `false`.
185    #[inline]
186    pub fn get(self) -> bool {
187        self.0
188    }
189
190    /// Sets the visibility to `true`. This should not be considered reversible for a given frame,
191    /// as this component tracks whether or not the entity visible in _any_ view.
192    ///
193    /// This will be automatically reset to `false` every frame in [`VisibilityPropagate`] and then set
194    /// to the proper value in [`CheckVisibility`].
195    ///
196    /// You should only manually set this if you are defining a custom visibility system,
197    /// in which case the system should be placed in the [`CheckVisibility`] set.
198    /// For normal user-defined entity visibility, see [`Visibility`].
199    ///
200    /// [`VisibilityPropagate`]: VisibilitySystems::VisibilityPropagate
201    /// [`CheckVisibility`]: VisibilitySystems::CheckVisibility
202    #[inline]
203    pub fn set(&mut self) {
204        self.0 = true;
205    }
206}
207
208/// Use this component to opt-out of built-in frustum culling for entities, see
209/// [`Frustum`].
210///
211/// It can be used for example:
212/// - when a [`Mesh`] is updated but its [`Aabb`] is not, which might happen with animations,
213/// - when using some light effects, like wanting a [`Mesh`] out of the [`Frustum`]
214///   to appear in the reflection of a [`Mesh`] within.
215#[derive(Debug, Component, Default, Reflect)]
216#[reflect(Component, Default, Debug)]
217pub struct NoFrustumCulling;
218
219/// Collection of entities visible from the current view.
220///
221/// This component contains all entities which are visible from the currently
222/// rendered view. The collection is updated automatically by the [`VisibilitySystems::CheckVisibility`]
223/// system set. Renderers can use the equivalent `RenderVisibleEntities` to optimize rendering of
224/// a particular view, to prevent drawing items not visible from that view.
225///
226/// This component is intended to be attached to the same entity as the [`Camera`] and
227/// the [`Frustum`] defining the view.
228#[derive(Clone, Component, Default, Debug, Reflect)]
229#[reflect(Component, Default, Debug, Clone)]
230pub struct VisibleEntities {
231    #[reflect(ignore, clone)]
232    pub entities: TypeIdMap<Vec<Entity>>,
233}
234
235impl VisibleEntities {
236    pub fn get(&self, type_id: TypeId) -> &[Entity] {
237        match self.entities.get(&type_id) {
238            Some(entities) => &entities[..],
239            None => &[],
240        }
241    }
242
243    pub fn get_mut(&mut self, type_id: TypeId) -> &mut Vec<Entity> {
244        self.entities.entry(type_id).or_default()
245    }
246
247    pub fn iter(&self, type_id: TypeId) -> impl DoubleEndedIterator<Item = &Entity> {
248        self.get(type_id).iter()
249    }
250
251    pub fn len(&self, type_id: TypeId) -> usize {
252        self.get(type_id).len()
253    }
254
255    pub fn is_empty(&self, type_id: TypeId) -> bool {
256        self.get(type_id).is_empty()
257    }
258
259    pub fn clear(&mut self, type_id: TypeId) {
260        self.get_mut(type_id).clear();
261    }
262
263    pub fn clear_all(&mut self) {
264        // Don't just nuke the hash table; we want to reuse allocations.
265        for entities in self.entities.values_mut() {
266            entities.clear();
267        }
268    }
269
270    pub fn push(&mut self, entity: Entity, type_id: TypeId) {
271        self.get_mut(type_id).push(entity);
272    }
273}
274
275/// Collection of mesh entities visible for 3D lighting.
276///
277/// This component contains all mesh entities visible from the current light view.
278/// The collection is updated automatically by `bevy_pbr::SimulationLightSystems`.
279#[derive(Component, Clone, Debug, Default, Reflect, Deref, DerefMut)]
280#[reflect(Component, Debug, Default, Clone)]
281pub struct VisibleMeshEntities {
282    #[reflect(ignore, clone)]
283    pub entities: Vec<Entity>,
284}
285
286#[derive(Component, Clone, Debug, Default, Reflect)]
287#[reflect(Component, Debug, Default, Clone)]
288pub struct CubemapVisibleEntities {
289    #[reflect(ignore, clone)]
290    data: [VisibleMeshEntities; 6],
291}
292
293impl CubemapVisibleEntities {
294    pub fn get(&self, i: usize) -> &VisibleMeshEntities {
295        &self.data[i]
296    }
297
298    pub fn get_mut(&mut self, i: usize) -> &mut VisibleMeshEntities {
299        &mut self.data[i]
300    }
301
302    pub fn iter(&self) -> impl DoubleEndedIterator<Item = &VisibleMeshEntities> {
303        self.data.iter()
304    }
305
306    pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut VisibleMeshEntities> {
307        self.data.iter_mut()
308    }
309}
310
311#[derive(Component, Clone, Debug, Default, Reflect)]
312#[reflect(Component, Default, Clone)]
313pub struct CascadesVisibleEntities {
314    /// Map of view entity to the visible entities for each cascade frustum.
315    #[reflect(ignore, clone)]
316    pub entities: EntityHashMap<Vec<VisibleMeshEntities>>,
317}
318
319#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
320pub enum VisibilitySystems {
321    /// Label for the [`calculate_bounds`], `calculate_bounds_2d` and `calculate_bounds_text2d` systems,
322    /// calculating and inserting an [`Aabb`] to relevant entities.
323    CalculateBounds,
324    /// Label for [`update_frusta`] in [`CameraProjectionPlugin`](crate::CameraProjectionPlugin).
325    UpdateFrusta,
326    /// Label for the system propagating the [`InheritedVisibility`] in a
327    /// [`ChildOf`] / [`Children`] hierarchy.
328    VisibilityPropagate,
329    /// Label for the [`check_visibility`] system updating [`ViewVisibility`]
330    /// of each entity and the [`VisibleEntities`] of each view.\
331    ///
332    /// System order ambiguities between systems in this set are ignored:
333    /// the order of systems within this set is irrelevant, as [`check_visibility`]
334    /// assumes that its operations are irreversible during the frame.
335    CheckVisibility,
336    /// Label for the `mark_newly_hidden_entities_invisible` system, which sets
337    /// [`ViewVisibility`] to [`ViewVisibility::HIDDEN`] for entities that no
338    /// view has marked as visible.
339    MarkNewlyHiddenEntitiesInvisible,
340}
341
342pub struct VisibilityPlugin;
343
344impl Plugin for VisibilityPlugin {
345    fn build(&self, app: &mut bevy_app::App) {
346        use VisibilitySystems::*;
347
348        app.register_required_components::<Mesh3d, Visibility>()
349            .register_required_components::<Mesh3d, VisibilityClass>()
350            .register_required_components::<Mesh2d, Visibility>()
351            .register_required_components::<Mesh2d, VisibilityClass>()
352            .configure_sets(
353                PostUpdate,
354                (
355                    CalculateBounds
356                        .ambiguous_with(mark_3d_meshes_as_changed_if_their_assets_changed),
357                    UpdateFrusta,
358                    VisibilityPropagate,
359                )
360                    .before(CheckVisibility)
361                    .after(TransformSystems::Propagate),
362            )
363            .configure_sets(
364                PostUpdate,
365                MarkNewlyHiddenEntitiesInvisible.after(CheckVisibility),
366            )
367            .init_resource::<PreviousVisibleEntities>()
368            .add_systems(
369                PostUpdate,
370                (
371                    calculate_bounds.in_set(CalculateBounds),
372                    (visibility_propagate_system, reset_view_visibility)
373                        .in_set(VisibilityPropagate),
374                    check_visibility.in_set(CheckVisibility),
375                    mark_newly_hidden_entities_invisible.in_set(MarkNewlyHiddenEntitiesInvisible),
376                ),
377            );
378        app.world_mut()
379            .register_component_hooks::<Mesh3d>()
380            .on_add(add_visibility_class::<Mesh3d>);
381        app.world_mut()
382            .register_component_hooks::<Mesh2d>()
383            .on_add(add_visibility_class::<Mesh2d>);
384    }
385}
386
387/// Computes and adds an [`Aabb`] component to entities with a
388/// [`Mesh3d`] component and without a [`NoFrustumCulling`] component.
389///
390/// This system is used in system set [`VisibilitySystems::CalculateBounds`].
391pub fn calculate_bounds(
392    mut commands: Commands,
393    meshes: Res<Assets<Mesh>>,
394    without_aabb: Query<(Entity, &Mesh3d), (Without<Aabb>, Without<NoFrustumCulling>)>,
395) {
396    for (entity, mesh_handle) in &without_aabb {
397        if let Some(mesh) = meshes.get(mesh_handle)
398            && let Some(aabb) = mesh.compute_aabb()
399        {
400            commands.entity(entity).try_insert(aabb);
401        }
402    }
403}
404
405/// Updates [`Frustum`].
406///
407/// This system is used in [`CameraProjectionPlugin`](crate::CameraProjectionPlugin).
408pub fn update_frusta(
409    mut views: Query<
410        (&GlobalTransform, &Projection, &mut Frustum),
411        Or<(Changed<GlobalTransform>, Changed<Projection>)>,
412    >,
413) {
414    for (transform, projection, mut frustum) in &mut views {
415        *frustum = projection.compute_frustum(transform);
416    }
417}
418
419fn visibility_propagate_system(
420    changed: Query<
421        (Entity, &Visibility, Option<&ChildOf>, Option<&Children>),
422        (
423            With<InheritedVisibility>,
424            Or<(Changed<Visibility>, Changed<ChildOf>)>,
425        ),
426    >,
427    mut visibility_query: Query<(&Visibility, &mut InheritedVisibility)>,
428    children_query: Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,
429) {
430    for (entity, visibility, child_of, children) in &changed {
431        let is_visible = match visibility {
432            Visibility::Visible => true,
433            Visibility::Hidden => false,
434            // fall back to true if no parent is found or parent lacks components
435            Visibility::Inherited => child_of
436                .and_then(|c| visibility_query.get(c.parent()).ok())
437                .is_none_or(|(_, x)| x.get()),
438        };
439        let (_, mut inherited_visibility) = visibility_query
440            .get_mut(entity)
441            .expect("With<InheritedVisibility> ensures this query will return a value");
442
443        // Only update the visibility if it has changed.
444        // This will also prevent the visibility from propagating multiple times in the same frame
445        // if this entity's visibility has been updated recursively by its parent.
446        if inherited_visibility.get() != is_visible {
447            inherited_visibility.0 = is_visible;
448
449            // Recursively update the visibility of each child.
450            for &child in children.into_iter().flatten() {
451                let _ =
452                    propagate_recursive(is_visible, child, &mut visibility_query, &children_query);
453            }
454        }
455    }
456}
457
458fn propagate_recursive(
459    parent_is_visible: bool,
460    entity: Entity,
461    visibility_query: &mut Query<(&Visibility, &mut InheritedVisibility)>,
462    children_query: &Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,
463    // BLOCKED: https://github.com/rust-lang/rust/issues/31436
464    // We use a result here to use the `?` operator. Ideally we'd use a try block instead
465) -> Result<(), ()> {
466    // Get the visibility components for the current entity.
467    // If the entity does not have the required components, just return early.
468    let (visibility, mut inherited_visibility) = visibility_query.get_mut(entity).map_err(drop)?;
469
470    let is_visible = match visibility {
471        Visibility::Visible => true,
472        Visibility::Hidden => false,
473        Visibility::Inherited => parent_is_visible,
474    };
475
476    // Only update the visibility if it has changed.
477    if inherited_visibility.get() != is_visible {
478        inherited_visibility.0 = is_visible;
479
480        // Recursively update the visibility of each child.
481        for &child in children_query.get(entity).ok().into_iter().flatten() {
482            let _ = propagate_recursive(is_visible, child, visibility_query, children_query);
483        }
484    }
485
486    Ok(())
487}
488
489/// Stores all entities that were visible in the previous frame.
490///
491/// As systems that check visibility judge entities visible, they remove them
492/// from this set. Afterward, the `mark_newly_hidden_entities_invisible` system
493/// runs and marks every mesh still remaining in this set as hidden.
494#[derive(Resource, Default, Deref, DerefMut)]
495pub struct PreviousVisibleEntities(EntityHashSet);
496
497/// Resets the view visibility of every entity.
498/// Entities that are visible will be marked as such later this frame
499/// by a [`VisibilitySystems::CheckVisibility`] system.
500fn reset_view_visibility(
501    mut query: Query<(Entity, &ViewVisibility)>,
502    mut previous_visible_entities: ResMut<PreviousVisibleEntities>,
503) {
504    previous_visible_entities.clear();
505
506    query.iter_mut().for_each(|(entity, view_visibility)| {
507        // Record the entities that were previously visible.
508        if view_visibility.get() {
509            previous_visible_entities.insert(entity);
510        }
511    });
512}
513
514/// System updating the visibility of entities each frame.
515///
516/// The system is part of the [`VisibilitySystems::CheckVisibility`] set. Each
517/// frame, it updates the [`ViewVisibility`] of all entities, and for each view
518/// also compute the [`VisibleEntities`] for that view.
519///
520/// To ensure that an entity is checked for visibility, make sure that it has a
521/// [`VisibilityClass`] component and that that component is nonempty.
522pub fn check_visibility(
523    mut thread_queues: Local<Parallel<TypeIdMap<Vec<Entity>>>>,
524    mut view_query: Query<(
525        Entity,
526        &mut VisibleEntities,
527        &Frustum,
528        Option<&RenderLayers>,
529        &Camera,
530        Has<NoCpuCulling>,
531    )>,
532    mut visible_aabb_query: Query<(
533        Entity,
534        &InheritedVisibility,
535        &mut ViewVisibility,
536        &VisibilityClass,
537        Option<&RenderLayers>,
538        Option<&Aabb>,
539        &GlobalTransform,
540        Has<NoFrustumCulling>,
541        Has<VisibilityRange>,
542    )>,
543    visible_entity_ranges: Option<Res<VisibleEntityRanges>>,
544    mut previous_visible_entities: ResMut<PreviousVisibleEntities>,
545) {
546    let visible_entity_ranges = visible_entity_ranges.as_deref();
547
548    for (view, mut visible_entities, frustum, maybe_view_mask, camera, no_cpu_culling) in
549        &mut view_query
550    {
551        if !camera.is_active {
552            continue;
553        }
554
555        let view_mask = maybe_view_mask.unwrap_or_default();
556
557        visible_aabb_query.par_iter_mut().for_each_init(
558            || thread_queues.borrow_local_mut(),
559            |queue, query_item| {
560                let (
561                    entity,
562                    inherited_visibility,
563                    mut view_visibility,
564                    visibility_class,
565                    maybe_entity_mask,
566                    maybe_model_aabb,
567                    transform,
568                    no_frustum_culling,
569                    has_visibility_range,
570                ) = query_item;
571
572                // Skip computing visibility for entities that are configured to be hidden.
573                // ViewVisibility has already been reset in `reset_view_visibility`.
574                if !inherited_visibility.get() {
575                    return;
576                }
577
578                let entity_mask = maybe_entity_mask.unwrap_or_default();
579                if !view_mask.intersects(entity_mask) {
580                    return;
581                }
582
583                // If outside of the visibility range, cull.
584                if has_visibility_range
585                    && visible_entity_ranges.is_some_and(|visible_entity_ranges| {
586                        !visible_entity_ranges.entity_is_in_range_of_view(entity, view)
587                    })
588                {
589                    return;
590                }
591
592                // If we have an aabb, do frustum culling
593                if !no_frustum_culling
594                    && !no_cpu_culling
595                    && let Some(model_aabb) = maybe_model_aabb
596                {
597                    let world_from_local = transform.affine();
598                    let model_sphere = Sphere {
599                        center: world_from_local.transform_point3a(model_aabb.center),
600                        radius: transform.radius_vec3a(model_aabb.half_extents),
601                    };
602                    // Do quick sphere-based frustum culling
603                    if !frustum.intersects_sphere(&model_sphere, false) {
604                        return;
605                    }
606                    // Do aabb-based frustum culling
607                    if !frustum.intersects_obb(model_aabb, &world_from_local, true, false) {
608                        return;
609                    }
610                }
611
612                // Make sure we don't trigger changed notifications
613                // unnecessarily by checking whether the flag is set before
614                // setting it.
615                if !**view_visibility {
616                    view_visibility.set();
617                }
618
619                // Add the entity to the queue for all visibility classes the
620                // entity is in.
621                for visibility_class_id in visibility_class.iter() {
622                    queue.entry(*visibility_class_id).or_default().push(entity);
623                }
624            },
625        );
626
627        visible_entities.clear_all();
628
629        // Drain all the thread queues into the `visible_entities` list.
630        for class_queues in thread_queues.iter_mut() {
631            for (class, entities) in class_queues {
632                let visible_entities_for_class = visible_entities.get_mut(*class);
633                for entity in entities.drain(..) {
634                    // As we mark entities as visible, we remove them from the
635                    // `previous_visible_entities` list. At the end, all of the
636                    // entities remaining in `previous_visible_entities` will be
637                    // entities that were visible last frame but are no longer
638                    // visible this frame.
639                    previous_visible_entities.remove(&entity);
640
641                    visible_entities_for_class.push(entity);
642                }
643            }
644        }
645    }
646}
647
648/// Marks any entities that weren't judged visible this frame as invisible.
649///
650/// As visibility-determining systems run, they remove entities that they judge
651/// visible from [`PreviousVisibleEntities`]. At the end of visibility
652/// determination, all entities that remain in [`PreviousVisibleEntities`] must
653/// be invisible. This system goes through those entities and marks them newly
654/// invisible (which sets the change flag for them).
655fn mark_newly_hidden_entities_invisible(
656    mut view_visibilities: Query<&mut ViewVisibility>,
657    mut previous_visible_entities: ResMut<PreviousVisibleEntities>,
658) {
659    // Whatever previous visible entities are left are entities that were
660    // visible last frame but just became invisible.
661    for entity in previous_visible_entities.drain() {
662        if let Ok(mut view_visibility) = view_visibilities.get_mut(entity) {
663            *view_visibility = ViewVisibility::HIDDEN;
664        }
665    }
666}
667
668/// A generic component add hook that automatically adds the appropriate
669/// [`VisibilityClass`] to an entity.
670///
671/// This can be handy when creating custom renderable components. To use this
672/// hook, add it to your renderable component like this:
673///
674/// ```ignore
675/// #[derive(Component)]
676/// #[component(on_add = add_visibility_class::<MyComponent>)]
677/// struct MyComponent {
678///     ...
679/// }
680/// ```
681pub fn add_visibility_class<C>(
682    mut world: DeferredWorld<'_>,
683    HookContext { entity, .. }: HookContext,
684) where
685    C: 'static,
686{
687    if let Some(mut visibility_class) = world.get_mut::<VisibilityClass>(entity) {
688        visibility_class.push(TypeId::of::<C>());
689    }
690}
691
692#[cfg(test)]
693mod test {
694    use super::*;
695    use bevy_app::prelude::*;
696
697    #[test]
698    fn visibility_propagation() {
699        let mut app = App::new();
700        app.add_systems(Update, visibility_propagate_system);
701
702        let root1 = app.world_mut().spawn(Visibility::Hidden).id();
703        let root1_child1 = app.world_mut().spawn(Visibility::default()).id();
704        let root1_child2 = app.world_mut().spawn(Visibility::Hidden).id();
705        let root1_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
706        let root1_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
707
708        app.world_mut()
709            .entity_mut(root1)
710            .add_children(&[root1_child1, root1_child2]);
711        app.world_mut()
712            .entity_mut(root1_child1)
713            .add_children(&[root1_child1_grandchild1]);
714        app.world_mut()
715            .entity_mut(root1_child2)
716            .add_children(&[root1_child2_grandchild1]);
717
718        let root2 = app.world_mut().spawn(Visibility::default()).id();
719        let root2_child1 = app.world_mut().spawn(Visibility::default()).id();
720        let root2_child2 = app.world_mut().spawn(Visibility::Hidden).id();
721        let root2_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
722        let root2_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
723
724        app.world_mut()
725            .entity_mut(root2)
726            .add_children(&[root2_child1, root2_child2]);
727        app.world_mut()
728            .entity_mut(root2_child1)
729            .add_children(&[root2_child1_grandchild1]);
730        app.world_mut()
731            .entity_mut(root2_child2)
732            .add_children(&[root2_child2_grandchild1]);
733
734        app.update();
735
736        let is_visible = |e: Entity| {
737            app.world()
738                .entity(e)
739                .get::<InheritedVisibility>()
740                .unwrap()
741                .get()
742        };
743        assert!(
744            !is_visible(root1),
745            "invisibility propagates down tree from root"
746        );
747        assert!(
748            !is_visible(root1_child1),
749            "invisibility propagates down tree from root"
750        );
751        assert!(
752            !is_visible(root1_child2),
753            "invisibility propagates down tree from root"
754        );
755        assert!(
756            !is_visible(root1_child1_grandchild1),
757            "invisibility propagates down tree from root"
758        );
759        assert!(
760            !is_visible(root1_child2_grandchild1),
761            "invisibility propagates down tree from root"
762        );
763
764        assert!(
765            is_visible(root2),
766            "visibility propagates down tree from root"
767        );
768        assert!(
769            is_visible(root2_child1),
770            "visibility propagates down tree from root"
771        );
772        assert!(
773            !is_visible(root2_child2),
774            "visibility propagates down tree from root, but local invisibility is preserved"
775        );
776        assert!(
777            is_visible(root2_child1_grandchild1),
778            "visibility propagates down tree from root"
779        );
780        assert!(
781            !is_visible(root2_child2_grandchild1),
782            "child's invisibility propagates down to grandchild"
783        );
784    }
785
786    #[test]
787    fn test_visibility_propagation_on_parent_change() {
788        // Setup the world and schedule
789        let mut app = App::new();
790
791        app.add_systems(Update, visibility_propagate_system);
792
793        // Create entities with visibility and hierarchy
794        let parent1 = app.world_mut().spawn((Visibility::Hidden,)).id();
795        let parent2 = app.world_mut().spawn((Visibility::Visible,)).id();
796        let child1 = app.world_mut().spawn((Visibility::Inherited,)).id();
797        let child2 = app.world_mut().spawn((Visibility::Inherited,)).id();
798
799        // Build hierarchy
800        app.world_mut()
801            .entity_mut(parent1)
802            .add_children(&[child1, child2]);
803
804        // Run the system initially to set up visibility
805        app.update();
806
807        // Change parent visibility to Hidden
808        app.world_mut()
809            .entity_mut(parent2)
810            .insert(Visibility::Visible);
811        // Simulate a change in the parent component
812        app.world_mut().entity_mut(child2).insert(ChildOf(parent2)); // example of changing parent
813
814        // Run the system again to propagate changes
815        app.update();
816
817        let is_visible = |e: Entity| {
818            app.world()
819                .entity(e)
820                .get::<InheritedVisibility>()
821                .unwrap()
822                .get()
823        };
824
825        // Retrieve and assert visibility
826
827        assert!(
828            !is_visible(child1),
829            "Child1 should inherit visibility from parent"
830        );
831
832        assert!(
833            is_visible(child2),
834            "Child2 should inherit visibility from parent"
835        );
836    }
837
838    #[test]
839    fn visibility_propagation_unconditional_visible() {
840        use Visibility::{Hidden, Inherited, Visible};
841
842        let mut app = App::new();
843        app.add_systems(Update, visibility_propagate_system);
844
845        let root1 = app.world_mut().spawn(Visible).id();
846        let root1_child1 = app.world_mut().spawn(Inherited).id();
847        let root1_child2 = app.world_mut().spawn(Hidden).id();
848        let root1_child1_grandchild1 = app.world_mut().spawn(Visible).id();
849        let root1_child2_grandchild1 = app.world_mut().spawn(Visible).id();
850
851        let root2 = app.world_mut().spawn(Inherited).id();
852        let root3 = app.world_mut().spawn(Hidden).id();
853
854        app.world_mut()
855            .entity_mut(root1)
856            .add_children(&[root1_child1, root1_child2]);
857        app.world_mut()
858            .entity_mut(root1_child1)
859            .add_children(&[root1_child1_grandchild1]);
860        app.world_mut()
861            .entity_mut(root1_child2)
862            .add_children(&[root1_child2_grandchild1]);
863
864        app.update();
865
866        let is_visible = |e: Entity| {
867            app.world()
868                .entity(e)
869                .get::<InheritedVisibility>()
870                .unwrap()
871                .get()
872        };
873        assert!(
874            is_visible(root1),
875            "an unconditionally visible root is visible"
876        );
877        assert!(
878            is_visible(root1_child1),
879            "an inheriting child of an unconditionally visible parent is visible"
880        );
881        assert!(
882            !is_visible(root1_child2),
883            "a hidden child on an unconditionally visible parent is hidden"
884        );
885        assert!(
886            is_visible(root1_child1_grandchild1),
887            "an unconditionally visible child of an inheriting parent is visible"
888        );
889        assert!(
890            is_visible(root1_child2_grandchild1),
891            "an unconditionally visible child of a hidden parent is visible"
892        );
893        assert!(is_visible(root2), "an inheriting root is visible");
894        assert!(!is_visible(root3), "a hidden root is hidden");
895    }
896
897    #[test]
898    fn visibility_propagation_change_detection() {
899        let mut world = World::new();
900        let mut schedule = Schedule::default();
901        schedule.add_systems(visibility_propagate_system);
902
903        // Set up an entity hierarchy.
904
905        let id1 = world.spawn(Visibility::default()).id();
906
907        let id2 = world.spawn(Visibility::default()).id();
908        world.entity_mut(id1).add_children(&[id2]);
909
910        let id3 = world.spawn(Visibility::Hidden).id();
911        world.entity_mut(id2).add_children(&[id3]);
912
913        let id4 = world.spawn(Visibility::default()).id();
914        world.entity_mut(id3).add_children(&[id4]);
915
916        // Test the hierarchy.
917
918        // Make sure the hierarchy is up-to-date.
919        schedule.run(&mut world);
920        world.clear_trackers();
921
922        let mut q = world.query::<Ref<InheritedVisibility>>();
923
924        assert!(!q.get(&world, id1).unwrap().is_changed());
925        assert!(!q.get(&world, id2).unwrap().is_changed());
926        assert!(!q.get(&world, id3).unwrap().is_changed());
927        assert!(!q.get(&world, id4).unwrap().is_changed());
928
929        world.clear_trackers();
930        world.entity_mut(id1).insert(Visibility::Hidden);
931        schedule.run(&mut world);
932
933        assert!(q.get(&world, id1).unwrap().is_changed());
934        assert!(q.get(&world, id2).unwrap().is_changed());
935        assert!(!q.get(&world, id3).unwrap().is_changed());
936        assert!(!q.get(&world, id4).unwrap().is_changed());
937
938        world.clear_trackers();
939        schedule.run(&mut world);
940
941        assert!(!q.get(&world, id1).unwrap().is_changed());
942        assert!(!q.get(&world, id2).unwrap().is_changed());
943        assert!(!q.get(&world, id3).unwrap().is_changed());
944        assert!(!q.get(&world, id4).unwrap().is_changed());
945
946        world.clear_trackers();
947        world.entity_mut(id3).insert(Visibility::Inherited);
948        schedule.run(&mut world);
949
950        assert!(!q.get(&world, id1).unwrap().is_changed());
951        assert!(!q.get(&world, id2).unwrap().is_changed());
952        assert!(!q.get(&world, id3).unwrap().is_changed());
953        assert!(!q.get(&world, id4).unwrap().is_changed());
954
955        world.clear_trackers();
956        world.entity_mut(id2).insert(Visibility::Visible);
957        schedule.run(&mut world);
958
959        assert!(!q.get(&world, id1).unwrap().is_changed());
960        assert!(q.get(&world, id2).unwrap().is_changed());
961        assert!(q.get(&world, id3).unwrap().is_changed());
962        assert!(q.get(&world, id4).unwrap().is_changed());
963
964        world.clear_trackers();
965        schedule.run(&mut world);
966
967        assert!(!q.get(&world, id1).unwrap().is_changed());
968        assert!(!q.get(&world, id2).unwrap().is_changed());
969        assert!(!q.get(&world, id3).unwrap().is_changed());
970        assert!(!q.get(&world, id4).unwrap().is_changed());
971    }
972
973    #[test]
974    fn visibility_propagation_with_invalid_parent() {
975        let mut world = World::new();
976        let mut schedule = Schedule::default();
977        schedule.add_systems(visibility_propagate_system);
978
979        let parent = world.spawn(()).id();
980        let child = world.spawn(Visibility::default()).id();
981        world.entity_mut(parent).add_children(&[child]);
982
983        schedule.run(&mut world);
984        world.clear_trackers();
985
986        let child_visible = world.entity(child).get::<InheritedVisibility>().unwrap().0;
987        // defaults to same behavior of parent not found: visible = true
988        assert!(child_visible);
989    }
990
991    #[test]
992    fn ensure_visibility_enum_size() {
993        assert_eq!(1, size_of::<Visibility>());
994        assert_eq!(1, size_of::<Option<Visibility>>());
995    }
996
997    #[derive(Component, Default, Clone, Reflect)]
998    #[require(VisibilityClass)]
999    #[reflect(Component, Default, Clone)]
1000    #[component(on_add = add_visibility_class::<Self>)]
1001    struct TestVisibilityClassHook;
1002
1003    #[test]
1004    fn test_add_visibility_class_hook() {
1005        let mut world = World::new();
1006        let entity = world.spawn(TestVisibilityClassHook).id();
1007        let entity_clone = world.spawn_empty().id();
1008        world
1009            .entity_mut(entity)
1010            .clone_with_opt_out(entity_clone, |_| {});
1011
1012        let entity_visibility_class = world.entity(entity).get::<VisibilityClass>().unwrap();
1013        assert_eq!(entity_visibility_class.len(), 1);
1014
1015        let entity_clone_visibility_class =
1016            world.entity(entity_clone).get::<VisibilityClass>().unwrap();
1017        assert_eq!(entity_clone_visibility_class.len(), 1);
1018    }
1019}