aboutsummaryrefslogtreecommitdiff
path: root/src/dynamics/solver/contact_constraint
diff options
context:
space:
mode:
authorSébastien Crozet <sebcrozet@dimforge.com>2024-04-21 18:55:11 +0200
committerSébastien Crozet <sebastien@crozet.re>2024-04-30 23:10:46 +0200
commitf58b4f7c195ab7acf0778ea65c46ebf37ac8188c (patch)
tree16a5009b1decbf0395b93657b5c992b277bff8f9 /src/dynamics/solver/contact_constraint
parentda79d6fb5b28dd12e17ef4c8985fb589a37c9f9c (diff)
downloadrapier-f58b4f7c195ab7acf0778ea65c46ebf37ac8188c.tar.gz
rapier-f58b4f7c195ab7acf0778ea65c46ebf37ac8188c.tar.bz2
rapier-f58b4f7c195ab7acf0778ea65c46ebf37ac8188c.zip
feat: add warmstarting to contact constraints resolution
Diffstat (limited to 'src/dynamics/solver/contact_constraint')
-rw-r--r--src/dynamics/solver/contact_constraint/contact_constraints_set.rs11
-rw-r--r--src/dynamics/solver/contact_constraint/generic_one_body_constraint.rs18
-rw-r--r--src/dynamics/solver/contact_constraint/generic_one_body_constraint_element.rs86
-rw-r--r--src/dynamics/solver/contact_constraint/generic_two_body_constraint.rs49
-rw-r--r--src/dynamics/solver/contact_constraint/generic_two_body_constraint_element.rs196
-rw-r--r--src/dynamics/solver/contact_constraint/one_body_constraint.rs38
-rw-r--r--src/dynamics/solver/contact_constraint/one_body_constraint_element.rs100
-rw-r--r--src/dynamics/solver/contact_constraint/one_body_constraint_simd.rs59
-rw-r--r--src/dynamics/solver/contact_constraint/two_body_constraint.rs60
-rw-r--r--src/dynamics/solver/contact_constraint/two_body_constraint_element.rs131
-rw-r--r--src/dynamics/solver/contact_constraint/two_body_constraint_simd.rs69
11 files changed, 621 insertions, 196 deletions
diff --git a/src/dynamics/solver/contact_constraint/contact_constraints_set.rs b/src/dynamics/solver/contact_constraint/contact_constraints_set.rs
index 4d88949..6809c37 100644
--- a/src/dynamics/solver/contact_constraint/contact_constraints_set.rs
+++ b/src/dynamics/solver/contact_constraint/contact_constraints_set.rs
@@ -442,6 +442,17 @@ impl ContactConstraintsSet {
assert_eq!(curr_start, total_num_constraints);
}
+ pub fn warmstart(
+ &mut self,
+ solver_vels: &mut [SolverVel<Real>],
+ generic_solver_vels: &mut DVector<Real>,
+ ) {
+ let (jac, constraints) = self.iter_constraints_mut();
+ for mut c in constraints {
+ c.warmstart(jac, solver_vels, generic_solver_vels);
+ }
+ }
+
pub fn solve_restitution(
&mut self,
solver_vels: &mut [SolverVel<Real>],
diff --git a/src/dynamics/solver/contact_constraint/generic_one_body_constraint.rs b/src/dynamics/solver/contact_constraint/generic_one_body_constraint.rs
index d99ad60..301ce71 100644
--- a/src/dynamics/solver/contact_constraint/generic_one_body_constraint.rs
+++ b/src/dynamics/solver/contact_constraint/generic_one_body_constraint.rs
@@ -259,6 +259,24 @@ impl GenericOneBodyConstraint {
}
}
+ pub fn warmstart(
+ &mut self,
+ jacobians: &DVector<Real>,
+ generic_solver_vels: &mut DVector<Real>,
+ ) {
+ let solver_vel2 = self.inner.solver_vel2;
+
+ let elements = &mut self.inner.elements[..self.inner.num_contacts as usize];
+ OneBodyConstraintElement::generic_warmstart_group(
+ elements,
+ jacobians,
+ self.ndofs2,
+ self.j_id,
+ solver_vel2,
+ generic_solver_vels,
+ );
+ }
+
pub fn solve(
&mut self,
jacobians: &DVector<Real>,
diff --git a/src/dynamics/solver/contact_constraint/generic_one_body_constraint_element.rs b/src/dynamics/solver/contact_constraint/generic_one_body_constraint_element.rs
index 1003a9a..4928f36 100644
--- a/src/dynamics/solver/contact_constraint/generic_one_body_constraint_element.rs
+++ b/src/dynamics/solver/contact_constraint/generic_one_body_constraint_element.rs
@@ -8,6 +8,40 @@ use na::SimdPartialOrd;
impl OneBodyConstraintTangentPart<Real> {
#[inline]
+ pub fn generic_warmstart(
+ &mut self,
+ j_id2: usize,
+ jacobians: &DVector<Real>,
+ ndofs2: usize,
+ solver_vel2: usize,
+ solver_vels: &mut DVector<Real>,
+ ) {
+ #[cfg(feature = "dim2")]
+ {
+ solver_vels.rows_mut(solver_vel2, ndofs2).axpy(
+ self.impulse[0],
+ &jacobians.rows(j_id2 + ndofs2, ndofs2),
+ 1.0,
+ );
+ }
+
+ #[cfg(feature = "dim3")]
+ {
+ let j_step = ndofs2 * 2;
+ solver_vels.rows_mut(solver_vel2, ndofs2).axpy(
+ self.impulse[0],
+ &jacobians.rows(j_id2 + ndofs2, ndofs2),
+ 1.0,
+ );
+ solver_vels.rows_mut(solver_vel2, ndofs2).axpy(
+ self.impulse[1],
+ &jacobians.rows(j_id2 + j_step + ndofs2, ndofs2),
+ 1.0,
+ );
+ }
+ }
+
+ #[inline]
pub fn generic_solve(
&mut self,
j_id2: usize,
@@ -72,6 +106,22 @@ impl OneBodyConstraintTangentPart<Real> {
impl OneBodyConstraintNormalPart<Real> {
#[inline]
+ pub fn generic_warmstart(
+ &mut self,
+ j_id2: usize,
+ jacobians: &DVector<Real>,
+ ndofs2: usize,
+ solver_vel2: usize,
+ solver_vels: &mut DVector<Real>,
+ ) {
+ solver_vels.rows_mut(solver_vel2, ndofs2).axpy(
+ self.impulse,
+ &jacobians.rows(j_id2 + ndofs2, ndofs2),
+ 1.0,
+ );
+ }
+
+ #[inline]
pub fn generic_solve(
&mut self,
cfm_factor: Real,
@@ -100,6 +150,42 @@ impl OneBodyConstraintNormalPart<Real> {
impl OneBodyConstraintElement<Real> {
#[inline]
+ pub fn generic_warmstart_group(
+ elements: &mut [Self],
+ jacobians: &DVector<Real>,
+ ndofs2: usize,
+ // Jacobian index of the first constraint.
+ j_id: usize,
+ solver_vel2: usize,
+ solver_vels: &mut DVector<Real>,
+ ) {
+ let j_step = ndofs2 * 2 * DIM;
+
+ // Solve penetration.
+ let mut nrm_j_id = j_id;
+
+ for element in elements.iter_mut() {
+ element.normal_part.generic_warmstart(
+ nrm_j_id,
+ jacobians,
+ ndofs2,
+ solver_vel2,
+ solver_vels,
+ );
+ nrm_j_id += j_step;
+ }
+
+ // Solve friction.
+ let mut tng_j_id = j_id + ndofs2 * 2;
+
+ for element in elements.iter_mut() {
+ let part = &mut element.tangent_part;
+ part.generic_warmstart(tng_j_id, jacobians, ndofs2, solver_vel2, solver_vels);
+ tng_j_id += j_step;
+ }
+ }
+
+ #[inline]
pub fn generic_solve_group(
cfm_factor: Real,
elements: &mut [Self],
diff --git a/src/dynamics/solver/contact_constraint/generic_two_body_constraint.rs b/src/dynamics/solver/contact_constraint/generic_two_body_constraint.rs
index 4fa8694..be839f0 100644
--- a/src/dynamics/solver/contact_constraint/generic_two_body_constraint.rs
+++ b/src/dynamics/solver/contact_constraint/generic_two_body_constraint.rs
@@ -202,7 +202,7 @@ impl GenericTwoBodyConstraintBuilder {
rhs: na::zero(),
rhs_wo_bias: na::zero(),
impulse_accumulator: na::zero(),
- impulse: na::zero(),
+ impulse: manifold_point.warmstart_impulse,
r,
r_mat_elts: [0.0; 2],
};
@@ -210,7 +210,8 @@ impl GenericTwoBodyConstraintBuilder {
// Tangent parts.
{
- constraint.inner.elements[k].tangent_part.impulse = na::zero();
+ constraint.inner.elements[k].tangent_part.impulse =
+ manifold_point.warmstart_tangent_impulse;
for j in 0..DIM - 1 {
let torque_dir1 = dp1.gcross(tangents1[j]);
@@ -374,6 +375,50 @@ impl GenericTwoBodyConstraint {
}
}
+ pub fn warmstart(
+ &mut self,
+ jacobians: &DVector<Real>,
+ solver_vels: &mut [SolverVel<Real>],
+ generic_solver_vels: &mut DVector<Real>,
+ ) {
+ let mut solver_vel1 = if self.generic_constraint_mask & 0b01 == 0 {
+ GenericRhs::SolverVel(solver_vels[self.inner.solver_vel1])
+ } else {
+ GenericRhs::GenericId(self.inner.solver_vel1)
+ };
+
+ let mut solver_vel2 = if self.generic_constraint_mask & 0b10 == 0 {
+ GenericRhs::SolverVel(solver_vels[self.inner.solver_vel2])
+ } else {
+ GenericRhs::GenericId(self.inner.solver_vel2)
+ };
+
+ let elements = &mut self.inner.elements[..self.inner.num_contacts as usize];
+ TwoBodyConstraintElement::generic_warmstart_group(
+ elements,
+ jacobians,
+ &self.inner.dir1,
+ #[cfg(feature = "dim3")]
+ &self.inner.tangent1,
+ &self.inner.im1,
+ &self.inner.im2,
+ self.ndofs1,
+ self.ndofs2,
+ self.j_id,
+ &mut solver_vel1,
+ &mut solver_vel2,
+ generic_solver_vels,
+ );
+
+ if let GenericRhs::SolverVel(solver_vel1) = solver_vel1 {
+ solver_vels[self.inner.solver_vel1] = solver_vel1;
+ }
+
+ if let GenericRhs::SolverVel(solver_vel2) = solver_vel2 {
+ solver_vels[self.inner.solver_vel2] = solver_vel2;
+ }
+ }
+
pub fn solve(
&mut self,
jacobians: &DVector<Real>,
diff --git a/src/dynamics/solver/contact_constraint/generic_two_body_constraint_element.rs b/src/dynamics/solver/contact_constraint/generic_two_body_constraint_element.rs
index 40740a0..389b603 100644
--- a/src/dynamics/solver/contact_constraint/generic_two_body_constraint_element.rs
+++ b/src/dynamics/solver/contact_constraint/generic_two_body_constraint_element.rs
@@ -89,6 +89,95 @@ impl GenericRhs {
impl TwoBodyConstraintTangentPart<Real> {
#[inline]
+ pub fn generic_warmstart(
+ &mut self,
+ j_id: usize,
+ jacobians: &DVector<Real>,
+ tangents1: [&Vector<Real>; DIM - 1],
+ im1: &Vector<Real>,
+ im2: &Vector<Real>,
+ ndofs1: usize,
+ ndofs2: usize,
+ solver_vel1: &mut GenericRhs,
+ solver_vel2: &mut GenericRhs,
+ solver_vels: &mut DVector<Real>,
+ ) {
+ let j_id1 = j_id1(j_id, ndofs1, ndofs2);
+ let j_id2 = j_id2(j_id, ndofs1, ndofs2);
+ #[cfg(feature = "dim3")]
+ let j_step = j_step(ndofs1, ndofs2);
+
+ #[cfg(feature = "dim2")]
+ {
+ solver_vel1.apply_impulse(
+ j_id1,
+ ndofs1,
+ self.impulse[0],
+ jacobians,
+ tangents1[0],
+ &self.gcross1[0],
+ solver_vels,
+ im1,
+ );
+ solver_vel2.apply_impulse(
+ j_id2,
+ ndofs2,
+ self.impulse[0],
+ jacobians,
+ &-tangents1[0],
+ &self.gcross2[0],
+ solver_vels,
+ im2,
+ );
+ }
+
+ #[cfg(feature = "dim3")]
+ {
+ solver_vel1.apply_impulse(
+ j_id1,
+ ndofs1,
+ self.impulse[0],
+ jacobians,
+ tangents1[0],
+ &self.gcross1[0],
+ solver_vels,
+ im1,
+ );
+ solver_vel1.apply_impulse(
+ j_id1 + j_step,
+ ndofs1,
+ self.impulse[1],
+ jacobians,
+ tangents1[1],
+ &self.gcross1[1],
+ solver_vels,
+ im1,
+ );
+
+ solver_vel2.apply_impulse(
+ j_id2,
+ ndofs2,
+ self.impulse[0],
+ jacobians,
+ &-tangents1[0],
+ &self.gcross2[0],
+ solver_vels,
+ im2,
+ );
+ solver_vel2.apply_impulse(
+ j_id2 + j_step,
+ ndofs2,
+ self.impulse[1],
+ jacobians,
+ &-tangents1[1],
+ &self.gcross2[1],
+ solver_vels,
+ im2,
+ );
+ }
+ }
+
+ #[inline]
pub fn generic_solve(
&mut self,
j_id: usize,
@@ -241,6 +330,45 @@ impl TwoBodyConstraintTangentPart<Real> {
impl TwoBodyConstraintNormalPart<Real> {
#[inline]
+ pub fn generic_warmstart(
+ &mut self,
+ j_id: usize,
+ jacobians: &DVector<Real>,
+ dir1: &Vector<Real>,
+ im1: &Vector<Real>,
+ im2: &Vector<Real>,
+ ndofs1: usize,
+ ndofs2: usize,
+ solver_vel1: &mut GenericRhs,
+ solver_vel2: &mut GenericRhs,
+ solver_vels: &mut DVector<Real>,
+ ) {
+ let j_id1 = j_id1(j_id, ndofs1, ndofs2);
+ let j_id2 = j_id2(j_id, ndofs1, ndofs2);
+
+ solver_vel1.apply_impulse(
+ j_id1,
+ ndofs1,
+ self.impulse,
+ jacobians,
+ dir1,
+ &self.gcross1,
+ solver_vels,
+ im1,
+ );
+ solver_vel2.apply_impulse(
+ j_id2,
+ ndofs2,
+ self.impulse,
+ jacobians,
+ &-dir1,
+ &self.gcross2,
+ solver_vels,
+ im2,
+ );
+ }
+
+ #[inline]
pub fn generic_solve(
&mut self,
cfm_factor: Real,
@@ -291,6 +419,74 @@ impl TwoBodyConstraintNormalPart<Real> {
impl TwoBodyConstraintElement<Real> {
#[inline]
+ pub fn generic_warmstart_group(
+ elements: &mut [Self],
+ jacobians: &DVector<Real>,
+ dir1: &Vector<Real>,
+ #[cfg(feature = "dim3")] tangent1: &Vector<Real>,
+ im1: &Vector<Real>,
+ im2: &Vector<Real>,
+ // ndofs is 0 for a non-multibody body, or a multibody with zero
+ // degrees of freedom.
+ ndofs1: usize,
+ ndofs2: usize,
+ // Jacobian index of the first constraint.
+ j_id: usize,
+ solver_vel1: &mut GenericRhs,
+ solver_vel2: &mut GenericRhs,
+ solver_vels: &mut DVector<Real>,
+ ) {
+ let j_step = j_step(ndofs1, ndofs2) * DIM;
+
+ // Solve penetration.
+ {
+ let mut nrm_j_id = normal_j_id(j_id, ndofs1, ndofs2);
+
+ for element in elements.iter_mut() {
+ element.normal_part.generic_warmstart(
+ nrm_j_id,
+ jacobians,
+ dir1,
+ im1,
+ im2,
+ ndofs1,
+ ndofs2,
+ solver_vel1,
+ solver_vel2,
+ solver_vels,
+ );
+ nrm_j_id += j_step;
+ }
+ }
+
+ // Solve friction.
+ {
+ #[cfg(feature = "dim3")]
+ let tangents1 = [tangent1, &dir1.cross(tangent1)];
+ #[cfg(feature = "dim2")]
+ let tangents1 = [&dir1.orthonormal_vector()];
+ let mut tng_j_id = tangent_j_id(j_id, ndofs1, ndofs2);
+
+ for element in elements.iter_mut() {
+ let part = &mut element.tangent_part;
+ part.generic_warmstart(
+ tng_j_id,
+ jacobians,
+ tangents1,
+ im1,
+ im2,
+ ndofs1,
+ ndofs2,
+ solver_vel1,
+ solver_vel2,
+ solver_vels,
+ );
+ tng_j_id += j_step;
+ }
+ }
+ }
+
+ #[inline]
pub fn generic_solve_group(
cfm_factor: Real,
elements: &mut [Self],
diff --git a/src/dynamics/solver/contact_constraint/one_body_constraint.rs b/src/dynamics/solver/contact_constraint/one_body_constraint.rs
index 357f1c4..c31d2aa 100644
--- a/src/dynamics/solver/contact_constraint/one_body_constraint.rs
+++ b/src/dynamics/solver/contact_constraint/one_body_constraint.rs
@@ -151,7 +151,7 @@ impl OneBodyConstraintBuilder {
gcross2,
rhs: na::zero(),
rhs_wo_bias: na::zero(),
- impulse: na::zero(),
+ impulse: manifold_point.warmstart_impulse,
impulse_accumulator: na::zero(),
r: projected_mass,
r_mat_elts: [0.0; 2],
@@ -160,7 +160,8 @@ impl OneBodyConstraintBuilder {
// Tangent parts.
{
- constraint.elements[k].tangent_part.impulse = na::zero();
+ constraint.elements[k].tangent_part.impulse =
+ manifold_point.warmstart_tangent_impulse;
for j in 0..DIM - 1 {
let gcross2 = mprops2
@@ -317,13 +318,13 @@ impl OneBodyConstraintBuilder {
element.normal_part.rhs_wo_bias = rhs_wo_bias;
element.normal_part.rhs = new_rhs;
element.normal_part.impulse_accumulator += element.normal_part.impulse;
- element.normal_part.impulse = na::zero();
+ element.normal_part.impulse *= params.warmstart_coefficient;
}
// Tangent part.
{
element.tangent_part.impulse_accumulator += element.tangent_part.impulse;
- element.tangent_part.impulse = na::zero();
+ element.tangent_part.impulse *= params.warmstart_coefficient;
for j in 0..DIM - 1 {
let bias = (p1 - p2).dot(&tangents1[j]) * inv_dt;
@@ -369,6 +370,21 @@ impl OneBodyConstraint {
}
}
+ pub fn warmstart(&mut self, solver_vels: &mut [SolverVel<Real>]) {
+ let mut solver_vel2 = solver_vels[self.solver_vel2];
+
+ OneBodyConstraintElement::warmstart_group(
+ &mut self.elements[..self.num_contacts as usize],
+ &self.dir1,
+ #[cfg(feature = "dim3")]
+ &self.tangent1,
+ &self.im2,
+ &mut solver_vel2,
+ );
+
+ solver_vels[self.solver_vel2] = solver_vel2;
+ }
+
pub fn solve(
&mut self,
solver_vels: &mut [SolverVel<Real>],
@@ -400,17 +416,11 @@ impl OneBodyConstraint {
for k in 0..self.num_contacts as usize {
let contact_id = self.manifold_contact_id[k];
let active_contact = &mut manifold.points[contact_id as usize];
- active_contact.data.impulse = self.elements[k].normal_part.total_impulse();
- #[cfg(feature = "dim2")]
- {
- active_contact.data.tangent_impulse =
- self.elements[k].tangent_part.total_impulse()[0];
- }
- #[cfg(feature = "dim3")]
- {
- active_contact.data.tangent_impulse = self.elements[k].tangent_part.total_impulse();
- }
+ active_contact.data.warmstart_impulse = self.elements[k].normal_part.impulse;
+ active_contact.data.warmstart_tangent_impulse = self.elements[k].tangent_part.impulse;
+ active_contact.data.impulse = self.elements[k].normal_part.total_impulse();
+ active_contact.data.tangent_impulse = self.elements[k].tangent_part.total_impulse();
}
}
diff --git a/src/dynamics/solver/contact_constraint/one_body_constraint_element.rs b/src/dynamics/solver/contact_constraint/one_body_constraint_element.rs
index e5ae140..7ec8c5d 100644
--- a/src/dynamics/solver/contact_constraint/one_body_constraint_element.rs
+++ b/src/dynamics/solver/contact_constraint/one_body_constraint_element.rs
@@ -1,9 +1,7 @@
-use crate::dynamics::integration_parameters::{
- BLOCK_SOLVER_ENABLED, DISABLE_FRICTION_LIMIT_REAPPLY,
-};
+use crate::dynamics::integration_parameters::BLOCK_SOLVER_ENABLED;
use crate::dynamics::solver::contact_constraint::TwoBodyConstraintNormalPart;
use crate::dynamics::solver::SolverVel;
-use crate::math::{AngVector, Vector, DIM};
+use crate::math::{AngVector, TangentImpulse, Vector, DIM};
use crate::utils::{SimdBasis, SimdDot, SimdRealCopy};
use na::Vector2;
@@ -12,14 +10,8 @@ pub(crate) struct OneBodyConstraintTangentPart<N: SimdRealCopy> {
pub gcross2: [AngVector<N>; DIM - 1],
pub rhs: [N; DIM - 1],
pub rhs_wo_bias: [N; DIM - 1],
- #[cfg(feature = "dim2")]
- pub impulse: na::Vector1<N>,
- #[cfg(feature = "dim3")]
- pub impulse: na::Vector2<N>,
- #[cfg(feature = "dim2")]
- pub impulse_accumulator: na::Vector1<N>,
- #[cfg(feature = "dim3")]
- pub impulse_accumulator: na::Vector2<N>,
+ pub impulse: TangentImpulse<N>,
+ pub impulse_accumulator: TangentImpulse<N>,
#[cfg(feature = "dim2")]
pub r: [N; 1],
#[cfg(feature = "dim3")]
@@ -43,57 +35,29 @@ impl<N: SimdRealCopy> OneBodyConstraintTangentPart<N> {
/// Total impulse applied across all the solver substeps.
#[inline]
- #[cfg(feature = "dim2")]
- pub fn total_impulse(&self) -> na::Vector1<N> {
+ pub fn total_impulse(&self) -> TangentImpulse<N> {
self.impulse_accumulator + self.impulse
}
- /// Total impulse applied across all the solver substeps.
#[inline]
- #[cfg(feature = "dim3")]
- pub fn total_impulse(&self) -> na::Vector2<N> {
- self.impulse_accumulator + self.impulse
- }
-
- #[inline]
- pub fn apply_limit(
+ pub fn warmstart(
&mut self,
tangents1: [&Vector<N>; DIM - 1],
im2: &Vector<N>,
- limit: N,
solver_vel2: &mut SolverVel<N>,
- ) where
- AngVector<N>: SimdDot<AngVector<N>, Result = N>,
- {
- if DISABLE_FRICTION_LIMIT_REAPPLY {
- return;
- }
-
+ ) {
#[cfg(feature = "dim2")]
{
- let new_impulse = self.impulse[0].simd_clamp(-limit, limit);
- let dlambda = new_impulse - self.impulse[0];
- self.impulse[0] = new_impulse;
-
- solver_vel2.linear += tangents1[0].component_mul(im2) * -dlambda;
- solver_vel2.angular += self.gcross2[0] * dlambda;
+ solver_vel2.linear += tangents1[0].component_mul(im2) * -self.impulse[0];
+ solver_vel2.angular += self.gcross2[0] * self.impulse[0];
}
#[cfg(feature = "dim3")]
{
- let new_impulse = self.impulse;
- let new_impulse = {
- let _disable_fe_except =
- crate::utils::DisableFloatingPointExceptionsFlags::
- disable_floating_point_exceptions();
- new_impulse.simd_cap_magnitude(limit)
- };
- let dlambda = new_impulse - self.impulse;
- self.impulse = new_impulse;
-
- solver_vel2.linear += tangents1[0].component_mul(im2) * -dlambda[0]
- + tangents1[1].component_mul(im2) * -dlambda[1];
- solver_vel2.angular += self.gcross2[0] * dlambda[0] + self.gcross2[1] * dlambda[1];
+ solver_vel2.linear += tangents1[0].component_mul(im2) * -self.impulse[0]
+ + tangents1[1].component_mul(im2) * -self.impulse[1];
+ solver_vel2.angular +=
+ self.gcross2[0] * self.impulse[0] + self.gcross2[1] * self.impulse[1];
}
}
@@ -185,6 +149,12 @@ impl<N: SimdRealCopy> OneBodyConstraintNormalPart<N> {
}
#[inline]
+ pub fn warmstart(&mut self, dir1: &Vector<N>, im2: &Vector<N>, solver_vel2: &mut SolverVel<N>) {
+ solver_vel2.linear += dir1.component_mul(im2) * -self.impulse;
+ solver_vel2.angular += self.gcross2 * self.impulse;
+ }
+
+ #[inline]
pub fn solve(
&mut self,
cfm_factor: N,
@@ -258,6 +228,25 @@ impl<N: SimdRealCopy> OneBodyConstraintElement<N> {
}
#[inline]
+ pub fn warmstart_group(
+ elements: &mut [Self],
+ dir1: &Vector<N>,
+ #[cfg(feature = "dim3")] tangent1: &Vector<N>,
+ im2: &Vector<N>,
+ solver_vel2: &mut SolverVel<N>,
+ ) {
+ #[cfg(feature = "dim3")]
+ let tangents1 = [tangent1, &dir1.cross(tangent1)];
+ #[cfg(feature = "dim2")]
+ let tangents1 = [&dir1.orthonormal_vector()];
+
+ for element in elements.iter_mut() {
+ element.normal_part.warmstart(dir1, im2, solver_vel2);
+ element.tangent_part.warmstart(tangents1, im2, solver_vel2);
+ }
+ }
+
+ #[inline]
pub fn solve_group(
cfm_factor: N,
elements: &mut [Self],
@@ -293,13 +282,6 @@ impl<N: SimdRealCopy> OneBodyConstraintElement<N> {
im2,
solver_vel2,
);
-
- // There is one constraint left to solve if there isn’t an even number.
- for i in 0..2 {
- let limit = limit * elements[i].normal_part.impulse;
- let part = &mut elements[i].tangent_part;
- part.apply_limit(tangents1, im2, limit, solver_vel2);
- }
}
if elements.len() % 2 == 1 {
@@ -307,18 +289,12 @@ impl<N: SimdRealCopy> OneBodyConstraintElement<N> {
element
.normal_part
.solve(cfm_factor, dir1, im2, solver_vel2);
- let limit = limit * element.normal_part.impulse;
- let part = &mut element.tangent_part;
- part.apply_limit(tangents1, im2, limit, solver_vel2);
}
} else {
for element in elements.iter_mut() {
element
.normal_part
.solve(cfm_factor, dir1, im2, solver_vel2);
- let limit = limit * element.normal_part.impulse;
- let part = &mut element.tangent_part;
- part.apply_limit(tangents1, im2, limit, solver_vel2);
}
}
}
diff --git a/src/dynamics/solver/contact_constraint/one_body_constraint_simd.rs b/src/dynamics/solver/contact_constraint/one_body_constraint_simd.rs
index 8819cea..77dfc42 100644
--- a/src/dynamics/solver/contact_constraint/one_body_constraint_simd.rs
+++ b/src/dynamics/solver/contact_constraint/one_body_constraint_simd.rs
@@ -8,8 +8,8 @@ use crate::dynamics::{
};
use crate::geometry::{ContactManifold, ContactManifoldIndex};
use crate::math::{
- AngVector, AngularInertia, Isometry, Point, Real, SimdReal, Vector, DIM, MAX_MANIFOLD_POINTS,
- SIMD_WIDTH,
+ AngVector, AngularInertia, Isometry, Point, Real, SimdReal, TangentImpulse, Vector, DIM,
+ MAX_MANIFOLD_POINTS, SIMD_WIDTH,
};
#[cfg(feature = "dim2")]
use crate::utils::SimdBasis;
@@ -120,6 +120,11 @@ impl SimdOneBodyConstraintBuilder {
let is_bouncy = SimdReal::from(gather![
|ii| manifold_points[ii][k].is_bouncy() as u32 as Real
]);
+ let warmstart_impulse =
+ SimdReal::from(gather![|ii| manifold_points[ii][k].warmstart_impulse]);
+ let warmstart_tangent_impulse = TangentImpulse::from(gather![|ii| manifold_points
+ [ii][k]
+ .warmstart_tangent_impulse]);
let dist = SimdReal::from(gather![|ii| manifold_points[ii][k].dist]);
let point = Point::from(gather![|ii| manifold_points[ii][k].point]);
@@ -155,7 +160,7 @@ impl SimdOneBodyConstraintBuilder {
gcross2,
rhs: na::zero(),
rhs_wo_bias: na::zero(),
- impulse: na::zero(),
+ impulse: warmstart_impulse,
impulse_accumulator: na::zero(),
r: projected_mass,
r_mat_elts: [SimdReal::zero(); 2],
@@ -163,7 +168,7 @@ impl SimdOneBodyConstraintBuilder {
}
// tangent parts.
- constraint.elements[k].tangent_part.impulse = na::zero();
+ constraint.elements[k].tangent_part.impulse = warmstart_tangent_impulse;
for j in 0..DIM - 1 {
let gcross2 = ii2.transform_vector(dp2.gcross(-tangents1[j]));
@@ -263,6 +268,7 @@ impl SimdOneBodyConstraintBuilder {
let allowed_lin_err = SimdReal::splat(params.allowed_linear_error);
let erp_inv_dt = SimdReal::splat(params.erp_inv_dt());
let max_penetration_correction = SimdReal::splat(params.max_penetration_correction);
+ let warmstart_coeff = SimdReal::splat(params.warmstart_coefficient);
let rb2 = gather![|ii| &bodies[constraint.solver_vel2[ii]]];
let ccd_thickness = SimdReal::from(gather![|ii| rb2[ii].ccd_thickness]);
@@ -309,13 +315,13 @@ impl SimdOneBodyConstraintBuilder {
element.normal_part.rhs_wo_bias = rhs_wo_bias;
element.normal_part.rhs = new_rhs;
element.normal_part.impulse_accumulator += element.normal_part.impulse;
- element.normal_part.impulse = na::zero();
+ element.normal_part.impulse *= warmstart_coeff;
}
// tangent parts.
{
element.tangent_part.impulse_accumulator += element.tangent_part.impulse;
- element.tangent_part.impulse = na::zero();
+ element.tangent_part.impulse *= warmstart_coeff;
for j in 0..DIM - 1 {
let bias = (p1 - p2).dot(&tangents1[j]) * inv_dt;
@@ -344,6 +350,27 @@ pub(crate) struct OneBodyConstraintSimd {
}
impl OneBodyConstraintSimd {
+ pub fn warmstart(&mut self, solver_vels: &mut [SolverVel<Real>]) {
+ let mut solver_vel2 = SolverVel {
+ linear: Vector::from(gather![|ii| solver_vels[self.solver_vel2[ii]].linear]),
+ angular: AngVector::from(gather![|ii| solver_vels[self.solver_vel2[ii]].angular]),
+ };
+
+ OneBodyConstraintElement::warmstart_group(
+ &mut self.elements[..self.num_contacts as usize],
+ &self.dir1,
+ #[cfg(feature = "dim3")]
+ &self.tangent1,
+ &self.im2,
+ &mut solver_vel2,
+ );
+
+ for ii in 0..SIMD_WIDTH {
+ solver_vels[self.solver_vel2[ii]].linear = solver_vel2.linear.extract(ii);
+ solver_vels[self.solver_vel2[ii]].angular = solver_vel2.angular.extract(ii);
+ }
+ }
+
pub fn solve(
&mut self,
solver_vels: &mut [SolverVel<Real>],
@@ -377,27 +404,21 @@ impl OneBodyConstraintSimd {
// FIXME: duplicated code. This is exactly the same as in the two-body velocity constraint.
pub fn writeback_impulses(&self, manifolds_all: &mut [&mut ContactManifold]) {
for k in 0..self.num_contacts as usize {
+ let warmstart_impulses: [_; SIMD_WIDTH] = self.elements[k].normal_part.impulse.into();
+ let warmstart_tangent_impulses = self.elements[k].tangent_part.impulse;
let impulses: [_; SIMD_WIDTH] = self.elements[k].normal_part.total_impulse().into();
- #[cfg(feature = "dim2")]
- let tangent_impulses: [_; SIMD_WIDTH] =
- self.elements[k].tangent_part.total_impulse()[0].into();
- #[cfg(feature = "dim3")]
let tangent_impulses = self.elements[k].tangent_part.total_impulse();
for ii in 0..SIMD_WIDTH {
let manifold = &mut manifolds_all[self.manifold_id[ii]];
let contact_id = self.manifold_contact_id[k][ii];
let active_contact = &mut manifold.points[contact_id as usize];
- active_contact.data.impulse = impulses[ii];
- #[cfg(feature = "dim2")]
- {
- active_contact.data.tangent_impulse = tangent_impulses[ii];
- }
- #[cfg(feature = "dim3")]
-