fyrox_impl/renderer/
visibility.rs1use crate::{
24 core::{algebra::Vector3, pool::Handle},
25 graph::SceneGraph,
26 graphics::{
27 error::FrameworkError,
28 query::{GpuQuery, QueryKind, QueryResult},
29 server::GraphicsServer,
30 },
31 scene::{graph::Graph, node::Node},
32};
33use fxhash::FxHashMap;
34use std::fmt::{Debug, Formatter};
35
36struct PendingQuery {
37 query: GpuQuery,
38 observer_position: Vector3<f32>,
39 node: Handle<Node>,
40}
41
42impl Debug for PendingQuery {
43 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
44 write!(f, "pos: {}, node: {}", self.observer_position, self.node)
45 }
46}
47
48#[derive(Debug)]
49enum Visibility {
50 Undefined,
51 Invisible,
52 Visible,
53}
54
55type NodeVisibilityMap = FxHashMap<Handle<Node>, Visibility>;
56
57#[derive(Debug)]
59pub struct ObserverVisibilityCache {
60 cells: FxHashMap<Vector3<i32>, NodeVisibilityMap>,
61 pending_queries: Vec<PendingQuery>,
62 granularity: Vector3<u32>,
63 distance_discard_threshold: f32,
64}
65
66fn world_to_grid(world_position: Vector3<f32>, granularity: Vector3<u32>) -> Vector3<i32> {
67 Vector3::new(
68 (world_position.x * (granularity.x as f32)).round() as i32,
69 (world_position.y * (granularity.y as f32)).round() as i32,
70 (world_position.z * (granularity.z as f32)).round() as i32,
71 )
72}
73
74fn grid_to_world(grid_position: Vector3<i32>, granularity: Vector3<u32>) -> Vector3<f32> {
75 Vector3::new(
76 grid_position.x as f32 / (granularity.x as f32),
77 grid_position.y as f32 / (granularity.y as f32),
78 grid_position.z as f32 / (granularity.z as f32),
79 )
80}
81
82impl ObserverVisibilityCache {
83 pub fn new(granularity: Vector3<u32>, distance_discard_threshold: f32) -> Self {
88 Self {
89 cells: Default::default(),
90 pending_queries: Default::default(),
91 granularity,
92 distance_discard_threshold,
93 }
94 }
95
96 pub fn world_to_grid(&self, world_position: Vector3<f32>) -> Vector3<i32> {
98 world_to_grid(world_position, self.granularity)
99 }
100
101 pub fn grid_to_world(&self, grid_position: Vector3<i32>) -> Vector3<f32> {
103 grid_to_world(grid_position, self.granularity)
104 }
105
106 fn visibility_info(
107 &self,
108 observer_position: Vector3<f32>,
109 node: Handle<Node>,
110 ) -> Option<&Visibility> {
111 let grid_position = self.world_to_grid(observer_position);
112
113 self.cells
114 .get(&grid_position)
115 .and_then(|cell| cell.get(&node))
116 }
117
118 pub fn needs_occlusion_query(
120 &self,
121 observer_position: Vector3<f32>,
122 node: Handle<Node>,
123 ) -> bool {
124 let Some(visibility) = self.visibility_info(observer_position, node) else {
125 return true;
127 };
128
129 match visibility {
130 Visibility::Undefined => {
131 false
133 }
134 Visibility::Invisible => {
135 true
139 }
140 Visibility::Visible => {
141 false
144 }
145 }
146 }
147
148 pub fn is_visible(&self, observer_position: Vector3<f32>, node: Handle<Node>) -> bool {
152 let Some(visibility_info) = self.visibility_info(observer_position, node) else {
153 return false;
154 };
155
156 match *visibility_info {
157 Visibility::Visible
158 | Visibility::Undefined => true,
161 Visibility::Invisible => false,
162 }
163 }
164
165 pub fn begin_query(
168 &mut self,
169 server: &dyn GraphicsServer,
170 observer_position: Vector3<f32>,
171 node: Handle<Node>,
172 ) -> Result<(), FrameworkError> {
173 let query = server.create_query()?;
174 query.begin(QueryKind::AnySamplesPassed);
175 self.pending_queries.push(PendingQuery {
176 query,
177 observer_position,
178 node,
179 });
180
181 let grid_position = self.world_to_grid(observer_position);
182 self.cells
183 .entry(grid_position)
184 .or_default()
185 .entry(node)
186 .or_insert(Visibility::Undefined);
187
188 Ok(())
189 }
190
191 pub fn end_query(&mut self) {
193 let last_pending_query = self
194 .pending_queries
195 .last()
196 .expect("begin_query/end_query calls mismatch!");
197 last_pending_query.query.end();
198 }
199
200 pub fn update(&mut self, observer_position: Vector3<f32>) {
202 self.pending_queries.retain_mut(|pending_query| {
203 if let Some(QueryResult::AnySamplesPassed(query_result)) =
204 pending_query.query.try_get_result()
205 {
206 let grid_position =
207 world_to_grid(pending_query.observer_position, self.granularity);
208
209 let Some(cell) = self.cells.get_mut(&grid_position) else {
210 return false;
211 };
212
213 let Some(visibility) = cell.get_mut(&pending_query.node) else {
214 return false;
215 };
216
217 match visibility {
218 Visibility::Undefined => match query_result {
219 true => {
220 *visibility = Visibility::Visible;
221 }
222 false => {
223 *visibility = Visibility::Invisible;
224 }
225 },
226 Visibility::Invisible => {
227 if query_result {
228 *visibility = Visibility::Visible;
231 }
232 }
233 Visibility::Visible => {
234 }
236 }
237
238 false
239 } else {
240 true
241 }
242 });
243
244 self.cells.retain(|grid_position, _| {
246 let world_position = grid_to_world(*grid_position, self.granularity);
247
248 world_position.metric_distance(&observer_position) < self.distance_discard_threshold
249 });
250 }
251}
252
253#[derive(Debug)]
254struct ObserverData {
255 position: Vector3<f32>,
256 visibility_cache: ObserverVisibilityCache,
257}
258
259#[derive(Default, Debug)]
261pub struct VisibilityCache {
262 observers: FxHashMap<Handle<Node>, ObserverData>,
263}
264
265impl VisibilityCache {
266 pub fn get_or_register(
268 &mut self,
269 graph: &Graph,
270 observer: Handle<Node>,
271 ) -> &mut ObserverVisibilityCache {
272 &mut self
273 .observers
274 .entry(observer)
275 .or_insert_with(|| ObserverData {
276 position: graph[observer].global_position(),
277 visibility_cache: ObserverVisibilityCache::new(Vector3::repeat(2), 100.0),
278 })
279 .visibility_cache
280 }
281
282 pub fn update(&mut self, graph: &Graph) {
284 self.observers.retain(|observer, data| {
285 let Ok(observer_ref) = graph.try_get_node(*observer) else {
286 return false;
287 };
288
289 data.position = observer_ref.global_position();
290
291 data.visibility_cache.update(data.position);
292
293 true
294 });
295 }
296}