1#![allow(missing_docs)] use 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 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 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}