1use bevy::{
2 ecs::{entity::Entity, resource::Resource},
3 reflect::prelude::*,
4};
5use obvhs::{
6 aabb::Aabb,
7 bvh2::{Bvh2, insertion_removal::SiblingInsertionCandidate, reinsertion::ReinsertionOptimizer},
8 faststack::HeapStack,
9 ploc::{PlocBuilder, PlocSearchDistance, SortPrecision, rebuild::compute_rebuild_path_flags},
10};
11
12use crate::{
13 collider_tree::ProxyId,
14 data_structures::stable_vec::StableVec,
15 prelude::{ActiveCollisionHooks, CollisionLayers},
16};
17
18#[derive(Clone, Default)]
24pub struct ColliderTree {
25 pub bvh: Bvh2,
27 pub proxies: StableVec<ColliderTreeProxy>,
29 pub moved_proxies: Vec<ProxyId>,
34 pub workspace: ColliderTreeWorkspace,
36}
37
38#[derive(Clone, Debug)]
40pub struct ColliderTreeProxy {
41 pub collider: Entity,
43 pub body: Option<Entity>,
45 pub layers: CollisionLayers,
47 pub flags: ColliderTreeProxyFlags,
49}
50
51#[repr(transparent)]
53#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Reflect)]
54#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
55#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
56#[reflect(Debug, PartialEq)]
57pub struct ColliderTreeProxyFlags(u32);
58
59bitflags::bitflags! {
60 impl ColliderTreeProxyFlags: u32 {
61 const SENSOR = 1 << 0;
63 const BODY_DISABLED = 1 << 1;
65 const CUSTOM_FILTER = 1 << 2;
67 const MODIFY_CONTACTS = 1 << 3;
69 const CONTACT_EVENTS = 1 << 4;
71 }
72}
73
74impl ColliderTreeProxyFlags {
75 #[inline]
77 pub fn new(
78 is_sensor: bool,
79 is_body_disabled: bool,
80 events_enabled: bool,
81 active_hooks: ActiveCollisionHooks,
82 ) -> Self {
83 let mut flags = ColliderTreeProxyFlags::empty();
84 if is_sensor {
85 flags |= ColliderTreeProxyFlags::SENSOR;
86 }
87 if is_body_disabled {
88 flags |= ColliderTreeProxyFlags::BODY_DISABLED;
89 }
90 if active_hooks.contains(ActiveCollisionHooks::FILTER_PAIRS) {
91 flags |= ColliderTreeProxyFlags::CUSTOM_FILTER;
92 }
93 if active_hooks.contains(ActiveCollisionHooks::MODIFY_CONTACTS) {
94 flags |= ColliderTreeProxyFlags::MODIFY_CONTACTS;
95 }
96 if events_enabled {
97 flags |= ColliderTreeProxyFlags::CONTACT_EVENTS;
98 }
99 flags
100 }
101}
102
103impl ColliderTreeProxy {
104 #[inline]
106 pub fn is_sensor(&self) -> bool {
107 self.flags.contains(ColliderTreeProxyFlags::SENSOR)
108 }
109
110 #[inline]
112 pub fn has_custom_filter(&self) -> bool {
113 self.flags.contains(ColliderTreeProxyFlags::CUSTOM_FILTER)
114 }
115
116 #[inline]
118 pub fn has_contact_modification(&self) -> bool {
119 self.flags.contains(ColliderTreeProxyFlags::MODIFY_CONTACTS)
120 }
121}
122
123#[derive(Resource)]
129pub struct ColliderTreeWorkspace {
130 pub ploc_builder: PlocBuilder,
132 pub reinsertion_optimizer: ReinsertionOptimizer,
134 pub insertion_stack: HeapStack<SiblingInsertionCandidate>,
136 pub temp_flags: Vec<bool>,
138}
139
140impl Clone for ColliderTreeWorkspace {
141 fn clone(&self) -> Self {
142 Self {
143 ploc_builder: self.ploc_builder.clone(),
144 reinsertion_optimizer: ReinsertionOptimizer::default(),
145 insertion_stack: self.insertion_stack.clone(),
146 temp_flags: Vec::new(),
147 }
148 }
149}
150
151impl Default for ColliderTreeWorkspace {
152 fn default() -> Self {
153 Self {
154 ploc_builder: PlocBuilder::default(),
155 reinsertion_optimizer: ReinsertionOptimizer::default(),
156 insertion_stack: HeapStack::new_with_capacity(2000),
157 temp_flags: Vec::new(),
158 }
159 }
160}
161
162impl ColliderTree {
163 #[inline]
165 pub fn add_proxy(&mut self, aabb: Aabb, proxy: ColliderTreeProxy) -> ProxyId {
166 let id = self.proxies.push(proxy) as u32;
167
168 self.bvh
170 .insert_primitive(aabb, id, &mut self.workspace.insertion_stack);
171
172 self.moved_proxies.push(ProxyId::new(id));
174
175 ProxyId::new(id)
176 }
177
178 #[inline]
182 pub fn remove_proxy(&mut self, proxy_id: ProxyId) -> Option<ColliderTreeProxy> {
183 if let Some(proxy) = self.proxies.try_remove(proxy_id.index()) {
184 self.bvh.remove_primitive(proxy_id.id());
186
187 for i in 0..self.moved_proxies.len() {
189 if self.moved_proxies[i] == proxy_id {
190 self.moved_proxies.swap_remove(i);
191 break;
192 }
193 }
194
195 Some(proxy)
196 } else {
197 None
198 }
199 }
200
201 #[inline]
205 pub fn get_proxy(&self, proxy_id: ProxyId) -> Option<&ColliderTreeProxy> {
206 self.proxies.get(proxy_id.index())
207 }
208
209 #[inline]
213 pub fn get_proxy_mut(&mut self, proxy_id: ProxyId) -> Option<&mut ColliderTreeProxy> {
214 self.proxies.get_mut(proxy_id.index())
215 }
216
217 #[inline]
223 pub unsafe fn get_proxy_unchecked(&self, proxy_id: ProxyId) -> &ColliderTreeProxy {
224 unsafe { self.proxies.get_unchecked(proxy_id.index()) }
225 }
226
227 #[inline]
233 pub unsafe fn get_proxy_unchecked_mut(&mut self, proxy_id: ProxyId) -> &mut ColliderTreeProxy {
234 unsafe { self.proxies.get_unchecked_mut(proxy_id.index()) }
235 }
236
237 #[inline]
241 pub fn get_proxy_aabb(&self, proxy_id: ProxyId) -> Option<Aabb> {
242 let node_id = self.bvh.primitives_to_nodes.get(proxy_id.index())?;
243 self.bvh.nodes.get(*node_id as usize).map(|node| node.aabb)
244 }
245
246 #[inline]
252 pub unsafe fn get_proxy_aabb_unchecked(&self, proxy_id: ProxyId) -> Aabb {
253 unsafe {
254 let node_id = *self.bvh.primitives_to_nodes.get_unchecked(proxy_id.index()) as usize;
255 self.bvh.nodes.get_unchecked(node_id).aabb
256 }
257 }
258
259 #[inline]
267 pub fn set_proxy_aabb(&mut self, proxy_id: ProxyId, aabb: Aabb) {
268 let node_index = self.bvh.primitives_to_nodes[proxy_id.index()] as usize;
270
271 self.bvh.nodes[node_index].set_aabb(aabb);
273 }
274
275 #[inline]
283 pub fn resize_proxy_aabb(&mut self, proxy_id: ProxyId, aabb: Aabb) {
284 let node_index = self.bvh.primitives_to_nodes[proxy_id.index()] as usize;
285 self.bvh.resize_node(node_index, aabb);
286 }
287
288 #[inline]
290 pub fn reinsert_proxy(&mut self, proxy_id: ProxyId, aabb: Aabb) {
291 let node_id = self.bvh.primitives_to_nodes[proxy_id.index()];
293 self.bvh.resize_node(node_id as usize, aabb);
294 self.bvh.reinsert_node(node_id as usize);
295 }
296
297 #[inline]
299 pub fn refit_all(&mut self) {
300 self.bvh.refit_all();
301 }
302
303 #[inline]
305 pub fn rebuild_full(&mut self) {
306 self.workspace.ploc_builder.full_rebuild(
307 &mut self.bvh,
308 PlocSearchDistance::Minimum,
309 SortPrecision::U64,
310 0,
311 );
312 }
313
314 #[inline]
316 pub fn rebuild_partial(&mut self, leaves: &[u32]) {
317 self.bvh.init_parents_if_uninit();
318
319 compute_rebuild_path_flags(&self.bvh, leaves, &mut self.workspace.temp_flags);
321
322 self.workspace.ploc_builder.partial_rebuild(
323 &mut self.bvh,
324 |node_id| self.workspace.temp_flags[node_id],
325 PlocSearchDistance::Minimum,
326 SortPrecision::U64,
327 0,
328 );
329 }
330
331 #[inline]
336 pub fn optimize(&mut self, batch_size_ratio: f32) {
337 self.workspace
338 .reinsertion_optimizer
339 .run(&mut self.bvh, batch_size_ratio, None);
340 }
341
342 #[inline]
346 pub fn optimize_candidates(&mut self, candidates: &[u32], iterations: u32) {
347 self.workspace.reinsertion_optimizer.run_with_candidates(
348 &mut self.bvh,
349 candidates,
350 iterations,
351 );
352 }
353}