use super::bodies::Bodies;
use crate::math::sqrt;
use crate::physics::sim::config::{SimConfig, Softness};
use crate::physics::sim::contact::{MAX_MANIFOLD_POINTS, Manifold};
use crate::physics::sim::coupling::Coupling;
use crate::physics::sim::math::Vec3;
use super::bodies::SolverBody;
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct ConstraintPoint {
anchor_a: Vec3,
anchor_b: Vec3,
base_separation: f32,
tangent_mass: [f32; 2],
normal_impulse: f32,
tangent_impulse: [f32; 2],
max_normal_impulse: f32,
step_impulse: f32,
approach_velocity: f32,
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct ContactConstraint {
a: u32,
b: u32,
manifold: u32,
normal: Vec3,
tangent: [Vec3; 2],
friction: f32,
restitution: f32,
coupling: Coupling,
points: [ConstraintPoint; MAX_MANIFOLD_POINTS],
count: u8,
}
impl ContactConstraint {
pub(crate) fn is_empty(&self) -> bool {
self.count == 0
}
pub(crate) fn manifold(&self) -> u32 {
self.manifold
}
pub(crate) fn delivered(&self) -> f32 {
self.points[..self.count as usize]
.iter()
.map(|point| point.step_impulse)
.sum()
}
pub(crate) fn store(&self, manifolds: &mut [Manifold]) {
let manifold = &mut manifolds[self.manifold as usize];
for (point, stored) in manifold
.points_mut()
.iter_mut()
.zip(&self.points[..self.count as usize])
{
point.normal_impulse = stored.normal_impulse;
point.tangent_impulse = stored.tangent_impulse;
}
}
}
pub(crate) fn prepare(
constraints: &mut [ContactConstraint],
source: &[u32],
manifolds: &[Manifold],
bodies: &Bodies<'_>,
) {
for (constraint, &index) in constraints.iter_mut().zip(source) {
let manifold = &manifolds[index as usize];
let (a, b) = (manifold.a, manifold.b);
let (body_a, body_b) = (*bodies.get(a), *bodies.get(b));
let normal = manifold.normal;
let tangent0 = normal.any_perpendicular();
let tangent1 = normal.cross(tangent0);
let mut anchors = [(Vec3::ZERO, Vec3::ZERO); MAX_MANIFOLD_POINTS];
let mut count = 0usize;
for (anchor, point) in anchors.iter_mut().zip(manifold.points()) {
*anchor = (point.point - body_a.position, point.point - body_b.position);
count += 1;
}
if count == 0 {
*constraint = ContactConstraint {
manifold: index,
..ContactConstraint::default()
};
continue;
}
*constraint = ContactConstraint {
a,
b,
manifold: index,
normal,
tangent: [tangent0, tangent1],
friction: manifold.friction,
restitution: manifold.restitution,
coupling: Coupling::build(&body_a, &body_b, normal, &anchors[..count]),
points: [ConstraintPoint::default(); MAX_MANIFOLD_POINTS],
count: count as u8,
};
for ((slot, point), &(ra, rb)) in constraint
.points
.iter_mut()
.zip(manifold.points())
.zip(&anchors)
{
*slot = ConstraintPoint {
anchor_a: ra,
anchor_b: rb,
base_separation: point.separation - (rb - ra).dot(normal),
tangent_mass: [
effective_mass(&body_a, &body_b, ra, rb, tangent0),
effective_mass(&body_a, &body_b, ra, rb, tangent1),
],
normal_impulse: point.normal_impulse,
tangent_impulse: point.tangent_impulse,
max_normal_impulse: 0.0,
step_impulse: 0.0,
approach_velocity: (body_b.velocity_at(rb) - body_a.velocity_at(ra)).dot(normal),
};
}
}
}
pub(crate) fn warm_start(constraints: &mut [ContactConstraint], bodies: &mut Bodies<'_>) {
for constraint in constraints.iter_mut() {
let normal = constraint.normal;
let [t0, t1] = constraint.tangent;
let (a, b) = (constraint.a, constraint.b);
for point in &mut constraint.points[..constraint.count as usize] {
let impulse = normal * point.normal_impulse
+ t0 * point.tangent_impulse[0]
+ t1 * point.tangent_impulse[1];
let ra = bodies.get(a).delta_rotation.rotate(point.anchor_a);
let rb = bodies.get(b).delta_rotation.rotate(point.anchor_b);
point.step_impulse += point.normal_impulse;
bodies.get_mut(a).apply_impulse(-impulse, ra);
bodies.get_mut(b).apply_impulse(impulse, rb);
}
}
}
pub(crate) fn solve(
constraints: &mut [ContactConstraint],
bodies: &mut Bodies<'_>,
soft: &Softness,
config: &SimConfig,
inv_h: f32,
use_bias: bool,
) {
for constraint in constraints.iter_mut() {
let normal = constraint.normal;
let [t0, t1] = constraint.tangent;
let (a, b) = (constraint.a, constraint.b);
let count = constraint.count as usize;
let mut arms = [(Vec3::ZERO, Vec3::ZERO); MAX_MANIFOLD_POINTS];
let mut held = [0.0; MAX_MANIFOLD_POINTS];
for (index, point) in constraint.points[..count].iter().enumerate() {
arms[index] = (
bodies.get(a).delta_rotation.rotate(point.anchor_a),
bodies.get(b).delta_rotation.rotate(point.anchor_b),
);
held[index] = point.normal_impulse;
}
let carried = if use_bias {
constraint.coupling.approach_from(&held)
} else {
[0.0; MAX_MANIFOLD_POINTS]
};
let mut error = [None; MAX_MANIFOLD_POINTS];
for (index, point) in constraint.points[..count].iter().enumerate() {
let (ra, rb) = arms[index];
let travel = (bodies.get(b).delta_position + rb) - (bodies.get(a).delta_position + ra);
let separation = travel.dot(normal) + point.base_separation;
let (bias, mass_scale, impulse_scale) = if separation > 0.0 {
(separation * inv_h, 1.0, 0.0)
} else if use_bias {
(
(soft.bias_rate * separation).max(-config.max_push_velocity),
soft.mass_scale,
soft.impulse_scale,
)
} else {
(0.0, 1.0, 0.0)
};
let approach =
(bodies.get(b).velocity_at(rb) - bodies.get(a).velocity_at(ra)).dot(normal);
error[index] = Some(mass_scale * (approach + bias) + impulse_scale * carried[index]);
}
let solved = constraint.coupling.solve(&held, &error);
for (index, point) in constraint.points[..count].iter_mut().enumerate() {
let applied = solved[index];
point.normal_impulse += applied;
point.max_normal_impulse = point.max_normal_impulse.max(point.normal_impulse);
point.step_impulse += applied;
let (ra, rb) = arms[index];
let impulse = normal * applied;
bodies.get_mut(a).apply_impulse(-impulse, ra);
bodies.get_mut(b).apply_impulse(impulse, rb);
}
for point in &mut constraint.points[..count] {
let ra = bodies.get(a).delta_rotation.rotate(point.anchor_a);
let rb = bodies.get(b).delta_rotation.rotate(point.anchor_b);
let relative = bodies.get(b).velocity_at(rb) - bodies.get(a).velocity_at(ra);
let mut wanted = [
point.tangent_impulse[0] - point.tangent_mass[0] * relative.dot(t0),
point.tangent_impulse[1] - point.tangent_mass[1] * relative.dot(t1),
];
let limit = constraint.friction * point.normal_impulse;
let magnitude = sqrt(wanted[0] * wanted[0] + wanted[1] * wanted[1]);
if magnitude > limit {
let scale = if magnitude > f32::MIN_POSITIVE {
limit / magnitude
} else {
0.0
};
wanted[0] *= scale;
wanted[1] *= scale;
}
let impulse = t0 * (wanted[0] - point.tangent_impulse[0])
+ t1 * (wanted[1] - point.tangent_impulse[1]);
point.tangent_impulse = wanted;
bodies.get_mut(a).apply_impulse(-impulse, ra);
bodies.get_mut(b).apply_impulse(impulse, rb);
}
}
}
pub(crate) fn apply_restitution(
constraints: &mut [ContactConstraint],
bodies: &mut Bodies<'_>,
config: &SimConfig,
) {
let threshold = config.restitution_threshold;
for constraint in constraints.iter_mut() {
if constraint.restitution <= 0.0 {
continue;
}
let normal = constraint.normal;
let (a, b) = (constraint.a, constraint.b);
let count = constraint.count as usize;
let mut arms = [(Vec3::ZERO, Vec3::ZERO); MAX_MANIFOLD_POINTS];
let mut held = [0.0; MAX_MANIFOLD_POINTS];
let mut error = [None; MAX_MANIFOLD_POINTS];
for (index, point) in constraint.points[..count].iter().enumerate() {
let (ra, rb) = (
bodies.get(a).delta_rotation.rotate(point.anchor_a),
bodies.get(b).delta_rotation.rotate(point.anchor_b),
);
arms[index] = (ra, rb);
held[index] = point.normal_impulse;
if point.approach_velocity > -threshold || point.max_normal_impulse == 0.0 {
continue;
}
let approach =
(bodies.get(b).velocity_at(rb) - bodies.get(a).velocity_at(ra)).dot(normal);
error[index] = Some(approach + constraint.restitution * point.approach_velocity);
}
if error[..count].iter().all(Option::is_none) {
continue;
}
let solved = constraint.coupling.solve(&held, &error);
for (index, point) in constraint.points[..count].iter_mut().enumerate() {
let applied = solved[index];
point.normal_impulse += applied;
point.step_impulse += applied;
let (ra, rb) = arms[index];
let impulse = normal * applied;
bodies.get_mut(a).apply_impulse(-impulse, ra);
bodies.get_mut(b).apply_impulse(impulse, rb);
}
}
}
pub(crate) fn effective_mass(
a: &SolverBody,
b: &SolverBody,
ra: Vec3,
rb: Vec3,
direction: Vec3,
) -> f32 {
let angular_a = ra.cross(direction);
let angular_b = rb.cross(direction);
let k = a.inv_mass
+ b.inv_mass
+ angular_a.dot(a.inv_inertia.mul_vec3(angular_a))
+ angular_b.dot(b.inv_inertia.mul_vec3(angular_b));
if k > 0.0 { 1.0 / k } else { 0.0 }
}