1use super::{contact_flags, ContactSim};
8use crate::collision::LocalManifold;
9use crate::collision::Manifold;
10use crate::constants::linear_slop;
11use crate::distance::SimplexCache;
12use crate::id::ShapeId;
13use crate::manifold::{
14 collide_capsule_and_circle, collide_capsules, collide_chain_segment_and_capsule,
15 collide_chain_segment_and_circle, collide_chain_segment_and_polygon, collide_circles,
16 collide_polygon_and_capsule, collide_polygon_and_circle, collide_polygons,
17 collide_segment_and_capsule, collide_segment_and_circle, collide_segment_and_polygon,
18};
19use crate::math_functions::{
20 add, inv_mul_world_transforms, max_float, offset_pos, rotate_vector, sub, sub_pos, Transform,
21 Vec2, WorldTransform,
22};
23use crate::shape::Shape;
24use crate::types::{FrictionCallback, RestitutionCallback};
25use crate::world::{PreSolveFcn, World};
26
27fn compute_manifold(
31 shape_a: &Shape,
32 shape_b: &Shape,
33 xf: Transform,
34 cache: &mut SimplexCache,
35) -> LocalManifold {
36 use crate::collision::ShapeGeometry;
37 match (&shape_a.geometry, &shape_b.geometry) {
38 (ShapeGeometry::Circle(a), ShapeGeometry::Circle(b)) => collide_circles(a, b, xf),
39 (ShapeGeometry::Capsule(a), ShapeGeometry::Circle(b)) => {
40 collide_capsule_and_circle(a, b, xf)
41 }
42 (ShapeGeometry::Capsule(a), ShapeGeometry::Capsule(b)) => collide_capsules(a, b, xf),
43 (ShapeGeometry::Polygon(a), ShapeGeometry::Circle(b)) => {
44 collide_polygon_and_circle(a, b, xf)
45 }
46 (ShapeGeometry::Polygon(a), ShapeGeometry::Capsule(b)) => {
47 collide_polygon_and_capsule(a, b, xf)
48 }
49 (ShapeGeometry::Polygon(a), ShapeGeometry::Polygon(b)) => collide_polygons(a, b, xf),
50 (ShapeGeometry::Segment(a), ShapeGeometry::Circle(b)) => {
51 collide_segment_and_circle(a, b, xf)
52 }
53 (ShapeGeometry::Segment(a), ShapeGeometry::Capsule(b)) => {
54 collide_segment_and_capsule(a, b, xf)
55 }
56 (ShapeGeometry::Segment(a), ShapeGeometry::Polygon(b)) => {
57 collide_segment_and_polygon(a, b, xf)
58 }
59 (ShapeGeometry::ChainSegment(a), ShapeGeometry::Circle(b)) => {
60 collide_chain_segment_and_circle(a, b, xf)
61 }
62 (ShapeGeometry::ChainSegment(a), ShapeGeometry::Capsule(b)) => {
63 collide_chain_segment_and_capsule(a, b, xf, cache)
64 }
65 (ShapeGeometry::ChainSegment(a), ShapeGeometry::Polygon(b)) => {
66 collide_chain_segment_and_polygon(a, b, xf, cache)
67 }
68 _ => unreachable!("no manifold function for this shape pair"),
71 }
72}
73
74#[derive(Clone, Copy)]
78pub struct ContactUpdateContext {
79 pub friction_callback: FrictionCallback,
80 pub restitution_callback: RestitutionCallback,
81 pub pre_solve_fcn: Option<PreSolveFcn>,
82 pub pre_solve_context: u64,
83 pub world_id: u16,
84 pub enable_speculative: bool,
85}
86
87impl ContactUpdateContext {
88 pub fn new(world: &World) -> ContactUpdateContext {
89 ContactUpdateContext {
90 friction_callback: world.friction_callback.unwrap(),
91 restitution_callback: world.restitution_callback.unwrap(),
92 pre_solve_fcn: world.pre_solve_fcn,
93 pre_solve_context: world.pre_solve_context,
94 world_id: world.world_id,
95 enable_speculative: world.enable_speculative,
96 }
97 }
98}
99
100#[allow(clippy::too_many_arguments)]
104pub fn update_contact(
105 ctx: &ContactUpdateContext,
106 contact_sim: &mut ContactSim,
107 shape_a: &Shape,
108 transform_a: WorldTransform,
109 center_offset_a: Vec2,
110 shape_b: &Shape,
111 transform_b: WorldTransform,
112 center_offset_b: Vec2,
113) -> bool {
114 let mut old_manifold = contact_sim.manifold;
116
117 let relative_transform = inv_mul_world_transforms(transform_a, transform_b);
123 let local = compute_manifold(shape_a, shape_b, relative_transform, &mut contact_sim.cache);
124
125 contact_sim.manifold = Manifold::default();
126 contact_sim.manifold.normal = rotate_vector(transform_a.q, local.normal);
127 contact_sim.manifold.point_count = local.point_count;
128
129 let origin_delta = sub_pos(transform_a.p, transform_b.p);
130 for i in 0..local.point_count as usize {
131 let mp = &mut contact_sim.manifold.points[i];
132 mp.anchor_a = rotate_vector(transform_a.q, local.points[i].point);
133 mp.anchor_b = add(mp.anchor_a, origin_delta);
134 mp.separation = local.points[i].separation;
135 mp.id = local.points[i].id;
136 }
137
138 contact_sim.friction = (ctx.friction_callback)(
140 shape_a.material.friction,
141 shape_a.material.user_material_id,
142 shape_b.material.friction,
143 shape_b.material.user_material_id,
144 );
145 contact_sim.restitution = (ctx.restitution_callback)(
146 shape_a.material.restitution,
147 shape_a.material.user_material_id,
148 shape_b.material.restitution,
149 shape_b.material.user_material_id,
150 );
151
152 if shape_a.material.rolling_resistance > 0.0 || shape_b.material.rolling_resistance > 0.0 {
153 let radius_a = shape_a.radius();
154 let radius_b = shape_b.radius();
155 let max_radius = max_float(radius_a, radius_b);
156 contact_sim.rolling_resistance = max_float(
157 shape_a.material.rolling_resistance,
158 shape_b.material.rolling_resistance,
159 ) * max_radius;
160 } else {
161 contact_sim.rolling_resistance = 0.0;
162 }
163
164 contact_sim.tangent_speed = shape_a.material.tangent_speed + shape_b.material.tangent_speed;
165
166 let mut point_count = contact_sim.manifold.point_count;
167 let mut touching = point_count > 0;
168
169 if let (true, Some(pre_solve_fcn), true) = (
170 touching,
171 ctx.pre_solve_fcn,
172 (contact_sim.sim_flags & contact_flags::SIM_ENABLE_PRE_SOLVE_EVENTS) != 0,
173 ) {
174 let shape_id_a = ShapeId {
175 index1: shape_a.id + 1,
176 world0: ctx.world_id,
177 generation: shape_a.generation,
178 };
179 let shape_id_b = ShapeId {
180 index1: shape_b.id + 1,
181 world0: ctx.world_id,
182 generation: shape_b.generation,
183 };
184
185 let manifold = &contact_sim.manifold;
186 let mut best_separation = manifold.points[0].separation;
187 let mut best_point = offset_pos(transform_a.p, manifold.points[0].anchor_a);
188
189 for i in 1..manifold.point_count as usize {
191 let separation = manifold.points[i].separation;
192 if separation < best_separation {
193 best_separation = separation;
194 best_point = offset_pos(transform_a.p, manifold.points[i].anchor_a);
195 }
196 }
197
198 touching = pre_solve_fcn(
200 shape_id_a,
201 shape_id_b,
202 best_point,
203 manifold.normal,
204 ctx.pre_solve_context,
205 );
206 if !touching {
207 point_count = 0;
209 contact_sim.manifold.point_count = 0;
210 }
211 }
212
213 if !ctx.enable_speculative && point_count == 2 {
215 if contact_sim.manifold.points[0].separation > 1.5 * linear_slop() {
216 contact_sim.manifold.points[0] = contact_sim.manifold.points[1];
217 contact_sim.manifold.point_count = 1;
218 } else if contact_sim.manifold.points[1].separation > 1.5 * linear_slop() {
219 contact_sim.manifold.point_count = 1;
220 }
221
222 point_count = contact_sim.manifold.point_count;
223 }
224
225 if touching && (shape_a.enable_hit_events || shape_b.enable_hit_events) {
226 contact_sim.sim_flags |= contact_flags::SIM_ENABLE_HIT_EVENT;
227 } else {
228 contact_sim.sim_flags &= !contact_flags::SIM_ENABLE_HIT_EVENT;
229 }
230
231 if point_count > 0 {
232 contact_sim.manifold.rolling_impulse = old_manifold.rolling_impulse;
233 }
234
235 let mut unmatched_count = 0;
238 for i in 0..point_count as usize {
239 let mp2 = &mut contact_sim.manifold.points[i];
240
241 mp2.anchor_a = sub(mp2.anchor_a, center_offset_a);
243 mp2.anchor_b = sub(mp2.anchor_b, center_offset_b);
244
245 mp2.normal_impulse = 0.0;
246 mp2.tangent_impulse = 0.0;
247 mp2.total_normal_impulse = 0.0;
248 mp2.normal_velocity = 0.0;
249 mp2.persisted = false;
250
251 let id2 = mp2.id;
252
253 for j in 0..old_manifold.point_count as usize {
254 let mp1 = &mut old_manifold.points[j];
255
256 if mp1.id == id2 {
257 mp2.normal_impulse = mp1.normal_impulse;
258 mp2.tangent_impulse = mp1.tangent_impulse;
259 mp2.persisted = true;
260
261 mp1.normal_impulse = 0.0;
263 mp1.tangent_impulse = 0.0;
264 break;
265 }
266 }
267
268 unmatched_count += if mp2.persisted { 0 } else { 1 };
269 }
270
271 let _ = unmatched_count;
273
274 if touching {
275 contact_sim.sim_flags |= contact_flags::SIM_TOUCHING;
276 } else {
277 contact_sim.sim_flags &= !contact_flags::SIM_TOUCHING;
278 }
279
280 touching
281}