1use super::World;
13use crate::body::BodySim;
14use crate::constants::{speculative_distance, GRAPH_COLOR_COUNT};
15use crate::contact::{contact_flags, update_contact, Contact, ContactSim, ContactUpdateContext};
16use crate::core::NULL_INDEX;
17use crate::events::{ContactBeginTouchEvent, ContactEndTouchEvent};
18use crate::id::{ContactId, ShapeId};
19use crate::math_functions::{
20 aabb_overlaps, abs_float, distance, dot, inv_mul_rot, inv_mul_world_transforms, invert_rot,
21 max_float, min_float, mul_rot, rotate_vector, sub, sub_pos, Rot,
22};
23use crate::solver::StepContext;
24use crate::solver_set::{SolverSet, AWAKE_SET, FIRST_SLEEPING_SET};
25use crate::types::BodyType;
26
27fn relative_cos(a: Rot, b: Rot) -> f32 {
29 a.c * b.c + a.s * b.s
30}
31
32#[derive(Clone, Copy)]
36struct ContactSimLocation {
37 color_index: i32,
38 local_index: i32,
39}
40
41fn located_body_sim<'a>(
47 head_sets: &'a [SolverSet],
48 awake_body_sims: &'a [BodySim],
49 sleeping_sets: &'a [SolverSet],
50 set_index: i32,
51 local_index: i32,
52) -> &'a BodySim {
53 let local = local_index as usize;
54 if set_index == AWAKE_SET {
55 &awake_body_sims[local]
56 } else if set_index < AWAKE_SET {
57 &head_sets[set_index as usize].body_sims[local]
58 } else {
59 &sleeping_sets[(set_index - FIRST_SLEEPING_SET) as usize].body_sims[local]
60 }
61}
62
63fn collide_contact(
65 world: &mut World,
66 location: ContactSimLocation,
67 update_context: &ContactUpdateContext,
68) {
69 let recycle_distance = world.contact_recycle_distance;
70 let speculative_distance_ = speculative_distance();
71 let recycle_distance_non_touching = min_float(recycle_distance, speculative_distance_);
72
73 let (head_sets, tail_sets) = world.solver_sets.split_at_mut(AWAKE_SET as usize);
79 let (awake_slice, sleeping_sets) = tail_sets.split_at_mut(1);
80 let SolverSet {
81 contact_sims: awake_contact_sims,
82 body_sims: awake_body_sims,
83 ..
84 } = &mut awake_slice[0];
85
86 let contact_sim: &mut ContactSim = if location.color_index != NULL_INDEX {
89 &mut world.constraint_graph.colors[location.color_index as usize].contact_sims
90 [location.local_index as usize]
91 } else {
92 &mut awake_contact_sims[location.local_index as usize]
93 };
94
95 let contact_id = contact_sim.contact_id;
96
97 let overlap = {
99 let shape_a = &world.shapes[contact_sim.shape_id_a as usize];
100 let shape_b = &world.shapes[contact_sim.shape_id_b as usize];
101 aabb_overlaps(shape_a.fat_aabb, shape_b.fat_aabb)
102 };
103
104 if !overlap {
105 contact_sim.sim_flags |= contact_flags::SIM_DISJOINT;
106 contact_sim.sim_flags &= !contact_flags::SIM_TOUCHING;
107 world.task_contexts[0]
108 .contact_state_bit_set
109 .set_bit(contact_id as u32);
110 return;
111 }
112
113 let was_touching = contact_sim.sim_flags & contact_flags::SIM_TOUCHING != 0;
114
115 let (shape_body_id_a, shape_body_id_b) = {
117 let shape_a = &world.shapes[contact_sim.shape_id_a as usize];
118 let shape_b = &world.shapes[contact_sim.shape_id_b as usize];
119 (shape_a.body_id, shape_b.body_id)
120 };
121
122 let body_a = &world.bodies[shape_body_id_a as usize];
123 let body_b = &world.bodies[shape_body_id_b as usize];
124 let body_sim_a = located_body_sim(
125 &*head_sets,
126 &*awake_body_sims,
127 &*sleeping_sets,
128 body_a.set_index,
129 body_a.local_index,
130 );
131 let body_sim_b = located_body_sim(
132 &*head_sets,
133 &*awake_body_sims,
134 &*sleeping_sets,
135 body_b.set_index,
136 body_b.local_index,
137 );
138 let transform_a = body_sim_a.transform;
139 let transform_b = body_sim_b.transform;
140
141 contact_sim.body_sim_index_a = if body_a.set_index == AWAKE_SET {
143 body_a.local_index
144 } else {
145 NULL_INDEX
146 };
147 contact_sim.inv_mass_a = body_sim_a.inv_mass;
148 contact_sim.inv_i_a = body_sim_a.inv_inertia;
149
150 contact_sim.body_sim_index_b = if body_b.set_index == AWAKE_SET {
151 body_b.local_index
152 } else {
153 NULL_INDEX
154 };
155 contact_sim.inv_mass_b = body_sim_b.inv_mass;
156 contact_sim.inv_i_b = body_sim_b.inv_inertia;
157
158 let type_a = body_a.type_;
159 let type_b = body_b.type_;
160
161 if recycle_distance > 0.0
166 && contact_sim.sim_flags & contact_flags::SIM_RELATIVE_TRANSFORM_VALID != 0
167 && contact_sim.sim_flags & contact_flags::RECYCLE != 0
168 {
169 let cached_q_a = contact_sim.cached_rotation_a;
170 let cached_q_b = contact_sim.cached_rotation_b;
171 let xfc = contact_sim.cached_relative_pose;
172 let xf = inv_mul_world_transforms(transform_a, transform_b);
173
174 let cos_a = relative_cos(transform_a.q, cached_q_a);
175 let cos_b = relative_cos(transform_b.q, cached_q_b);
176 let min_cos = min_float(cos_a, cos_b);
177
178 let max_extent_a = if type_a == BodyType::Static {
179 0.0
180 } else {
181 body_sim_a.max_extent
182 };
183 let max_extent_b = if type_b == BodyType::Static {
184 0.0
185 } else {
186 body_sim_b.max_extent
187 };
188 let max_extent = max_float(max_extent_a, max_extent_b);
189 let distance_ = distance(xf.p, xfc.p);
190 let qr = inv_mul_rot(xf.q, xfc.q);
191
192 let tolerance = if was_touching {
197 recycle_distance
198 } else {
199 recycle_distance_non_touching
200 };
201
202 if min_cos > crate::constants::CONTACT_RECYCLE_COS_ANGLE
203 && distance_ + max_extent * abs_float(qr.s) < tolerance
204 {
205 let dq_a = mul_rot(transform_a.q, invert_rot(cached_q_a));
206 let dq_b = mul_rot(transform_b.q, invert_rot(cached_q_b));
207 let normal = contact_sim.manifold.normal;
208
209 let dc = sub_pos(body_sim_b.center, body_sim_a.center);
211
212 for i in 0..contact_sim.manifold.point_count as usize {
213 let mp = &mut contact_sim.manifold.points[i];
216 let r_a = rotate_vector(dq_a, mp.anchor_a);
217 let r_b = rotate_vector(dq_b, mp.anchor_b);
218 let dp = crate::math_functions::add(dc, sub(r_b, r_a));
219 mp.separation = mp.base_separation + dot(dp, normal);
220 mp.persisted = true;
221 }
222
223 world.task_contexts[0].recycled_contact_count += 1;
224
225 return;
228 }
229 }
230
231 contact_sim.cached_rotation_a = transform_a.q;
233 contact_sim.cached_rotation_b = transform_b.q;
234 contact_sim.cached_relative_pose = inv_mul_world_transforms(transform_a, transform_b);
235 contact_sim.sim_flags |= contact_flags::SIM_RELATIVE_TRANSFORM_VALID;
236
237 let center_offset_a = rotate_vector(transform_a.q, body_sim_a.local_center);
238 let center_offset_b = rotate_vector(transform_b.q, body_sim_b.local_center);
239
240 let touching = {
242 let shape_a = &world.shapes[contact_sim.shape_id_a as usize];
243 let shape_b = &world.shapes[contact_sim.shape_id_b as usize];
244 update_contact(
245 update_context,
246 contact_sim,
247 shape_a,
248 transform_a,
249 center_offset_a,
250 shape_b,
251 transform_b,
252 center_offset_b,
253 )
254 };
255
256 if touching && !was_touching {
259 contact_sim.sim_flags |= contact_flags::SIM_STARTED_TOUCHING;
260 world.task_contexts[0]
261 .contact_state_bit_set
262 .set_bit(contact_id as u32);
263 } else if !touching && was_touching {
264 contact_sim.sim_flags |= contact_flags::SIM_STOPPED_TOUCHING;
265 world.task_contexts[0]
266 .contact_state_bit_set
267 .set_bit(contact_id as u32);
268 }
269
270 for i in 0..contact_sim.manifold.point_count as usize {
271 let mp = &mut contact_sim.manifold.points[i];
272 mp.base_separation = mp.separation;
273 }
274}
275
276fn add_non_touching_contact(world: &mut World, contact_id: i32, contact_sim: ContactSim) {
278 debug_assert!(world.contacts[contact_id as usize].set_index == AWAKE_SET);
279 let local_index = world.solver_sets[AWAKE_SET as usize].contact_sims.len() as i32;
280 {
281 let contact = &mut world.contacts[contact_id as usize];
282 contact.color_index = NULL_INDEX;
283 contact.local_index = local_index;
284 }
285 world.solver_sets[AWAKE_SET as usize]
286 .contact_sims
287 .push(contact_sim);
288}
289
290fn remove_non_touching_contact(world: &mut World, set_index: i32, local_index: i32) {
292 let set = &mut world.solver_sets[set_index as usize];
293 let moved_index = set.contact_sims.len() as i32 - 1;
294 set.contact_sims.swap_remove(local_index as usize);
295 if moved_index != local_index {
296 let moved_contact_id =
297 world.solver_sets[set_index as usize].contact_sims[local_index as usize].contact_id;
298 let moved_contact = &mut world.contacts[moved_contact_id as usize];
299 debug_assert!(moved_contact.set_index == set_index);
300 debug_assert!(moved_contact.local_index == moved_index);
301 debug_assert!(moved_contact.color_index == NULL_INDEX);
302 moved_contact.local_index = local_index;
303 }
304}
305
306pub(super) fn collide(world: &mut World, _context: &StepContext) {
308 let mut contact_count = world.solver_sets[AWAKE_SET as usize].contact_sims.len();
313 for color_index in 0..GRAPH_COLOR_COUNT {
314 contact_count += world.constraint_graph.colors[color_index as usize]
315 .contact_sims
316 .len();
317 }
318 let mut locations: Vec<ContactSimLocation> = Vec::with_capacity(contact_count);
319 for color_index in 0..GRAPH_COLOR_COUNT {
320 let count = world.constraint_graph.colors[color_index as usize]
321 .contact_sims
322 .len();
323 for local_index in 0..count as i32 {
324 locations.push(ContactSimLocation {
325 color_index,
326 local_index,
327 });
328 }
329 }
330 {
331 let count = world.solver_sets[AWAKE_SET as usize].contact_sims.len();
332 for local_index in 0..count as i32 {
333 locations.push(ContactSimLocation {
334 color_index: NULL_INDEX,
335 local_index,
336 });
337 }
338 }
339
340 if locations.is_empty() {
341 return;
342 }
343
344 let contact_id_capacity = world.contact_id_pool.id_capacity();
347 {
348 let task_context = &mut world.task_contexts[0];
349 task_context
350 .contact_state_bit_set
351 .set_bit_count_and_clear(contact_id_capacity as u32);
352 task_context.recycled_contact_count = 0;
353 }
354
355 let update_context = ContactUpdateContext::new(world);
357 for &location in &locations {
358 collide_contact(world, location, &update_context);
359 }
360
361 let end_event_array_index = world.end_event_array_index as usize;
364 let world_id = world.world_id;
365
366 let block_count = world.task_contexts[0].contact_state_bit_set.block_count();
367 for k in 0..block_count {
368 let mut bits = world.task_contexts[0].contact_state_bit_set.block(k);
369 while bits != 0 {
370 let ctz = bits.trailing_zeros();
371 let contact_id = (64 * k + ctz) as i32;
372
373 let (color_index, local_index, flags, generation, shape_id_a, shape_id_b) = {
374 let contact: &Contact = &world.contacts[contact_id as usize];
375 debug_assert!(contact.set_index == AWAKE_SET);
376 (
377 contact.color_index,
378 contact.local_index,
379 contact.flags,
380 contact.generation,
381 contact.shape_id_a,
382 contact.shape_id_b,
383 )
384 };
385
386 let sim_flags = if color_index != NULL_INDEX {
387 debug_assert!((0..GRAPH_COLOR_COUNT).contains(&color_index));
389 world.constraint_graph.colors[color_index as usize].contact_sims
390 [local_index as usize]
391 .sim_flags
392 } else {
393 world.solver_sets[AWAKE_SET as usize].contact_sims[local_index as usize].sim_flags
394 };
395
396 let (shape_id_a_full, shape_id_b_full) = {
397 let shape_a = &world.shapes[shape_id_a as usize];
398 let shape_b = &world.shapes[shape_id_b as usize];
399 (
400 ShapeId {
401 index1: shape_a.id + 1,
402 world0: world_id,
403 generation: shape_a.generation,
404 },
405 ShapeId {
406 index1: shape_b.id + 1,
407 world0: world_id,
408 generation: shape_b.generation,
409 },
410 )
411 };
412 let contact_full_id = ContactId {
413 index1: contact_id + 1,
414 world0: world_id,
415 padding: 0,
416 generation,
417 };
418
419 if sim_flags & contact_flags::SIM_DISJOINT != 0 {
420 crate::contact::destroy_contact(world, contact_id, false);
422 } else if sim_flags & contact_flags::SIM_STARTED_TOUCHING != 0 {
423 debug_assert!(world.contacts[contact_id as usize].island_id == NULL_INDEX);
424
425 if flags & contact_flags::ENABLE_CONTACT_EVENTS != 0 {
426 world.contact_begin_events.push(ContactBeginTouchEvent {
427 shape_id_a: shape_id_a_full,
428 shape_id_b: shape_id_b_full,
429 contact_id: contact_full_id,
430 });
431 }
432
433 debug_assert!(color_index == NULL_INDEX);
434 debug_assert!(
435 world.solver_sets[AWAKE_SET as usize].contact_sims[local_index as usize]
436 .manifold
437 .point_count
438 > 0
439 );
440
441 world.contacts[contact_id as usize].flags |= contact_flags::TOUCHING;
444 crate::island::link_contact(world, contact_id);
445
446 debug_assert!(world.contacts[contact_id as usize].color_index == NULL_INDEX);
448 debug_assert!(world.contacts[contact_id as usize].local_index == local_index);
449
450 let mut contact_sim =
452 world.solver_sets[AWAKE_SET as usize].contact_sims[local_index as usize];
453 contact_sim.sim_flags &= !contact_flags::SIM_STARTED_TOUCHING;
454
455 crate::constraint_graph::add_contact_to_graph(world, contact_sim, contact_id);
457
458 remove_non_touching_contact(world, AWAKE_SET, local_index);
460 } else if sim_flags & contact_flags::SIM_STOPPED_TOUCHING != 0 {
461 debug_assert!(color_index != NULL_INDEX);
462 let contact_sim = {
463 let sim = &mut world.constraint_graph.colors[color_index as usize].contact_sims
464 [local_index as usize];
465 sim.sim_flags &= !contact_flags::SIM_STOPPED_TOUCHING;
466 *sim
467 };
468 world.contacts[contact_id as usize].flags &= !contact_flags::TOUCHING;
469
470 if flags & contact_flags::ENABLE_CONTACT_EVENTS != 0 {
471 world.contact_end_events[end_event_array_index].push(ContactEndTouchEvent {
472 shape_id_a: shape_id_a_full,
473 shape_id_b: shape_id_b_full,
474 contact_id: contact_full_id,
475 });
476 }
477
478 debug_assert!(contact_sim.manifold.point_count == 0);
479
480 crate::island::unlink_contact(world, contact_id);
481 let body_id_a = world.contacts[contact_id as usize].edges[0].body_id;
482 let body_id_b = world.contacts[contact_id as usize].edges[1].body_id;
483
484 add_non_touching_contact(world, contact_id, contact_sim);
486 crate::constraint_graph::remove_contact_from_graph(
487 world,
488 body_id_a,
489 body_id_b,
490 color_index,
491 local_index,
492 );
493 }
494
495 bits &= bits - 1;
497 }
498 }
499
500 world.validate_solver_sets();
501 world.validate_contacts();
502}
503
504impl World {
505 pub fn validate_contacts(&self) {
508 if !cfg!(debug_assertions) {
509 return;
510 }
511
512 let contact_count = self.contacts.len() as i32;
513 debug_assert!(contact_count == self.contact_id_pool.id_capacity());
514 let mut allocated_contact_count = 0;
515
516 for contact_index in 0..contact_count {
517 let contact = &self.contacts[contact_index as usize];
518 if contact.contact_id == NULL_INDEX {
519 continue;
520 }
521
522 debug_assert!(contact.contact_id == contact_index);
523
524 allocated_contact_count += 1;
525
526 let touching = contact.flags & contact_flags::TOUCHING != 0;
527
528 let set_id = contact.set_index;
529
530 if set_id == AWAKE_SET {
531 if touching {
532 debug_assert!(
533 0 <= contact.color_index && contact.color_index < GRAPH_COLOR_COUNT
534 );
535 } else {
536 debug_assert!(contact.color_index == NULL_INDEX);
537 }
538 } else if set_id >= FIRST_SLEEPING_SET {
539 debug_assert!(touching);
541 } else {
542 debug_assert!(!touching && set_id == crate::solver_set::DISABLED_SET);
544 }
545
546 let contact_sim = if set_id == AWAKE_SET && contact.color_index != NULL_INDEX {
547 &self.constraint_graph.colors[contact.color_index as usize].contact_sims
548 [contact.local_index as usize]
549 } else {
550 &self.solver_sets[set_id as usize].contact_sims[contact.local_index as usize]
551 };
552 debug_assert!(contact_sim.contact_id == contact_index);
553 debug_assert!(contact_sim.body_id_a == contact.edges[0].body_id);
554 debug_assert!(contact_sim.body_id_b == contact.edges[1].body_id);
555
556 let sim_touching = contact_sim.sim_flags & contact_flags::SIM_TOUCHING != 0;
557 debug_assert!(touching == sim_touching);
558
559 debug_assert!(
560 0 <= contact_sim.manifold.point_count && contact_sim.manifold.point_count <= 2
561 );
562 }
563
564 let contact_id_count = self.contact_id_pool.id_count();
565 debug_assert!(allocated_contact_count == contact_id_count);
566 let _ = allocated_contact_count;
567 }
568}