1#![expect(deprecated)]
2
3mod range;
4mod render_layers;
5
6use core::any::TypeId;
7
8pub use range::*;
9pub use render_layers::*;
10
11use bevy_app::{Plugin, PostUpdate};
12use bevy_asset::Assets;
13use bevy_derive::Deref;
14use bevy_ecs::{prelude::*, query::QueryFilter};
15use bevy_hierarchy::{Children, Parent};
16use bevy_reflect::{std_traits::ReflectDefault, Reflect};
17use bevy_transform::{components::GlobalTransform, TransformSystem};
18use bevy_utils::{Parallel, TypeIdMap};
19
20use super::NoCpuCulling;
21use crate::sync_world::MainEntity;
22use crate::{
23 camera::{Camera, CameraProjection},
24 mesh::{Mesh, Mesh3d, MeshAabb},
25 primitives::{Aabb, Frustum, Sphere},
26};
27
28#[derive(Component, Clone, Copy, Reflect, Debug, PartialEq, Eq, Default)]
36#[reflect(Component, Default, Debug, PartialEq)]
37#[require(InheritedVisibility, ViewVisibility)]
38pub enum Visibility {
39 #[default]
43 Inherited,
44 Hidden,
46 Visible,
51}
52
53impl Visibility {
54 #[inline]
57 pub fn toggle_inherited_visible(&mut self) {
58 *self = match *self {
59 Visibility::Inherited => Visibility::Visible,
60 Visibility::Visible => Visibility::Inherited,
61 _ => *self,
62 };
63 }
64 #[inline]
67 pub fn toggle_inherited_hidden(&mut self) {
68 *self = match *self {
69 Visibility::Inherited => Visibility::Hidden,
70 Visibility::Hidden => Visibility::Inherited,
71 _ => *self,
72 };
73 }
74 #[inline]
77 pub fn toggle_visible_hidden(&mut self) {
78 *self = match *self {
79 Visibility::Visible => Visibility::Hidden,
80 Visibility::Hidden => Visibility::Visible,
81 _ => *self,
82 };
83 }
84}
85
86impl PartialEq<Visibility> for &Visibility {
88 #[inline]
89 fn eq(&self, other: &Visibility) -> bool {
90 <Visibility as PartialEq<Visibility>>::eq(*self, other)
92 }
93}
94
95impl PartialEq<&Visibility> for Visibility {
97 #[inline]
98 fn eq(&self, other: &&Visibility) -> bool {
99 <Visibility as PartialEq<Visibility>>::eq(self, *other)
101 }
102}
103
104#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)]
111#[reflect(Component, Default, Debug, PartialEq)]
112pub struct InheritedVisibility(bool);
113
114impl InheritedVisibility {
115 pub const HIDDEN: Self = Self(false);
117 pub const VISIBLE: Self = Self(true);
119
120 #[inline]
123 pub fn get(self) -> bool {
124 self.0
125 }
126}
127
128#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)]
139#[reflect(Component, Default, Debug, PartialEq)]
140pub struct ViewVisibility(bool);
141
142impl ViewVisibility {
143 pub const HIDDEN: Self = Self(false);
145
146 #[inline]
149 pub fn get(self) -> bool {
150 self.0
151 }
152
153 #[inline]
166 pub fn set(&mut self) {
167 self.0 = true;
168 }
169}
170
171#[derive(Bundle, Debug, Clone, Default)]
180#[deprecated(
181 since = "0.15.0",
182 note = "Use the `Visibility` component instead. Inserting it will now also insert `InheritedVisibility` and `ViewVisibility` automatically."
183)]
184pub struct VisibilityBundle {
185 pub visibility: Visibility,
187 pub inherited_visibility: InheritedVisibility,
189 pub view_visibility: ViewVisibility,
191}
192
193#[derive(Debug, Component, Default, Reflect)]
201#[reflect(Component, Default, Debug)]
202pub struct NoFrustumCulling;
203
204#[derive(Clone, Component, Default, Debug, Reflect)]
214#[reflect(Component, Default, Debug)]
215pub struct VisibleEntities {
216 #[reflect(ignore)]
217 pub entities: TypeIdMap<Vec<Entity>>,
218}
219
220impl VisibleEntities {
221 pub fn get<QF>(&self) -> &[Entity]
222 where
223 QF: 'static,
224 {
225 match self.entities.get(&TypeId::of::<QF>()) {
226 Some(entities) => &entities[..],
227 None => &[],
228 }
229 }
230
231 pub fn get_mut<QF>(&mut self) -> &mut Vec<Entity>
232 where
233 QF: 'static,
234 {
235 self.entities.entry(TypeId::of::<QF>()).or_default()
236 }
237
238 pub fn iter<QF>(&self) -> impl DoubleEndedIterator<Item = &Entity>
239 where
240 QF: 'static,
241 {
242 self.get::<QF>().iter()
243 }
244
245 pub fn len<QF>(&self) -> usize
246 where
247 QF: 'static,
248 {
249 self.get::<QF>().len()
250 }
251
252 pub fn is_empty<QF>(&self) -> bool
253 where
254 QF: 'static,
255 {
256 self.get::<QF>().is_empty()
257 }
258
259 pub fn clear<QF>(&mut self)
260 where
261 QF: 'static,
262 {
263 self.get_mut::<QF>().clear();
264 }
265
266 pub fn push<QF>(&mut self, entity: Entity)
267 where
268 QF: 'static,
269 {
270 self.get_mut::<QF>().push(entity);
271 }
272}
273
274#[derive(Clone, Component, Default, Debug, Reflect)]
278#[reflect(Component, Default, Debug)]
279pub struct RenderVisibleEntities {
280 #[reflect(ignore)]
281 pub entities: TypeIdMap<Vec<(Entity, MainEntity)>>,
282}
283
284impl RenderVisibleEntities {
285 pub fn get<QF>(&self) -> &[(Entity, MainEntity)]
286 where
287 QF: 'static,
288 {
289 match self.entities.get(&TypeId::of::<QF>()) {
290 Some(entities) => &entities[..],
291 None => &[],
292 }
293 }
294
295 pub fn iter<QF>(&self) -> impl DoubleEndedIterator<Item = &(Entity, MainEntity)>
296 where
297 QF: 'static,
298 {
299 self.get::<QF>().iter()
300 }
301
302 pub fn len<QF>(&self) -> usize
303 where
304 QF: 'static,
305 {
306 self.get::<QF>().len()
307 }
308
309 pub fn is_empty<QF>(&self) -> bool
310 where
311 QF: 'static,
312 {
313 self.get::<QF>().is_empty()
314 }
315}
316
317#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
318pub enum VisibilitySystems {
319 CalculateBounds,
322 UpdateFrusta,
324 VisibilityPropagate,
327 CheckVisibility,
334}
335
336pub struct VisibilityPlugin;
337
338impl Plugin for VisibilityPlugin {
339 fn build(&self, app: &mut bevy_app::App) {
340 use VisibilitySystems::*;
341
342 app.configure_sets(
343 PostUpdate,
344 (CalculateBounds, UpdateFrusta, VisibilityPropagate)
345 .before(CheckVisibility)
346 .after(TransformSystem::TransformPropagate),
347 )
348 .configure_sets(PostUpdate, CheckVisibility.ambiguous_with(CheckVisibility))
349 .add_systems(
350 PostUpdate,
351 (
352 calculate_bounds.in_set(CalculateBounds),
353 (visibility_propagate_system, reset_view_visibility).in_set(VisibilityPropagate),
354 check_visibility::<With<Mesh3d>>.in_set(CheckVisibility),
355 ),
356 );
357 }
358}
359
360pub fn calculate_bounds(
365 mut commands: Commands,
366 meshes: Res<Assets<Mesh>>,
367 without_aabb: Query<(Entity, &Mesh3d), (Without<Aabb>, Without<NoFrustumCulling>)>,
368) {
369 for (entity, mesh_handle) in &without_aabb {
370 if let Some(mesh) = meshes.get(mesh_handle) {
371 if let Some(aabb) = mesh.compute_aabb() {
372 commands.entity(entity).try_insert(aabb);
373 }
374 }
375 }
376}
377
378pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(
382 mut views: Query<
383 (&GlobalTransform, &T, &mut Frustum),
384 Or<(Changed<GlobalTransform>, Changed<T>)>,
385 >,
386) {
387 for (transform, projection, mut frustum) in &mut views {
388 *frustum = projection.compute_frustum(transform);
389 }
390}
391
392fn visibility_propagate_system(
393 changed: Query<
394 (Entity, &Visibility, Option<&Parent>, Option<&Children>),
395 (With<InheritedVisibility>, Changed<Visibility>),
396 >,
397 mut visibility_query: Query<(&Visibility, &mut InheritedVisibility)>,
398 children_query: Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,
399) {
400 for (entity, visibility, parent, children) in &changed {
401 let is_visible = match visibility {
402 Visibility::Visible => true,
403 Visibility::Hidden => false,
404 Visibility::Inherited => parent
406 .and_then(|p| visibility_query.get(p.get()).ok())
407 .map_or(true, |(_, x)| x.get()),
408 };
409 let (_, mut inherited_visibility) = visibility_query
410 .get_mut(entity)
411 .expect("With<InheritedVisibility> ensures this query will return a value");
412
413 if inherited_visibility.get() != is_visible {
417 inherited_visibility.0 = is_visible;
418
419 for &child in children.into_iter().flatten() {
421 let _ =
422 propagate_recursive(is_visible, child, &mut visibility_query, &children_query);
423 }
424 }
425 }
426}
427
428fn propagate_recursive(
429 parent_is_visible: bool,
430 entity: Entity,
431 visibility_query: &mut Query<(&Visibility, &mut InheritedVisibility)>,
432 children_query: &Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,
433 ) -> Result<(), ()> {
436 let (visibility, mut inherited_visibility) = visibility_query.get_mut(entity).map_err(drop)?;
439
440 let is_visible = match visibility {
441 Visibility::Visible => true,
442 Visibility::Hidden => false,
443 Visibility::Inherited => parent_is_visible,
444 };
445
446 if inherited_visibility.get() != is_visible {
448 inherited_visibility.0 = is_visible;
449
450 for &child in children_query.get(entity).ok().into_iter().flatten() {
452 let _ = propagate_recursive(is_visible, child, visibility_query, children_query);
453 }
454 }
455
456 Ok(())
457}
458
459fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) {
463 query.iter_mut().for_each(|mut view_visibility| {
464 *view_visibility.bypass_change_detection() = ViewVisibility::HIDDEN;
468 });
469}
470
471pub fn check_visibility<QF>(
482 mut thread_queues: Local<Parallel<Vec<Entity>>>,
483 mut view_query: Query<(
484 Entity,
485 &mut VisibleEntities,
486 &Frustum,
487 Option<&RenderLayers>,
488 &Camera,
489 Has<NoCpuCulling>,
490 )>,
491 mut visible_aabb_query: Query<
492 (
493 Entity,
494 &InheritedVisibility,
495 &mut ViewVisibility,
496 Option<&RenderLayers>,
497 Option<&Aabb>,
498 &GlobalTransform,
499 Has<NoFrustumCulling>,
500 Has<VisibilityRange>,
501 ),
502 QF,
503 >,
504 visible_entity_ranges: Option<Res<VisibleEntityRanges>>,
505) where
506 QF: QueryFilter + 'static,
507{
508 let visible_entity_ranges = visible_entity_ranges.as_deref();
509
510 for (view, mut visible_entities, frustum, maybe_view_mask, camera, no_cpu_culling) in
511 &mut view_query
512 {
513 if !camera.is_active {
514 continue;
515 }
516
517 let view_mask = maybe_view_mask.unwrap_or_default();
518
519 visible_aabb_query.par_iter_mut().for_each_init(
520 || thread_queues.borrow_local_mut(),
521 |queue, query_item| {
522 let (
523 entity,
524 inherited_visibility,
525 mut view_visibility,
526 maybe_entity_mask,
527 maybe_model_aabb,
528 transform,
529 no_frustum_culling,
530 has_visibility_range,
531 ) = query_item;
532
533 if !inherited_visibility.get() {
536 return;
537 }
538
539 let entity_mask = maybe_entity_mask.unwrap_or_default();
540 if !view_mask.intersects(entity_mask) {
541 return;
542 }
543
544 if has_visibility_range
546 && visible_entity_ranges.is_some_and(|visible_entity_ranges| {
547 !visible_entity_ranges.entity_is_in_range_of_view(entity, view)
548 })
549 {
550 return;
551 }
552
553 if !no_frustum_culling && !no_cpu_culling {
555 if let Some(model_aabb) = maybe_model_aabb {
556 let world_from_local = transform.affine();
557 let model_sphere = Sphere {
558 center: world_from_local.transform_point3a(model_aabb.center),
559 radius: transform.radius_vec3a(model_aabb.half_extents),
560 };
561 if !frustum.intersects_sphere(&model_sphere, false) {
563 return;
564 }
565 if !frustum.intersects_obb(model_aabb, &world_from_local, true, false) {
567 return;
568 }
569 }
570 }
571
572 view_visibility.set();
573 queue.push(entity);
574 },
575 );
576
577 visible_entities.clear::<QF>();
578 thread_queues.drain_into(visible_entities.get_mut::<QF>());
579 }
580}
581
582#[cfg(test)]
583mod test {
584 use super::*;
585 use bevy_app::prelude::*;
586 use bevy_hierarchy::BuildChildren;
587
588 #[test]
589 fn visibility_propagation() {
590 let mut app = App::new();
591 app.add_systems(Update, visibility_propagate_system);
592
593 let root1 = app.world_mut().spawn(Visibility::Hidden).id();
594 let root1_child1 = app.world_mut().spawn(Visibility::default()).id();
595 let root1_child2 = app.world_mut().spawn(Visibility::Hidden).id();
596 let root1_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
597 let root1_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
598
599 app.world_mut()
600 .entity_mut(root1)
601 .add_children(&[root1_child1, root1_child2]);
602 app.world_mut()
603 .entity_mut(root1_child1)
604 .add_children(&[root1_child1_grandchild1]);
605 app.world_mut()
606 .entity_mut(root1_child2)
607 .add_children(&[root1_child2_grandchild1]);
608
609 let root2 = app.world_mut().spawn(Visibility::default()).id();
610 let root2_child1 = app.world_mut().spawn(Visibility::default()).id();
611 let root2_child2 = app.world_mut().spawn(Visibility::Hidden).id();
612 let root2_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
613 let root2_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
614
615 app.world_mut()
616 .entity_mut(root2)
617 .add_children(&[root2_child1, root2_child2]);
618 app.world_mut()
619 .entity_mut(root2_child1)
620 .add_children(&[root2_child1_grandchild1]);
621 app.world_mut()
622 .entity_mut(root2_child2)
623 .add_children(&[root2_child2_grandchild1]);
624
625 app.update();
626
627 let is_visible = |e: Entity| {
628 app.world()
629 .entity(e)
630 .get::<InheritedVisibility>()
631 .unwrap()
632 .get()
633 };
634 assert!(
635 !is_visible(root1),
636 "invisibility propagates down tree from root"
637 );
638 assert!(
639 !is_visible(root1_child1),
640 "invisibility propagates down tree from root"
641 );
642 assert!(
643 !is_visible(root1_child2),
644 "invisibility propagates down tree from root"
645 );
646 assert!(
647 !is_visible(root1_child1_grandchild1),
648 "invisibility propagates down tree from root"
649 );
650 assert!(
651 !is_visible(root1_child2_grandchild1),
652 "invisibility propagates down tree from root"
653 );
654
655 assert!(
656 is_visible(root2),
657 "visibility propagates down tree from root"
658 );
659 assert!(
660 is_visible(root2_child1),
661 "visibility propagates down tree from root"
662 );
663 assert!(
664 !is_visible(root2_child2),
665 "visibility propagates down tree from root, but local invisibility is preserved"
666 );
667 assert!(
668 is_visible(root2_child1_grandchild1),
669 "visibility propagates down tree from root"
670 );
671 assert!(
672 !is_visible(root2_child2_grandchild1),
673 "child's invisibility propagates down to grandchild"
674 );
675 }
676
677 #[test]
678 fn visibility_propagation_unconditional_visible() {
679 use Visibility::{Hidden, Inherited, Visible};
680
681 let mut app = App::new();
682 app.add_systems(Update, visibility_propagate_system);
683
684 let root1 = app.world_mut().spawn(Visible).id();
685 let root1_child1 = app.world_mut().spawn(Inherited).id();
686 let root1_child2 = app.world_mut().spawn(Hidden).id();
687 let root1_child1_grandchild1 = app.world_mut().spawn(Visible).id();
688 let root1_child2_grandchild1 = app.world_mut().spawn(Visible).id();
689
690 let root2 = app.world_mut().spawn(Inherited).id();
691 let root3 = app.world_mut().spawn(Hidden).id();
692
693 app.world_mut()
694 .entity_mut(root1)
695 .add_children(&[root1_child1, root1_child2]);
696 app.world_mut()
697 .entity_mut(root1_child1)
698 .add_children(&[root1_child1_grandchild1]);
699 app.world_mut()
700 .entity_mut(root1_child2)
701 .add_children(&[root1_child2_grandchild1]);
702
703 app.update();
704
705 let is_visible = |e: Entity| {
706 app.world()
707 .entity(e)
708 .get::<InheritedVisibility>()
709 .unwrap()
710 .get()
711 };
712 assert!(
713 is_visible(root1),
714 "an unconditionally visible root is visible"
715 );
716 assert!(
717 is_visible(root1_child1),
718 "an inheriting child of an unconditionally visible parent is visible"
719 );
720 assert!(
721 !is_visible(root1_child2),
722 "a hidden child on an unconditionally visible parent is hidden"
723 );
724 assert!(
725 is_visible(root1_child1_grandchild1),
726 "an unconditionally visible child of an inheriting parent is visible"
727 );
728 assert!(
729 is_visible(root1_child2_grandchild1),
730 "an unconditionally visible child of a hidden parent is visible"
731 );
732 assert!(is_visible(root2), "an inheriting root is visible");
733 assert!(!is_visible(root3), "a hidden root is hidden");
734 }
735
736 #[test]
737 fn visibility_propagation_change_detection() {
738 let mut world = World::new();
739 let mut schedule = Schedule::default();
740 schedule.add_systems(visibility_propagate_system);
741
742 let id1 = world.spawn(Visibility::default()).id();
745
746 let id2 = world.spawn(Visibility::default()).id();
747 world.entity_mut(id1).add_children(&[id2]);
748
749 let id3 = world.spawn(Visibility::Hidden).id();
750 world.entity_mut(id2).add_children(&[id3]);
751
752 let id4 = world.spawn(Visibility::default()).id();
753 world.entity_mut(id3).add_children(&[id4]);
754
755 schedule.run(&mut world);
759 world.clear_trackers();
760
761 let mut q = world.query::<Ref<InheritedVisibility>>();
762
763 assert!(!q.get(&world, id1).unwrap().is_changed());
764 assert!(!q.get(&world, id2).unwrap().is_changed());
765 assert!(!q.get(&world, id3).unwrap().is_changed());
766 assert!(!q.get(&world, id4).unwrap().is_changed());
767
768 world.clear_trackers();
769 world.entity_mut(id1).insert(Visibility::Hidden);
770 schedule.run(&mut world);
771
772 assert!(q.get(&world, id1).unwrap().is_changed());
773 assert!(q.get(&world, id2).unwrap().is_changed());
774 assert!(!q.get(&world, id3).unwrap().is_changed());
775 assert!(!q.get(&world, id4).unwrap().is_changed());
776
777 world.clear_trackers();
778 schedule.run(&mut world);
779
780 assert!(!q.get(&world, id1).unwrap().is_changed());
781 assert!(!q.get(&world, id2).unwrap().is_changed());
782 assert!(!q.get(&world, id3).unwrap().is_changed());
783 assert!(!q.get(&world, id4).unwrap().is_changed());
784
785 world.clear_trackers();
786 world.entity_mut(id3).insert(Visibility::Inherited);
787 schedule.run(&mut world);
788
789 assert!(!q.get(&world, id1).unwrap().is_changed());
790 assert!(!q.get(&world, id2).unwrap().is_changed());
791 assert!(!q.get(&world, id3).unwrap().is_changed());
792 assert!(!q.get(&world, id4).unwrap().is_changed());
793
794 world.clear_trackers();
795 world.entity_mut(id2).insert(Visibility::Visible);
796 schedule.run(&mut world);
797
798 assert!(!q.get(&world, id1).unwrap().is_changed());
799 assert!(q.get(&world, id2).unwrap().is_changed());
800 assert!(q.get(&world, id3).unwrap().is_changed());
801 assert!(q.get(&world, id4).unwrap().is_changed());
802
803 world.clear_trackers();
804 schedule.run(&mut world);
805
806 assert!(!q.get(&world, id1).unwrap().is_changed());
807 assert!(!q.get(&world, id2).unwrap().is_changed());
808 assert!(!q.get(&world, id3).unwrap().is_changed());
809 assert!(!q.get(&world, id4).unwrap().is_changed());
810 }
811
812 #[test]
813 fn visibility_propagation_with_invalid_parent() {
814 let mut world = World::new();
815 let mut schedule = Schedule::default();
816 schedule.add_systems(visibility_propagate_system);
817
818 let parent = world.spawn(()).id();
819 let child = world.spawn(Visibility::default()).id();
820 world.entity_mut(parent).add_children(&[child]);
821
822 schedule.run(&mut world);
823 world.clear_trackers();
824
825 let child_visible = world.entity(child).get::<InheritedVisibility>().unwrap().0;
826 assert!(child_visible);
828 }
829
830 #[test]
831 fn ensure_visibility_enum_size() {
832 assert_eq!(1, size_of::<Visibility>());
833 assert_eq!(1, size_of::<Option<Visibility>>());
834 }
835}