use core::ops::Range;
#[cfg(not(feature = "std"))]
#[allow(unused_imports)]
use simba::scalar::{ComplexField as _, RealField as _};
use crate::dynamics::soft_body::SoftMeshId;
use crate::dynamics::solver::solver_body::SolverBodies;
use crate::geometry::ColliderHandle;
use crate::math::{AngVector, DIM, Matrix, Real, Rotation, Vector};
use crate::utils::ComponentMul;
use super::super::soft_attachment::FemAttachment;
#[derive(Copy, Clone, Debug)]
pub(crate) struct SoftShapeConstraint {
pub particle: u32,
pub cluster: u32,
pub solver_id: u32,
pub im: Vector,
pub goal: Vector,
pub goal_vel: Vector,
pub erp_inv_dt: Real,
pub cfm_coeff: Real,
pub inv_lhs: Vector,
pub cfm_gain: Vector,
pub rhs: Vector,
pub impulse: Vector,
pub fem: Option<FemAttachment>,
pub inv_lhs_block: Matrix,
pub cfm_gain_block: Matrix,
}
impl SoftShapeConstraint {
#[inline]
pub fn update(&mut self, bodies: &SolverBodies) {
let pos = bodies.get_pose(self.solver_id).translation;
if let Some(fem) = &self.fem {
let cfm_gain = fem.inv_mass * self.cfm_coeff;
self.cfm_gain_block = cfm_gain;
let lhs = fem.inv_mass + cfm_gain;
let inv = lhs.inverse();
self.inv_lhs_block = if inv.is_finite() { inv } else { Matrix::ZERO };
} else {
let cfm_gain = self.im * self.cfm_coeff;
self.cfm_gain = cfm_gain;
let lhs = self.im + cfm_gain;
let mut inv_lhs = Vector::ZERO;
for k in 0..DIM {
inv_lhs[k] = crate::utils::inv(lhs[k]);
}
self.inv_lhs = inv_lhs;
}
self.rhs = (pos - self.goal) * self.erp_inv_dt;
}
#[inline]
fn apply(&self, bodies: &mut SolverBodies, pool: &[Vector], impulse: Vector) {
if let Some(fem) = &self.fem {
let n = fem.num_particles as usize;
let first = fem.first_slot as usize;
for k in 0..DIM {
let lambda = impulse[k];
if lambda == 0.0 {
continue;
}
let u = &pool[fem.start as usize + k * n..fem.start as usize + (k + 1) * n];
for (i, ui) in u.iter().enumerate() {
bodies.vels[first + i].linear -= *ui * lambda;
}
}
} else if (self.solver_id as usize) < bodies.len() {
bodies.vels[self.solver_id as usize].linear -= self.im.component_mul(&impulse);
}
}
#[inline]
pub fn warmstart(&self, bodies: &mut SolverBodies, pool: &[Vector]) {
self.apply(bodies, pool, self.impulse);
}
#[inline]
pub fn solve(&mut self, bodies: &mut SolverBodies, pool: &[Vector]) {
if (self.solver_id as usize) >= bodies.len() {
return;
}
let v = bodies.vels[self.solver_id as usize].linear;
let dc = v - self.goal_vel;
let total = if self.fem.is_some() {
self.impulse + self.inv_lhs_block * (dc + self.rhs - self.cfm_gain_block * self.impulse)
} else {
self.impulse
+ self
.inv_lhs
.component_mul(&(dc + self.rhs - self.cfm_gain.component_mul(&self.impulse)))
};
let delta = total - self.impulse;
self.impulse = total;
self.apply(bodies, pool, delta);
}
}
#[derive(Clone, Debug)]
pub(crate) struct SoftVolumeConstraint {
pub soft_body: u32,
pub piece: u32,
pub target: Real,
pub grads: Range<usize>,
pub erp_inv_dt: Real,
pub cfm_coeff: Real,
pub max_bias_velocity: Real,
pub inv_lhs: Real,
pub cfm_gain: Real,
pub rhs: Real,
pub impulse: Real,
pub fem: Option<FemVolumeSide>,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct FemVolumeSide {
pub start: u32,
#[cfg(feature = "fem")]
pub gain: Real,
pub u_max: Real,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct FemOverlapSide {
pub first_slot: u32,
pub num_particles: u32,
pub start: u32,
pub warm: u32,
pub gain: Real,
pub u_max: Real,
}
#[derive(Clone, Debug)]
pub(crate) struct SoftOverlapConstraint {
pub grads: Range<usize>,
pub rhs: Real,
pub erp_inv_dt: Real,
pub cfm_coeff: Real,
pub max_bias_velocity: Real,
pub impulse: Real,
pub speculative_inv_dt: Real,
pub rigid: Option<(u32, Vector, AngVector)>,
pub hard: bool,
pub rhs0: Real,
pub rigid_pose0: (Vector, Rotation),
pub warm: Option<(u32, SoftMeshId, ColliderHandle)>,
pub warm_pending: bool,
pub warm_rigid: (Vector, AngVector),
pub fem_sides: Range<u32>,
pub report: (ColliderHandle, ColliderHandle, u32),
}