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