rafx_visibility/
visibility_world.rs

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
13// Private keys.
14
15new_key_type! { struct ChunkHandle; }
16
17// Public keys.
18
19new_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 bounding_sphere: BoundingSphere,
46    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            //bounding_sphere,
60            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    /// Queries visibility for a `ViewFrustum`. The `result` is a `VisibilityQuery`. This function is thread-safe.
185    #[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    /// Queries shadow casters for a `ViewFrustum` representing a light. The `result` is a `VisibilityQuery`.
213    /// The objects in `result` are able to cast shadows into at least one of the `shadowed` frustums.
214    /// This function is thread-safe.
215    pub fn query_shadow_casters(
216        &self,
217        _light: ViewFrustumHandle,
218        _shadowed: &[ViewFrustumHandle],
219        _result: &mut VisibilityQuery,
220    ) {
221        unimplemented!();
222    }
223}