bevy_camera/visibility/
mod.rs

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