use crate::{
core::{algebra::Vector3, math::frustum::Frustum, pool::Handle},
scene::{graph::Graph, node::Node},
};
use fxhash::FxHashMap;
#[derive(Default, Debug)]
pub struct VisibilityCache {
map: FxHashMap<Handle<Node>, bool>,
}
impl From<FxHashMap<Handle<Node>, bool>> for VisibilityCache {
fn from(map: FxHashMap<Handle<Node>, bool>) -> Self {
Self { map }
}
}
impl VisibilityCache {
pub fn invalidate(&mut self) -> FxHashMap<Handle<Node>, bool> {
std::mem::take(&mut self.map)
}
pub fn update(
&mut self,
graph: &Graph,
observer_position: Vector3<f32>,
z_near: f32,
z_far: f32,
frustums: Option<&[&Frustum]>,
) {
self.map.clear();
for node in graph.linear_iter() {
if let Some(lod_group) = node.lod_group() {
for level in lod_group.levels.iter() {
for &object in level.objects.iter() {
if let Some(object_ref) = graph.try_get(*object) {
let distance =
observer_position.metric_distance(&object_ref.global_position());
let z_range = z_far - z_near;
let normalized_distance = (distance - z_near) / z_range;
let visible = normalized_distance >= level.begin()
&& normalized_distance <= level.end();
self.map.insert(*object, visible);
}
}
}
}
}
for (handle, node) in graph.pair_iter() {
self.map.entry(handle).or_insert_with(|| {
let mut visibility = node.global_visibility();
if visibility && node.frustum_culling() {
if let Some(frustums) = frustums {
let mut visible_by_any_frustum = false;
for frustum in frustums {
if frustum.is_intersects_aabb(&node.world_bounding_box()) {
visible_by_any_frustum = true;
break;
}
}
visibility = visible_by_any_frustum;
}
}
visibility
});
}
}
pub fn is_visible(&self, node: Handle<Node>) -> bool {
self.map.get(&node).cloned().unwrap_or(false)
}
}