Skip to content

Commit a6a242c

Browse files
authored
Render Layers (#1209)
Adds RenderLayers, which enable cameras and entities to opt in to layers that apply to them
1 parent 3f2dd22 commit a6a242c

File tree

1 file changed

+190
-4
lines changed

1 file changed

+190
-4
lines changed

crates/bevy_render/src/camera/visible_entities.rs

Lines changed: 190 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,208 @@ impl VisibleEntities {
2424
}
2525
}
2626

27+
type LayerMask = u32;
28+
29+
/// An identifier for a rendering layer.
30+
pub type Layer = u8;
31+
32+
/// Describes which rendering layers an entity belongs to.
33+
///
34+
/// Cameras with this component will only render entities with intersecting
35+
/// layers.
36+
///
37+
/// There are 32 layers numbered `0` - [`TOTAL_LAYERS`]. Entities may belong to one or more
38+
/// layers, or no layer at all.
39+
///
40+
/// The [`Default`] instance of `RenderLayers` contains layer `0`, the first layer.
41+
///
42+
/// An entity with this component without any layers is invisible.
43+
///
44+
/// Entities without this component belong to layer `0`.
45+
#[derive(Copy, Clone, Reflect, PartialEq, Eq, PartialOrd, Ord)]
46+
#[reflect(Component)]
47+
pub struct RenderLayers(LayerMask);
48+
49+
impl std::fmt::Debug for RenderLayers {
50+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51+
f.debug_tuple("RenderLayers")
52+
.field(&self.iter().collect::<Vec<_>>())
53+
.finish()
54+
}
55+
}
56+
57+
impl std::iter::FromIterator<Layer> for RenderLayers {
58+
fn from_iter<T: IntoIterator<Item = Layer>>(i: T) -> Self {
59+
i.into_iter().fold(Self::none(), |mask, g| mask.with(g))
60+
}
61+
}
62+
63+
/// Defaults to containing to layer `0`, the first layer.
64+
impl Default for RenderLayers {
65+
fn default() -> Self {
66+
RenderLayers::layer(0)
67+
}
68+
}
69+
70+
impl RenderLayers {
71+
/// The total number of layers supported.
72+
pub const TOTAL_LAYERS: usize = std::mem::size_of::<LayerMask>() * 8;
73+
74+
/// Create a new `RenderLayers` belonging to the given layer.
75+
pub fn layer(n: Layer) -> Self {
76+
RenderLayers(0).with(n)
77+
}
78+
79+
/// Create a new `RenderLayers` that belongs to all layers.
80+
pub fn all() -> Self {
81+
RenderLayers(u32::MAX)
82+
}
83+
84+
/// Create a new `RenderLayers` that belongs to no layers.
85+
pub fn none() -> Self {
86+
RenderLayers(0)
87+
}
88+
89+
/// Create a `RenderLayers` from a list of layers.
90+
pub fn from_layers(layers: &[Layer]) -> Self {
91+
layers.iter().copied().collect()
92+
}
93+
94+
/// Add the given layer.
95+
///
96+
/// This may be called multiple times to allow an entity to belong
97+
/// to multiple rendering layers. The maximum layer is `TOTAL_LAYERS - 1`.
98+
///
99+
/// # Panics
100+
/// Panics when called with a layer greater than `TOTAL_LAYERS - 1`.
101+
pub fn with(mut self, layer: Layer) -> Self {
102+
assert!(usize::from(layer) < Self::TOTAL_LAYERS);
103+
self.0 |= 1 << layer;
104+
self
105+
}
106+
107+
/// Removes the given rendering layer.
108+
///
109+
/// # Panics
110+
/// Panics when called with a layer greater than `TOTAL_LAYERS - 1`.
111+
pub fn without(mut self, layer: Layer) -> Self {
112+
assert!(usize::from(layer) < Self::TOTAL_LAYERS);
113+
self.0 |= 0 << layer;
114+
self
115+
}
116+
117+
/// Get an iterator of the layers.
118+
pub fn iter(&self) -> impl Iterator<Item = Layer> {
119+
let total: Layer = std::convert::TryInto::try_into(Self::TOTAL_LAYERS).unwrap();
120+
let mask = *self;
121+
(0..total).filter(move |g| RenderLayers::layer(*g).intersects(&mask))
122+
}
123+
124+
/// Determine if a `RenderLayers` intersects another.
125+
///
126+
/// `RenderLayers`s intersect if they share any common layers.
127+
///
128+
/// A `RenderLayers` with no layers will not match any other
129+
/// `RenderLayers`, even another with no layers.
130+
pub fn intersects(&self, other: &RenderLayers) -> bool {
131+
(self.0 & other.0) > 0
132+
}
133+
}
134+
135+
#[cfg(test)]
136+
mod rendering_mask_tests {
137+
use super::{Layer, RenderLayers};
138+
139+
#[test]
140+
fn rendering_mask_sanity() {
141+
assert_eq!(
142+
RenderLayers::TOTAL_LAYERS,
143+
32,
144+
"total layers is what we think it is"
145+
);
146+
assert_eq!(RenderLayers::layer(0).0, 1, "layer 0 is mask 1");
147+
assert_eq!(RenderLayers::layer(1).0, 2, "layer 1 is mask 2");
148+
assert_eq!(RenderLayers::layer(0).with(1).0, 3, "layer 0 + 1 is mask 3");
149+
assert!(
150+
RenderLayers::layer(1).intersects(&RenderLayers::layer(1)),
151+
"layers match like layers"
152+
);
153+
assert!(
154+
RenderLayers::layer(0).intersects(&RenderLayers(1)),
155+
"a layer of 0 means the mask is just 1 bit"
156+
);
157+
158+
assert!(
159+
RenderLayers::layer(0)
160+
.with(3)
161+
.intersects(&RenderLayers::layer(3)),
162+
"a mask will match another mask containing any similar layers"
163+
);
164+
165+
assert!(
166+
RenderLayers::default().intersects(&RenderLayers::default()),
167+
"default masks match each other"
168+
);
169+
170+
assert_eq!(
171+
RenderLayers::layer(0).intersects(&RenderLayers::layer(1)),
172+
false,
173+
"masks with differing layers do not match"
174+
);
175+
assert_eq!(
176+
RenderLayers(0).intersects(&RenderLayers(0)),
177+
false,
178+
"empty masks don't match"
179+
);
180+
assert_eq!(
181+
RenderLayers::from_layers(&[0, 2, 16, 30])
182+
.iter()
183+
.collect::<Vec<_>>(),
184+
vec![0, 2, 16, 30],
185+
"from_layers and get_layers should roundtrip"
186+
);
187+
assert_eq!(
188+
format!("{:?}", RenderLayers::from_layers(&[0, 1, 2, 3])).as_str(),
189+
"RenderLayers([0, 1, 2, 3])",
190+
"Debug instance shows layers"
191+
);
192+
assert_eq!(
193+
RenderLayers::from_layers(&[0, 1, 2]),
194+
<RenderLayers as std::iter::FromIterator<Layer>>::from_iter(vec![0, 1, 2]),
195+
"from_layers and from_iter are equivalent"
196+
)
197+
}
198+
}
199+
27200
pub fn visible_entities_system(
28-
mut camera_query: Query<(&Camera, &GlobalTransform, &mut VisibleEntities)>,
29-
visible_query: Query<(Entity, &Visible)>,
201+
mut camera_query: Query<(
202+
&Camera,
203+
&GlobalTransform,
204+
&mut VisibleEntities,
205+
Option<&RenderLayers>,
206+
)>,
207+
visible_query: Query<(Entity, &Visible, Option<&RenderLayers>)>,
30208
visible_transform_query: Query<&GlobalTransform, With<Visible>>,
31209
) {
32-
for (camera, camera_global_transform, mut visible_entities) in camera_query.iter_mut() {
210+
for (camera, camera_global_transform, mut visible_entities, maybe_camera_mask) in
211+
camera_query.iter_mut()
212+
{
33213
visible_entities.value.clear();
34214
let camera_position = camera_global_transform.translation;
215+
let camera_mask = maybe_camera_mask.copied().unwrap_or_default();
35216

36217
let mut no_transform_order = 0.0;
37218
let mut transparent_entities = Vec::new();
38-
for (entity, visible) in visible_query.iter() {
219+
for (entity, visible, maybe_entity_mask) in visible_query.iter() {
39220
if !visible.is_visible {
40221
continue;
41222
}
42223

224+
let entity_mask = maybe_entity_mask.copied().unwrap_or_default();
225+
if !camera_mask.intersects(&entity_mask) {
226+
continue;
227+
}
228+
43229
let order = if let Ok(global_transform) = visible_transform_query.get(entity) {
44230
let position = global_transform.translation;
45231
// smaller distances are sorted to lower indices by using the distance from the camera

0 commit comments

Comments
 (0)