use bevy_ecs::prelude::{Component, ReflectComponent};
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;
type LayerMask = u32;
pub type Layer = u8;
#[derive(Component, Copy, Clone, Reflect, PartialEq, Eq, PartialOrd, Ord)]
#[reflect(Component, Default, PartialEq)]
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.iter().collect::<Vec<_>>())
.finish()
}
}
impl FromIterator<Layer> for RenderLayers {
fn from_iter<T: IntoIterator<Item = Layer>>(i: T) -> Self {
i.into_iter().fold(Self::none(), |mask, g| mask.with(g))
}
}
impl Default for RenderLayers {
fn default() -> Self {
RenderLayers::layer(0)
}
}
impl RenderLayers {
pub const TOTAL_LAYERS: usize = std::mem::size_of::<LayerMask>() * 8;
pub const fn layer(n: Layer) -> Self {
RenderLayers(0).with(n)
}
pub const fn all() -> Self {
RenderLayers(u32::MAX)
}
pub const fn none() -> Self {
RenderLayers(0)
}
pub fn from_layers(layers: &[Layer]) -> Self {
layers.iter().copied().collect()
}
#[must_use]
pub const fn with(mut self, layer: Layer) -> Self {
assert!((layer as usize) < Self::TOTAL_LAYERS);
self.0 |= 1 << layer;
self
}
#[must_use]
pub const fn without(mut self, layer: Layer) -> Self {
assert!((layer as usize) < Self::TOTAL_LAYERS);
self.0 &= !(1 << layer);
self
}
pub fn iter(&self) -> impl Iterator<Item = Layer> {
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))
}
pub fn intersects(&self, other: &RenderLayers) -> bool {
(self.0 & other.0) > 0
}
pub fn bits(&self) -> u32 {
self.0
}
}
#[cfg(test)]
mod rendering_mask_tests {
use super::{Layer, RenderLayers};
#[test]
fn rendering_mask_sanity() {
assert_eq!(
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_eq!(
RenderLayers::layer(0).with(1).without(0).0,
2,
"layer 0 + 1 - 0 is mask 2"
);
assert!(
RenderLayers::layer(1).intersects(&RenderLayers::layer(1)),
"layers match like layers"
);
assert!(
RenderLayers::layer(0).intersects(&RenderLayers(1)),
"a layer of 0 means the mask is just 1 bit"
);
assert!(
RenderLayers::layer(0)
.with(3)
.intersects(&RenderLayers::layer(3)),
"a mask will match another mask containing any similar layers"
);
assert!(
RenderLayers::default().intersects(&RenderLayers::default()),
"default masks match each other"
);
assert!(
!RenderLayers::layer(0).intersects(&RenderLayers::layer(1)),
"masks with differing layers do not match"
);
assert!(
!RenderLayers(0).intersects(&RenderLayers(0)),
"empty masks don't match"
);
assert_eq!(
RenderLayers::from_layers(&[0, 2, 16, 30])
.iter()
.collect::<Vec<_>>(),
vec![0, 2, 16, 30],
"from_layers and get_layers should roundtrip"
);
assert_eq!(
format!("{:?}", RenderLayers::from_layers(&[0, 1, 2, 3])).as_str(),
"RenderLayers([0, 1, 2, 3])",
"Debug instance shows layers"
);
assert_eq!(
RenderLayers::from_layers(&[0, 1, 2]),
<RenderLayers as FromIterator<Layer>>::from_iter(vec![0, 1, 2]),
"from_layers and from_iter are equivalent"
);
}
}