avian3d/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, dynamics::solver::ContactSoftnessCoefficients, prelude::*,
13};
14#[cfg(feature = "serialize")]
15use bevy::reflect::{ReflectDeserialize, ReflectSerialize};
16use bevy::{reflect::Reflect, utils::default};
17
18use super::solver_body::{SolverBody, SolverBodyIndex, SolverBodyInertia};
19
20#[derive(Clone, Debug, PartialEq, Reflect)]
23#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
25#[reflect(Debug, PartialEq)]
26pub struct ContactConstraintPoint {
27 pub normal_part: ContactNormalPart,
29
30 pub tangent_part: Option<ContactTangentPart>,
34
35 pub anchor1: Vector,
37
38 pub anchor2: Vector,
40
41 pub normal_speed: f32,
43
44 pub initial_separation: f32,
48}
49
50#[derive(Clone, Debug, PartialEq, Reflect)]
55#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
56#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
57#[reflect(Debug, PartialEq)]
58pub struct ContactConstraint {
59 pub body_index1: SolverBodyIndex,
62 pub body_index2: SolverBodyIndex,
65 pub relative_dominance: i16,
70 pub friction: f32,
72 pub restitution: f32,
74 #[cfg(feature = "2d")]
80 pub tangent_speed: f32,
81 #[cfg(feature = "3d")]
87 pub tangent_velocity: Vector,
88 pub normal: Vector,
90 #[cfg(feature = "3d")]
92 pub tangent1: Vector,
93 pub points: Vec<ContactConstraintPoint>,
96 pub contact_id: ContactId,
100 pub manifold_index: usize,
102}
103
104impl ContactConstraint {
105 pub(super) fn generate(
107 body_index1: SolverBodyIndex,
108 body_index2: SolverBodyIndex,
109 inertia1: &SolverBodyInertia,
110 inertia2: &SolverBodyInertia,
111 linear_velocity1: Vector,
112 linear_velocity2: Vector,
113 contact_id: ContactId,
114 manifold: &ContactManifold,
115 manifold_index: usize,
116 warm_start_enabled: bool,
117 softness: &ContactSoftnessCoefficients,
118 ) -> Self {
119 let relative_dominance = inertia1.dominance() - inertia2.dominance();
121
122 let (inv_mass1, i1, inv_mass2, i2) = match relative_dominance.cmp(&0) {
124 Ordering::Equal => (
125 inertia1.effective_inv_mass(),
126 inertia1.effective_inv_angular_inertia(),
127 inertia2.effective_inv_mass(),
128 inertia2.effective_inv_angular_inertia(),
129 ),
130 Ordering::Greater => (
131 Vector::ZERO,
132 SymmetricTensor::ZERO,
133 inertia2.effective_inv_mass(),
134 inertia2.effective_inv_angular_inertia(),
135 ),
136 Ordering::Less => (
137 inertia1.effective_inv_mass(),
138 inertia1.effective_inv_angular_inertia(),
139 Vector::ZERO,
140 SymmetricTensor::ZERO,
141 ),
142 };
143
144 let softness = if relative_dominance != 0 {
145 softness.non_dynamic
146 } else {
147 softness.dynamic
148 };
149
150 let effective_inverse_mass_sum = inv_mass1 + inv_mass2;
151
152 let tangents =
153 compute_tangent_directions(manifold.normal, linear_velocity1, linear_velocity2);
154
155 let mut points = Vec::with_capacity(manifold.points.len());
156
157 for point in manifold.points.iter() {
158 let anchor1 = point.anchor1;
161 let anchor2 = point.anchor2;
162
163 let point = ContactConstraintPoint {
164 normal_part: ContactNormalPart::generate(
166 effective_inverse_mass_sum,
167 &i1,
168 &i2,
169 anchor1,
170 anchor2,
171 manifold.normal,
172 warm_start_enabled.then_some(point.warm_start_normal_impulse),
173 softness,
174 ),
175 tangent_part: (manifold.friction > 0.0).then_some(ContactTangentPart::generate(
177 effective_inverse_mass_sum,
178 &i1,
179 &i2,
180 anchor1,
181 anchor2,
182 tangents,
183 warm_start_enabled.then_some(point.warm_start_tangent_impulse),
184 )),
185 anchor1,
186 anchor2,
187 normal_speed: point.normal_speed,
188 initial_separation: -point.penetration - (anchor2 - anchor1).dot(manifold.normal),
189 };
190
191 points.push(point);
192 }
193
194 ContactConstraint {
195 body_index1,
196 body_index2,
197 relative_dominance,
198 friction: manifold.friction,
199 restitution: manifold.restitution,
200 #[cfg(feature = "2d")]
201 tangent_speed: manifold.tangent_speed,
202 #[cfg(feature = "3d")]
203 tangent_velocity: manifold.tangent_velocity,
204 normal: manifold.normal,
205 #[cfg(feature = "3d")]
206 tangent1: tangents[0],
207 points,
208 contact_id,
209 manifold_index,
210 }
211 }
212
213 pub fn warm_start(
215 &self,
216 body1: &mut SolverBody,
217 body2: &mut SolverBody,
218 inertia1: &SolverBodyInertia,
219 inertia2: &SolverBodyInertia,
220 warm_start_coefficient: f32,
221 ) {
222 let inv_mass1 = inertia1.effective_inv_mass();
223 let inv_mass2 = inertia2.effective_inv_mass();
224 let inv_angular_inertia1 = inertia1.effective_inv_angular_inertia();
225 let inv_angular_inertia2 = inertia2.effective_inv_angular_inertia();
226
227 let tangent_directions = self.tangent_directions();
228
229 for point in self.points.iter() {
230 let r1 = point.anchor1;
232 let r2 = point.anchor2;
233
234 let tangent_impulse = point
235 .tangent_part
236 .as_ref()
237 .map_or(default(), |part| part.impulse);
238
239 #[cfg(feature = "2d")]
240 let p = warm_start_coefficient
241 * (point.normal_part.impulse * self.normal
242 + tangent_impulse * tangent_directions[0]);
243 #[cfg(feature = "3d")]
244 let p = warm_start_coefficient
245 * (point.normal_part.impulse * self.normal
246 + tangent_impulse.x * tangent_directions[0]
247 + tangent_impulse.y * tangent_directions[1]);
248
249 body1.linear_velocity -= p * inv_mass1;
250 body1.angular_velocity -= inv_angular_inertia1 * cross(r1, p);
251
252 body2.linear_velocity += p * inv_mass2;
253 body2.angular_velocity += inv_angular_inertia2 * cross(r2, p);
254 }
255 }
256
257 pub fn solve<const USE_BIAS: bool>(
259 &mut self,
260 body1: &mut SolverBody,
261 body2: &mut SolverBody,
262 inertia1: &SolverBodyInertia,
263 inertia2: &SolverBodyInertia,
264 delta_secs: f32,
265 max_overlap_solve_speed: f32,
266 ) {
267 let inv_mass1 = inertia1.effective_inv_mass();
268 let inv_mass2 = inertia2.effective_inv_mass();
269 let inv_angular_inertia1 = inertia1.effective_inv_angular_inertia();
270 let inv_angular_inertia2 = inertia2.effective_inv_angular_inertia();
271
272 let delta_translation = body2.delta_position - body1.delta_position;
273
274 for point in self.points.iter_mut() {
276 let r1 = body1.delta_rotation * point.anchor1;
277 let r2 = body2.delta_rotation * point.anchor2;
278
279 let delta_separation = delta_translation + (r2 - r1);
281 let separation = delta_separation.dot(self.normal) + point.initial_separation;
282
283 let r1 = point.anchor1;
285 let r2 = point.anchor2;
286
287 let relative_velocity = body2.velocity_at_point(r2) - body1.velocity_at_point(r1);
289
290 let impulse_magnitude = point.normal_part.solve_impulse::<USE_BIAS>(
292 separation,
293 relative_velocity,
294 self.normal,
295 max_overlap_solve_speed,
296 delta_secs,
297 );
298
299 let impulse = impulse_magnitude * self.normal;
300
301 body1.linear_velocity -= impulse * inv_mass1;
303 body1.angular_velocity -= inv_angular_inertia1 * cross(r1, impulse);
304
305 body2.linear_velocity += impulse * inv_mass2;
306 body2.angular_velocity += inv_angular_inertia2 * cross(r2, impulse);
307 }
308
309 if !USE_BIAS {
312 let tangent_directions = self.tangent_directions();
313
314 for point in self.points.iter_mut() {
315 let Some(ref mut friction_part) = point.tangent_part else {
316 continue;
317 };
318
319 let r1 = point.anchor1;
321 let r2 = point.anchor2;
322
323 let relative_velocity = body2.velocity_at_point(r2) - body1.velocity_at_point(r1);
325
326 let impulse = friction_part.solve_impulse(
328 tangent_directions,
329 relative_velocity,
330 #[cfg(feature = "2d")]
331 self.tangent_speed,
332 #[cfg(feature = "3d")]
333 self.tangent_velocity,
334 self.friction,
335 point.normal_part.impulse,
336 );
337
338 body1.linear_velocity -= impulse * inv_mass1;
340 body1.angular_velocity -= inv_angular_inertia1 * cross(r1, impulse);
341
342 body2.linear_velocity += impulse * inv_mass2;
343 body2.angular_velocity += inv_angular_inertia2 * cross(r2, impulse);
344 }
345 }
346 }
347
348 pub fn apply_restitution(
351 &mut self,
352 body1: &mut SolverBody,
353 body2: &mut SolverBody,
354 inertia1: &SolverBodyInertia,
355 inertia2: &SolverBodyInertia,
356 threshold: f32,
357 ) {
358 let inv_mass1 = inertia1.effective_inv_mass();
359 let inv_mass2 = inertia2.effective_inv_mass();
360 let inv_angular_inertia1 = inertia1.effective_inv_angular_inertia();
361 let inv_angular_inertia2 = inertia2.effective_inv_angular_inertia();
362
363 for point in self.points.iter_mut() {
364 if point.normal_speed > -threshold || point.normal_part.total_impulse == 0.0 {
367 continue;
368 }
369
370 let r1 = point.anchor1;
372 let r2 = point.anchor2;
373
374 let relative_velocity = body2.velocity_at_point(r2) - body1.velocity_at_point(r1);
376 let normal_speed = relative_velocity.dot(self.normal);
377
378 let mut impulse = -point.normal_part.effective_mass
380 * (normal_speed + self.restitution * point.normal_speed);
381
382 let new_impulse = (point.normal_part.impulse + impulse).max(0.0);
384 impulse = new_impulse - point.normal_part.impulse;
385 point.normal_part.impulse = new_impulse;
386
387 point.normal_part.total_impulse += impulse;
389
390 let impulse = impulse * self.normal;
392
393 body1.linear_velocity -= impulse * inv_mass1;
394 body1.angular_velocity -= inv_angular_inertia1 * cross(r1, impulse);
395
396 body2.linear_velocity += impulse * inv_mass2;
397 body2.angular_velocity += inv_angular_inertia2 * cross(r2, impulse);
398 }
399 }
400
401 #[inline(always)]
403 pub fn tangent_directions(&self) -> [Vector; DIM - 1] {
404 #[cfg(feature = "2d")]
405 {
406 [Vector::new(self.normal.y, -self.normal.x)]
407 }
408 #[cfg(feature = "3d")]
409 {
410 [self.tangent1, self.tangent1.cross(self.normal)]
412 }
413 }
414}
415
416#[allow(unused_variables)]
418#[inline(always)]
419fn compute_tangent_directions(
420 normal: Vector,
421 velocity1: Vector,
422 velocity2: Vector,
423) -> [Vector; DIM - 1] {
424 #[cfg(feature = "2d")]
425 {
426 [Vector::new(normal.y, -normal.x)]
427 }
428 #[cfg(feature = "3d")]
429 {
430 let force_direction = -normal;
431 let relative_velocity = velocity1 - velocity2;
432 let tangent_velocity =
433 relative_velocity - force_direction * force_direction.dot(relative_velocity);
434
435 let tangent = tangent_velocity
436 .try_normalize()
437 .unwrap_or(force_direction.any_orthonormal_vector());
438 let bitangent = force_direction.cross(tangent);
439 [tangent, bitangent]
440 }
441}