fyrox_impl/scene/
accel.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21#![allow(missing_docs)] // TODO
22
23use crate::{
24    core::{
25        algebra::Vector3,
26        math::aabb::AxisAlignedBoundingBox,
27        pool::{Handle, Pool},
28    },
29    graph::SceneGraph,
30    scene::{graph::Graph, node::Node},
31};
32
33#[allow(dead_code)]
34#[derive(Clone, Debug)]
35pub struct Entry {
36    node: Handle<Node>,
37    world_aabb: AxisAlignedBoundingBox,
38}
39
40#[derive(Clone, Debug)]
41pub enum OctreeNode {
42    Leaf {
43        entries: Vec<Entry>,
44        bounds: AxisAlignedBoundingBox,
45    },
46    Branch {
47        bounds: AxisAlignedBoundingBox,
48        leaves: [Handle<OctreeNode>; 8],
49    },
50}
51
52#[derive(Default, Clone, Debug)]
53pub struct Octree {
54    nodes: Pool<OctreeNode>,
55    root: Handle<OctreeNode>,
56}
57
58impl Octree {
59    pub fn new(graph: &Graph, split_threshold: usize) -> Self {
60        // Calculate bounds.
61        let mut bounds = AxisAlignedBoundingBox::default();
62
63        let mut entries = Vec::new();
64        for (handle, node) in graph.pair_iter() {
65            let aabb = node.world_bounding_box();
66            entries.push(Entry {
67                node: handle,
68                world_aabb: aabb,
69            });
70            bounds.add_box(aabb);
71        }
72
73        // Inflate initial bounds by very low value to fix floating-point calculation
74        // issues when splitting and checking intersection later on.
75        let inflation = 2.0 * f32::EPSILON;
76        bounds.inflate(Vector3::new(inflation, inflation, inflation));
77
78        let mut nodes = Pool::new();
79
80        let root = build_recursive(&mut nodes, entries, bounds, split_threshold);
81
82        Self { nodes, root }
83    }
84
85    pub fn sphere_query(&self, position: Vector3<f32>, radius: f32, buffer: &mut Vec<Entry>) {
86        buffer.clear();
87        self.sphere_recursive_query(self.root, position, radius, buffer);
88    }
89
90    fn sphere_recursive_query(
91        &self,
92        node: Handle<OctreeNode>,
93        position: Vector3<f32>,
94        radius: f32,
95        buffer: &mut Vec<Entry>,
96    ) {
97        match self.nodes.borrow(node) {
98            OctreeNode::Leaf { entries, bounds } => {
99                if bounds.is_intersects_sphere(position, radius) {
100                    buffer.extend_from_slice(entries)
101                }
102            }
103            OctreeNode::Branch { bounds, leaves } => {
104                if bounds.is_intersects_sphere(position, radius) {
105                    for leaf in leaves {
106                        self.sphere_recursive_query(*leaf, position, radius, buffer)
107                    }
108                }
109            }
110        }
111    }
112
113    pub fn aabb_query(&self, aabb: &AxisAlignedBoundingBox, buffer: &mut Vec<Entry>) {
114        buffer.clear();
115        self.aabb_recursive_query(self.root, aabb, buffer);
116    }
117
118    fn aabb_recursive_query(
119        &self,
120        node: Handle<OctreeNode>,
121        aabb: &AxisAlignedBoundingBox,
122        buffer: &mut Vec<Entry>,
123    ) {
124        match self.nodes.borrow(node) {
125            OctreeNode::Leaf { entries, bounds } => {
126                if bounds.is_intersects_aabb(aabb) {
127                    buffer.extend_from_slice(entries)
128                }
129            }
130            OctreeNode::Branch { bounds, leaves } => {
131                if bounds.is_intersects_aabb(aabb) {
132                    for leaf in leaves {
133                        self.aabb_recursive_query(*leaf, aabb, buffer)
134                    }
135                }
136            }
137        }
138    }
139
140    pub fn node(&self, handle: Handle<OctreeNode>) -> &OctreeNode {
141        &self.nodes[handle]
142    }
143
144    pub fn nodes(&self) -> &Pool<OctreeNode> {
145        &self.nodes
146    }
147
148    pub fn point_query(&self, point: Vector3<f32>, buffer: &mut Vec<Entry>) {
149        buffer.clear();
150        self.point_recursive_query(self.root, point, buffer);
151    }
152
153    fn point_recursive_query(
154        &self,
155        node: Handle<OctreeNode>,
156        point: Vector3<f32>,
157        buffer: &mut Vec<Entry>,
158    ) {
159        match self.nodes.borrow(node) {
160            OctreeNode::Leaf { entries, bounds } => {
161                if bounds.is_contains_point(point) {
162                    buffer.extend_from_slice(entries)
163                }
164            }
165            OctreeNode::Branch { bounds, leaves } => {
166                if bounds.is_contains_point(point) {
167                    for leaf in leaves {
168                        self.point_recursive_query(*leaf, point, buffer)
169                    }
170                }
171            }
172        }
173    }
174}
175
176fn build_recursive(
177    nodes: &mut Pool<OctreeNode>,
178    entries: Vec<Entry>,
179    bounds: AxisAlignedBoundingBox,
180    split_threshold: usize,
181) -> Handle<OctreeNode> {
182    if entries.len() <= split_threshold {
183        nodes.spawn(OctreeNode::Leaf { bounds, entries })
184    } else {
185        let mut leaves = [Handle::NONE; 8];
186
187        let leaf_bounds = bounds.split();
188
189        for (leaf, leaf_bounds) in leaves.iter_mut().zip(leaf_bounds) {
190            let mut leaf_entries = Vec::new();
191
192            leaf_entries.extend(
193                entries
194                    .iter()
195                    .filter(|entry| entry.world_aabb.is_intersects_aabb(&bounds))
196                    .cloned(),
197            );
198
199            *leaf = build_recursive(nodes, leaf_entries, leaf_bounds, split_threshold);
200        }
201
202        nodes.spawn(OctreeNode::Branch { leaves, bounds })
203    }
204}