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