use super::{AnyVelocityConstraint, DeltaVel}; use crate::dynamics::{IntegrationParameters, RigidBodySet}; use crate::geometry::{ContactManifold, ContactManifoldIndex}; use crate::math::{ AngVector, AngularInertia, Point, Real, SimdReal, Vector, DIM, MAX_MANIFOLD_POINTS, SIMD_WIDTH, }; #[cfg(feature = "dim2")] use crate::utils::WBasis; use crate::utils::{WAngularInertia, WCross, WDot}; use num::Zero; use simba::simd::{SimdPartialOrd, SimdValue}; #[derive(Copy, Clone, Debug)] pub(crate) struct WVelocityConstraintTangentPart { pub gcross1: [AngVector; DIM - 1], pub gcross2: [AngVector; DIM - 1], pub rhs: [SimdReal; DIM - 1], #[cfg(feature = "dim2")] pub impulse: [SimdReal; DIM - 1], #[cfg(feature = "dim3")] pub impulse: na::Vector2, pub r: [SimdReal; DIM - 1], } impl WVelocityConstraintTangentPart { pub fn zero() -> Self { Self { gcross1: [AngVector::zero(); DIM - 1], gcross2: [AngVector::zero(); DIM - 1], rhs: [SimdReal::zero(); DIM - 1], #[cfg(feature = "dim2")] impulse: [SimdReal::zero(); DIM - 1], #[cfg(feature = "dim3")] impulse: na::Vector2::zeros(), r: [SimdReal::zero(); DIM - 1], } } } #[derive(Copy, Clone, Debug)] pub(crate) struct WVelocityConstraintNormalPart { pub gcross1: AngVector, pub gcross2: AngVector, pub rhs: SimdReal, pub impulse: SimdReal, pub r: SimdReal, } impl WVelocityConstraintNormalPart { pub fn zero() -> Self { Self { gcross1: AngVector::zero(), gcross2: AngVector::zero(), rhs: SimdReal::zero(), impulse: SimdReal::zero(), r: SimdReal::zero(), } } } #[derive(Copy, Clone, Debug)] pub(crate) struct WVelocityConstraintElement { pub normal_part: WVelocityConstraintNormalPart, pub tangent_part: WVelocityConstraintTangentPart, } impl WVelocityConstraintElement { pub fn zero() -> Self { Self { normal_part: WVelocityConstraintNormalPart::zero(), tangent_part: WVelocityConstraintTangentPart::zero(), } } } #[derive(Copy, Clone, Debug)] pub(crate) struct WVelocityConstraint { pub dir1: Vector, // Non-penetration force direction for the first body. #[cfg(feature = "dim3")] pub tangent1: Vector, // One of the friction force directions. #[cfg(feature = "dim3")] pub tangent_rot1: na::UnitComplex, // Orientation of the tangent basis wrt. the reference basis. pub elements: [WVelocityConstraintElement; MAX_MANIFOLD_POINTS], pub num_contacts: u8, pub im1: SimdReal, pub im2: SimdReal, pub limit: SimdReal, pub mj_lambda1: [usize; SIMD_WIDTH], pub mj_lambda2: [usize; SIMD_WIDTH], pub manifold_id: [ContactManifoldIndex; SIMD_WIDTH], pub manifold_contact_id: [[u8; SIMD_WIDTH]; MAX_MANIFOLD_POINTS], } impl WVelocityConstraint { pub fn generate( params: &IntegrationParameters, manifold_id: [ContactManifoldIndex; SIMD_WIDTH], manifolds: [&ContactManifold; SIMD_WIDTH], bodies: &RigidBodySet, out_constraints: &mut Vec, push: bool, ) { for ii in 0..SIMD_WIDTH { assert_eq!(manifolds[ii].data.relative_dominance, 0); } let inv_dt = SimdReal::splat(params.inv_dt()); let velocity_solve_fraction = SimdReal::splat(params.velocity_solve_fraction); let velocity_based_erp_inv_dt = SimdReal::splat(params.velocity_based_erp_inv_dt()); let rbs1 = array![|ii| &bodies[manifolds[ii].data.body_pair.body1]; SIMD_WIDTH]; let rbs2 = array![|ii| &bodies[manifolds[ii].data.body_pair.body2]; SIMD_WIDTH]; let im1 = SimdReal::from(array![|ii| rbs1[ii].effective_inv_mass; SIMD_WIDTH]); let ii1: AngularInertia = AngularInertia::from( array![|ii| rbs1[ii].effective_world_inv_inertia_sqrt; SIMD_WIDTH], ); let linvel1 = Vector::from(array![|ii| rbs1[ii].linvel; SIMD_WIDTH]); let angvel1 = AngVector::::from(array![|ii| rbs1[ii].angvel; SIMD_WIDTH]); let world_com1 = Point::from(array![|ii| rbs1[ii].world_com; SIMD_WIDTH]); let im2 = SimdReal::from(array![|ii| rbs2[ii].effective_inv_mass; SIMD_WIDTH]); let ii2: AngularInertia = AngularInertia::from( array![|ii| rbs2[ii].effective_world_inv_inertia_sqrt; SIMD_WIDTH], ); let linvel2 = Vector::from(array![|ii| rbs2[ii].linvel; SIMD_WIDTH]); let angvel2 = AngVector::::from(array![|ii| rbs2[ii].angvel; SIMD_WIDTH]); let world_com2 = Point::from(array![|ii| rbs2[ii].world_com; SIMD_WIDTH]); let force_dir1 = -Vector::from(array![|ii| manifolds[ii].data.normal; SIMD_WIDTH]); let mj_lambda1 = array![|ii| rbs1[ii].active_set_offset; SIMD_WIDTH]; let mj_lambda2 = array![|ii| rbs2[ii].active_set_offset; SIMD_WIDTH]; let warmstart_multiplier = SimdReal::from(array![|ii| manifolds[ii].data.warmstart_multiplier; SIMD_WIDTH]); let warmstart_coeff = warmstart_multiplier * SimdReal::splat(params.warmstart_coeff); let num_active_contacts = manifolds[0].data.num_active_contacts(); #[cfg(feature = "dim2")] let tangents1 = force_dir1.orthonormal_basis(); #[cfg(feature = "dim3")] let (tangents1, tangent_rot1) = super::compute_tangent_contact_directions(&force_dir1, &linvel1, &linvel2); for l in (0..num_active_contacts).step_by(MAX_MANIFOLD_POINTS) { let manifold_points = array![|ii| &manifolds[ii].data.solver_contacts[l..num_active_contacts]; SIMD_WIDTH ]; let num_points = manifold_points[0].len().min(MAX_MANIFOLD_POINTS); let mut constraint = WVelocityConstraint { dir1: force_dir1, #[cfg(feature = "dim3")] tangent1: tangents1[0], #[cfg(feature = "dim3")] tangent_rot1, elements: [WVelocityConstraintElement::zero(); MAX_MANIFOLD_POINTS], im1, im2, limit: SimdReal::splat(0.0), mj_lambda1, mj_lambda2, manifold_id, manifold_contact_id: [[0; SIMD_WIDTH]; MAX_MANIFOLD_POINTS], num_contacts: num_points as u8, }; for k in 0..num_points { let friction = SimdReal::from(array![|ii| manifold_points[ii][k].friction; SIMD_WIDTH]); let restitution = SimdReal::from(array![|ii| manifold_points[ii][k].restitution; SIMD_WIDTH]); let is_bouncy = SimdReal::from( array![|ii| manifold_points[ii][k].is_bouncy() as u32 as Real; SIMD_WIDTH], ); let is_resting = SimdReal::splat(1.0) - is_bouncy; let point = Point::from(array![|ii| manifold_points[ii][k].point; SIMD_WIDTH]); let dist = SimdReal::from(array![|ii| manifold_points[ii][k].dist; SIMD_WIDTH]); let tangent_velocity = Vector::from(array![|ii| manifold_points[ii][k].tangent_velocity; SIMD_WIDTH]); let impulse = SimdReal::from(array![|ii| manifold_points[ii][k].data.impulse; SIMD_WIDTH]); let dp1 = point - world_com1; let dp2 = point - world_com2; let vel1 = linvel1 + angvel1.gcross(dp1); let vel2 = linvel2 + angvel2.gcross(dp2); constraint.limit = friction; constraint.manifold_contact_id[k] = array![|ii| manifold_points[ii][k].contact_id; SIMD_WIDTH]; // Normal part. { let gcross1 = ii1.transform_vector(dp1.gcross(force_dir1)); let gcross2 = ii2.transform_vector(dp2.gcross(-force_dir1)); let r = SimdReal::splat(1.0) / (im1 + im2 + gcross1.gdot(gcross1) + gcross2.gdot(gcross2)); let projected_velocity = (vel1 - vel2).dot(&force_dir1); let mut rhs = (SimdReal::splat(1.0) + is_bouncy * restitution) * projected_velocity; rhs += dist.simd_max(SimdReal::zero()) * inv_dt; rhs *= is_bouncy + is_resting * velocity_solve_fraction; rhs += dist.simd_min(SimdReal::zero()) * (velocity_based_erp_inv_dt * is_resting); constraint.elements[k].normal_part = WVelocityConstraintNormalPart { gcross1, gcross2, rhs, impulse: impulse * warmstart_coeff, r, }; } // tangent parts. #[cfg(feature = "dim2")] let impulse = [SimdReal::from( array![|ii| manifold_points[ii][k].data.tangent_impulse; SIMD_WIDTH], )]; #[cfg(feature = "dim3")] let impulse = tangent_rot1 * na::Vector2::from( array![|ii| manifold_points[ii][k].data.tangent_impulse; SIMD_WIDTH], ); constraint.elements[k].tangent_part.impulse = impulse; for j in 0..DIM - 1 { let gcross1 = ii1.transform_vector(dp1.gcross(tangents1[j])); let gcross2 = ii2.transform_vector(dp2.gcross(-tangents1[j])); let r = SimdReal::splat(1.0) / (im1 + im2 + gcross1.gdot(gcross1) + gcross2.gdot(gcross2)); let rhs = (vel1 - vel2 + tangent_velocity).dot(&tangents1[j]); constraint.elements[k].tangent_part.gcross1[j] = gcross1; constraint.elements[k].tangent_part.gcross2[j] = gcross2; constraint.elements[k].tangent_part.rhs[j] = rhs; constraint.elements[k].tangent_part.r[j] = r; } } if push { out_constraints.push(AnyVelocityConstraint::Grouped(constraint)); } else { out_constraints[manifolds[0].data.constraint_index + l / MAX_MANIFOLD_POINTS] = AnyVelocityConstraint::Grouped(constraint); } } } pub fn warmstart(&self, mj_lambdas: &mut [DeltaVel]) { let mut mj_lambda1 = DeltaVel { linear: Vector::from( array![|ii| mj_lambdas[self.mj_lambda1[ii] as usize].linear; SIMD_WIDTH], ), angular: AngVector::from( array![|ii| mj_lambdas[self.mj_lambda1[ii] as usize].angular; SIMD_WIDTH], ), }; let mut mj_lambda2 = DeltaVel { linear: Vector::from( array![|ii| mj_lambdas[self.mj_lambda2[ii] as usize].linear; SIMD_WIDTH], ), angular: AngVector::from( array![|ii| mj_lambdas[self.mj_lambda2[ii] as usize].angular; SIMD_WIDTH], ), }; #[cfg(feature = "dim3")] let tangents1 = [self.tangent1, self.dir1.cross(&self.tangent1)]; #[cfg(feature = "dim2")] let tangents1 = self.dir1.orthonormal_basis(); for i in 0..self.num_contacts as usize { let elt = &self.elements[i].normal_part; mj_lambda1.linear += self.dir1 * (self.im1 * elt.impulse); mj_lambda1.angular += elt.gcross1 * elt.impulse; mj_lambda2.linear += self.dir1 * (-self.im2 * elt.impulse); mj_lambda2.angular += elt.gcross2 * elt.impulse; for j in 0..DIM - 1 { let elt = &self.elements[i].tangent_part; mj_lambda1.linear += tangents1[j] * (self.im1 * elt.impulse[j]); mj_lambda1.angular += elt.gcross1[j] * elt.impulse[j]; mj_lambda2.linear += tangents1[j] * (-self.im2 * elt.impulse[j]); mj_lambda2.angular += elt.gcross2[j] * elt.impulse[j]; } } for ii in 0..SIMD_WIDTH { mj_lambdas[self.mj_lambda1[ii] as usize].linear = mj_lambda1.linear.extract(ii); mj_lambdas[self.mj_lambda1[ii] as usize].angular = mj_lambda1.angular.extract(ii); } for ii in 0..SIMD_WIDTH { mj_lambdas[self.mj_lambda2[ii] as usize].linear = mj_lambda2.linear.extract(ii); mj_lambdas[self.mj_lambda2[ii] as usize].angular = mj_lambda2.angular.extract(ii); } } pub fn solve(&mut self, mj_lambdas: &mut [DeltaVel]) { let mut mj_lambda1 = DeltaVel { linear: Vector::from( array![|ii| mj_lambdas[self.mj_lambda1[ii] as usize].linear; SIMD_WIDTH], ), angular: AngVector::from( array![|ii| mj_lambdas[self.mj_lambda1[ii] as usize].angular; SIMD_WIDTH], ), }; let mut mj_lambda2 = DeltaVel { linear: Vector::from( array![|ii| mj_lambdas[ self.mj_lambda2[ii] as usize].linear; SIMD_WIDTH], ), angular: AngVector::from( array![|ii| mj_lambdas[ self.mj_lambda2[ii] as usize].angular; SIMD_WIDTH], ), }; // Solve friction. #[cfg(feature = "dim3")] let bitangent1 = self.dir1.cross(&self.tangent1); #[cfg(feature = "dim2")] let tangents1 = self.dir1.orthonormal_basis(); #[cfg(feature = "dim2")] for i in 0..self.num_contacts as usize { let normal_elt = &self.elements[i].normal_part; let elt = &mut self.elements[i].tangent_part; let dimpulse = tangents1[0].dot(&mj_lambda1.linear) + elt.gcross1[0].gdot(mj_lambda1.angular) - tangents1[0].dot(&mj_lambda2.linear) + elt.gcross2[0].gdot(mj_lambda2.angular) + elt.rhs[0]; let limit = self.limit * normal_elt.impulse; let new_impulse = (elt.impulse[0] - elt.r[0] * dimpulse).simd_clamp(-limit, limit); let dlambda = new_impulse - elt.impulse[0]; elt.impulse[0] = new_impulse; mj_lambda1.linear += tangents1[0] * (self.im1 * dlambda); mj_lambda1.angular += elt.gcross1[0] * dlambda; mj_lambda2.linear += tangents1[0] * (-self.im2 * dlambda); mj_lambda2.angular += elt.gcross2[0] * dlambda; } #[cfg(feature = "dim3")] for i in 0..self.num_contacts as usize { let limit = self.limit * self.elements[i].normal_part.impulse; let elts = &mut self.elements[i].tangent_part; let dimpulse_0 = self.tangent1.dot(&mj_lambda1.linear) + elts.gcross1[0].gdot(mj_lambda1.angular) - self.tangent1.dot(&mj_lambda2.linear) + elts.gcross2[0].gdot(mj_lambda2.angular) + elts.rhs[0]; let dimpulse_1 = bitangent1.dot(&mj_lambda1.linear) + elts.gcross1[1].gdot(mj_lambda1.angular) - bitangent1.dot(&mj_lambda2.linear) + elts.gcross2[1].gdot(mj_lambda2.angular) + elts.rhs[1]; let new_impulse = na::Vector2::new( elts.impulse[0] - elts.r[0] * dimpulse_0, elts.impulse[1] - elts.r[1] * dimpulse_1, ); let new_impulse = new_impulse.simd_cap_magnitude(limit); let dlambda = new_impulse - elts.impulse; elts.impulse = new_impulse; mj_lambda1.linear += self.tangent1 * (self.im1 * dlambda[0]) + bitangent1 * (self.im1 * dlambda[1]); mj_lambda1.angular += elts.gcross1[0] * dlambda[0] + elts.gcross1[1] * dlambda[1]; mj_lambda2.linear += self.tangent1 * (-self.im2 * dlambda[0]) + bitangent1 * (-self.im2 * dlambda[1]); mj_lambda2.angular += elts.gcross2[0] * dlambda[0] + elts.gcross2[1] * dlambda[1]; } // Solve non-penetration after friction. for i in 0..self.num_contacts as usize { let elt = &mut self.elements[i].normal_part; let dimpulse = self.dir1.dot(&mj_lambda1.linear) + elt.gcross1.gdot(mj_lambda1.angular) - self.dir1.dot(&mj_lambda2.linear) + elt.gcross2.gdot(mj_lambda2.angular) + elt.rhs; let new_impulse = (elt.impulse - elt.r * dimpulse).simd_max(SimdReal::zero()); let dlambda = new_impulse - elt.impulse; elt.impulse = new_impulse; mj_lambda1.linear += self.dir1 * (self.im1 * dlambda); mj_lambda1.angular += elt.gcross1 * dlambda; mj_lambda2.linear += self.dir1 * (-self.im2 * dlambda); mj_lambda2.angular += elt.gcross2 * dlambda; } for ii in 0..SIMD_WIDTH { mj_lambdas[self.mj_lambda1[ii] as usize].linear = mj_lambda1.linear.extract(ii); mj_lambdas[self.mj_lambda1[ii] as usize].angular = mj_lambda1.angular.extract(ii); } for ii in 0..SIMD_WIDTH { mj_lambdas[self.mj_lambda2[ii] as usize].linear = mj_lambda2.linear.extract(ii); mj_lambdas[self.mj_lambda2[ii] as usize].angular = mj_lambda2.angular.extract(ii); } } pub fn writeback_impulses(&self, manifolds_all: &mut [&mut ContactManifold]) { for k in 0..self.num_contacts as usize { let impulses: [_; SIMD_WIDTH] = self.elements[k].normal_part.impulse.into(); #[cfg(feature = "dim2")] let tangent_impulses: [_; SIMD_WIDTH] = self.elements[k].tangent_part.impulse[0].into(); #[cfg(feature = "dim3")] let tangent_impulses = self .tangent_rot1 .inverse_transform_vector(&self.elements[k].tangent_part.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")] { active_contact.data.tangent_impulse = tangent_impulses.extract(ii); } } } } }