use slotmap::SecondaryMap;
use crate::lights::{Light, LightKey, Lights};
use crate::meshes::MeshKey;
use crate::scene_spatial::SceneSpatial;
#[derive(Default)]
pub struct LightMeshBuckets {
per_light: SecondaryMap<LightKey, Vec<MeshKey>>,
light_order: Vec<LightKey>,
last_max_bucket: usize,
shadow_receiver: SecondaryMap<MeshKey, ()>,
any_directional_shadow_caster: bool,
}
impl LightMeshBuckets {
pub fn rebuild(&mut self, lights: &Lights, spatial: &SceneSpatial) {
self.per_light.clear();
self.light_order.clear();
self.last_max_bucket = 0;
self.shadow_receiver.clear();
self.any_directional_shadow_caster = false;
for (light_key, light) in lights.iter() {
match light {
Light::Directional { .. } => {}
Light::Point { .. } | Light::Spot { .. } => {
let Some(aabb) = light.world_aabb() else {
continue;
};
let bucket: Vec<MeshKey> = spatial
.query_envelope(&aabb)
.map(|node| node.mesh_key)
.collect();
self.last_max_bucket = self.last_max_bucket.max(bucket.len());
self.per_light.insert(light_key, bucket);
self.light_order.push(light_key);
}
}
}
}
pub fn mark_shadow_receivers(
&mut self,
lights: &Lights,
casts_shadow_for: impl Fn(LightKey) -> bool,
) {
self.any_directional_shadow_caster = lights
.iter_directional()
.any(|(key, _)| casts_shadow_for(key));
if self.any_directional_shadow_caster {
return;
}
let mut to_mark: Vec<MeshKey> = Vec::new();
for (light_key, bucket) in self.iter_punctual() {
if !casts_shadow_for(light_key) {
continue;
}
to_mark.extend(bucket.iter().copied());
}
for mesh_key in to_mark {
self.shadow_receiver.entry(mesh_key).unwrap().or_insert(());
}
}
pub fn is_shadow_receiver(&self, mesh_key: MeshKey) -> bool {
self.any_directional_shadow_caster || self.shadow_receiver.contains_key(mesh_key)
}
pub fn iter_punctual(&self) -> impl Iterator<Item = (LightKey, &Vec<MeshKey>)> {
self.light_order
.iter()
.copied()
.filter_map(move |key| self.per_light.get(key).map(|bucket| (key, bucket)))
}
pub fn last_max_bucket(&self) -> usize {
self.last_max_bucket
}
}