avian2d/dynamics/solver/contact/
mod.rs1mod normal_part;
4mod tangent_part;
5
6pub use normal_part::ContactNormalPart;
7pub use tangent_part::ContactTangentPart;
8
9use core::cmp::Ordering;
10
11use crate::{
12 collision::contact_types::ContactId,
13 dynamics::solver::{BodyQueryItem, ContactSoftnessCoefficients},
14 prelude::*,
15};
16#[cfg(feature = "serialize")]
17use bevy::reflect::{ReflectDeserialize, ReflectSerialize};
18use bevy::{
19 ecs::entity::{Entity, EntityMapper, MapEntities},
20 reflect::Reflect,
21 utils::default,
22};
23
24use super::solver_body::{SolverBody, SolverBodyInertia};
25
26#[derive(Clone, Debug, PartialEq, Reflect)]
29#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
30#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
31#[reflect(Debug, PartialEq)]
32pub struct ContactConstraintPoint {
33 pub normal_part: ContactNormalPart,
35
36 pub tangent_part: Option<ContactTangentPart>,
40
41 pub anchor1: Vector,
43
44 pub anchor2: Vector,
46
47 pub normal_speed: Scalar,
49
50 pub initial_separation: Scalar,
54}
55
56#[derive(Clone, Debug, PartialEq, Reflect)]
61#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
62#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
63#[reflect(Debug, PartialEq)]
64pub struct ContactConstraint {
65 pub body1: Entity,
67 pub body2: Entity,
69 pub relative_dominance: i16,
74 pub friction: Scalar,
76 pub restitution: Scalar,
78 #[cfg(feature = "2d")]
84 pub tangent_speed: Scalar,
85 #[cfg(feature = "3d")]
91 pub tangent_velocity: Vector,
92 pub normal: Vector,
94 #[cfg(feature = "3d")]
96 pub tangent1: Vector,
97 pub points: Vec<ContactConstraintPoint>,
100 pub contact_id: ContactId,
104 pub manifold_index: usize,
106}
107
108impl ContactConstraint {
109 pub(super) fn generate(
111 body1_entity: Entity,
112 body2_entity: Entity,
113 body1: BodyQueryItem,
114 body2: BodyQueryItem,
115 contact_id: ContactId,
116 manifold: &ContactManifold,
117 manifold_index: usize,
118 warm_start_enabled: bool,
119 softness: &ContactSoftnessCoefficients,
120 ) -> Self {
121 let inertia1 = body1.inertia.unwrap_or(&SolverBodyInertia::DUMMY);
123 let inertia2 = body2.inertia.unwrap_or(&SolverBodyInertia::DUMMY);
124
125 let relative_dominance = inertia1.dominance() - inertia2.dominance();
127
128 let (inv_mass1, i1, inv_mass2, i2) = match relative_dominance.cmp(&0) {
130 Ordering::Equal => (
131 inertia1.effective_inv_mass(),
132 inertia1.effective_inv_angular_inertia(),
133 inertia2.effective_inv_mass(),
134 inertia2.effective_inv_angular_inertia(),
135 ),
136 Ordering::Greater => (
137 Vector::ZERO,
138 SymmetricTensor::ZERO,
139 inertia2.effective_inv_mass(),
140 inertia2.effective_inv_angular_inertia(),
141 ),
142 Ordering::Less => (
143 inertia1.effective_inv_mass(),
144 inertia1.effective_inv_angular_inertia(),
145 Vector::ZERO,
146 SymmetricTensor::ZERO,
147 ),
148 };
149
150 let softness = if relative_dominance != 0 {
151 softness.non_dynamic
152 } else {
153 softness.dynamic
154 };
155
156 let effective_inverse_mass_sum = inv_mass1 + inv_mass2;
157
158 let tangents = compute_tangent_directions(
159 manifold.normal,
160 body1.linear_velocity.0,
161 body2.linear_velocity.0,
162 );
163
164 let mut points = Vec::with_capacity(manifold.points.len());
165
166 for point in manifold.points.iter() {
167 let anchor1 = point.anchor1;
170 let anchor2 = point.anchor2;
171
172 let point = ContactConstraintPoint {
173 normal_part: ContactNormalPart::generate(
175 effective_inverse_mass_sum,
176 &i1,
177 &i2,
178 anchor1,
179 anchor2,
180 manifold.normal,
181 warm_start_enabled.then_some(point.warm_start_normal_impulse),
182 softness,
183 ),
184 tangent_part: (manifold.friction > 0.0).then_some(ContactTangentPart::generate(
186 effective_inverse_mass_sum,
187 &i1,
188 &i2,
189 anchor1,
190 anchor2,
191 tangents,
192 warm_start_enabled.then_some(point.warm_start_tangent_impulse),
193 )),
194 anchor1,
195 anchor2,
196 normal_speed: point.normal_speed,
197 initial_separation: -point.penetration - (anchor2 - anchor1).dot(manifold.normal),
198 };
199
200 points.push(point);
201 }
202
203 ContactConstraint {
204 body1: body1_entity,
205 body2: body2_entity,
206 relative_dominance,
207 friction: manifold.friction,
208 restitution: manifold.restitution,
209 #[cfg(feature = "2d")]
210 tangent_speed: manifold.tangent_speed,
211 #[cfg(feature = "3d")]
212 tangent_velocity: manifold.tangent_velocity,
213 normal: manifold.normal,
214 #[cfg(feature = "3d")]
215 tangent1: tangents[0],
216 points,
217 contact_id,
218 manifold_index,
219 }
220 }
221
222 pub fn warm_start(
224 &self,
225 body1: &mut SolverBody,
226 body2: &mut SolverBody,
227 inertia1: &SolverBodyInertia,
228 inertia2: &SolverBodyInertia,
229 warm_start_coefficient: Scalar,
230 ) {
231 let inv_mass1 = inertia1.effective_inv_mass();
232 let inv_mass2 = inertia2.effective_inv_mass();
233 let inv_angular_inertia1 = inertia1.effective_inv_angular_inertia();
234 let inv_angular_inertia2 = inertia2.effective_inv_angular_inertia();
235
236 let tangent_directions = self.tangent_directions();
237
238 for point in self.points.iter() {
239 let r1 = point.anchor1;
241 let r2 = point.anchor2;
242
243 let tangent_impulse = point
244 .tangent_part
245 .as_ref()
246 .map_or(default(), |part| part.impulse);
247
248 #[cfg(feature = "2d")]
249 let p = warm_start_coefficient
250 * (point.normal_part.impulse * self.normal
251 + tangent_impulse * tangent_directions[0]);
252 #[cfg(feature = "3d")]
253 let p = warm_start_coefficient
254 * (point.normal_part.impulse * self.normal
255 + tangent_impulse.x * tangent_directions[0]
256 + tangent_impulse.y * tangent_directions[1]);
257
258 body1.linear_velocity -= p * inv_mass1;
259 body1.angular_velocity -= inv_angular_inertia1 * cross(r1, p);
260
261 body2.linear_velocity += p * inv_mass2;
262 body2.angular_velocity += inv_angular_inertia2 * cross(r2, p);
263 }
264 }
265
266 pub fn solve(
268 &mut self,
269 body1: &mut SolverBody,
270 body2: &mut SolverBody,
271 inertia1: &SolverBodyInertia,
272 inertia2: &SolverBodyInertia,
273 delta_secs: Scalar,
274 use_bias: bool,
275 max_overlap_solve_speed: Scalar,
276 ) {
277 let inv_mass1 = inertia1.effective_inv_mass();
278 let inv_mass2 = inertia2.effective_inv_mass();
279 let inv_angular_inertia1 = inertia1.effective_inv_angular_inertia();
280 let inv_angular_inertia2 = inertia2.effective_inv_angular_inertia();
281
282 let delta_translation = body2.delta_position - body1.delta_position;
283
284 for point in self.points.iter_mut() {
286 let r1 = body1.delta_rotation * point.anchor1;
287 let r2 = body2.delta_rotation * point.anchor2;
288
289 let delta_separation = delta_translation + (r2 - r1);
291 let separation = delta_separation.dot(self.normal) + point.initial_separation;
292
293 let r1 = point.anchor1;
295 let r2 = point.anchor2;
296
297 let relative_velocity = body2.velocity_at_point(r2) - body1.velocity_at_point(r1);
299
300 let impulse_magnitude = point.normal_part.solve_impulse(
302 separation,
303 relative_velocity,
304 self.normal,
305 use_bias,
306 max_overlap_solve_speed,
307 delta_secs,
308 );
309
310 let impulse = impulse_magnitude * self.normal;
311
312 body1.linear_velocity -= impulse * inv_mass1;
314 body1.angular_velocity -= inv_angular_inertia1 * cross(r1, impulse);
315
316 body2.linear_velocity += impulse * inv_mass2;
317 body2.angular_velocity += inv_angular_inertia2 * cross(r2, impulse);
318 }
319
320 let tangent_directions = self.tangent_directions();
321
322 for point in self.points.iter_mut() {
324 let Some(ref mut friction_part) = point.tangent_part else {
325 continue;
326 };
327
328 let r1 = point.anchor1;
330 let r2 = point.anchor2;
331
332 let relative_velocity = body2.velocity_at_point(r2) - body1.velocity_at_point(r1);
334
335 let impulse = friction_part.solve_impulse(
337 tangent_directions,
338 relative_velocity,
339 #[cfg(feature = "2d")]
340 self.tangent_speed,
341 #[cfg(feature = "3d")]
342 self.tangent_velocity,
343 self.friction,
344 point.normal_part.impulse,
345 );
346
347 body1.linear_velocity -= impulse * inv_mass1;
349 body1.angular_velocity -= inv_angular_inertia1 * cross(r1, impulse);
350
351 body2.linear_velocity += impulse * inv_mass2;
352 body2.angular_velocity += inv_angular_inertia2 * cross(r2, impulse);
353 }
354 }
355
356 pub fn apply_restitution(
359 &mut self,
360 body1: &mut SolverBody,
361 body2: &mut SolverBody,
362 inertia1: &SolverBodyInertia,
363 inertia2: &SolverBodyInertia,
364 threshold: Scalar,
365 ) {
366 let inv_mass1 = inertia1.effective_inv_mass();
367 let inv_mass2 = inertia2.effective_inv_mass();
368 let inv_angular_inertia1 = inertia1.effective_inv_angular_inertia();
369 let inv_angular_inertia2 = inertia2.effective_inv_angular_inertia();
370
371 for point in self.points.iter_mut() {
372 if point.normal_speed > -threshold || point.normal_part.total_impulse == 0.0 {
375 continue;
376 }
377
378 let r1 = point.anchor1;
380 let r2 = point.anchor2;
381
382 let relative_velocity = body2.velocity_at_point(r2) - body1.velocity_at_point(r1);
384 let normal_speed = relative_velocity.dot(self.normal);
385
386 let mut impulse = -point.normal_part.effective_mass
388 * (normal_speed + self.restitution * point.normal_speed);
389
390 let new_impulse = (point.normal_part.impulse + impulse).max(0.0);
392 impulse = new_impulse - point.normal_part.impulse;
393 point.normal_part.impulse = new_impulse;
394
395 point.normal_part.total_impulse += impulse;
397
398 let impulse = impulse * self.normal;
400
401 body1.linear_velocity -= impulse * inv_mass1;
402 body1.angular_velocity -= inv_angular_inertia1 * cross(r1, impulse);
403
404 body2.linear_velocity += impulse * inv_mass2;
405 body2.angular_velocity += inv_angular_inertia2 * cross(r2, impulse);
406 }
407 }
408
409 #[inline(always)]
411 pub fn tangent_directions(&self) -> [Vector; DIM - 1] {
412 #[cfg(feature = "2d")]
413 {
414 [Vector::new(self.normal.y, -self.normal.x)]
415 }
416 #[cfg(feature = "3d")]
417 {
418 [self.tangent1, self.tangent1.cross(self.normal)]
420 }
421 }
422}
423
424#[allow(unused_variables)]
426#[inline(always)]
427fn compute_tangent_directions(
428 normal: Vector,
429 velocity1: Vector,
430 velocity2: Vector,
431) -> [Vector; DIM - 1] {
432 #[cfg(feature = "2d")]
433 {
434 [Vector::new(normal.y, -normal.x)]
435 }
436 #[cfg(feature = "3d")]
437 {
438 let force_direction = -normal;
439 let relative_velocity = velocity1 - velocity2;
440 let tangent_velocity =
441 relative_velocity - force_direction * force_direction.dot(relative_velocity);
442
443 let tangent = tangent_velocity
444 .try_normalize()
445 .unwrap_or(force_direction.any_orthonormal_vector());
446 let bitangent = force_direction.cross(tangent);
447 [tangent, bitangent]
448 }
449}
450
451impl MapEntities for ContactConstraint {
452 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
453 self.body1 = entity_mapper.get_mapped(self.body1);
454 self.body2 = entity_mapper.get_mapped(self.body2);
455 }
456}