sectorsync_core/
interest.rs1use crate::entity::{EntityRecord, EntityTags};
4use crate::ids::ClientId;
5use crate::spatial::{Frustum3, Position3};
6
7#[derive(Clone, Copy, Debug, PartialEq)]
9pub struct ViewerQuery {
10 pub client_id: ClientId,
12 pub position: Position3,
14 pub radius: f32,
16 pub max_entities: usize,
18}
19
20impl ViewerQuery {
21 pub fn radius_squared(self) -> f32 {
23 self.radius * self.radius
24 }
25}
26
27pub trait VisibilityFilter {
29 fn is_visible(&self, viewer: &ViewerQuery, entity: &EntityRecord) -> bool;
31
32 fn is_visible_with_distance(
37 &self,
38 viewer: &ViewerQuery,
39 entity: &EntityRecord,
40 _distance_squared: f32,
41 ) -> bool {
42 self.is_visible(viewer, entity)
43 }
44}
45
46#[derive(Clone, Copy, Debug, Default)]
48pub struct RangeOnlyVisibility;
49
50impl VisibilityFilter for RangeOnlyVisibility {
51 fn is_visible(&self, viewer: &ViewerQuery, entity: &EntityRecord) -> bool {
52 entity.position.distance_squared(viewer.position) <= viewer.radius_squared()
53 }
54
55 fn is_visible_with_distance(
56 &self,
57 viewer: &ViewerQuery,
58 _entity: &EntityRecord,
59 distance_squared: f32,
60 ) -> bool {
61 distance_squared <= viewer.radius_squared()
62 }
63}
64
65#[derive(Clone, Copy, Debug, PartialEq)]
67pub struct FrustumVisibility {
68 pub frustum: Frustum3,
70}
71
72impl FrustumVisibility {
73 pub const fn new(frustum: Frustum3) -> Self {
75 Self { frustum }
76 }
77}
78
79impl VisibilityFilter for FrustumVisibility {
80 fn is_visible(&self, _viewer: &ViewerQuery, entity: &EntityRecord) -> bool {
81 self.frustum
82 .intersects_bounds(entity.position, entity.bounds)
83 }
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq)]
88pub struct TagVisibility {
89 pub required: EntityTags,
91 pub excluded: EntityTags,
93}
94
95impl TagVisibility {
96 pub const fn new(required: EntityTags, excluded: EntityTags) -> Self {
98 Self { required, excluded }
99 }
100
101 pub const fn require(required: EntityTags) -> Self {
103 Self::new(required, EntityTags::EMPTY)
104 }
105
106 pub const fn exclude(excluded: EntityTags) -> Self {
108 Self::new(EntityTags::EMPTY, excluded)
109 }
110}
111
112impl VisibilityFilter for TagVisibility {
113 fn is_visible(&self, _viewer: &ViewerQuery, entity: &EntityRecord) -> bool {
114 entity.tags.contains(self.required) && !entity.tags.intersects(self.excluded)
115 }
116}
117
118#[derive(Clone, Copy, Debug, PartialEq, Eq)]
120pub struct AndVisibility<A, B> {
121 pub left: A,
123 pub right: B,
125}
126
127impl<A, B> AndVisibility<A, B> {
128 pub const fn new(left: A, right: B) -> Self {
130 Self { left, right }
131 }
132}
133
134impl<A, B> VisibilityFilter for AndVisibility<A, B>
135where
136 A: VisibilityFilter,
137 B: VisibilityFilter,
138{
139 fn is_visible(&self, viewer: &ViewerQuery, entity: &EntityRecord) -> bool {
140 self.left.is_visible(viewer, entity) && self.right.is_visible(viewer, entity)
141 }
142
143 fn is_visible_with_distance(
144 &self,
145 viewer: &ViewerQuery,
146 entity: &EntityRecord,
147 distance_squared: f32,
148 ) -> bool {
149 self.left
150 .is_visible_with_distance(viewer, entity, distance_squared)
151 && self
152 .right
153 .is_visible_with_distance(viewer, entity, distance_squared)
154 }
155}