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