use crate::entity::{EntityRecord, EntityTags};
use crate::ids::ClientId;
use crate::spatial::{Frustum3, Position3};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ViewerQuery {
pub client_id: ClientId,
pub position: Position3,
pub radius: f32,
pub max_entities: usize,
}
impl ViewerQuery {
pub fn radius_squared(self) -> f32 {
self.radius * self.radius
}
}
pub trait VisibilityFilter {
fn is_visible(&self, viewer: &ViewerQuery, entity: &EntityRecord) -> bool;
fn is_visible_with_distance(
&self,
viewer: &ViewerQuery,
entity: &EntityRecord,
_distance_squared: f32,
) -> bool {
self.is_visible(viewer, entity)
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct RangeOnlyVisibility;
impl VisibilityFilter for RangeOnlyVisibility {
fn is_visible(&self, viewer: &ViewerQuery, entity: &EntityRecord) -> bool {
entity.position.distance_squared(viewer.position) <= viewer.radius_squared()
}
fn is_visible_with_distance(
&self,
viewer: &ViewerQuery,
_entity: &EntityRecord,
distance_squared: f32,
) -> bool {
distance_squared <= viewer.radius_squared()
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FrustumVisibility {
pub frustum: Frustum3,
}
impl FrustumVisibility {
pub const fn new(frustum: Frustum3) -> Self {
Self { frustum }
}
}
impl VisibilityFilter for FrustumVisibility {
fn is_visible(&self, _viewer: &ViewerQuery, entity: &EntityRecord) -> bool {
self.frustum
.intersects_bounds(entity.position, entity.bounds)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TagVisibility {
pub required: EntityTags,
pub excluded: EntityTags,
}
impl TagVisibility {
pub const fn new(required: EntityTags, excluded: EntityTags) -> Self {
Self { required, excluded }
}
pub const fn require(required: EntityTags) -> Self {
Self::new(required, EntityTags::EMPTY)
}
pub const fn exclude(excluded: EntityTags) -> Self {
Self::new(EntityTags::EMPTY, excluded)
}
}
impl VisibilityFilter for TagVisibility {
fn is_visible(&self, _viewer: &ViewerQuery, entity: &EntityRecord) -> bool {
entity.tags.contains(self.required) && !entity.tags.intersects(self.excluded)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AndVisibility<A, B> {
pub left: A,
pub right: B,
}
impl<A, B> AndVisibility<A, B> {
pub const fn new(left: A, right: B) -> Self {
Self { left, right }
}
}
impl<A, B> VisibilityFilter for AndVisibility<A, B>
where
A: VisibilityFilter,
B: VisibilityFilter,
{
fn is_visible(&self, viewer: &ViewerQuery, entity: &EntityRecord) -> bool {
self.left.is_visible(viewer, entity) && self.right.is_visible(viewer, entity)
}
fn is_visible_with_distance(
&self,
viewer: &ViewerQuery,
entity: &EntityRecord,
distance_squared: f32,
) -> bool {
self.left
.is_visible_with_distance(viewer, entity, distance_squared)
&& self
.right
.is_visible_with_distance(viewer, entity, distance_squared)
}
}