use glam::Vec3;
use rstar::{
primitives::{GeomWithData, Rectangle as RstarRectangle},
SelectionFunction, AABB as RstarAABB,
};
use crate::{frustum::Frustum, meshes::MeshKey};
use super::node::SceneNode;
pub(crate) type Leaf = GeomWithData<RstarRectangle<[f32; 3]>, MeshKey>;
pub(crate) struct FrustumSelector {
pub(crate) frustum: Frustum,
}
impl FrustumSelector {
fn rstar_envelope_intersects(&self, envelope: &RstarAABB<[f32; 3]>) -> bool {
let lower = envelope.lower();
let upper = envelope.upper();
intersects(
self.frustum,
Vec3::new(lower[0], lower[1], lower[2]),
Vec3::new(upper[0], upper[1], upper[2]),
)
}
}
impl SelectionFunction<Leaf> for FrustumSelector {
fn should_unpack_parent(&self, envelope: &RstarAABB<[f32; 3]>) -> bool {
self.rstar_envelope_intersects(envelope)
}
fn should_unpack_leaf(&self, leaf: &Leaf) -> bool {
let rect = leaf.geom();
let min = rect.lower();
let max = rect.upper();
intersects(
self.frustum,
Vec3::new(min[0], min[1], min[2]),
Vec3::new(max[0], max[1], max[2]),
)
}
}
pub(crate) fn intersects(frustum: Frustum, min: Vec3, max: Vec3) -> bool {
for plane in &frustum.planes {
let px = if plane.normal.x >= 0.0 { max.x } else { min.x };
let py = if plane.normal.y >= 0.0 { max.y } else { min.y };
let pz = if plane.normal.z >= 0.0 { max.z } else { min.z };
if plane.distance(Vec3::new(px, py, pz)) < 0.0 {
return false;
}
}
true
}
pub(crate) fn frustum_intersects_node(frustum: &Frustum, node: &SceneNode) -> bool {
intersects(*frustum, node.aabb.min, node.aabb.max)
}