rg3d/scene/
visibility.rs

1//! Visibility cache stores information about objects visibility for a single frame.
2//!
3//! For more info see [`VisibilityCache`]
4
5use crate::{
6    core::{algebra::Vector3, math::frustum::Frustum, pool::Handle},
7    scene::{graph::Graph, node::Node},
8};
9use fxhash::FxHashMap;
10
11/// Visibility cache stores information about objects visibility for a single frame. Allows you to quickly check
12/// if an object is visible or not.
13///
14/// # Notes
15///
16/// Visibility cache stores very coarse information about object visibility, it does not include any kind of occlusion
17/// tests of whatsoever. It just a simple frustum test + level-of-detail (LOD) system.
18///
19/// LODs have priority over other visibility options, if a level is not active, then its every object will be hidden,
20/// not matter if the actual visibility state is `visible`.
21///
22/// # Performance
23///
24/// The cache is based on hash map, so it is very fast and has O(1) complexity for fetching.
25#[derive(Default, Debug)]
26pub struct VisibilityCache {
27    map: FxHashMap<Handle<Node>, bool>,
28}
29
30impl From<FxHashMap<Handle<Node>, bool>> for VisibilityCache {
31    fn from(map: FxHashMap<Handle<Node>, bool>) -> Self {
32        Self { map }
33    }
34}
35
36impl VisibilityCache {
37    /// Replaces internal map with empty and returns previous value. This trick is useful
38    /// to reuse hash map to prevent redundant memory allocations.
39    pub fn invalidate(&mut self) -> FxHashMap<Handle<Node>, bool> {
40        std::mem::take(&mut self.map)
41    }
42
43    /// Updates visibility cache - checks visibility for each node in given graph, also performs
44    /// frustum culling if frustum set is specified.
45    pub fn update(
46        &mut self,
47        graph: &Graph,
48        observer_position: Vector3<f32>,
49        z_near: f32,
50        z_far: f32,
51        frustums: Option<&[&Frustum]>,
52    ) {
53        self.map.clear();
54
55        // Check LODs first, it has priority over other visibility settings.
56        for node in graph.linear_iter() {
57            if let Some(lod_group) = node.lod_group() {
58                for level in lod_group.levels.iter() {
59                    for &object in level.objects.iter() {
60                        if let Some(object_ref) = graph.try_get(*object) {
61                            let distance =
62                                observer_position.metric_distance(&object_ref.global_position());
63                            let z_range = z_far - z_near;
64                            let normalized_distance = (distance - z_near) / z_range;
65                            let visible = normalized_distance >= level.begin()
66                                && normalized_distance <= level.end();
67                            self.map.insert(*object, visible);
68                        }
69                    }
70                }
71            }
72        }
73
74        // Fill rest of data from global visibility flag of nodes and check frustums (if any).
75        for (handle, node) in graph.pair_iter() {
76            // We need to fill only unfilled entries, none of visibility flags of a node can
77            // make it visible again if lod group hid it.
78            self.map.entry(handle).or_insert_with(|| {
79                let mut visibility = node.global_visibility();
80                if visibility && node.frustum_culling() {
81                    // If a node globally visible, check it with each frustum (if any).
82                    if let Some(frustums) = frustums {
83                        let mut visible_by_any_frustum = false;
84                        for frustum in frustums {
85                            if frustum.is_intersects_aabb(&node.world_bounding_box()) {
86                                visible_by_any_frustum = true;
87                                break;
88                            }
89                        }
90                        visibility = visible_by_any_frustum;
91                    }
92                }
93                visibility
94            });
95        }
96    }
97
98    /// Checks whether the node is visible or not.
99    ///
100    /// # Complexity
101    ///
102    /// Constant, O(1)
103    pub fn is_visible(&self, node: Handle<Node>) -> bool {
104        self.map.get(&node).cloned().unwrap_or(false)
105    }
106}