1use super::contact_flags;
9use super::{destroy_contact, update_contact};
10use crate::bitset::BitSet;
11use crate::body::body_flags;
12use crate::constants::{
13 speculative_distance, CONTACT_MANIFOLD_COUNT_BUCKETS, CONTACT_RECYCLE_ANGULAR_DISTANCE,
14 GRAPH_COLOR_COUNT,
15};
16use crate::constraint_graph::{add_contact_to_graph, remove_contact_from_graph};
17use crate::core::{ctz64, NULL_INDEX};
18use crate::events::{ContactBeginTouchEvent, ContactEndTouchEvent};
19use crate::id::{ContactId, ShapeId};
20use crate::island::{link_contact, unlink_contact};
21use crate::math_functions::{
22 aabb_overlaps, abs, conjugate, distance_squared, dot, dot_quat, inv_mul_quat,
23 inv_mul_world_transforms, length_squared, make_matrix_from_quat, max, min_float, min_int,
24 mul_mv, mul_quat, sub, sub_pos, Vec3,
25};
26use crate::solver_set::{AWAKE_SET, STATIC_SET};
27use crate::types::BodyType;
28use crate::world::World;
29
30fn modified_cross(a: Vec3, b: Vec3) -> Vec3 {
32 Vec3 {
33 x: a.y * b.z + a.z * b.y,
34 y: a.z * b.x + a.x * b.z,
35 z: a.x * b.y + a.y * b.x,
36 }
37}
38
39fn add_non_touching_contact(world: &mut World, contact_id: i32) {
41 debug_assert!(world.contacts[contact_id as usize].set_index == AWAKE_SET);
42 let local_index = world.solver_sets[AWAKE_SET as usize].contact_indices.len() as i32;
43 {
44 let contact = &mut world.contacts[contact_id as usize];
45 contact.color_index = NULL_INDEX;
46 contact.local_index = local_index;
47 contact.body_sim_index_a = NULL_INDEX;
48 contact.body_sim_index_b = NULL_INDEX;
49 }
50 world.solver_sets[AWAKE_SET as usize]
51 .contact_indices
52 .push(contact_id);
53}
54
55fn remove_non_touching_contact(world: &mut World, set_index: i32, local_index: i32) {
57 let set = &mut world.solver_sets[set_index as usize];
58 let moved_index = set.contact_indices.len() as i32 - 1;
59 set.contact_indices.swap_remove(local_index as usize);
60 if moved_index != local_index {
61 let moved_contact_id = set.contact_indices[local_index as usize];
62 let moved = &mut world.contacts[moved_contact_id as usize];
63 debug_assert!(moved.set_index == set_index);
64 debug_assert!(moved.color_index == NULL_INDEX);
65 debug_assert!(moved.local_index == moved_index);
66 moved.local_index = local_index;
67 }
68}
69
70fn collide_task(world: &mut World, contact_indices: &[i32], worker_index: i32) {
72 let recycle_distance = world.contact_recycle_distance;
73 let speculative = speculative_distance();
74 let recycle_distance_non_touching = min_float(recycle_distance, speculative);
75
76 for &contact_index in contact_indices {
77 debug_assert!((contact_index as usize) < world.contacts.len());
78 debug_assert!(world.contacts[contact_index as usize].contact_id == contact_index);
79
80 let shape_id_a = world.contacts[contact_index as usize].shape_id_a;
81 let shape_id_b = world.contacts[contact_index as usize].shape_id_b;
82
83 let overlap = aabb_overlaps(
84 world.shapes[shape_id_a as usize].fat_aabb,
85 world.shapes[shape_id_b as usize].fat_aabb,
86 );
87 if !overlap {
88 world.contacts[contact_index as usize].flags |= contact_flags::SIM_DISJOINT;
89 world.contacts[contact_index as usize].flags &= !contact_flags::SIM_TOUCHING;
90 world.task_contexts[worker_index as usize]
91 .contact_state_bit_set
92 .set_bit(contact_index as u32);
93 continue;
94 }
95
96 let body_id_a = world.shapes[shape_id_a as usize].body_id;
97 let body_id_b = world.shapes[shape_id_b as usize].body_id;
98 let is_static_a = world.bodies[body_id_a as usize].type_ == BodyType::Static;
99 let is_static_b = world.bodies[body_id_b as usize].type_ == BodyType::Static;
100 let was_touching =
101 (world.contacts[contact_index as usize].flags & contact_flags::SIM_TOUCHING) != 0;
102 let is_mesh_contact =
103 (world.contacts[contact_index as usize].flags & contact_flags::SIM_MESH_CONTACT) != 0;
104
105 let set_a = world.bodies[body_id_a as usize].set_index;
106 let set_b = world.bodies[body_id_b as usize].set_index;
107 let local_a = world.bodies[body_id_a as usize].local_index;
108 let local_b = world.bodies[body_id_b as usize].local_index;
109
110 if was_touching {
111 debug_assert!(set_a == AWAKE_SET || set_a == STATIC_SET);
112 debug_assert!(set_b == AWAKE_SET || set_b == STATIC_SET);
113 }
114
115 let sim_a = world.solver_sets[set_a as usize].body_sims[local_a as usize];
116 let sim_b = world.solver_sets[set_b as usize].body_sims[local_b as usize];
117 let transform_a = sim_a.transform;
118 let transform_b = sim_b.transform;
119 let is_fast =
120 (sim_a.flags & body_flags::IS_FAST) != 0 || (sim_b.flags & body_flags::IS_FAST) != 0;
121
122 {
123 let contact = &mut world.contacts[contact_index as usize];
124 contact.body_sim_index_a = if is_static_a { NULL_INDEX } else { local_a };
125 contact.body_sim_index_b = if is_static_b { NULL_INDEX } else { local_b };
126 }
127
128 let recycle_tolerance = if was_touching {
129 recycle_distance
130 } else {
131 recycle_distance_non_touching
132 };
133
134 let flags = world.contacts[contact_index as usize].flags;
135 if (!is_fast || !is_mesh_contact)
136 && recycle_distance > 0.0
137 && (flags & contact_flags::RELATIVE_TRANSFORM_VALID) != 0
138 && (flags & contact_flags::RECYCLE) != 0
139 {
140 let contact = &world.contacts[contact_index as usize];
141 let angle_a = dot_quat(transform_a.q, contact.cached_rotation_a);
142 let angle_b = dot_quat(transform_b.q, contact.cached_rotation_b);
143 let angular_distance = min_float(angle_a * angle_a, angle_b * angle_b);
144
145 let xf = inv_mul_world_transforms(transform_a, transform_b);
146 let xfc = contact.cached_relative_pose;
147 let max_extent_a = if is_static_a {
148 Vec3 {
149 x: 0.0,
150 y: 0.0,
151 z: 0.0,
152 }
153 } else {
154 sim_a.max_extent
155 };
156 let max_extent_b = if is_static_b {
157 Vec3 {
158 x: 0.0,
159 y: 0.0,
160 z: 0.0,
161 }
162 } else {
163 sim_b.max_extent
164 };
165 let max_extent = max(max_extent_a, max_extent_b);
166 let dist_squared = distance_squared(xf.p, xfc.p);
167
168 if angular_distance > CONTACT_RECYCLE_ANGULAR_DISTANCE
169 && dist_squared < recycle_tolerance * recycle_tolerance
170 {
171 let distance = dist_squared.sqrt();
172 let slack = recycle_tolerance - distance;
173 let qr = inv_mul_quat(xfc.q, xf.q);
174 let arc = modified_cross(abs(qr.v), max_extent);
175 let arc_sq = 4.0 * length_squared(arc);
176 if arc_sq < slack * slack {
177 let dq_a = mul_quat(transform_a.q, conjugate(contact.cached_rotation_a));
178 let dq_b = mul_quat(transform_b.q, conjugate(contact.cached_rotation_b));
179 let matrix_a = make_matrix_from_quat(dq_a);
180 let matrix_b = make_matrix_from_quat(dq_b);
181 let dc = sub_pos(sim_b.center, sim_a.center);
182
183 let manifold_count = contact.manifold_count();
184 for manifold_index in 0..manifold_count as usize {
185 let normal =
186 world.contacts[contact_index as usize].manifolds[manifold_index].normal;
187 let point_count = world.contacts[contact_index as usize].manifolds
188 [manifold_index]
189 .point_count;
190 for point_index in 0..point_count as usize {
191 let mp = &mut world.contacts[contact_index as usize].manifolds
192 [manifold_index]
193 .points[point_index];
194 let r_a = mul_mv(matrix_a, mp.anchor_a);
195 let r_b = mul_mv(matrix_b, mp.anchor_b);
196 let dp = crate::math_functions::add(dc, sub(r_b, r_a));
197 mp.separation = mp.base_separation + dot(dp, normal);
198 mp.persisted = true;
199 }
200 }
201
202 world.task_contexts[worker_index as usize].recycled_contact_count += 1;
203 let bucket_index =
204 min_int(manifold_count, CONTACT_MANIFOLD_COUNT_BUCKETS as i32 - 1);
205 if bucket_index > 0 {
206 world.task_contexts[worker_index as usize].manifold_counts
207 [(bucket_index - 1) as usize] += 1;
208 }
209 continue;
210 }
211 }
212 }
213
214 {
215 let contact = &mut world.contacts[contact_index as usize];
216 contact.cached_rotation_a = transform_a.q;
217 contact.cached_rotation_b = transform_b.q;
218 contact.cached_relative_pose = inv_mul_world_transforms(transform_a, transform_b);
219 contact.flags |= contact_flags::RELATIVE_TRANSFORM_VALID;
220 }
221
222 let touching = update_contact(
223 world,
224 worker_index,
225 contact_index,
226 shape_id_a,
227 sim_a.local_center,
228 transform_a,
229 shape_id_b,
230 sim_b.local_center,
231 transform_b,
232 is_fast,
233 );
234
235 let manifold_count = world.contacts[contact_index as usize].manifold_count();
236 let bucket_index = min_int(manifold_count, CONTACT_MANIFOLD_COUNT_BUCKETS as i32 - 1);
237 if bucket_index > 0 {
238 world.task_contexts[worker_index as usize].manifold_counts
239 [(bucket_index - 1) as usize] += 1;
240 }
241
242 if touching
243 && was_touching
244 && (world.contacts[contact_index as usize].flags & contact_flags::SIM_MESH_CONTACT) != 0
245 {
246 let color_index = world.contacts[contact_index as usize].color_index;
247 let local_index = world.contacts[contact_index as usize].local_index;
248 debug_assert!(color_index != NULL_INDEX);
249 debug_assert!(0 <= color_index && color_index < GRAPH_COLOR_COUNT);
250 world.constraint_graph.colors[color_index as usize].contacts[local_index as usize]
251 .manifold_count = manifold_count as u16;
252 }
253
254 if touching && !was_touching {
255 world.contacts[contact_index as usize].flags |= contact_flags::SIM_STARTED_TOUCHING;
256 world.task_contexts[worker_index as usize]
257 .contact_state_bit_set
258 .set_bit(contact_index as u32);
259 } else if !touching && was_touching {
260 world.contacts[contact_index as usize].flags |= contact_flags::SIM_STOPPED_TOUCHING;
261 world.task_contexts[worker_index as usize]
262 .contact_state_bit_set
263 .set_bit(contact_index as u32);
264 }
265
266 let manifold_count = world.contacts[contact_index as usize].manifold_count();
267 for manifold_index in 0..manifold_count as usize {
268 let point_count =
269 world.contacts[contact_index as usize].manifolds[manifold_index].point_count;
270 for point_index in 0..point_count as usize {
271 let mp = &mut world.contacts[contact_index as usize].manifolds[manifold_index]
272 .points[point_index];
273 mp.base_separation = mp.separation;
274 }
275 }
276 }
277}
278
279pub fn collide(world: &mut World, dt: f32) {
281 debug_assert!(world.worker_count > 0);
282
283 let mut touching_count = 0;
284 for i in 0..GRAPH_COLOR_COUNT as usize {
285 let color = &world.constraint_graph.colors[i];
286 touching_count += color.convex_contacts.len() + color.contacts.len();
287 }
288
289 let non_touching_count = world.solver_sets[AWAKE_SET as usize].contact_indices.len();
290 let contact_count = touching_count + non_touching_count;
291 if contact_count == 0 {
292 return;
293 }
294
295 let mut contact_indices = Vec::with_capacity(contact_count);
296 for i in 0..GRAPH_COLOR_COUNT as usize {
297 let color = &world.constraint_graph.colors[i];
298 contact_indices.extend_from_slice(&color.convex_contacts);
299 for spec in &color.contacts {
300 contact_indices.push(spec.contact_id);
301 }
302 }
303 debug_assert!(contact_indices.len() == touching_count);
304 if non_touching_count > 0 {
305 contact_indices.extend_from_slice(&world.solver_sets[AWAKE_SET as usize].contact_indices);
306 }
307
308 let contact_id_capacity = world.contact_id_pool.id_capacity();
309 for task in &mut world.task_contexts {
310 task.contact_state_bit_set
311 .set_bit_count_and_clear(contact_id_capacity as u32);
312 task.sat_call_count = 0;
313 task.sat_cache_hit_count = 0;
314 task.recycled_contact_count = 0;
315 task.manifold_counts = [0; CONTACT_MANIFOLD_COUNT_BUCKETS];
316 }
317
318 collide_task(world, &contact_indices, 0);
320
321 let sat_multiplier = if dt > 0.0 { 1 } else { 0 };
322 world.sat_call_count = sat_multiplier * world.task_contexts[0].sat_call_count;
323 world.sat_cache_hit_count = sat_multiplier * world.task_contexts[0].sat_cache_hit_count;
324 world.manifold_counts = world.task_contexts[0].manifold_counts;
325
326 let bit_set: BitSet = world.task_contexts[0].contact_state_bit_set.clone();
328 let end_event_array_index = world.end_event_array_index;
329 let world_id = world.world_id;
330
331 for k in 0..bit_set.block_count() {
332 let mut bits = bit_set.block(k);
333 while bits != 0 {
334 let ctz = ctz64(bits);
335 let contact_id = (64 * k + ctz) as i32;
336
337 let (shape_id_a, shape_id_b, flags, generation) = {
338 let contact = &world.contacts[contact_id as usize];
339 debug_assert!(contact.set_index == AWAKE_SET);
340 (
341 contact.shape_id_a,
342 contact.shape_id_b,
343 contact.flags,
344 contact.generation,
345 )
346 };
347
348 let shape_a = &world.shapes[shape_id_a as usize];
349 let shape_b = &world.shapes[shape_id_b as usize];
350 let shape_id_a_full = ShapeId {
351 index1: shape_a.id + 1,
352 world0: world_id,
353 generation: shape_a.generation,
354 };
355 let shape_id_b_full = ShapeId {
356 index1: shape_b.id + 1,
357 world0: world_id,
358 generation: shape_b.generation,
359 };
360 let contact_full_id = ContactId {
361 index1: contact_id + 1,
362 world0: world_id,
363 padding: 0,
364 generation,
365 };
366
367 if (flags & contact_flags::SIM_DISJOINT) != 0 {
368 destroy_contact(world, contact_id, false);
369 } else if (flags & contact_flags::SIM_STARTED_TOUCHING) != 0 {
370 debug_assert!(world.contacts[contact_id as usize].island_id == NULL_INDEX);
371
372 if (flags & contact_flags::ENABLE_CONTACT_EVENTS) != 0 {
373 world.contact_begin_events.push(ContactBeginTouchEvent {
374 shape_id_a: shape_id_a_full,
375 shape_id_b: shape_id_b_full,
376 contact_id: contact_full_id,
377 });
378 }
379
380 debug_assert!(world.contacts[contact_id as usize].manifold_count() > 0);
381 debug_assert!(world.contacts[contact_id as usize].set_index == AWAKE_SET);
382
383 world.contacts[contact_id as usize].flags &= !contact_flags::SIM_STARTED_TOUCHING;
384 world.contacts[contact_id as usize].flags |= contact_flags::TOUCHING;
385 link_contact(world, contact_id);
386
387 debug_assert!(world.contacts[contact_id as usize].color_index == NULL_INDEX);
388
389 let old_local_index = world.contacts[contact_id as usize].local_index;
390 add_contact_to_graph(world, contact_id);
391 remove_non_touching_contact(world, AWAKE_SET, old_local_index);
392 } else if (flags & contact_flags::SIM_STOPPED_TOUCHING) != 0 {
393 world.contacts[contact_id as usize].flags &= !contact_flags::SIM_STOPPED_TOUCHING;
394 world.contacts[contact_id as usize].flags &= !contact_flags::TOUCHING;
395
396 if (world.contacts[contact_id as usize].flags
397 & contact_flags::ENABLE_CONTACT_EVENTS)
398 != 0
399 {
400 world.contact_end_events[end_event_array_index as usize].push(
401 ContactEndTouchEvent {
402 shape_id_a: shape_id_a_full,
403 shape_id_b: shape_id_b_full,
404 contact_id: contact_full_id,
405 },
406 );
407 }
408
409 debug_assert!(world.contacts[contact_id as usize].manifolds.is_empty());
410
411 let color_index = world.contacts[contact_id as usize].color_index;
412 let local_index = world.contacts[contact_id as usize].local_index;
413 let body_id_a = world.contacts[contact_id as usize].edges[0].body_id;
414 let body_id_b = world.contacts[contact_id as usize].edges[1].body_id;
415 let is_mesh = (world.contacts[contact_id as usize].flags
416 & contact_flags::SIM_MESH_CONTACT)
417 != 0;
418
419 unlink_contact(world, contact_id);
420 add_non_touching_contact(world, contact_id);
421 remove_contact_from_graph(
422 world,
423 body_id_a,
424 body_id_b,
425 color_index,
426 local_index,
427 is_mesh,
428 );
429 }
430
431 bits &= bits - 1;
432 }
433 }
434
435 world.validate_solver_sets();
436}