bevy_render/view/visibility/
mod.rs

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