Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_camera/src/visibility/mod.rs
9297 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
&GlobalTransform,
661
Has<NoFrustumCulling>,
662
Has<VisibilityRange>,
663
Has<NoCpuCulling>,
664
)>,
665
visible_entity_ranges: Option<Res<VisibleEntityRanges>>,
666
) {
667
let visible_entity_ranges = visible_entity_ranges.as_deref();
668
669
for (view, mut visible_entities, frustum, maybe_view_mask, camera, no_cpu_culling_camera) in
670
&mut view_query
671
{
672
if !camera.is_active {
673
continue;
674
}
675
676
let view_mask = maybe_view_mask.unwrap_or_default();
677
678
visible_aabb_query.par_iter_mut().for_each_init(
679
|| thread_queues.borrow_local_mut(),
680
|queue, query_item| {
681
let (
682
entity,
683
inherited_visibility,
684
mut view_visibility,
685
visibility_class,
686
maybe_entity_mask,
687
maybe_model_aabb,
688
transform,
689
no_frustum_culling,
690
has_visibility_range,
691
no_cpu_culling_entity,
692
) = query_item;
693
694
// Skip computing visibility for entities that are configured to be hidden.
695
// ViewVisibility has already been reset in `reset_view_visibility`.
696
if !inherited_visibility.get() {
697
return;
698
}
699
700
let entity_mask = maybe_entity_mask.unwrap_or_default();
701
if !view_mask.intersects(entity_mask) {
702
return;
703
}
704
705
// If outside of the visibility range, cull.
706
if has_visibility_range
707
&& visible_entity_ranges.is_some_and(|visible_entity_ranges| {
708
!visible_entity_ranges.entity_is_in_range_of_view(entity, view)
709
})
710
{
711
return;
712
}
713
714
// If we have an aabb, do frustum culling
715
if !no_frustum_culling
716
&& !no_cpu_culling_camera
717
&& !no_cpu_culling_entity
718
&& let Some(model_aabb) = maybe_model_aabb
719
{
720
let world_from_local = transform.affine();
721
let model_sphere = Sphere {
722
center: world_from_local.transform_point3a(model_aabb.center),
723
radius: transform.radius_vec3a(model_aabb.half_extents),
724
};
725
// Do quick sphere-based frustum culling
726
if !frustum.intersects_sphere(&model_sphere, false) {
727
return;
728
}
729
// Do aabb-based frustum culling
730
if !frustum.intersects_obb(model_aabb, &world_from_local, true, false) {
731
return;
732
}
733
}
734
735
view_visibility.set_visible();
736
737
// The visibility class may be None here because AABB gizmos can be enabled via
738
// config without a renderable component being added to the entity. This workaround
739
// allows view visibility to be set for entities without a renderable component, but
740
// still need to render gizmos.
741
if let Some(visibility_class) = visibility_class {
742
// Add the entity to the queue for all visibility classes the entity is in.
743
for visibility_class_id in visibility_class.iter() {
744
queue.entry(*visibility_class_id).or_default().push(entity);
745
}
746
}
747
},
748
);
749
750
visible_entities.clear_all();
751
752
// Drain all the thread queues into the `visible_entities` list.
753
for class_queues in thread_queues.iter_mut() {
754
for (class, entities) in class_queues {
755
visible_entities.get_mut(*class).append(entities);
756
}
757
}
758
}
759
}
760
761
/// The last step in the visibility pipeline. Looks at entities that were visible last frame but not
762
/// marked as visible this frame and marks them as hidden by setting the [`ViewVisibility`]. This
763
/// process is needed to ensure we only trigger change detection on [`ViewVisibility`] when needed.
764
fn mark_newly_hidden_entities_invisible(mut view_visibilities: Query<&mut ViewVisibility>) {
765
view_visibilities
766
.par_iter_mut()
767
.for_each(|mut view_visibility| {
768
if view_visibility.as_ref().was_visible_now_hidden() {
769
*view_visibility = ViewVisibility::HIDDEN;
770
}
771
});
772
}
773
774
/// A generic component add hook that automatically adds the appropriate
775
/// [`VisibilityClass`] to an entity.
776
///
777
/// This can be handy when creating custom renderable components. To use this
778
/// hook, add it to your renderable component like this:
779
///
780
/// ```ignore
781
/// #[derive(Component)]
782
/// #[component(on_add = add_visibility_class::<MyComponent>)]
783
/// struct MyComponent {
784
/// ...
785
/// }
786
/// ```
787
pub fn add_visibility_class<C>(
788
mut world: DeferredWorld<'_>,
789
HookContext { entity, .. }: HookContext,
790
) where
791
C: 'static,
792
{
793
if let Some(mut visibility_class) = world.get_mut::<VisibilityClass>(entity) {
794
visibility_class.push(TypeId::of::<C>());
795
}
796
}
797
798
#[cfg(test)]
799
mod test {
800
use super::*;
801
use bevy_app::prelude::*;
802
803
#[test]
804
fn visibility_propagation() {
805
let mut app = App::new();
806
app.add_systems(Update, visibility_propagate_system);
807
808
let root1 = app.world_mut().spawn(Visibility::Hidden).id();
809
let root1_child1 = app.world_mut().spawn(Visibility::default()).id();
810
let root1_child2 = app.world_mut().spawn(Visibility::Hidden).id();
811
let root1_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
812
let root1_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
813
814
app.world_mut()
815
.entity_mut(root1)
816
.add_children(&[root1_child1, root1_child2]);
817
app.world_mut()
818
.entity_mut(root1_child1)
819
.add_children(&[root1_child1_grandchild1]);
820
app.world_mut()
821
.entity_mut(root1_child2)
822
.add_children(&[root1_child2_grandchild1]);
823
824
let root2 = app.world_mut().spawn(Visibility::default()).id();
825
let root2_child1 = app.world_mut().spawn(Visibility::default()).id();
826
let root2_child2 = app.world_mut().spawn(Visibility::Hidden).id();
827
let root2_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
828
let root2_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
829
830
app.world_mut()
831
.entity_mut(root2)
832
.add_children(&[root2_child1, root2_child2]);
833
app.world_mut()
834
.entity_mut(root2_child1)
835
.add_children(&[root2_child1_grandchild1]);
836
app.world_mut()
837
.entity_mut(root2_child2)
838
.add_children(&[root2_child2_grandchild1]);
839
840
app.update();
841
842
let is_visible = |e: Entity| {
843
app.world()
844
.entity(e)
845
.get::<InheritedVisibility>()
846
.unwrap()
847
.get()
848
};
849
assert!(
850
!is_visible(root1),
851
"invisibility propagates down tree from root"
852
);
853
assert!(
854
!is_visible(root1_child1),
855
"invisibility propagates down tree from root"
856
);
857
assert!(
858
!is_visible(root1_child2),
859
"invisibility propagates down tree from root"
860
);
861
assert!(
862
!is_visible(root1_child1_grandchild1),
863
"invisibility propagates down tree from root"
864
);
865
assert!(
866
!is_visible(root1_child2_grandchild1),
867
"invisibility propagates down tree from root"
868
);
869
870
assert!(
871
is_visible(root2),
872
"visibility propagates down tree from root"
873
);
874
assert!(
875
is_visible(root2_child1),
876
"visibility propagates down tree from root"
877
);
878
assert!(
879
!is_visible(root2_child2),
880
"visibility propagates down tree from root, but local invisibility is preserved"
881
);
882
assert!(
883
is_visible(root2_child1_grandchild1),
884
"visibility propagates down tree from root"
885
);
886
assert!(
887
!is_visible(root2_child2_grandchild1),
888
"child's invisibility propagates down to grandchild"
889
);
890
}
891
892
#[test]
893
fn test_visibility_propagation_on_parent_change() {
894
// Setup the world and schedule
895
let mut app = App::new();
896
897
app.add_systems(Update, visibility_propagate_system);
898
899
// Create entities with visibility and hierarchy
900
let parent1 = app.world_mut().spawn((Visibility::Hidden,)).id();
901
let parent2 = app.world_mut().spawn((Visibility::Visible,)).id();
902
let child1 = app.world_mut().spawn((Visibility::Inherited,)).id();
903
let child2 = app.world_mut().spawn((Visibility::Inherited,)).id();
904
905
// Build hierarchy
906
app.world_mut()
907
.entity_mut(parent1)
908
.add_children(&[child1, child2]);
909
910
// Run the system initially to set up visibility
911
app.update();
912
913
// Change parent visibility to Hidden
914
app.world_mut()
915
.entity_mut(parent2)
916
.insert(Visibility::Visible);
917
// Simulate a change in the parent component
918
app.world_mut().entity_mut(child2).insert(ChildOf(parent2)); // example of changing parent
919
920
// Run the system again to propagate changes
921
app.update();
922
923
let is_visible = |e: Entity| {
924
app.world()
925
.entity(e)
926
.get::<InheritedVisibility>()
927
.unwrap()
928
.get()
929
};
930
931
// Retrieve and assert visibility
932
933
assert!(
934
!is_visible(child1),
935
"Child1 should inherit visibility from parent"
936
);
937
938
assert!(
939
is_visible(child2),
940
"Child2 should inherit visibility from parent"
941
);
942
}
943
944
#[test]
945
fn visibility_propagation_unconditional_visible() {
946
use Visibility::{Hidden, Inherited, Visible};
947
948
let mut app = App::new();
949
app.add_systems(Update, visibility_propagate_system);
950
951
let root1 = app.world_mut().spawn(Visible).id();
952
let root1_child1 = app.world_mut().spawn(Inherited).id();
953
let root1_child2 = app.world_mut().spawn(Hidden).id();
954
let root1_child1_grandchild1 = app.world_mut().spawn(Visible).id();
955
let root1_child2_grandchild1 = app.world_mut().spawn(Visible).id();
956
957
let root2 = app.world_mut().spawn(Inherited).id();
958
let root3 = app.world_mut().spawn(Hidden).id();
959
960
app.world_mut()
961
.entity_mut(root1)
962
.add_children(&[root1_child1, root1_child2]);
963
app.world_mut()
964
.entity_mut(root1_child1)
965
.add_children(&[root1_child1_grandchild1]);
966
app.world_mut()
967
.entity_mut(root1_child2)
968
.add_children(&[root1_child2_grandchild1]);
969
970
app.update();
971
972
let is_visible = |e: Entity| {
973
app.world()
974
.entity(e)
975
.get::<InheritedVisibility>()
976
.unwrap()
977
.get()
978
};
979
assert!(
980
is_visible(root1),
981
"an unconditionally visible root is visible"
982
);
983
assert!(
984
is_visible(root1_child1),
985
"an inheriting child of an unconditionally visible parent is visible"
986
);
987
assert!(
988
!is_visible(root1_child2),
989
"a hidden child on an unconditionally visible parent is hidden"
990
);
991
assert!(
992
is_visible(root1_child1_grandchild1),
993
"an unconditionally visible child of an inheriting parent is visible"
994
);
995
assert!(
996
is_visible(root1_child2_grandchild1),
997
"an unconditionally visible child of a hidden parent is visible"
998
);
999
assert!(is_visible(root2), "an inheriting root is visible");
1000
assert!(!is_visible(root3), "a hidden root is hidden");
1001
}
1002
1003
#[test]
1004
fn visibility_propagation_change_detection() {
1005
let mut world = World::new();
1006
let mut schedule = Schedule::default();
1007
schedule.add_systems(visibility_propagate_system);
1008
1009
// Set up an entity hierarchy.
1010
1011
let id1 = world.spawn(Visibility::default()).id();
1012
1013
let id2 = world.spawn(Visibility::default()).id();
1014
world.entity_mut(id1).add_children(&[id2]);
1015
1016
let id3 = world.spawn(Visibility::Hidden).id();
1017
world.entity_mut(id2).add_children(&[id3]);
1018
1019
let id4 = world.spawn(Visibility::default()).id();
1020
world.entity_mut(id3).add_children(&[id4]);
1021
1022
// Test the hierarchy.
1023
1024
// Make sure the hierarchy is up-to-date.
1025
schedule.run(&mut world);
1026
world.clear_trackers();
1027
1028
let mut q = world.query::<Ref<InheritedVisibility>>();
1029
1030
assert!(!q.get(&world, id1).unwrap().is_changed());
1031
assert!(!q.get(&world, id2).unwrap().is_changed());
1032
assert!(!q.get(&world, id3).unwrap().is_changed());
1033
assert!(!q.get(&world, id4).unwrap().is_changed());
1034
1035
world.clear_trackers();
1036
world.entity_mut(id1).insert(Visibility::Hidden);
1037
schedule.run(&mut world);
1038
1039
assert!(q.get(&world, id1).unwrap().is_changed());
1040
assert!(q.get(&world, id2).unwrap().is_changed());
1041
assert!(!q.get(&world, id3).unwrap().is_changed());
1042
assert!(!q.get(&world, id4).unwrap().is_changed());
1043
1044
world.clear_trackers();
1045
schedule.run(&mut world);
1046
1047
assert!(!q.get(&world, id1).unwrap().is_changed());
1048
assert!(!q.get(&world, id2).unwrap().is_changed());
1049
assert!(!q.get(&world, id3).unwrap().is_changed());
1050
assert!(!q.get(&world, id4).unwrap().is_changed());
1051
1052
world.clear_trackers();
1053
world.entity_mut(id3).insert(Visibility::Inherited);
1054
schedule.run(&mut world);
1055
1056
assert!(!q.get(&world, id1).unwrap().is_changed());
1057
assert!(!q.get(&world, id2).unwrap().is_changed());
1058
assert!(!q.get(&world, id3).unwrap().is_changed());
1059
assert!(!q.get(&world, id4).unwrap().is_changed());
1060
1061
world.clear_trackers();
1062
world.entity_mut(id2).insert(Visibility::Visible);
1063
schedule.run(&mut world);
1064
1065
assert!(!q.get(&world, id1).unwrap().is_changed());
1066
assert!(q.get(&world, id2).unwrap().is_changed());
1067
assert!(q.get(&world, id3).unwrap().is_changed());
1068
assert!(q.get(&world, id4).unwrap().is_changed());
1069
1070
world.clear_trackers();
1071
schedule.run(&mut world);
1072
1073
assert!(!q.get(&world, id1).unwrap().is_changed());
1074
assert!(!q.get(&world, id2).unwrap().is_changed());
1075
assert!(!q.get(&world, id3).unwrap().is_changed());
1076
assert!(!q.get(&world, id4).unwrap().is_changed());
1077
}
1078
1079
#[test]
1080
fn visibility_propagation_with_invalid_parent() {
1081
let mut world = World::new();
1082
let mut schedule = Schedule::default();
1083
schedule.add_systems(visibility_propagate_system);
1084
1085
let parent = world.spawn(()).id();
1086
let child = world.spawn(Visibility::default()).id();
1087
world.entity_mut(parent).add_children(&[child]);
1088
1089
schedule.run(&mut world);
1090
world.clear_trackers();
1091
1092
let child_visible = world.entity(child).get::<InheritedVisibility>().unwrap().0;
1093
// defaults to same behavior of parent not found: visible = true
1094
assert!(child_visible);
1095
}
1096
1097
#[test]
1098
fn ensure_visibility_enum_size() {
1099
assert_eq!(1, size_of::<Visibility>());
1100
assert_eq!(1, size_of::<Option<Visibility>>());
1101
}
1102
1103
#[derive(Component, Default, Clone, Reflect)]
1104
#[require(VisibilityClass)]
1105
#[reflect(Component, Default, Clone)]
1106
#[component(on_add = add_visibility_class::<Self>)]
1107
struct TestVisibilityClassHook;
1108
1109
#[test]
1110
fn test_add_visibility_class_hook() {
1111
let mut world = World::new();
1112
let entity = world.spawn(TestVisibilityClassHook).id();
1113
let entity_clone = world.spawn_empty().id();
1114
world
1115
.entity_mut(entity)
1116
.clone_with_opt_out(entity_clone, |_| {});
1117
1118
let entity_visibility_class = world.entity(entity).get::<VisibilityClass>().unwrap();
1119
assert_eq!(entity_visibility_class.len(), 1);
1120
1121
let entity_clone_visibility_class =
1122
world.entity(entity_clone).get::<VisibilityClass>().unwrap();
1123
assert_eq!(entity_clone_visibility_class.len(), 1);
1124
}
1125
1126
#[test]
1127
fn view_visibility_lifecycle() {
1128
let mut app = App::new();
1129
app.add_plugins((
1130
TaskPoolPlugin::default(),
1131
bevy_asset::AssetPlugin::default(),
1132
bevy_mesh::MeshPlugin,
1133
bevy_transform::TransformPlugin,
1134
VisibilityPlugin,
1135
));
1136
1137
#[derive(Resource, Default)]
1138
struct ManualMark(bool);
1139
#[derive(Resource, Default)]
1140
struct ObservedChanged(bool);
1141
app.init_resource::<ManualMark>();
1142
app.init_resource::<ObservedChanged>();
1143
1144
app.add_systems(
1145
PostUpdate,
1146
(
1147
(|mut q: Query<&mut ViewVisibility>, mark: Res<ManualMark>| {
1148
if mark.0 {
1149
for mut v in &mut q {
1150
v.set_visible();
1151
}
1152
}
1153
})
1154
.in_set(VisibilitySystems::CheckVisibility),
1155
(|q: Query<(), Changed<ViewVisibility>>, mut observed: ResMut<ObservedChanged>| {
1156
if !q.is_empty() {
1157
observed.0 = true;
1158
}
1159
})
1160
.after(VisibilitySystems::MarkNewlyHiddenEntitiesInvisible),
1161
),
1162
);
1163
1164
let entity = app.world_mut().spawn(ViewVisibility::HIDDEN).id();
1165
1166
// Advance system ticks and clear spawn change
1167
app.update();
1168
app.world_mut().resource_mut::<ObservedChanged>().0 = false;
1169
1170
// Frame 1: do nothing
1171
app.update();
1172
{
1173
assert!(
1174
!app.world()
1175
.entity(entity)
1176
.get::<ViewVisibility>()
1177
.unwrap()
1178
.get(),
1179
"Frame 1: should be hidden"
1180
);
1181
assert!(
1182
!app.world().resource::<ObservedChanged>().0,
1183
"Frame 1: should not be changed"
1184
);
1185
}
1186
1187
// Frame 2: set entity as visible
1188
app.world_mut().resource_mut::<ManualMark>().0 = true;
1189
app.update();
1190
{
1191
assert!(
1192
app.world()
1193
.entity(entity)
1194
.get::<ViewVisibility>()
1195
.unwrap()
1196
.get(),
1197
"Frame 2: should be visible"
1198
);
1199
assert!(
1200
app.world().resource::<ObservedChanged>().0,
1201
"Frame 2: should be changed"
1202
);
1203
}
1204
1205
// Frame 3: still visible
1206
app.world_mut().resource_mut::<ManualMark>().0 = true;
1207
app.world_mut().resource_mut::<ObservedChanged>().0 = false;
1208
app.update();
1209
{
1210
assert!(
1211
app.world()
1212
.entity(entity)
1213
.get::<ViewVisibility>()
1214
.unwrap()
1215
.get(),
1216
"Frame 3: should be visible"
1217
);
1218
assert!(
1219
!app.world().resource::<ObservedChanged>().0,
1220
"Frame 3: should NOT be changed"
1221
);
1222
}
1223
1224
// Frame 4: do nothing (becomes hidden)
1225
app.world_mut().resource_mut::<ManualMark>().0 = false;
1226
app.world_mut().resource_mut::<ObservedChanged>().0 = false;
1227
app.update();
1228
{
1229
assert!(
1230
!app.world()
1231
.entity(entity)
1232
.get::<ViewVisibility>()
1233
.unwrap()
1234
.get(),
1235
"Frame 4: should be hidden"
1236
);
1237
assert!(
1238
app.world().resource::<ObservedChanged>().0,
1239
"Frame 4: should be changed"
1240
);
1241
}
1242
1243
// Frame 5: do nothing
1244
app.world_mut().resource_mut::<ManualMark>().0 = false;
1245
app.world_mut().resource_mut::<ObservedChanged>().0 = false;
1246
app.update();
1247
{
1248
assert!(
1249
!app.world()
1250
.entity(entity)
1251
.get::<ViewVisibility>()
1252
.unwrap()
1253
.get(),
1254
"Frame 5: should be hidden"
1255
);
1256
assert!(
1257
!app.world().resource::<ObservedChanged>().0,
1258
"Frame 5: should NOT be changed"
1259
);
1260
}
1261
}
1262
}
1263
1264