From 8e9e00dfd5f5f5cd934b900920987f3b09de6f22 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Mon, 4 Jan 2021 21:18:59 -0800 Subject: [PATCH 01/13] can group rendering entities with masks --- .../src/camera/visible_entities.rs | 70 +++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index e1d54bc1c40ad..e1a13edfe98ab 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -24,22 +24,84 @@ impl VisibleEntities { } } +/// A mask that describes which rendering group an entity belongs to. +/// Cameras with this component will only render entities with a matching +/// mask. In other words, only an entity with a matching camera will be +/// rendered. +#[derive(Debug, Reflect, PartialEq, Eq, PartialOrd, Ord)] +#[reflect(Component)] +pub struct RenderingMask(pub u8); + +impl Default for RenderingMask { + fn default() -> Self { + RenderingMask( 0 ) + } +} + +impl RenderingMask { + pub fn group(n: u8) -> Self { + RenderingMask::default().with_group(n) + } + + pub fn with_group(mut self, group: u8) -> Self { + self.0 = self.0 | (1 << group); + self + } + + pub fn without_group(mut self, group: u8) -> Self { + self.0 = self.0 | (0 << group); + self + } + + pub fn matches(&self, other: &RenderingMask) -> bool { + (self.0 & other.0) > 0 + } +} + + +#[cfg(test)] +mod rendering_mask_tests { + use super::RenderingMask; + + #[test] + fn rendering_mask_sanity() { + // groups match groups + assert!(RenderingMask::group(1).matches(&RenderingMask::group(1))); + // a group of 0 means the mask is just 1 bit + assert!(RenderingMask::group(0).matches(&RenderingMask(1))); + // a mask will match another mask containing any similar groups + assert!(RenderingMask::group(0).with_group(3).matches(&RenderingMask::group(3))); + } +} + pub fn visible_entities_system( - mut camera_query: Query<(&Camera, &GlobalTransform, &mut VisibleEntities)>, - visible_query: Query<(Entity, &Visible)>, + mut camera_query: Query<(&Camera, &GlobalTransform, &mut VisibleEntities, Option<&RenderingMask>)>, + visible_query: Query<(Entity, &Visible, Option<&RenderingMask>)>, visible_transform_query: Query<&GlobalTransform, With>, ) { - for (camera, camera_global_transform, mut visible_entities) in camera_query.iter_mut() { + for (camera, camera_global_transform, mut visible_entities, maybe_camera_mask) in camera_query.iter_mut() { visible_entities.value.clear(); let camera_position = camera_global_transform.translation; let mut no_transform_order = 0.0; let mut transparent_entities = Vec::new(); - for (entity, visible) in visible_query.iter() { + for (entity, visible, maybe_ent_mask) in visible_query.iter() { if !visible.is_visible { continue; } + if let Some(camera_mask) = maybe_camera_mask { + if let Some(entity_mask) = maybe_ent_mask { + if !camera_mask.matches(entity_mask) { + continue; + } + } else { + continue; + } + } else if maybe_ent_mask.is_some() { + continue; + } + let order = if let Ok(global_transform) = visible_transform_query.get(entity) { let position = global_transform.translation; // smaller distances are sorted to lower indices by using the distance from the camera From 9ee41aa0ac9ed85caca93fbe44a6d15ef4cf840f Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Mon, 4 Jan 2021 21:22:35 -0800 Subject: [PATCH 02/13] docs --- crates/bevy_render/src/camera/visible_entities.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index e1a13edfe98ab..14ea767d2d146 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -26,8 +26,7 @@ impl VisibleEntities { /// A mask that describes which rendering group an entity belongs to. /// Cameras with this component will only render entities with a matching -/// mask. In other words, only an entity with a matching camera will be -/// rendered. +/// mask. #[derive(Debug, Reflect, PartialEq, Eq, PartialOrd, Ord)] #[reflect(Component)] pub struct RenderingMask(pub u8); From 5a605a7ebf4491139e21447a91a797b537ae36e0 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Mon, 4 Jan 2021 21:35:59 -0800 Subject: [PATCH 03/13] 0 mask matches 0 mask makes much easier logic --- .../src/camera/visible_entities.rs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 14ea767d2d146..d19258a8f2d62 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -27,9 +27,9 @@ impl VisibleEntities { /// A mask that describes which rendering group an entity belongs to. /// Cameras with this component will only render entities with a matching /// mask. -#[derive(Debug, Reflect, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, Reflect, PartialEq, Eq, PartialOrd, Ord)] #[reflect(Component)] -pub struct RenderingMask(pub u8); +pub struct RenderingMask(pub u32); impl Default for RenderingMask { fn default() -> Self { @@ -52,8 +52,12 @@ impl RenderingMask { self } + /// Determine if a `RenderingMask` matches. + /// `RenderingMask`s match if the first mask contains any of the groups + /// in the second, or if both masks are `0`. pub fn matches(&self, other: &RenderingMask) -> bool { - (self.0 & other.0) > 0 + ((self.0 & other.0) > 0) + || (self.0 == 0 && other.0 == 0) } } @@ -89,15 +93,9 @@ pub fn visible_entities_system( continue; } - if let Some(camera_mask) = maybe_camera_mask { - if let Some(entity_mask) = maybe_ent_mask { - if !camera_mask.matches(entity_mask) { - continue; - } - } else { - continue; - } - } else if maybe_ent_mask.is_some() { + let camera_mask = maybe_camera_mask.map(|m| *m).unwrap_or_else(|| RenderingMask(0)); + let entity_mask = maybe_ent_mask.map(|m| *m).unwrap_or_else(|| RenderingMask(0)); + if !camera_mask.matches(&entity_mask) { continue; } From c5e78b401b3f6b20efbaa1305a6ba82e1e2cd649 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Mon, 4 Jan 2021 21:43:56 -0800 Subject: [PATCH 04/13] fmt --- .../src/camera/visible_entities.rs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index d19258a8f2d62..712d4a63faeef 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -26,14 +26,14 @@ impl VisibleEntities { /// A mask that describes which rendering group an entity belongs to. /// Cameras with this component will only render entities with a matching -/// mask. +/// mask. #[derive(Copy, Clone, Debug, Reflect, PartialEq, Eq, PartialOrd, Ord)] #[reflect(Component)] pub struct RenderingMask(pub u32); impl Default for RenderingMask { fn default() -> Self { - RenderingMask( 0 ) + RenderingMask(0) } } @@ -56,12 +56,10 @@ impl RenderingMask { /// `RenderingMask`s match if the first mask contains any of the groups /// in the second, or if both masks are `0`. pub fn matches(&self, other: &RenderingMask) -> bool { - ((self.0 & other.0) > 0) - || (self.0 == 0 && other.0 == 0) + ((self.0 & other.0) > 0) || (self.0 == 0 && other.0 == 0) } } - #[cfg(test)] mod rendering_mask_tests { use super::RenderingMask; @@ -73,16 +71,25 @@ mod rendering_mask_tests { // a group of 0 means the mask is just 1 bit assert!(RenderingMask::group(0).matches(&RenderingMask(1))); // a mask will match another mask containing any similar groups - assert!(RenderingMask::group(0).with_group(3).matches(&RenderingMask::group(3))); + assert!(RenderingMask::group(0) + .with_group(3) + .matches(&RenderingMask::group(3))); } } pub fn visible_entities_system( - mut camera_query: Query<(&Camera, &GlobalTransform, &mut VisibleEntities, Option<&RenderingMask>)>, + mut camera_query: Query<( + &Camera, + &GlobalTransform, + &mut VisibleEntities, + Option<&RenderingMask>, + )>, visible_query: Query<(Entity, &Visible, Option<&RenderingMask>)>, visible_transform_query: Query<&GlobalTransform, With>, ) { - for (camera, camera_global_transform, mut visible_entities, maybe_camera_mask) in camera_query.iter_mut() { + for (camera, camera_global_transform, mut visible_entities, maybe_camera_mask) in + camera_query.iter_mut() + { visible_entities.value.clear(); let camera_position = camera_global_transform.translation; @@ -93,8 +100,12 @@ pub fn visible_entities_system( continue; } - let camera_mask = maybe_camera_mask.map(|m| *m).unwrap_or_else(|| RenderingMask(0)); - let entity_mask = maybe_ent_mask.map(|m| *m).unwrap_or_else(|| RenderingMask(0)); + let camera_mask = maybe_camera_mask + .map(|m| *m) + .unwrap_or_else(|| RenderingMask(0)); + let entity_mask = maybe_ent_mask + .map(|m| *m) + .unwrap_or_else(|| RenderingMask(0)); if !camera_mask.matches(&entity_mask) { continue; } From 0c14c6d9b93fe306819a50804a82342302673264 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Tue, 5 Jan 2021 09:43:02 -0800 Subject: [PATCH 05/13] PR fixup - RenderingMask defaults to group 0 --- crates/bevy_render/src/camera/visible_entities.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 712d4a63faeef..0f5cad41bc2e2 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -33,7 +33,7 @@ pub struct RenderingMask(pub u32); impl Default for RenderingMask { fn default() -> Self { - RenderingMask(0) + RenderingMask(1) } } @@ -43,12 +43,12 @@ impl RenderingMask { } pub fn with_group(mut self, group: u8) -> Self { - self.0 = self.0 | (1 << group); + self.0 |= 1 << group; self } pub fn without_group(mut self, group: u8) -> Self { - self.0 = self.0 | (0 << group); + self.0 |= 0 << group; self } @@ -101,10 +101,10 @@ pub fn visible_entities_system( } let camera_mask = maybe_camera_mask - .map(|m| *m) + .copied() .unwrap_or_else(|| RenderingMask(0)); let entity_mask = maybe_ent_mask - .map(|m| *m) + .copied() .unwrap_or_else(|| RenderingMask(0)); if !camera_mask.matches(&entity_mask) { continue; From 3231e2f26c0e7ead161f1f1ada765b7c0b2cfefa Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Tue, 5 Jan 2021 13:03:59 -0800 Subject: [PATCH 06/13] render mask logic uses unwrap_or_default, better docs, fmt --- .../src/camera/visible_entities.rs | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 0f5cad41bc2e2..0b6e7461f37c4 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -24,37 +24,46 @@ impl VisibleEntities { } } -/// A mask that describes which rendering group an entity belongs to. +/// A mask describes which rendering group an entity belongs to. +/// /// Cameras with this component will only render entities with a matching /// mask. +/// +/// The [`Default`] instance of `RenderingMask` returns a mask that contains +/// no groups. #[derive(Copy, Clone, Debug, Reflect, PartialEq, Eq, PartialOrd, Ord)] #[reflect(Component)] pub struct RenderingMask(pub u32); impl Default for RenderingMask { fn default() -> Self { - RenderingMask(1) + RenderingMask(0) } } impl RenderingMask { + /// Create new `RenderingMask` with the given rendering group set. pub fn group(n: u8) -> Self { - RenderingMask::default().with_group(n) + RenderingMask::default().set_group(n) } - pub fn with_group(mut self, group: u8) -> Self { + /// Set the given group on the mask. + /// This may be called multiple times to allow an entity to belong + /// to multiple rendering groups. + pub fn set_group(mut self, group: u8) -> Self { self.0 |= 1 << group; self } - pub fn without_group(mut self, group: u8) -> Self { + /// Unset the given rendering group from the mask. + pub fn unset_group(mut self, group: u8) -> Self { self.0 |= 0 << group; self } - /// Determine if a `RenderingMask` matches. + /// Determine if a `RenderingMask` matches another. /// `RenderingMask`s match if the first mask contains any of the groups - /// in the second, or if both masks are `0`. + /// in the other, or if both masks contain no groups. pub fn matches(&self, other: &RenderingMask) -> bool { ((self.0 & other.0) > 0) || (self.0 == 0 && other.0 == 0) } @@ -72,8 +81,12 @@ mod rendering_mask_tests { assert!(RenderingMask::group(0).matches(&RenderingMask(1))); // a mask will match another mask containing any similar groups assert!(RenderingMask::group(0) - .with_group(3) + .set_group(3) .matches(&RenderingMask::group(3))); + // default masks match each other + assert!(RenderingMask::default().matches(&RenderingMask::default())); + // masks with differing groups do not match + assert_eq!(RenderingMask::group(0).matches(&RenderingMask::group(1)), false); } } @@ -95,17 +108,13 @@ pub fn visible_entities_system( let mut no_transform_order = 0.0; let mut transparent_entities = Vec::new(); - for (entity, visible, maybe_ent_mask) in visible_query.iter() { + for (entity, visible, maybe_entity_mask) in visible_query.iter() { if !visible.is_visible { continue; } - let camera_mask = maybe_camera_mask - .copied() - .unwrap_or_else(|| RenderingMask(0)); - let entity_mask = maybe_ent_mask - .copied() - .unwrap_or_else(|| RenderingMask(0)); + let camera_mask = maybe_camera_mask.copied().unwrap_or_default(); + let entity_mask = maybe_entity_mask.copied().unwrap_or_default(); if !camera_mask.matches(&entity_mask) { continue; } From d4df730ceba6d275e12c1c91a160354411f55a8d Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Tue, 5 Jan 2021 13:08:16 -0800 Subject: [PATCH 07/13] ugh fmt --- crates/bevy_render/src/camera/visible_entities.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 0b6e7461f37c4..e364ecdfd963b 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -86,7 +86,10 @@ mod rendering_mask_tests { // default masks match each other assert!(RenderingMask::default().matches(&RenderingMask::default())); // masks with differing groups do not match - assert_eq!(RenderingMask::group(0).matches(&RenderingMask::group(1)), false); + assert_eq!( + RenderingMask::group(0).matches(&RenderingMask::group(1)), + false + ); } } From b1a43f8fa7e24c80d786e99200e3392cbd105ff6 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Thu, 7 Jan 2021 10:55:27 -0800 Subject: [PATCH 08/13] default RenderingMask is group 0 (mask 1), more tests, fix inner loop, private mask, docs, fn names, added all_groups and no_groups --- .../src/camera/visible_entities.rs | 100 +++++++++++++----- 1 file changed, 74 insertions(+), 26 deletions(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index e364ecdfd963b..9bb1bceb649e6 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -29,43 +29,72 @@ impl VisibleEntities { /// Cameras with this component will only render entities with a matching /// mask. /// -/// The [`Default`] instance of `RenderingMask` returns a mask that contains -/// no groups. +/// There are 32 groups numbered `0` - `31`. A mask may belong to one or more +/// groups, or no group at all. +/// +/// An entity with a mask belonging to no groups is invisible. +/// +/// The [`Default`] instance of `RenderingMask` returns a mask belonging to +/// group `0`, the first group. #[derive(Copy, Clone, Debug, Reflect, PartialEq, Eq, PartialOrd, Ord)] #[reflect(Component)] -pub struct RenderingMask(pub u32); +pub struct RenderingMask(u32); +/// Defaults to a mask belonging to group `0`, the first group. impl Default for RenderingMask { fn default() -> Self { - RenderingMask(0) + RenderingMask(1) } } impl RenderingMask { - /// Create new `RenderingMask` with the given rendering group set. + /// Create a new `RenderingMask` belonging to the given rendering group. pub fn group(n: u8) -> Self { - RenderingMask::default().set_group(n) + RenderingMask(0).with_group(n) + } + + /// Create a new `RenderingMask` that belongs to all rendering groups. + pub fn all_groups() -> Self { + RenderingMask(u32::MAX) } - /// Set the given group on the mask. + /// Create a new `RenderingMask` that belongs to no rendering groups. + pub fn no_groups() -> Self { + RenderingMask(0) + } + + /// Add the given group to the mask. + /// /// This may be called multiple times to allow an entity to belong - /// to multiple rendering groups. - pub fn set_group(mut self, group: u8) -> Self { + /// to multiple rendering groups. The maximum group is 31. + /// + /// # Panics + /// Panics when called with a group greater than 31. + pub fn with_group(mut self, group: u8) -> Self { + assert!(group < 32, "RenderingMask only supports groups 0 to 31"); self.0 |= 1 << group; self } - /// Unset the given rendering group from the mask. - pub fn unset_group(mut self, group: u8) -> Self { + /// Removes the given rendering group from the mask. + /// + /// # Panics + /// Panics when called with a group greater than 31. + pub fn without_group(mut self, group: u8) -> Self { + assert!(group < 32, "RenderingMask only supports groups 0 to 31"); self.0 |= 0 << group; self } /// Determine if a `RenderingMask` matches another. + /// /// `RenderingMask`s match if the first mask contains any of the groups - /// in the other, or if both masks contain no groups. + /// in the other. + /// + /// A `RenderingMask` belonging to no groups will not match any other + /// mask, even another belonging to no groups. pub fn matches(&self, other: &RenderingMask) -> bool { - ((self.0 & other.0) > 0) || (self.0 == 0 && other.0 == 0) + (self.0 & other.0) > 0 } } @@ -75,20 +104,39 @@ mod rendering_mask_tests { #[test] fn rendering_mask_sanity() { - // groups match groups - assert!(RenderingMask::group(1).matches(&RenderingMask::group(1))); - // a group of 0 means the mask is just 1 bit - assert!(RenderingMask::group(0).matches(&RenderingMask(1))); - // a mask will match another mask containing any similar groups - assert!(RenderingMask::group(0) - .set_group(3) - .matches(&RenderingMask::group(3))); - // default masks match each other - assert!(RenderingMask::default().matches(&RenderingMask::default())); - // masks with differing groups do not match + assert_eq!(RenderingMask::group(0).0, 1, "group 0 is mask 1"); + assert_eq!(RenderingMask::group(1).0, 2, "group 1 is mask 2"); + assert_eq!(RenderingMask::group(0).with_group(1).0, 3, "group 0 + 1 is mask 3"); + assert!( + RenderingMask::group(1).matches(&RenderingMask::group(1)), + "groups match like groups" + ); + assert!( + RenderingMask::group(0).matches(&RenderingMask(1)), + "a group of 0 means the mask is just 1 bit" + ); + + assert!( + RenderingMask::group(0) + .with_group(3) + .matches(&RenderingMask::group(3)), + "a mask will match another mask containing any similar groups" + ); + + assert!( + RenderingMask::default().matches(&RenderingMask::default()), + "default masks match each other" + ); + assert_eq!( RenderingMask::group(0).matches(&RenderingMask::group(1)), - false + false, + "masks with differing groups do not match" + ); + assert_eq!( + RenderingMask(0).matches(&RenderingMask(0)), + false, + "empty masks don't match" ); } } @@ -108,6 +156,7 @@ pub fn visible_entities_system( { visible_entities.value.clear(); let camera_position = camera_global_transform.translation; + let camera_mask = maybe_camera_mask.copied().unwrap_or_default(); let mut no_transform_order = 0.0; let mut transparent_entities = Vec::new(); @@ -116,7 +165,6 @@ pub fn visible_entities_system( continue; } - let camera_mask = maybe_camera_mask.copied().unwrap_or_default(); let entity_mask = maybe_entity_mask.copied().unwrap_or_default(); if !camera_mask.matches(&entity_mask) { continue; From d39a4e654f164f42b5349d9ffa3587dad08ee0ad Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Thu, 7 Jan 2021 11:02:50 -0800 Subject: [PATCH 09/13] fmt --- crates/bevy_render/src/camera/visible_entities.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 9bb1bceb649e6..086790dac9fde 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -106,7 +106,11 @@ mod rendering_mask_tests { fn rendering_mask_sanity() { assert_eq!(RenderingMask::group(0).0, 1, "group 0 is mask 1"); assert_eq!(RenderingMask::group(1).0, 2, "group 1 is mask 2"); - assert_eq!(RenderingMask::group(0).with_group(1).0, 3, "group 0 + 1 is mask 3"); + assert_eq!( + RenderingMask::group(0).with_group(1).0, + 3, + "group 0 + 1 is mask 3" + ); assert!( RenderingMask::group(1).matches(&RenderingMask::group(1)), "groups match like groups" From 01cd3cee91acc152849013d46da69a35bb3b6736 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Thu, 7 Jan 2021 11:55:33 -0800 Subject: [PATCH 10/13] rendering mask shows groups in Debug, FromIterator, from_groups, get_groups --- .../src/camera/visible_entities.rs | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 086790dac9fde..5697ab7bbec4f 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -36,10 +36,25 @@ impl VisibleEntities { /// /// The [`Default`] instance of `RenderingMask` returns a mask belonging to /// group `0`, the first group. -#[derive(Copy, Clone, Debug, Reflect, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Reflect, PartialEq, Eq, PartialOrd, Ord)] #[reflect(Component)] pub struct RenderingMask(u32); +impl std::fmt::Debug for RenderingMask { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("RenderingMask") + .field(&self.get_groups()) + .finish() + } +} + +impl std::iter::FromIterator for RenderingMask { + fn from_iter>(i: T) -> Self { + i.into_iter() + .fold(RenderingMask(0), |mask, g| mask.with_group(g)) + } +} + /// Defaults to a mask belonging to group `0`, the first group. impl Default for RenderingMask { fn default() -> Self { @@ -63,6 +78,13 @@ impl RenderingMask { RenderingMask(0) } + /// Create a `RenderingMask` from a list of groups. + pub fn from_groups(groups: &[u8]) -> Self { + groups + .iter() + .fold(RenderingMask(0), |mask, g| mask.with_group(*g)) + } + /// Add the given group to the mask. /// /// This may be called multiple times to allow an entity to belong @@ -86,6 +108,13 @@ impl RenderingMask { self } + /// Get a vector of this mask's groups. + pub fn get_groups(&self) -> Vec { + (0..32) + .filter(|g| RenderingMask::group(*g).matches(self)) + .collect::>() + } + /// Determine if a `RenderingMask` matches another. /// /// `RenderingMask`s match if the first mask contains any of the groups @@ -142,6 +171,21 @@ mod rendering_mask_tests { false, "empty masks don't match" ); + assert_eq!( + RenderingMask::from_groups(&[0, 2, 16, 30]).get_groups(), + vec![0, 2, 16, 30], + "from_groups and get_groups should roundtrip" + ); + assert_eq!( + format!("{:?}", RenderingMask::from_groups(&[0, 1, 2, 3])).as_str(), + "RenderingMask([0, 1, 2, 3])", + "Debug instance shows groups" + ); + assert_eq!( + RenderingMask::from_groups(&[0, 1, 2]), + >::from_iter(vec![0, 1, 2]), + "from_groups and from_iter are equivalent" + ) } } From b1e84a4eef5e5a7b836a408496c1020e98786499 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Thu, 7 Jan 2021 19:53:14 -0800 Subject: [PATCH 11/13] render layers rename, type aliases, iters --- .../src/camera/visible_entities.rs | 178 +++++++++--------- 1 file changed, 93 insertions(+), 85 deletions(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 5697ab7bbec4f..b9b38a9fa95d0 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -24,167 +24,175 @@ impl VisibleEntities { } } -/// A mask describes which rendering group an entity belongs to. +type LayerMask = u32; + +/// An identifier for a rendering layer. +pub type Layer = u8; + +/// Describes which rendering layers an entity belongs to. /// -/// Cameras with this component will only render entities with a matching -/// mask. +/// Cameras with this component will only render entities with intersecting +/// layers. /// -/// There are 32 groups numbered `0` - `31`. A mask may belong to one or more -/// groups, or no group at all. +/// There are 32 layers numbered `0` - [`TOTAL_LAYERS`]. Entities may belong to one or more +/// layers, or no layer at all. /// -/// An entity with a mask belonging to no groups is invisible. +/// The [`Default`] instance of `RenderLayers` contains layer `0`, the first layer. /// -/// The [`Default`] instance of `RenderingMask` returns a mask belonging to -/// group `0`, the first group. +/// An entity with this component without any layers is invisible. +/// +/// Entities without this component belong to layer `0`. #[derive(Copy, Clone, Reflect, PartialEq, Eq, PartialOrd, Ord)] #[reflect(Component)] -pub struct RenderingMask(u32); +pub struct RenderLayers(LayerMask); -impl std::fmt::Debug for RenderingMask { +impl std::fmt::Debug for RenderLayers { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("RenderingMask") - .field(&self.get_groups()) + f.debug_tuple("RenderLayers") + .field(&self.layers_iter().collect::>()) .finish() } } -impl std::iter::FromIterator for RenderingMask { - fn from_iter>(i: T) -> Self { - i.into_iter() - .fold(RenderingMask(0), |mask, g| mask.with_group(g)) +impl std::iter::FromIterator for RenderLayers { + fn from_iter>(i: T) -> Self { + i.into_iter().fold(RenderLayers(0), |mask, g| mask.with(g)) } } -/// Defaults to a mask belonging to group `0`, the first group. -impl Default for RenderingMask { +/// Defaults to containing to layer `0`, the first layer. +impl Default for RenderLayers { fn default() -> Self { - RenderingMask(1) + RenderLayers(1) } } -impl RenderingMask { - /// Create a new `RenderingMask` belonging to the given rendering group. - pub fn group(n: u8) -> Self { - RenderingMask(0).with_group(n) +impl RenderLayers { + /// The total number of layers supported. + pub const TOTAL_LAYERS: usize = std::mem::size_of::() * 8; + + /// Create a new `RenderLayers` belonging to the given layer. + pub fn layer(n: Layer) -> Self { + RenderLayers(0).with(n) } - /// Create a new `RenderingMask` that belongs to all rendering groups. - pub fn all_groups() -> Self { - RenderingMask(u32::MAX) + /// Create a new `RenderLayers` that belongs to all layers. + pub fn all() -> Self { + RenderLayers(u32::MAX) } - /// Create a new `RenderingMask` that belongs to no rendering groups. - pub fn no_groups() -> Self { - RenderingMask(0) + /// Create a new `RenderLayers` that belongs to no layers. + pub fn none() -> Self { + RenderLayers(0) } - /// Create a `RenderingMask` from a list of groups. - pub fn from_groups(groups: &[u8]) -> Self { - groups - .iter() - .fold(RenderingMask(0), |mask, g| mask.with_group(*g)) + /// Create a `RenderLayers` from a list of layers. + pub fn from_layers(layers: &[Layer]) -> Self { + layers.iter().fold(RenderLayers(0), |mask, g| mask.with(*g)) } - /// Add the given group to the mask. + /// Add the given layer. /// /// This may be called multiple times to allow an entity to belong - /// to multiple rendering groups. The maximum group is 31. + /// to multiple rendering layers. The maximum layer is `TOTAL_LAYERS - 1`. /// /// # Panics - /// Panics when called with a group greater than 31. - pub fn with_group(mut self, group: u8) -> Self { - assert!(group < 32, "RenderingMask only supports groups 0 to 31"); - self.0 |= 1 << group; + /// Panics when called with a layer greater than `TOTAL_LAYERS - 1`. + pub fn with(mut self, layer: Layer) -> Self { + assert!(usize::from(layer) < Self::TOTAL_LAYERS); + self.0 |= 1 << layer; self } - /// Removes the given rendering group from the mask. + /// Removes the given rendering layer. /// /// # Panics - /// Panics when called with a group greater than 31. - pub fn without_group(mut self, group: u8) -> Self { - assert!(group < 32, "RenderingMask only supports groups 0 to 31"); - self.0 |= 0 << group; + /// Panics when called with a layer greater than `TOTAL_LAYERS - 1`. + pub fn without(mut self, layer: Layer) -> Self { + assert!(usize::from(layer) < Self::TOTAL_LAYERS); + self.0 |= 0 << layer; self } - /// Get a vector of this mask's groups. - pub fn get_groups(&self) -> Vec { - (0..32) - .filter(|g| RenderingMask::group(*g).matches(self)) - .collect::>() + /// Get an iterator of the layers. + pub fn layers_iter(&self) -> impl Iterator { + let total: Layer = std::convert::TryInto::try_into(Self::TOTAL_LAYERS).unwrap(); + let mask = *self; + (0..total).filter(move |g| RenderLayers::layer(*g).intersects(&mask)) } - /// Determine if a `RenderingMask` matches another. + /// Determine if a `RenderLayers` intersects another. /// - /// `RenderingMask`s match if the first mask contains any of the groups - /// in the other. + /// `RenderLayers`s intersect if they share any common layers. /// - /// A `RenderingMask` belonging to no groups will not match any other - /// mask, even another belonging to no groups. - pub fn matches(&self, other: &RenderingMask) -> bool { + /// A `RenderLayers` with no layers will not match any other + /// `RenderLayers`, even another with no layers. + pub fn intersects(&self, other: &RenderLayers) -> bool { (self.0 & other.0) > 0 } } #[cfg(test)] mod rendering_mask_tests { - use super::RenderingMask; + use super::{Layer, RenderLayers}; #[test] fn rendering_mask_sanity() { - assert_eq!(RenderingMask::group(0).0, 1, "group 0 is mask 1"); - assert_eq!(RenderingMask::group(1).0, 2, "group 1 is mask 2"); assert_eq!( - RenderingMask::group(0).with_group(1).0, - 3, - "group 0 + 1 is mask 3" + RenderLayers::TOTAL_LAYERS, + 32, + "total layers is what we think it is" ); + assert_eq!(RenderLayers::layer(0).0, 1, "layer 0 is mask 1"); + assert_eq!(RenderLayers::layer(1).0, 2, "layer 1 is mask 2"); + assert_eq!(RenderLayers::layer(0).with(1).0, 3, "layer 0 + 1 is mask 3"); assert!( - RenderingMask::group(1).matches(&RenderingMask::group(1)), - "groups match like groups" + RenderLayers::layer(1).intersects(&RenderLayers::layer(1)), + "layers match like layers" ); assert!( - RenderingMask::group(0).matches(&RenderingMask(1)), - "a group of 0 means the mask is just 1 bit" + RenderLayers::layer(0).intersects(&RenderLayers(1)), + "a layer of 0 means the mask is just 1 bit" ); assert!( - RenderingMask::group(0) - .with_group(3) - .matches(&RenderingMask::group(3)), - "a mask will match another mask containing any similar groups" + RenderLayers::layer(0) + .with(3) + .intersects(&RenderLayers::layer(3)), + "a mask will match another mask containing any similar layers" ); assert!( - RenderingMask::default().matches(&RenderingMask::default()), + RenderLayers::default().intersects(&RenderLayers::default()), "default masks match each other" ); assert_eq!( - RenderingMask::group(0).matches(&RenderingMask::group(1)), + RenderLayers::layer(0).intersects(&RenderLayers::layer(1)), false, - "masks with differing groups do not match" + "masks with differing layers do not match" ); assert_eq!( - RenderingMask(0).matches(&RenderingMask(0)), + RenderLayers(0).intersects(&RenderLayers(0)), false, "empty masks don't match" ); assert_eq!( - RenderingMask::from_groups(&[0, 2, 16, 30]).get_groups(), + RenderLayers::from_layers(&[0, 2, 16, 30]) + .layers_iter() + .collect::>(), vec![0, 2, 16, 30], - "from_groups and get_groups should roundtrip" + "from_layers and get_layers should roundtrip" ); assert_eq!( - format!("{:?}", RenderingMask::from_groups(&[0, 1, 2, 3])).as_str(), - "RenderingMask([0, 1, 2, 3])", - "Debug instance shows groups" + format!("{:?}", RenderLayers::from_layers(&[0, 1, 2, 3])).as_str(), + "RenderLayers([0, 1, 2, 3])", + "Debug instance shows layers" ); assert_eq!( - RenderingMask::from_groups(&[0, 1, 2]), - >::from_iter(vec![0, 1, 2]), - "from_groups and from_iter are equivalent" + RenderLayers::from_layers(&[0, 1, 2]), + >::from_iter(vec![0, 1, 2]), + "from_layers and from_iter are equivalent" ) } } @@ -194,9 +202,9 @@ pub fn visible_entities_system( &Camera, &GlobalTransform, &mut VisibleEntities, - Option<&RenderingMask>, + Option<&RenderLayers>, )>, - visible_query: Query<(Entity, &Visible, Option<&RenderingMask>)>, + visible_query: Query<(Entity, &Visible, Option<&RenderLayers>)>, visible_transform_query: Query<&GlobalTransform, With>, ) { for (camera, camera_global_transform, mut visible_entities, maybe_camera_mask) in @@ -214,7 +222,7 @@ pub fn visible_entities_system( } let entity_mask = maybe_entity_mask.copied().unwrap_or_default(); - if !camera_mask.matches(&entity_mask) { + if !camera_mask.intersects(&entity_mask) { continue; } From 9ba851b23f2f6bb9e7829f147695fcc1cbb38e56 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Fri, 8 Jan 2021 08:26:00 -0800 Subject: [PATCH 12/13] PR fixup --- crates/bevy_render/src/camera/visible_entities.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index b9b38a9fa95d0..0a3531d63df21 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -56,14 +56,14 @@ impl std::fmt::Debug for RenderLayers { impl std::iter::FromIterator for RenderLayers { fn from_iter>(i: T) -> Self { - i.into_iter().fold(RenderLayers(0), |mask, g| mask.with(g)) + i.into_iter().fold(Self::none(), |mask, g| mask.with(g)) } } /// Defaults to containing to layer `0`, the first layer. impl Default for RenderLayers { fn default() -> Self { - RenderLayers(1) + RenderLayers::layer(0) } } @@ -88,7 +88,7 @@ impl RenderLayers { /// Create a `RenderLayers` from a list of layers. pub fn from_layers(layers: &[Layer]) -> Self { - layers.iter().fold(RenderLayers(0), |mask, g| mask.with(*g)) + layers.iter().copied().collect() } /// Add the given layer. From bf9a1c1f649ef3c6329f673b1dce113305379fff Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Fri, 8 Jan 2021 12:03:05 -0800 Subject: [PATCH 13/13] rename iter_layers to iter --- crates/bevy_render/src/camera/visible_entities.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 0a3531d63df21..206c9e763cf4e 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -49,7 +49,7 @@ pub struct RenderLayers(LayerMask); impl std::fmt::Debug for RenderLayers { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("RenderLayers") - .field(&self.layers_iter().collect::>()) + .field(&self.iter().collect::>()) .finish() } } @@ -115,7 +115,7 @@ impl RenderLayers { } /// Get an iterator of the layers. - pub fn layers_iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { let total: Layer = std::convert::TryInto::try_into(Self::TOTAL_LAYERS).unwrap(); let mask = *self; (0..total).filter(move |g| RenderLayers::layer(*g).intersects(&mask)) @@ -179,7 +179,7 @@ mod rendering_mask_tests { ); assert_eq!( RenderLayers::from_layers(&[0, 2, 16, 30]) - .layers_iter() + .iter() .collect::>(), vec![0, 2, 16, 30], "from_layers and get_layers should roundtrip"