1mod range;
2mod render_layers;
3
4use std::any::TypeId;
5
6pub use range::*;
7pub use render_layers::*;
8
9use bevy_app::{Plugin, PostUpdate};
10use bevy_asset::{Assets, Handle};
11use bevy_derive::Deref;
12use bevy_ecs::{prelude::*, query::QueryFilter};
13use bevy_hierarchy::{Children, Parent};
14use bevy_reflect::{std_traits::ReflectDefault, Reflect};
15use bevy_transform::{components::GlobalTransform, TransformSystem};
16use bevy_utils::{Parallel, TypeIdMap};
17
18use crate::{
19 camera::{Camera, CameraProjection},
20 mesh::Mesh,
21 primitives::{Aabb, Frustum, Sphere},
22};
23
24use super::NoCpuCulling;
25
26#[derive(Component, Clone, Copy, Reflect, Debug, PartialEq, Eq, Default)]
34#[reflect(Component, Default)]
35pub enum Visibility {
36 #[default]
40 Inherited,
41 Hidden,
43 Visible,
48}
49
50impl PartialEq<Visibility> for &Visibility {
52 #[inline]
53 fn eq(&self, other: &Visibility) -> bool {
54 <Visibility as PartialEq<Visibility>>::eq(*self, other)
56 }
57}
58
59impl PartialEq<&Visibility> for Visibility {
61 #[inline]
62 fn eq(&self, other: &&Visibility) -> bool {
63 <Visibility as PartialEq<Visibility>>::eq(self, *other)
65 }
66}
67
68#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)]
75#[reflect(Component, Default)]
76pub struct InheritedVisibility(bool);
77
78impl InheritedVisibility {
79 pub const HIDDEN: Self = Self(false);
81 pub const VISIBLE: Self = Self(true);
83
84 #[inline]
87 pub fn get(self) -> bool {
88 self.0
89 }
90}
91
92#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)]
103#[reflect(Component, Default)]
104pub struct ViewVisibility(bool);
105
106impl ViewVisibility {
107 pub const HIDDEN: Self = Self(false);
109
110 #[inline]
113 pub fn get(self) -> bool {
114 self.0
115 }
116
117 #[inline]
130 pub fn set(&mut self) {
131 self.0 = true;
132 }
133}
134
135#[derive(Bundle, Debug, Clone, Default)]
143pub struct VisibilityBundle {
144 pub visibility: Visibility,
146 pub inherited_visibility: InheritedVisibility,
148 pub view_visibility: ViewVisibility,
150}
151
152#[derive(Component, Default, Reflect)]
160#[reflect(Component, Default)]
161pub struct NoFrustumCulling;
162
163#[derive(Clone, Component, Default, Debug, Reflect)]
173#[reflect(Component, Default)]
174pub struct VisibleEntities {
175 #[reflect(ignore)]
176 pub entities: TypeIdMap<Vec<Entity>>,
177}
178
179impl VisibleEntities {
180 pub fn get<QF>(&self) -> &[Entity]
181 where
182 QF: 'static,
183 {
184 match self.entities.get(&TypeId::of::<QF>()) {
185 Some(entities) => &entities[..],
186 None => &[],
187 }
188 }
189
190 pub fn get_mut<QF>(&mut self) -> &mut Vec<Entity>
191 where
192 QF: 'static,
193 {
194 self.entities.entry(TypeId::of::<QF>()).or_default()
195 }
196
197 pub fn iter<QF>(&self) -> impl DoubleEndedIterator<Item = &Entity>
198 where
199 QF: 'static,
200 {
201 self.get::<QF>().iter()
202 }
203
204 pub fn len<QF>(&self) -> usize
205 where
206 QF: 'static,
207 {
208 self.get::<QF>().len()
209 }
210
211 pub fn is_empty<QF>(&self) -> bool
212 where
213 QF: 'static,
214 {
215 self.get::<QF>().is_empty()
216 }
217
218 pub fn clear<QF>(&mut self)
219 where
220 QF: 'static,
221 {
222 self.get_mut::<QF>().clear();
223 }
224
225 pub fn push<QF>(&mut self, entity: Entity)
226 where
227 QF: 'static,
228 {
229 self.get_mut::<QF>().push(entity);
230 }
231}
232
233pub type WithMesh = With<Handle<Mesh>>;
236
237#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
238pub enum VisibilitySystems {
239 CalculateBounds,
242 UpdateFrusta,
244 VisibilityPropagate,
247 CheckVisibility,
250}
251
252pub struct VisibilityPlugin;
253
254impl Plugin for VisibilityPlugin {
255 fn build(&self, app: &mut bevy_app::App) {
256 use VisibilitySystems::*;
257
258 app.configure_sets(
259 PostUpdate,
260 (CalculateBounds, UpdateFrusta, VisibilityPropagate)
261 .before(CheckVisibility)
262 .after(TransformSystem::TransformPropagate),
263 )
264 .add_systems(
265 PostUpdate,
266 (
267 calculate_bounds.in_set(CalculateBounds),
268 (visibility_propagate_system, reset_view_visibility).in_set(VisibilityPropagate),
269 check_visibility::<WithMesh>.in_set(CheckVisibility),
270 ),
271 );
272 }
273}
274
275pub fn calculate_bounds(
280 mut commands: Commands,
281 meshes: Res<Assets<Mesh>>,
282 without_aabb: Query<(Entity, &Handle<Mesh>), (Without<Aabb>, Without<NoFrustumCulling>)>,
283) {
284 for (entity, mesh_handle) in &without_aabb {
285 if let Some(mesh) = meshes.get(mesh_handle) {
286 if let Some(aabb) = mesh.compute_aabb() {
287 commands.entity(entity).try_insert(aabb);
288 }
289 }
290 }
291}
292
293pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(
297 mut views: Query<
298 (&GlobalTransform, &T, &mut Frustum),
299 Or<(Changed<GlobalTransform>, Changed<T>)>,
300 >,
301) {
302 for (transform, projection, mut frustum) in &mut views {
303 *frustum = projection.compute_frustum(transform);
304 }
305}
306
307fn visibility_propagate_system(
308 changed: Query<
309 (Entity, &Visibility, Option<&Parent>, Option<&Children>),
310 (With<InheritedVisibility>, Changed<Visibility>),
311 >,
312 mut visibility_query: Query<(&Visibility, &mut InheritedVisibility)>,
313 children_query: Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,
314) {
315 for (entity, visibility, parent, children) in &changed {
316 let is_visible = match visibility {
317 Visibility::Visible => true,
318 Visibility::Hidden => false,
319 Visibility::Inherited => parent
321 .and_then(|p| visibility_query.get(p.get()).ok())
322 .map_or(true, |(_, x)| x.get()),
323 };
324 let (_, mut inherited_visibility) = visibility_query
325 .get_mut(entity)
326 .expect("With<InheritedVisibility> ensures this query will return a value");
327
328 if inherited_visibility.get() != is_visible {
332 inherited_visibility.0 = is_visible;
333
334 for &child in children.into_iter().flatten() {
336 let _ =
337 propagate_recursive(is_visible, child, &mut visibility_query, &children_query);
338 }
339 }
340 }
341}
342
343fn propagate_recursive(
344 parent_is_visible: bool,
345 entity: Entity,
346 visibility_query: &mut Query<(&Visibility, &mut InheritedVisibility)>,
347 children_query: &Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,
348 ) -> Result<(), ()> {
351 let (visibility, mut inherited_visibility) = visibility_query.get_mut(entity).map_err(drop)?;
354
355 let is_visible = match visibility {
356 Visibility::Visible => true,
357 Visibility::Hidden => false,
358 Visibility::Inherited => parent_is_visible,
359 };
360
361 if inherited_visibility.get() != is_visible {
363 inherited_visibility.0 = is_visible;
364
365 for &child in children_query.get(entity).ok().into_iter().flatten() {
367 let _ = propagate_recursive(is_visible, child, visibility_query, children_query);
368 }
369 }
370
371 Ok(())
372}
373
374fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) {
378 query.iter_mut().for_each(|mut view_visibility| {
379 *view_visibility.bypass_change_detection() = ViewVisibility::HIDDEN;
383 });
384}
385
386pub fn check_visibility<QF>(
397 mut thread_queues: Local<Parallel<Vec<Entity>>>,
398 mut view_query: Query<(
399 Entity,
400 &mut VisibleEntities,
401 &Frustum,
402 Option<&RenderLayers>,
403 &Camera,
404 Has<NoCpuCulling>,
405 )>,
406 mut visible_aabb_query: Query<
407 (
408 Entity,
409 &InheritedVisibility,
410 &mut ViewVisibility,
411 Option<&RenderLayers>,
412 Option<&Aabb>,
413 &GlobalTransform,
414 Has<NoFrustumCulling>,
415 Has<VisibilityRange>,
416 ),
417 QF,
418 >,
419 visible_entity_ranges: Option<Res<VisibleEntityRanges>>,
420) where
421 QF: QueryFilter + 'static,
422{
423 let visible_entity_ranges = visible_entity_ranges.as_deref();
424
425 for (view, mut visible_entities, frustum, maybe_view_mask, camera, no_cpu_culling) in
426 &mut view_query
427 {
428 if !camera.is_active {
429 continue;
430 }
431
432 let view_mask = maybe_view_mask.unwrap_or_default();
433
434 visible_aabb_query.par_iter_mut().for_each_init(
435 || thread_queues.borrow_local_mut(),
436 |queue, query_item| {
437 let (
438 entity,
439 inherited_visibility,
440 mut view_visibility,
441 maybe_entity_mask,
442 maybe_model_aabb,
443 transform,
444 no_frustum_culling,
445 has_visibility_range,
446 ) = query_item;
447
448 if !inherited_visibility.get() {
451 return;
452 }
453
454 let entity_mask = maybe_entity_mask.unwrap_or_default();
455 if !view_mask.intersects(entity_mask) {
456 return;
457 }
458
459 if has_visibility_range
461 && visible_entity_ranges.is_some_and(|visible_entity_ranges| {
462 !visible_entity_ranges.entity_is_in_range_of_view(entity, view)
463 })
464 {
465 return;
466 }
467
468 if !no_frustum_culling && !no_cpu_culling {
470 if let Some(model_aabb) = maybe_model_aabb {
471 let world_from_local = transform.affine();
472 let model_sphere = Sphere {
473 center: world_from_local.transform_point3a(model_aabb.center),
474 radius: transform.radius_vec3a(model_aabb.half_extents),
475 };
476 if !frustum.intersects_sphere(&model_sphere, false) {
478 return;
479 }
480 if !frustum.intersects_obb(model_aabb, &world_from_local, true, false) {
482 return;
483 }
484 }
485 }
486
487 view_visibility.set();
488 queue.push(entity);
489 },
490 );
491
492 visible_entities.clear::<QF>();
493 thread_queues.drain_into(visible_entities.get_mut::<QF>());
494 }
495}
496
497#[cfg(test)]
498mod test {
499 use bevy_app::prelude::*;
500 use bevy_ecs::prelude::*;
501
502 use super::*;
503
504 use bevy_hierarchy::BuildWorldChildren;
505
506 fn visibility_bundle(visibility: Visibility) -> VisibilityBundle {
507 VisibilityBundle {
508 visibility,
509 ..Default::default()
510 }
511 }
512
513 #[test]
514 fn visibility_propagation() {
515 let mut app = App::new();
516 app.add_systems(Update, visibility_propagate_system);
517
518 let root1 = app
519 .world_mut()
520 .spawn(visibility_bundle(Visibility::Hidden))
521 .id();
522 let root1_child1 = app.world_mut().spawn(VisibilityBundle::default()).id();
523 let root1_child2 = app
524 .world_mut()
525 .spawn(visibility_bundle(Visibility::Hidden))
526 .id();
527 let root1_child1_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id();
528 let root1_child2_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id();
529
530 app.world_mut()
531 .entity_mut(root1)
532 .push_children(&[root1_child1, root1_child2]);
533 app.world_mut()
534 .entity_mut(root1_child1)
535 .push_children(&[root1_child1_grandchild1]);
536 app.world_mut()
537 .entity_mut(root1_child2)
538 .push_children(&[root1_child2_grandchild1]);
539
540 let root2 = app.world_mut().spawn(VisibilityBundle::default()).id();
541 let root2_child1 = app.world_mut().spawn(VisibilityBundle::default()).id();
542 let root2_child2 = app
543 .world_mut()
544 .spawn(visibility_bundle(Visibility::Hidden))
545 .id();
546 let root2_child1_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id();
547 let root2_child2_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id();
548
549 app.world_mut()
550 .entity_mut(root2)
551 .push_children(&[root2_child1, root2_child2]);
552 app.world_mut()
553 .entity_mut(root2_child1)
554 .push_children(&[root2_child1_grandchild1]);
555 app.world_mut()
556 .entity_mut(root2_child2)
557 .push_children(&[root2_child2_grandchild1]);
558
559 app.update();
560
561 let is_visible = |e: Entity| {
562 app.world()
563 .entity(e)
564 .get::<InheritedVisibility>()
565 .unwrap()
566 .get()
567 };
568 assert!(
569 !is_visible(root1),
570 "invisibility propagates down tree from root"
571 );
572 assert!(
573 !is_visible(root1_child1),
574 "invisibility propagates down tree from root"
575 );
576 assert!(
577 !is_visible(root1_child2),
578 "invisibility propagates down tree from root"
579 );
580 assert!(
581 !is_visible(root1_child1_grandchild1),
582 "invisibility propagates down tree from root"
583 );
584 assert!(
585 !is_visible(root1_child2_grandchild1),
586 "invisibility propagates down tree from root"
587 );
588
589 assert!(
590 is_visible(root2),
591 "visibility propagates down tree from root"
592 );
593 assert!(
594 is_visible(root2_child1),
595 "visibility propagates down tree from root"
596 );
597 assert!(
598 !is_visible(root2_child2),
599 "visibility propagates down tree from root, but local invisibility is preserved"
600 );
601 assert!(
602 is_visible(root2_child1_grandchild1),
603 "visibility propagates down tree from root"
604 );
605 assert!(
606 !is_visible(root2_child2_grandchild1),
607 "child's invisibility propagates down to grandchild"
608 );
609 }
610
611 #[test]
612 fn visibility_propagation_unconditional_visible() {
613 use Visibility::{Hidden, Inherited, Visible};
614
615 let mut app = App::new();
616 app.add_systems(Update, visibility_propagate_system);
617
618 let root1 = app.world_mut().spawn(visibility_bundle(Visible)).id();
619 let root1_child1 = app.world_mut().spawn(visibility_bundle(Inherited)).id();
620 let root1_child2 = app.world_mut().spawn(visibility_bundle(Hidden)).id();
621 let root1_child1_grandchild1 = app.world_mut().spawn(visibility_bundle(Visible)).id();
622 let root1_child2_grandchild1 = app.world_mut().spawn(visibility_bundle(Visible)).id();
623
624 let root2 = app.world_mut().spawn(visibility_bundle(Inherited)).id();
625 let root3 = app.world_mut().spawn(visibility_bundle(Hidden)).id();
626
627 app.world_mut()
628 .entity_mut(root1)
629 .push_children(&[root1_child1, root1_child2]);
630 app.world_mut()
631 .entity_mut(root1_child1)
632 .push_children(&[root1_child1_grandchild1]);
633 app.world_mut()
634 .entity_mut(root1_child2)
635 .push_children(&[root1_child2_grandchild1]);
636
637 app.update();
638
639 let is_visible = |e: Entity| {
640 app.world()
641 .entity(e)
642 .get::<InheritedVisibility>()
643 .unwrap()
644 .get()
645 };
646 assert!(
647 is_visible(root1),
648 "an unconditionally visible root is visible"
649 );
650 assert!(
651 is_visible(root1_child1),
652 "an inheriting child of an unconditionally visible parent is visible"
653 );
654 assert!(
655 !is_visible(root1_child2),
656 "a hidden child on an unconditionally visible parent is hidden"
657 );
658 assert!(
659 is_visible(root1_child1_grandchild1),
660 "an unconditionally visible child of an inheriting parent is visible"
661 );
662 assert!(
663 is_visible(root1_child2_grandchild1),
664 "an unconditionally visible child of a hidden parent is visible"
665 );
666 assert!(is_visible(root2), "an inheriting root is visible");
667 assert!(!is_visible(root3), "a hidden root is hidden");
668 }
669
670 #[test]
671 fn visibility_propagation_change_detection() {
672 let mut world = World::new();
673 let mut schedule = Schedule::default();
674 schedule.add_systems(visibility_propagate_system);
675
676 let id1 = world.spawn(VisibilityBundle::default()).id();
679
680 let id2 = world.spawn(VisibilityBundle::default()).id();
681 world.entity_mut(id1).push_children(&[id2]);
682
683 let id3 = world.spawn(visibility_bundle(Visibility::Hidden)).id();
684 world.entity_mut(id2).push_children(&[id3]);
685
686 let id4 = world.spawn(VisibilityBundle::default()).id();
687 world.entity_mut(id3).push_children(&[id4]);
688
689 schedule.run(&mut world);
693 world.clear_trackers();
694
695 let mut q = world.query::<Ref<InheritedVisibility>>();
696
697 assert!(!q.get(&world, id1).unwrap().is_changed());
698 assert!(!q.get(&world, id2).unwrap().is_changed());
699 assert!(!q.get(&world, id3).unwrap().is_changed());
700 assert!(!q.get(&world, id4).unwrap().is_changed());
701
702 world.clear_trackers();
703 world.entity_mut(id1).insert(Visibility::Hidden);
704 schedule.run(&mut world);
705
706 assert!(q.get(&world, id1).unwrap().is_changed());
707 assert!(q.get(&world, id2).unwrap().is_changed());
708 assert!(!q.get(&world, id3).unwrap().is_changed());
709 assert!(!q.get(&world, id4).unwrap().is_changed());
710
711 world.clear_trackers();
712 schedule.run(&mut world);
713
714 assert!(!q.get(&world, id1).unwrap().is_changed());
715 assert!(!q.get(&world, id2).unwrap().is_changed());
716 assert!(!q.get(&world, id3).unwrap().is_changed());
717 assert!(!q.get(&world, id4).unwrap().is_changed());
718
719 world.clear_trackers();
720 world.entity_mut(id3).insert(Visibility::Inherited);
721 schedule.run(&mut world);
722
723 assert!(!q.get(&world, id1).unwrap().is_changed());
724 assert!(!q.get(&world, id2).unwrap().is_changed());
725 assert!(!q.get(&world, id3).unwrap().is_changed());
726 assert!(!q.get(&world, id4).unwrap().is_changed());
727
728 world.clear_trackers();
729 world.entity_mut(id2).insert(Visibility::Visible);
730 schedule.run(&mut world);
731
732 assert!(!q.get(&world, id1).unwrap().is_changed());
733 assert!(q.get(&world, id2).unwrap().is_changed());
734 assert!(q.get(&world, id3).unwrap().is_changed());
735 assert!(q.get(&world, id4).unwrap().is_changed());
736
737 world.clear_trackers();
738 schedule.run(&mut world);
739
740 assert!(!q.get(&world, id1).unwrap().is_changed());
741 assert!(!q.get(&world, id2).unwrap().is_changed());
742 assert!(!q.get(&world, id3).unwrap().is_changed());
743 assert!(!q.get(&world, id4).unwrap().is_changed());
744 }
745
746 #[test]
747 fn visibility_propagation_with_invalid_parent() {
748 let mut world = World::new();
749 let mut schedule = Schedule::default();
750 schedule.add_systems(visibility_propagate_system);
751
752 let parent = world.spawn(()).id();
753 let child = world.spawn(VisibilityBundle::default()).id();
754 world.entity_mut(parent).push_children(&[child]);
755
756 schedule.run(&mut world);
757 world.clear_trackers();
758
759 let child_visible = world.entity(child).get::<InheritedVisibility>().unwrap().0;
760 assert!(child_visible);
762 }
763
764 #[test]
765 fn ensure_visibility_enum_size() {
766 use std::mem;
767 assert_eq!(1, mem::size_of::<Visibility>());
768 assert_eq!(1, mem::size_of::<Option<Visibility>>());
769 }
770}