use crate::prelude::*;
use bevy::{
ecs::entity::{EntityMapper, MapEntities},
prelude::*,
};
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct PenetrationConstraint {
pub entity1: Entity,
pub entity2: Entity,
pub collider_entity1: Entity,
pub collider_entity2: Entity,
pub contact: ContactData,
pub manifold_index: usize,
pub r1: Vector,
pub r2: Vector,
pub normal_lagrange: Scalar,
pub tangent_lagrange: Scalar,
pub compliance: Scalar,
pub friction: Friction,
pub restitution: Restitution,
}
impl XpbdConstraint<2> for PenetrationConstraint {
fn entities(&self) -> [Entity; 2] {
[self.entity1, self.entity2]
}
fn clear_lagrange_multipliers(&mut self) {
self.normal_lagrange = 0.0;
self.tangent_lagrange = 0.0;
}
fn solve(&mut self, bodies: [&mut RigidBodyQueryItem; 2], dt: Scalar) {
let [body1, body2] = bodies;
let p1 = body1.current_position() + body1.rotation.rotate(self.contact.point1);
let p2 = body2.current_position() + body2.rotation.rotate(self.contact.point2);
self.contact.penetration = (p1 - p2).dot(self.contact.global_normal1(&body1.rotation));
if self.contact.penetration <= Scalar::EPSILON {
return;
}
self.solve_contact(body1, body2, dt);
self.solve_friction(body1, body2, dt);
}
}
impl PenetrationConstraint {
pub fn new(
body1: &RigidBodyQueryItem,
body2: &RigidBodyQueryItem,
collider_entity1: Entity,
collider_entity2: Entity,
contact: ContactData,
manifold_index: usize,
) -> Self {
let r1 = contact.point1 - body1.center_of_mass.0;
let r2 = contact.point2 - body2.center_of_mass.0;
Self {
entity1: body1.entity,
entity2: body2.entity,
collider_entity1,
collider_entity2,
contact,
manifold_index,
r1,
r2,
normal_lagrange: 0.0,
tangent_lagrange: 0.0,
compliance: 0.0,
friction: body1.friction.combine(*body2.friction),
restitution: body1.restitution.combine(*body2.restitution),
}
}
fn solve_contact(
&mut self,
body1: &mut RigidBodyQueryItem,
body2: &mut RigidBodyQueryItem,
dt: Scalar,
) {
let compliance = self.compliance;
let lagrange = self.normal_lagrange;
let penetration = self.contact.penetration;
let normal = self.contact.global_normal1(&body1.rotation);
let r1 = body1.rotation.rotate(self.r1);
let r2 = body2.rotation.rotate(self.r2);
let w1 = self.compute_generalized_inverse_mass(body1, r1, normal);
let w2 = self.compute_generalized_inverse_mass(body2, r2, normal);
let gradients = [normal, -normal];
let w = [w1, w2];
let delta_lagrange =
self.compute_lagrange_update(lagrange, penetration, &gradients, &w, compliance, dt);
self.normal_lagrange += delta_lagrange;
self.apply_positional_correction(body1, body2, delta_lagrange, normal, r1, r2);
self.contact.normal_impulse += self.normal_lagrange / dt;
}
fn solve_friction(
&mut self,
body1: &mut RigidBodyQueryItem,
body2: &mut RigidBodyQueryItem,
dt: Scalar,
) {
let compliance = self.compliance;
let lagrange = self.tangent_lagrange;
let penetration = self.contact.penetration;
let normal = self.contact.global_normal1(&body1.rotation);
let r1 = body1.rotation.rotate(self.r1);
let r2 = body2.rotation.rotate(self.r2);
let delta_p1 = body1.current_position() - body1.previous_position.0
+ body1.rotation.rotate(self.contact.point1)
- body1.previous_rotation.rotate(self.contact.point1);
let delta_p2 = body2.current_position() - body2.previous_position.0
+ body2.rotation.rotate(self.contact.point2)
- body2.previous_rotation.rotate(self.contact.point2);
let delta_p = delta_p1 - delta_p2;
let delta_p_tangent = delta_p - delta_p.dot(normal) * normal;
let sliding_len = delta_p_tangent.length();
if sliding_len <= Scalar::EPSILON {
return;
}
let tangent = delta_p_tangent / sliding_len;
let w1 = self.compute_generalized_inverse_mass(body1, r1, tangent);
let w2 = self.compute_generalized_inverse_mass(body2, r2, tangent);
let gradients = [tangent, -tangent];
let w = [w1, w2];
if sliding_len < self.friction.static_coefficient * penetration {
let delta_lagrange =
self.compute_lagrange_update(lagrange, sliding_len, &gradients, &w, compliance, dt);
self.tangent_lagrange += delta_lagrange;
self.apply_positional_correction(body1, body2, delta_lagrange, tangent, r1, r2);
self.contact.tangent_impulse += self.tangent_lagrange / dt;
}
}
}
impl PositionConstraint for PenetrationConstraint {}
impl MapEntities for PenetrationConstraint {
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
self.entity1 = entity_mapper.map_entity(self.entity1);
self.entity2 = entity_mapper.map_entity(self.entity2);
}
}