rg3d_core/
octree.rs

1use crate::algebra::Vector3;
2use crate::{
3    math::{aabb::AxisAlignedBoundingBox, ray::Ray},
4    pool::{Handle, Pool},
5};
6use arrayvec::ArrayVec;
7
8#[derive(Clone, Debug)]
9pub enum OctreeNode {
10    Leaf {
11        indices: Vec<u32>,
12        bounds: AxisAlignedBoundingBox,
13    },
14    Branch {
15        bounds: AxisAlignedBoundingBox,
16        leaves: [Handle<OctreeNode>; 8],
17    },
18}
19
20#[derive(Default, Clone, Debug)]
21pub struct Octree {
22    nodes: Pool<OctreeNode>,
23    root: Handle<OctreeNode>,
24}
25
26impl Octree {
27    pub fn new(triangles: &[[Vector3<f32>; 3]], split_threshold: usize) -> Self {
28        // Calculate bounds.
29        let mut bounds = AxisAlignedBoundingBox::default();
30        for triangle in triangles {
31            for pt in triangle.iter() {
32                bounds.add_point(*pt);
33            }
34        }
35
36        // Inflate initial bounds by very low value to fix floating-point calculation
37        // issues when splitting and checking intersection later on.
38        let inflation = 2.0 * f32::EPSILON;
39        bounds.inflate(Vector3::new(inflation, inflation, inflation));
40
41        // Get initial list of indices.
42        let mut indices = Vec::new();
43        for i in 0..triangles.len() {
44            indices.push(i as u32);
45        }
46
47        let mut nodes = Pool::new();
48        let root = build_recursive(&mut nodes, triangles, bounds, indices, split_threshold);
49
50        Self { nodes, root }
51    }
52
53    pub fn sphere_query(&self, position: Vector3<f32>, radius: f32, buffer: &mut Vec<u32>) {
54        buffer.clear();
55        self.sphere_recursive_query(self.root, position, radius, buffer);
56    }
57
58    fn sphere_recursive_query(
59        &self,
60        node: Handle<OctreeNode>,
61        position: Vector3<f32>,
62        radius: f32,
63        buffer: &mut Vec<u32>,
64    ) {
65        match self.nodes.borrow(node) {
66            OctreeNode::Leaf { indices, bounds } => {
67                if bounds.is_intersects_sphere(position, radius) {
68                    buffer.extend_from_slice(indices)
69                }
70            }
71            OctreeNode::Branch { bounds, leaves } => {
72                if bounds.is_intersects_sphere(position, radius) {
73                    for leaf in leaves {
74                        self.sphere_recursive_query(*leaf, position, radius, buffer)
75                    }
76                }
77            }
78        }
79    }
80
81    pub fn aabb_query(&self, aabb: &AxisAlignedBoundingBox, buffer: &mut Vec<u32>) {
82        buffer.clear();
83        self.aabb_recursive_query(self.root, aabb, buffer);
84    }
85
86    fn aabb_recursive_query(
87        &self,
88        node: Handle<OctreeNode>,
89        aabb: &AxisAlignedBoundingBox,
90        buffer: &mut Vec<u32>,
91    ) {
92        match self.nodes.borrow(node) {
93            OctreeNode::Leaf { indices, bounds } => {
94                if bounds.intersect_aabb(aabb) {
95                    buffer.extend_from_slice(indices)
96                }
97            }
98            OctreeNode::Branch { bounds, leaves } => {
99                if bounds.intersect_aabb(aabb) {
100                    for leaf in leaves {
101                        self.aabb_recursive_query(*leaf, aabb, buffer)
102                    }
103                }
104            }
105        }
106    }
107
108    pub fn ray_query(&self, ray: &Ray, buffer: &mut Vec<u32>) {
109        buffer.clear();
110        self.ray_recursive_query(self.root, ray, buffer);
111    }
112
113    fn ray_recursive_query(&self, node: Handle<OctreeNode>, ray: &Ray, buffer: &mut Vec<u32>) {
114        match self.nodes.borrow(node) {
115            OctreeNode::Leaf { indices, bounds } => {
116                if ray.box_intersection(&bounds.min, &bounds.max).is_some() {
117                    buffer.extend_from_slice(indices)
118                }
119            }
120            OctreeNode::Branch { bounds, leaves } => {
121                if ray.box_intersection(&bounds.min, &bounds.max).is_some() {
122                    for leaf in leaves {
123                        self.ray_recursive_query(*leaf, ray, buffer)
124                    }
125                }
126            }
127        }
128    }
129
130    pub fn node(&self, handle: Handle<OctreeNode>) -> &OctreeNode {
131        &self.nodes[handle]
132    }
133
134    pub fn nodes(&self) -> &Pool<OctreeNode> {
135        &self.nodes
136    }
137
138    pub fn ray_query_static<const CAP: usize>(
139        &self,
140        ray: &Ray,
141        buffer: &mut ArrayVec<Handle<OctreeNode>, CAP>,
142    ) {
143        buffer.clear();
144        self.ray_recursive_query_static(self.root, ray, buffer);
145    }
146
147    fn ray_recursive_query_static<const CAP: usize>(
148        &self,
149        node: Handle<OctreeNode>,
150        ray: &Ray,
151        buffer: &mut ArrayVec<Handle<OctreeNode>, CAP>,
152    ) {
153        match self.nodes.borrow(node) {
154            OctreeNode::Leaf { bounds, .. } => {
155                if ray.box_intersection(&bounds.min, &bounds.max).is_some() {
156                    buffer.push(node);
157                }
158            }
159            OctreeNode::Branch { bounds, leaves } => {
160                if ray.box_intersection(&bounds.min, &bounds.max).is_some() {
161                    for leaf in leaves {
162                        self.ray_recursive_query_static(*leaf, ray, buffer)
163                    }
164                }
165            }
166        }
167    }
168
169    pub fn point_query(&self, point: Vector3<f32>, buffer: &mut Vec<u32>) {
170        buffer.clear();
171        self.point_recursive_query(self.root, point, buffer);
172    }
173
174    fn point_recursive_query(
175        &self,
176        node: Handle<OctreeNode>,
177        point: Vector3<f32>,
178        buffer: &mut Vec<u32>,
179    ) {
180        match self.nodes.borrow(node) {
181            OctreeNode::Leaf { indices, bounds } => {
182                if bounds.is_contains_point(point) {
183                    buffer.extend_from_slice(indices)
184                }
185            }
186            OctreeNode::Branch { bounds, leaves } => {
187                if bounds.is_contains_point(point) {
188                    for leaf in leaves {
189                        self.point_recursive_query(*leaf, point, buffer)
190                    }
191                }
192            }
193        }
194    }
195}
196
197fn build_recursive(
198    nodes: &mut Pool<OctreeNode>,
199    triangles: &[[Vector3<f32>; 3]],
200    bounds: AxisAlignedBoundingBox,
201    indices: Vec<u32>,
202    split_threshold: usize,
203) -> Handle<OctreeNode> {
204    if indices.len() <= split_threshold {
205        nodes.spawn(OctreeNode::Leaf { bounds, indices })
206    } else {
207        let mut leaves = [Handle::NONE; 8];
208        let leaf_bounds = bounds.split();
209
210        for i in 0..8 {
211            let mut leaf_indices = Vec::new();
212
213            for index in indices.iter() {
214                let index = *index;
215
216                let triangle_bounds =
217                    AxisAlignedBoundingBox::from_points(&triangles[index as usize]);
218
219                if triangle_bounds.intersect_aabb(&bounds) {
220                    leaf_indices.push(index);
221                }
222            }
223
224            leaves[i] = build_recursive(
225                nodes,
226                triangles,
227                leaf_bounds[i],
228                leaf_indices,
229                split_threshold,
230            );
231        }
232
233        nodes.spawn(OctreeNode::Branch { leaves, bounds })
234    }
235}