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_bvh: Bvh2,
138 pub temp_flags: Vec<bool>,
140}
141
142impl Clone for ColliderTreeWorkspace {
143 fn clone(&self) -> Self {
144 Self {
145 ploc_builder: self.ploc_builder.clone(),
146 reinsertion_optimizer: ReinsertionOptimizer::default(),
147 insertion_stack: self.insertion_stack.clone(),
148 temp_bvh: Bvh2::default(),
149 temp_flags: Vec::new(),
150 }
151 }
152}
153
154impl Default for ColliderTreeWorkspace {
155 fn default() -> Self {
156 Self {
157 ploc_builder: PlocBuilder::default(),
158 reinsertion_optimizer: ReinsertionOptimizer::default(),
159 insertion_stack: HeapStack::new_with_capacity(2000),
160 temp_bvh: Bvh2::default(),
161 temp_flags: Vec::new(),
162 }
163 }
164}
165
166impl ColliderTree {
167 #[inline]
169 pub fn add_proxy(&mut self, aabb: Aabb, proxy: ColliderTreeProxy) -> ProxyId {
170 let id = self.proxies.push(proxy) as u32;
171
172 self.bvh
174 .insert_primitive(aabb, id, &mut self.workspace.insertion_stack);
175
176 self.moved_proxies.push(ProxyId::new(id));
178
179 ProxyId::new(id)
180 }
181
182 #[inline]
186 pub fn remove_proxy(&mut self, proxy_id: ProxyId) -> Option<ColliderTreeProxy> {
187 if let Some(proxy) = self.proxies.try_remove(proxy_id.index()) {
188 self.bvh.remove_primitive(proxy_id.id());
190
191 for i in 0..self.moved_proxies.len() {
193 if self.moved_proxies[i] == proxy_id {
194 self.moved_proxies.swap_remove(i);
195 break;
196 }
197 }
198
199 Some(proxy)
200 } else {
201 None
202 }
203 }
204
205 #[inline]
209 pub fn get_proxy(&self, proxy_id: ProxyId) -> Option<&ColliderTreeProxy> {
210 self.proxies.get(proxy_id.index())
211 }
212
213 #[inline]
217 pub fn get_proxy_mut(&mut self, proxy_id: ProxyId) -> Option<&mut ColliderTreeProxy> {
218 self.proxies.get_mut(proxy_id.index())
219 }
220
221 #[inline]
227 pub unsafe fn get_proxy_unchecked(&self, proxy_id: ProxyId) -> &ColliderTreeProxy {
228 unsafe { self.proxies.get_unchecked(proxy_id.index()) }
229 }
230
231 #[inline]
237 pub unsafe fn get_proxy_unchecked_mut(&mut self, proxy_id: ProxyId) -> &mut ColliderTreeProxy {
238 unsafe { self.proxies.get_unchecked_mut(proxy_id.index()) }
239 }
240
241 #[inline]
245 pub fn get_proxy_aabb(&self, proxy_id: ProxyId) -> Option<Aabb> {
246 let node_id = self.bvh.primitives_to_nodes.get(proxy_id.index())?;
247 self.bvh.nodes.get(*node_id as usize).map(|node| node.aabb)
248 }
249
250 #[inline]
256 pub unsafe fn get_proxy_aabb_unchecked(&self, proxy_id: ProxyId) -> Aabb {
257 unsafe {
258 let node_id = *self.bvh.primitives_to_nodes.get_unchecked(proxy_id.index()) as usize;
259 self.bvh.nodes.get_unchecked(node_id).aabb
260 }
261 }
262
263 #[inline]
271 pub fn set_proxy_aabb(&mut self, proxy_id: ProxyId, aabb: Aabb) {
272 let node_index = self.bvh.primitives_to_nodes[proxy_id.index()] as usize;
274
275 self.bvh.nodes[node_index].set_aabb(aabb);
277 }
278
279 #[inline]
287 pub fn resize_proxy_aabb(&mut self, proxy_id: ProxyId, aabb: Aabb) {
288 let node_index = self.bvh.primitives_to_nodes[proxy_id.index()] as usize;
289 self.bvh.resize_node(node_index, aabb);
290 }
291
292 #[inline]
294 pub fn reinsert_proxy(&mut self, proxy_id: ProxyId, aabb: Aabb) {
295 let node_id = self.bvh.primitives_to_nodes[proxy_id.index()];
297 self.bvh.resize_node(node_id as usize, aabb);
298 self.bvh.reinsert_node(node_id as usize);
299 }
300
301 #[inline]
303 pub fn refit_all(&mut self) {
304 self.bvh.refit_all();
305 }
306
307 #[inline]
309 pub fn rebuild_full(&mut self) {
310 self.workspace.ploc_builder.full_rebuild(
311 &mut self.bvh,
312 PlocSearchDistance::Minimum,
313 SortPrecision::U64,
314 0,
315 );
316 }
317
318 #[inline]
320 pub fn rebuild_partial(&mut self, leaves: &[u32]) {
321 self.bvh.init_parents_if_uninit();
322
323 compute_rebuild_path_flags(&self.bvh, leaves, &mut self.workspace.temp_flags);
325
326 self.workspace.ploc_builder.partial_rebuild(
327 &mut self.bvh,
328 |node_id| self.workspace.temp_flags[node_id],
329 PlocSearchDistance::Minimum,
330 SortPrecision::U64,
331 0,
332 );
333 }
334
335 #[inline]
340 pub fn optimize(&mut self, batch_size_ratio: f32) {
341 self.workspace
342 .reinsertion_optimizer
343 .run(&mut self.bvh, batch_size_ratio, None);
344 }
345
346 #[inline]
350 pub fn optimize_candidates(&mut self, candidates: &[u32], iterations: u32) {
351 self.workspace.reinsertion_optimizer.run_with_candidates(
352 &mut self.bvh,
353 candidates,
354 iterations,
355 );
356 }
357}