1use crate::collision::Manifold;
37use crate::core::NULL_INDEX;
38use crate::distance::SimplexCache;
39use crate::math_functions::{Rot, Transform, ROT_IDENTITY, TRANSFORM_IDENTITY};
40
41mod api;
42mod lifecycle;
43mod update;
44
45pub use api::*;
46pub use lifecycle::*;
47pub use update::*;
48
49pub mod contact_flags {
51 pub const TOUCHING: u32 = 0x00000001;
53 pub const HIT_EVENT: u32 = 0x00000002;
55 pub const ENABLE_CONTACT_EVENTS: u32 = 0x00000004;
57 pub const RECYCLE: u32 = 0x00000008;
58
59 pub const SIM_TOUCHING: u32 = 0x00010000;
61 pub const SIM_DISJOINT: u32 = 0x00020000;
63 pub const SIM_STARTED_TOUCHING: u32 = 0x00040000;
65 pub const SIM_STOPPED_TOUCHING: u32 = 0x00080000;
67 pub const SIM_ENABLE_HIT_EVENT: u32 = 0x00100000;
69 pub const SIM_ENABLE_PRE_SOLVE_EVENTS: u32 = 0x00200000;
71 pub const SIM_RELATIVE_TRANSFORM_VALID: u32 = 0x00400000;
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub struct ContactEdge {
81 pub body_id: i32,
82 pub prev_key: i32,
83 pub next_key: i32,
84}
85
86impl Default for ContactEdge {
87 fn default() -> Self {
88 ContactEdge {
89 body_id: NULL_INDEX,
90 prev_key: NULL_INDEX,
91 next_key: NULL_INDEX,
92 }
93 }
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub struct Contact {
100 pub edges: [ContactEdge; 2],
101
102 pub island_id: i32,
104
105 pub island_index: i32,
108
109 pub set_index: i32,
111
112 pub color_index: i32,
115
116 pub local_index: i32,
118
119 pub shape_id_a: i32,
120 pub shape_id_b: i32,
121 pub contact_id: i32,
122
123 pub flags: u32,
125
126 pub generation: u32,
129}
130
131impl Default for Contact {
132 fn default() -> Self {
133 Contact {
134 edges: [ContactEdge::default(); 2],
135 island_id: NULL_INDEX,
136 island_index: NULL_INDEX,
137 set_index: NULL_INDEX,
138 color_index: NULL_INDEX,
139 local_index: NULL_INDEX,
140 shape_id_a: NULL_INDEX,
141 shape_id_b: NULL_INDEX,
142 contact_id: NULL_INDEX,
143 flags: 0,
144 generation: 0,
145 }
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq)]
156pub struct ContactSim {
157 pub contact_id: i32,
158
159 pub cached_rotation_a: Rot,
161 pub cached_rotation_b: Rot,
162 pub cached_relative_pose: Transform,
163
164 pub body_id_a: i32,
165 pub body_id_b: i32,
166
167 pub body_sim_index_a: i32,
169 pub body_sim_index_b: i32,
170
171 pub shape_id_a: i32,
172 pub shape_id_b: i32,
173
174 pub inv_mass_a: f32,
175 pub inv_i_a: f32,
176
177 pub inv_mass_b: f32,
178 pub inv_i_b: f32,
179
180 pub manifold: Manifold,
181
182 pub friction: f32,
184 pub restitution: f32,
185 pub rolling_resistance: f32,
186 pub tangent_speed: f32,
187
188 pub sim_flags: u32,
190
191 pub cache: SimplexCache,
192}
193
194impl Default for ContactSim {
195 fn default() -> Self {
196 ContactSim {
197 contact_id: NULL_INDEX,
198 cached_rotation_a: ROT_IDENTITY,
199 cached_rotation_b: ROT_IDENTITY,
200 cached_relative_pose: TRANSFORM_IDENTITY,
201 body_id_a: NULL_INDEX,
202 body_id_b: NULL_INDEX,
203 body_sim_index_a: NULL_INDEX,
204 body_sim_index_b: NULL_INDEX,
205 shape_id_a: NULL_INDEX,
206 shape_id_b: NULL_INDEX,
207 inv_mass_a: 0.0,
208 inv_i_a: 0.0,
209 inv_mass_b: 0.0,
210 inv_i_b: 0.0,
211 manifold: Manifold::default(),
212 friction: 0.0,
213 restitution: 0.0,
214 rolling_resistance: 0.0,
215 tangent_speed: 0.0,
216 sim_flags: 0,
217 cache: SimplexCache::default(),
218 }
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use crate::body::{create_body, get_body_full_id, get_body_transform};
226 use crate::broad_phase::update_broad_phase_pairs;
227 use crate::collision::ShapeType;
228 use crate::geometry::make_box;
229 use crate::math_functions::VEC2_ZERO;
230 use crate::shape::create_polygon_shape;
231 use crate::solver_set::AWAKE_SET;
232 use crate::table::shape_pair_key;
233 use crate::types::{default_body_def, default_shape_def, default_world_def, BodyType};
234 use crate::world::World;
235
236 #[test]
237 fn contact_register_pairs() {
238 use ShapeType::*;
239 for (a, b) in [
241 (Circle, Circle),
242 (Capsule, Circle),
243 (Capsule, Capsule),
244 (Polygon, Circle),
245 (Polygon, Capsule),
246 (Polygon, Polygon),
247 (Segment, Circle),
248 (Segment, Capsule),
249 (Segment, Polygon),
250 (ChainSegment, Circle),
251 (ChainSegment, Capsule),
252 (ChainSegment, Polygon),
253 ] {
254 assert!(can_collide(a, b), "{a:?} vs {b:?}");
255 assert!(can_collide(b, a), "{b:?} vs {a:?}");
256 }
257
258 assert!(!can_collide(Segment, Segment));
260 assert!(!can_collide(Segment, ChainSegment));
261 assert!(!can_collide(ChainSegment, ChainSegment));
262 }
263
264 #[test]
269 fn overlapping_boxes_create_touching_contact() {
270 let mut world = World::new(&default_world_def());
271
272 let mut body_def = default_body_def();
273 body_def.type_ = BodyType::Dynamic;
274 let body_a = create_body(&mut world, &body_def);
275 let body_b = create_body(&mut world, &body_def);
276 let body_index_a = get_body_full_id(&world, body_a);
277 let body_index_b = get_body_full_id(&world, body_b);
278
279 let box_poly = make_box(0.5, 0.5);
281 let shape_def = default_shape_def();
282 let sa = create_polygon_shape(&mut world, body_a, &shape_def, &box_poly);
283 let sb = create_polygon_shape(&mut world, body_b, &shape_def, &box_poly);
284 let shape_index_a = sa.index1 - 1;
285 let shape_index_b = sb.index1 - 1;
286
287 assert_eq!(world.broad_phase.move_array.len(), 2);
288
289 update_broad_phase_pairs(&mut world);
292 assert_eq!(world.contact_id_pool.id_count(), 1);
293 assert_eq!(world.solver_sets[AWAKE_SET as usize].contact_sims.len(), 1);
294 assert!(world.broad_phase.move_array.is_empty());
295 assert!(world
296 .broad_phase
297 .pair_set
298 .contains_key(shape_pair_key(shape_index_a, shape_index_b)));
299
300 let contact_id = world.solver_sets[AWAKE_SET as usize].contact_sims[0].contact_id;
301 {
302 let contact = &world.contacts[contact_id as usize];
303 assert_eq!(contact.set_index, AWAKE_SET);
304 assert_eq!(contact.color_index, NULL_INDEX);
305 assert_eq!(contact.flags & contact_flags::TOUCHING, 0);
306 assert_eq!(contact.edges[0].body_id, body_index_a);
307 assert_eq!(contact.edges[1].body_id, body_index_b);
308 }
309 assert_eq!(world.bodies[body_index_a as usize].contact_count, 1);
311 assert_eq!(world.bodies[body_index_b as usize].contact_count, 1);
312 assert_eq!(
313 world.bodies[body_index_a as usize].head_contact_key,
314 contact_id << 1
315 );
316 assert_eq!(
317 world.bodies[body_index_b as usize].head_contact_key,
318 (contact_id << 1) | 1
319 );
320
321 update_broad_phase_pairs(&mut world);
323 assert_eq!(world.contact_id_pool.id_count(), 1);
324
325 let (shape_id_a, shape_id_b) = {
327 let contact = &world.contacts[contact_id as usize];
328 (contact.shape_id_a, contact.shape_id_b)
329 };
330 let ctx = ContactUpdateContext::new(&world);
331 let transform_a = get_body_transform(&world, body_index_a);
332 let transform_b = get_body_transform(&world, body_index_b);
333 let shape_a = world.shapes[shape_id_a as usize].clone();
334 let shape_b = world.shapes[shape_id_b as usize].clone();
335
336 let mut contact_sim = world.solver_sets[AWAKE_SET as usize].contact_sims[0];
337 let touching = update_contact(
338 &ctx,
339 &mut contact_sim,
340 &shape_a,
341 transform_a,
342 VEC2_ZERO,
343 &shape_b,
344 transform_b,
345 VEC2_ZERO,
346 );
347 assert!(touching);
348 assert_eq!(contact_sim.manifold.point_count, 2);
349 assert!(contact_sim.sim_flags & contact_flags::SIM_TOUCHING != 0);
350 world.solver_sets[AWAKE_SET as usize].contact_sims[0] = contact_sim;
351
352 destroy_contact(&mut world, contact_id, false);
354 assert_eq!(world.contact_id_pool.id_count(), 0);
355 assert!(world.solver_sets[AWAKE_SET as usize]
356 .contact_sims
357 .is_empty());
358 assert!(!world
359 .broad_phase
360 .pair_set
361 .contains_key(shape_pair_key(shape_index_a, shape_index_b)));
362 assert_eq!(world.bodies[body_index_a as usize].contact_count, 0);
363 assert_eq!(world.bodies[body_index_b as usize].contact_count, 0);
364 assert_eq!(
365 world.bodies[body_index_a as usize].head_contact_key,
366 NULL_INDEX
367 );
368
369 world.validate_solver_sets();
370 }
371}