1use crate::frustum_culling::collect_visible_objects;
2use crate::geometry::{BoundingSphere, Transform};
3use crate::internal::VisibilityWorldInternal;
4use crate::Projection;
5use crossbeam_channel::{unbounded, Receiver, Sender};
6use glam::Vec3;
7use slotmap::new_key_type;
8use std::hash::Hash;
9
10pub type VisibleObjects = Vec<VisibilityResult<VisibilityObjectHandle>>;
11pub type VisibleVolumes = Vec<VisibilityResult<VolumeHandle>>;
12
13new_key_type! { struct ChunkHandle; }
16
17new_key_type! { pub struct ModelHandle; }
20new_key_type! { pub struct ZoneHandle; }
21new_key_type! { pub struct VisibilityObjectHandle; }
22new_key_type! { pub struct ViewFrustumHandle; }
23new_key_type! { pub struct VolumeHandle; }
24
25pub enum AsyncCommand {
26 SetObjectTransform(VisibilityObjectHandle, Transform),
27 SetObjectZone(VisibilityObjectHandle, Option<ZoneHandle>),
28 SetObjectId(VisibilityObjectHandle, u64),
29 SetObjectCullModel(VisibilityObjectHandle, Option<ModelHandle>),
30 SetViewFrustumZone(ViewFrustumHandle, Option<ZoneHandle>),
31 SetViewFrustumTransforms(ViewFrustumHandle, Vec3, Vec3, Vec3),
32 SetViewFrustumId(ViewFrustumHandle, u64),
33 SetViewFrustumProjection(ViewFrustumHandle, Projection),
34 DestroyViewFrustum(ViewFrustumHandle),
35 DestroyZone(ZoneHandle),
36 DestroyObject(VisibilityObjectHandle),
37 DestroyModel(ModelHandle),
38 QueuedCommands(Vec<AsyncCommand>),
39}
40
41#[derive(Copy, Clone, Default)]
42pub struct VisibilityResult<T> {
43 pub handle: T,
44 pub id: u64,
45 pub distance_from_view_frustum: f32,
47}
48
49impl<T> VisibilityResult<T> {
50 pub fn new(
51 handle: T,
52 id: u64,
53 view_frustum_position: Vec3,
54 bounding_sphere: BoundingSphere,
55 ) -> Self {
56 VisibilityResult {
57 handle,
58 id,
59 distance_from_view_frustum: view_frustum_position.distance(bounding_sphere.position),
61 }
62 }
63}
64
65#[derive(Default)]
66pub struct VisibilityQuery {
67 pub objects: VisibleObjects,
68 pub volumes: VisibleVolumes,
69}
70
71pub struct VisibilityWorld {
72 pub inner: VisibilityWorldInternal,
73 sender: Sender<AsyncCommand>,
74 receiver: Receiver<AsyncCommand>,
75}
76
77pub enum QueryError {
78 NoViewFrustumZone,
79}
80
81impl VisibilityWorld {
82 pub fn new() -> Self {
83 let (sender, receiver) = unbounded();
84 VisibilityWorld {
85 inner: VisibilityWorldInternal::new(),
86 receiver,
87 sender,
88 }
89 }
90
91 #[profiling::function]
92 pub fn update(&mut self) {
93 let mut inner = &mut self.inner;
94
95 for (_, object) in &mut inner.objects {
96 object.previous_frame_transform = object.transform;
97 }
98
99 {
100 profiling::scope!("receiver.try_iter");
101 for command in self.receiver.try_iter() {
102 VisibilityWorld::handle_command(&mut inner, command);
103 }
104 }
105 }
106
107 pub fn new_async_command_sender(&self) -> Sender<AsyncCommand> {
108 self.sender.clone()
109 }
110
111 fn handle_command(
112 inner: &mut VisibilityWorldInternal,
113 command: AsyncCommand,
114 ) {
115 match command {
116 AsyncCommand::SetObjectTransform(object, transform) => {
117 inner.set_object_transform(object, transform);
118 }
119 AsyncCommand::SetObjectZone(object, zone) => {
120 inner.set_object_zone(object, zone);
121 }
122 AsyncCommand::SetObjectId(object, id) => {
123 inner.set_object_id(object, id);
124 }
125 AsyncCommand::SetObjectCullModel(object, cull_model) => {
126 inner.set_object_cull_model(object, cull_model);
127 }
128 AsyncCommand::SetViewFrustumZone(view_frustum, zone) => {
129 inner.set_view_frustum_zone(view_frustum, zone);
130 }
131 AsyncCommand::SetViewFrustumTransforms(view_frustum, eye, look_at, up) => {
132 inner.set_view_frustum_transforms(view_frustum, eye, look_at, up);
133 }
134 AsyncCommand::SetViewFrustumId(view_frustum, id) => {
135 inner.set_view_frustum_id(view_frustum, id);
136 }
137 AsyncCommand::SetViewFrustumProjection(view_frustum, projection) => match projection {
138 Projection::Perspective(parameters) => {
139 inner.set_view_frustum_perspective(
140 view_frustum,
141 parameters.fov_y_radians(),
142 parameters.ratio(),
143 parameters.near_distance(),
144 parameters.far_distance(),
145 parameters.depth_range(),
146 );
147 }
148 Projection::Orthographic(parameters) => {
149 inner.set_view_frustum_orthographic(
150 view_frustum,
151 parameters.left(),
152 parameters.right(),
153 parameters.bottom(),
154 parameters.top(),
155 parameters.near_distance(),
156 parameters.far_distance(),
157 parameters.depth_range(),
158 );
159 }
160 Projection::Undefined => {
161 panic!("Cannot send `Undefined` projection on View Frustum.");
162 }
163 },
164 AsyncCommand::DestroyViewFrustum(view_frustum) => {
165 inner.destroy_view_frustum(view_frustum);
166 }
167 AsyncCommand::DestroyZone(zone) => {
168 inner.destroy_zone(zone);
169 }
170 AsyncCommand::DestroyObject(object) => {
171 inner.destroy_object(object);
172 }
173 AsyncCommand::DestroyModel(model) => {
174 inner.destroy_model(model);
175 }
176 AsyncCommand::QueuedCommands(commands) => {
177 for inner_command in commands {
178 VisibilityWorld::handle_command(inner, inner_command);
179 }
180 }
181 }
182 }
183
184 #[profiling::function]
186 pub fn query_visibility(
187 &self,
188 view_frustum: ViewFrustumHandle,
189 result: &mut VisibilityQuery,
190 ) -> Result<(), QueryError> {
191 let zone = {
192 let view_frustum_zone = self.inner.view_frustum_zones.get(view_frustum);
193 if let Some(zone) = view_frustum_zone {
194 Ok(*zone)
195 } else {
196 return Err(QueryError::NoViewFrustumZone);
197 }
198 }?;
199
200 let active_view_frustum = self.inner.view_frustums.get(view_frustum).unwrap();
201 let view_frustum_position = active_view_frustum.eye_position();
202 let frustum = active_view_frustum.acquire_frustum().clone();
203
204 let chunks = self.inner.zones.get(zone).unwrap().chunks.clone();
205 for chunk in &chunks {
206 collect_visible_objects(chunk, view_frustum_position, &frustum, &mut result.objects)
207 }
208
209 Ok(())
210 }
211
212 pub fn query_shadow_casters(
216 &self,
217 _light: ViewFrustumHandle,
218 _shadowed: &[ViewFrustumHandle],
219 _result: &mut VisibilityQuery,
220 ) {
221 unimplemented!();
222 }
223}