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