Add joint softness per joint (#901)
This commit is contained in:
@@ -13,6 +13,12 @@
|
|||||||
- Rename `RigidBodyChanges::CHANGED` to `::IN_CHANGED_SET` to make its meaning more precise.
|
- Rename `RigidBodyChanges::CHANGED` to `::IN_CHANGED_SET` to make its meaning more precise.
|
||||||
- Fix colliders ignoring user-changes after the first simulation step.
|
- Fix colliders ignoring user-changes after the first simulation step.
|
||||||
- Fix broad-phase incorrectly taking into account disabled colliders attached to an enabled dynamic rigid-body.
|
- Fix broad-phase incorrectly taking into account disabled colliders attached to an enabled dynamic rigid-body.
|
||||||
|
- Grouped `IntegrationParameters::contact_natural_frequency` and `IntegrationParameters::contact_damping_ratio` behind
|
||||||
|
a single `IntegrationParameters::contact_softness` (#789).
|
||||||
|
- Removed the `Integration_parameters` methods related to `erp` and `cfm`. They are now methods of the
|
||||||
|
`IntegrationParameters::softness` and `GenericJoint::softness` fields (#789).
|
||||||
|
- Removed `IntegrationParameters::joint_natural_frequency` and `IntegrationParameters::joint_damping_ratio` in favor of
|
||||||
|
per-joint softness coefficients `GenericJoint::softness` (#789).
|
||||||
|
|
||||||
## v0.30.1 (17 Oct. 2025)
|
## v0.30.1 (17 Oct. 2025)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ pub fn init_world(testbed: &mut Testbed) {
|
|||||||
let mut impulse_joints = ImpulseJointSet::new();
|
let mut impulse_joints = ImpulseJointSet::new();
|
||||||
let multibody_joints = MultibodyJointSet::new();
|
let multibody_joints = MultibodyJointSet::new();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable/disable softness.
|
||||||
|
*/
|
||||||
|
let settings = testbed.example_settings_mut();
|
||||||
|
let variable_softness = settings.get_or_set_bool("Variable softness", false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the balls
|
* Create the balls
|
||||||
*/
|
*/
|
||||||
@@ -41,10 +47,22 @@ pub fn init_world(testbed: &mut Testbed) {
|
|||||||
let collider = ColliderBuilder::ball(rad);
|
let collider = ColliderBuilder::ball(rad);
|
||||||
colliders.insert_with_parent(collider, child_handle, &mut bodies);
|
colliders.insert_with_parent(collider, child_handle, &mut bodies);
|
||||||
|
|
||||||
|
let softness = if variable_softness {
|
||||||
|
// If variable softness is enabled, joints closer to the fixed body are softer.
|
||||||
|
SpringCoefficients {
|
||||||
|
natural_frequency: 5.0 * (i.max(k) + 1) as f32,
|
||||||
|
damping_ratio: 0.1 * (i.max(k) + 1) as f32,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SpringCoefficients::joint_defaults()
|
||||||
|
};
|
||||||
|
|
||||||
// Vertical joint.
|
// Vertical joint.
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
let parent_handle = *body_handles.last().unwrap();
|
let parent_handle = *body_handles.last().unwrap();
|
||||||
let joint = RevoluteJointBuilder::new().local_anchor2(point![0.0, shift]);
|
let joint = RevoluteJointBuilder::new()
|
||||||
|
.local_anchor2(point![0.0, shift])
|
||||||
|
.softness(softness);
|
||||||
impulse_joints.insert(parent_handle, child_handle, joint, true);
|
impulse_joints.insert(parent_handle, child_handle, joint, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +70,9 @@ pub fn init_world(testbed: &mut Testbed) {
|
|||||||
if k > 0 {
|
if k > 0 {
|
||||||
let parent_index = body_handles.len() - numi;
|
let parent_index = body_handles.len() - numi;
|
||||||
let parent_handle = body_handles[parent_index];
|
let parent_handle = body_handles[parent_index];
|
||||||
let joint = RevoluteJointBuilder::new().local_anchor2(point![-shift, 0.0]);
|
let joint = RevoluteJointBuilder::new()
|
||||||
|
.local_anchor2(point![-shift, 0.0])
|
||||||
|
.softness(softness);
|
||||||
impulse_joints.insert(parent_handle, child_handle, joint, true);
|
impulse_joints.insert(parent_handle, child_handle, joint, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
use rapier2d::prelude::*;
|
use crate::utils::character;
|
||||||
|
use crate::utils::character::CharacterControlMode;
|
||||||
use rapier_testbed2d::Testbed;
|
use rapier_testbed2d::Testbed;
|
||||||
|
use rapier2d::control::{KinematicCharacterController, PidController};
|
||||||
|
use rapier2d::prelude::*;
|
||||||
|
|
||||||
pub fn init_world(testbed: &mut Testbed) {
|
pub fn init_world(testbed: &mut Testbed) {
|
||||||
/*
|
/*
|
||||||
@@ -77,10 +80,29 @@ pub fn init_world(testbed: &mut Testbed) {
|
|||||||
.build();
|
.build();
|
||||||
impulse_joints.insert(character_handle, cube_handle, pin_slot_joint, true);
|
impulse_joints.insert(character_handle, cube_handle, pin_slot_joint, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback to update the character based on user inputs.
|
||||||
|
*/
|
||||||
|
let mut control_mode = CharacterControlMode::Kinematic(0.1);
|
||||||
|
let mut controller = KinematicCharacterController::default();
|
||||||
|
let mut pid = PidController::default();
|
||||||
|
|
||||||
|
testbed.add_callback(move |graphics, physics, _, _| {
|
||||||
|
if let Some(graphics) = graphics {
|
||||||
|
character::update_character(
|
||||||
|
graphics,
|
||||||
|
physics,
|
||||||
|
&mut control_mode,
|
||||||
|
&mut controller,
|
||||||
|
&mut pid,
|
||||||
|
character_handle,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up the testbed.
|
* Set up the testbed.
|
||||||
*/
|
*/
|
||||||
testbed.set_world(bodies, colliders, impulse_joints, multibody_joints);
|
testbed.set_world(bodies, colliders, impulse_joints, multibody_joints);
|
||||||
testbed.set_character_body(character_handle);
|
|
||||||
testbed.look_at(point![0.0, 1.0], 100.0);
|
testbed.look_at(point![0.0, 1.0], 100.0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use crate::math::Real;
|
|
||||||
use na::RealField;
|
|
||||||
|
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use super::RigidBodyActivation;
|
use super::RigidBodyActivation;
|
||||||
|
use crate::math::Real;
|
||||||
|
use simba::simd::SimdRealField;
|
||||||
|
|
||||||
// TODO: enabling the block solver in 3d introduces a lot of jitters in
|
// TODO: enabling the block solver in 3d introduces a lot of jitters in
|
||||||
// the 3D domino demo. So for now we dont enable it in 3D.
|
// the 3D domino demo. So for now we dont enable it in 3D.
|
||||||
@@ -30,6 +29,114 @@ pub enum FrictionModel {
|
|||||||
Coulomb,
|
Coulomb,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||||
|
// TODO: we should be able to combine this with MotorModel.
|
||||||
|
/// Coefficients for a spring, typically used for configuring constraint softness for contacts and
|
||||||
|
/// joints.
|
||||||
|
pub struct SpringCoefficients<N> {
|
||||||
|
/// Sets the natural frequency (Hz) of the spring-like constraint.
|
||||||
|
///
|
||||||
|
/// Higher values make the constraint stiffer and resolve constraint violations more quickly.
|
||||||
|
pub natural_frequency: N,
|
||||||
|
/// Sets the damping ratio for the spring-like constraint.
|
||||||
|
///
|
||||||
|
/// Larger values make the joint more compliant (allowing more drift before stabilization).
|
||||||
|
pub damping_ratio: N,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: SimdRealField<Element = Real> + Copy> SpringCoefficients<N> {
|
||||||
|
/// Initializes spring coefficients from the spring natural frequency and damping ratio.
|
||||||
|
pub fn new(natural_frequency: N, damping_ratio: N) -> Self {
|
||||||
|
Self {
|
||||||
|
natural_frequency,
|
||||||
|
damping_ratio,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default softness coefficients for contacts.
|
||||||
|
pub fn contact_defaults() -> Self {
|
||||||
|
Self {
|
||||||
|
natural_frequency: N::splat(30.0),
|
||||||
|
damping_ratio: N::splat(5.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default softness coefficients for joints.
|
||||||
|
pub fn joint_defaults() -> Self {
|
||||||
|
Self {
|
||||||
|
natural_frequency: N::splat(1.0e6),
|
||||||
|
damping_ratio: N::splat(1.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The contact’s spring angular frequency for constraints regularization.
|
||||||
|
pub fn angular_frequency(&self) -> N {
|
||||||
|
self.natural_frequency * N::simd_two_pi()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`Self::erp`] coefficient, multiplied by the inverse timestep length.
|
||||||
|
pub fn erp_inv_dt(&self, dt: N) -> N {
|
||||||
|
let ang_freq = self.angular_frequency();
|
||||||
|
ang_freq / (dt * ang_freq + N::splat(2.0) * self.damping_ratio)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The effective Error Reduction Parameter applied for calculating regularization forces.
|
||||||
|
///
|
||||||
|
/// This parameter is computed automatically from [`Self::natural_frequency`],
|
||||||
|
/// [`Self::damping_ratio`] and the substep length.
|
||||||
|
pub fn erp(&self, dt: N) -> N {
|
||||||
|
dt * self.erp_inv_dt(dt)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute CFM assuming a critically damped spring multiplied by the damping ratio.
|
||||||
|
///
|
||||||
|
/// This coefficient softens the impulse applied at each solver iteration.
|
||||||
|
pub fn cfm_coeff(&self, dt: N) -> N {
|
||||||
|
let one = N::one();
|
||||||
|
let erp = self.erp(dt);
|
||||||
|
let erp_is_not_zero = erp.simd_ne(N::zero());
|
||||||
|
let inv_erp_minus_one = one / erp - one;
|
||||||
|
|
||||||
|
// let stiffness = 4.0 * damping_ratio * damping_ratio * projected_mass
|
||||||
|
// / (dt * dt * inv_erp_minus_one * inv_erp_minus_one);
|
||||||
|
// let damping = 4.0 * damping_ratio * damping_ratio * projected_mass
|
||||||
|
// / (dt * inv_erp_minus_one);
|
||||||
|
// let cfm = 1.0 / (dt * dt * stiffness + dt * damping);
|
||||||
|
// NOTE: This simplifies to cfm = cfm_coeff / projected_mass:
|
||||||
|
let result = inv_erp_minus_one * inv_erp_minus_one
|
||||||
|
/ ((one + inv_erp_minus_one) * N::splat(4.0) * self.damping_ratio * self.damping_ratio);
|
||||||
|
result.select(erp_is_not_zero, N::zero())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The CFM factor to be used in the constraint resolution.
|
||||||
|
///
|
||||||
|
/// This parameter is computed automatically from [`Self::natural_frequency`],
|
||||||
|
/// [`Self::damping_ratio`] and the substep length.
|
||||||
|
pub fn cfm_factor(&self, dt: N) -> N {
|
||||||
|
let one = N::one();
|
||||||
|
let cfm_coeff = self.cfm_coeff(dt);
|
||||||
|
|
||||||
|
// We use this coefficient inside the impulse resolution.
|
||||||
|
// Surprisingly, several simplifications happen there.
|
||||||
|
// Let `m` the projected mass of the constraint.
|
||||||
|
// Let `m’` the projected mass that includes CFM: `m’ = 1 / (1 / m + cfm_coeff / m) = m / (1 + cfm_coeff)`
|
||||||
|
// We have:
|
||||||
|
// new_impulse = old_impulse - m’ (delta_vel - cfm * old_impulse)
|
||||||
|
// = old_impulse - m / (1 + cfm_coeff) * (delta_vel - cfm_coeff / m * old_impulse)
|
||||||
|
// = old_impulse * (1 - cfm_coeff / (1 + cfm_coeff)) - m / (1 + cfm_coeff) * delta_vel
|
||||||
|
// = old_impulse / (1 + cfm_coeff) - m * delta_vel / (1 + cfm_coeff)
|
||||||
|
// = 1 / (1 + cfm_coeff) * (old_impulse - m * delta_vel)
|
||||||
|
// So, setting cfm_factor = 1 / (1 + cfm_coeff).
|
||||||
|
// We obtain:
|
||||||
|
// new_impulse = cfm_factor * (old_impulse - m * delta_vel)
|
||||||
|
//
|
||||||
|
// The value returned by this function is this cfm_factor that can be used directly
|
||||||
|
// in the constraint solver.
|
||||||
|
one / (one + cfm_coeff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration parameters that control the physics simulation quality and behavior.
|
/// Configuration parameters that control the physics simulation quality and behavior.
|
||||||
///
|
///
|
||||||
/// These parameters affect how the physics engine advances time, resolves collisions, and
|
/// These parameters affect how the physics engine advances time, resolves collisions, and
|
||||||
@@ -80,34 +187,8 @@ pub struct IntegrationParameters {
|
|||||||
/// to numerical instabilities.
|
/// to numerical instabilities.
|
||||||
pub min_ccd_dt: Real,
|
pub min_ccd_dt: Real,
|
||||||
|
|
||||||
/// > 0: the damping ratio used by the springs for contact constraint stabilization.
|
/// Softness coefficients for contact constraints.
|
||||||
///
|
pub contact_softness: SpringCoefficients<Real>,
|
||||||
/// Larger values make the constraints more compliant (allowing more visible
|
|
||||||
/// penetrations before stabilization).
|
|
||||||
/// (default `5.0`).
|
|
||||||
pub contact_damping_ratio: Real,
|
|
||||||
|
|
||||||
/// > 0: the natural frequency used by the springs for contact constraint regularization.
|
|
||||||
///
|
|
||||||
/// Increasing this value will make it so that penetrations get fixed more quickly at the
|
|
||||||
/// expense of potential jitter effects due to overshooting. In order to make the simulation
|
|
||||||
/// look stiffer, it is recommended to increase the [`Self::contact_damping_ratio`] instead of this
|
|
||||||
/// value.
|
|
||||||
/// (default: `30.0`).
|
|
||||||
pub contact_natural_frequency: Real,
|
|
||||||
|
|
||||||
/// > 0: the natural frequency used by the springs for joint constraint regularization.
|
|
||||||
///
|
|
||||||
/// Increasing this value will make it so that penetrations get fixed more quickly.
|
|
||||||
/// (default: `1.0e6`).
|
|
||||||
pub joint_natural_frequency: Real,
|
|
||||||
|
|
||||||
/// The fraction of critical damping applied to the joint for constraints regularization.
|
|
||||||
///
|
|
||||||
/// Larger values make the constraints more compliant (allowing more joint
|
|
||||||
/// drift before stabilization).
|
|
||||||
/// (default `1.0`).
|
|
||||||
pub joint_damping_ratio: Real,
|
|
||||||
|
|
||||||
/// The coefficient in `[0, 1]` applied to warmstart impulses, i.e., impulses that are used as the
|
/// The coefficient in `[0, 1]` applied to warmstart impulses, i.e., impulses that are used as the
|
||||||
/// initial solution (instead of 0) at the next simulation step.
|
/// initial solution (instead of 0) at the next simulation step.
|
||||||
@@ -193,110 +274,7 @@ impl IntegrationParameters {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The contact’s spring angular frequency for constraints regularization.
|
/// Amount of penetration the engine won't attempt to correct (default: `0.001` multiplied by
|
||||||
pub fn contact_angular_frequency(&self) -> Real {
|
|
||||||
self.contact_natural_frequency * Real::two_pi()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The [`Self::contact_erp`] coefficient, multiplied by the inverse timestep length.
|
|
||||||
pub fn contact_erp_inv_dt(&self) -> Real {
|
|
||||||
let ang_freq = self.contact_angular_frequency();
|
|
||||||
ang_freq / (self.dt * ang_freq + 2.0 * self.contact_damping_ratio)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The effective Error Reduction Parameter applied for calculating regularization forces
|
|
||||||
/// on contacts.
|
|
||||||
///
|
|
||||||
/// This parameter is computed automatically from [`Self::contact_natural_frequency`],
|
|
||||||
/// [`Self::contact_damping_ratio`] and the substep length.
|
|
||||||
pub fn contact_erp(&self) -> Real {
|
|
||||||
self.dt * self.contact_erp_inv_dt()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The joint’s spring angular frequency for constraint regularization.
|
|
||||||
pub fn joint_angular_frequency(&self) -> Real {
|
|
||||||
self.joint_natural_frequency * Real::two_pi()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The [`Self::joint_erp`] coefficient, multiplied by the inverse timestep length.
|
|
||||||
pub fn joint_erp_inv_dt(&self) -> Real {
|
|
||||||
let ang_freq = self.joint_angular_frequency();
|
|
||||||
ang_freq / (self.dt * ang_freq + 2.0 * self.joint_damping_ratio)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The effective Error Reduction Parameter applied for calculating regularization forces
|
|
||||||
/// on joints.
|
|
||||||
///
|
|
||||||
/// This parameter is computed automatically from [`Self::joint_natural_frequency`],
|
|
||||||
/// [`Self::joint_damping_ratio`] and the substep length.
|
|
||||||
pub fn joint_erp(&self) -> Real {
|
|
||||||
self.dt * self.joint_erp_inv_dt()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The CFM factor to be used in the constraint resolution.
|
|
||||||
///
|
|
||||||
/// This parameter is computed automatically from [`Self::contact_natural_frequency`],
|
|
||||||
/// [`Self::contact_damping_ratio`] and the substep length.
|
|
||||||
pub fn contact_cfm_factor(&self) -> Real {
|
|
||||||
// Compute CFM assuming a critically damped spring multiplied by the damping ratio.
|
|
||||||
// The logic is similar to [`Self::joint_cfm_coeff`].
|
|
||||||
let contact_erp = self.contact_erp();
|
|
||||||
if contact_erp == 0.0 {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
let inv_erp_minus_one = 1.0 / contact_erp - 1.0;
|
|
||||||
|
|
||||||
// let stiffness = 4.0 * damping_ratio * damping_ratio * projected_mass
|
|
||||||
// / (dt * dt * inv_erp_minus_one * inv_erp_minus_one);
|
|
||||||
// let damping = 4.0 * damping_ratio * damping_ratio * projected_mass
|
|
||||||
// / (dt * inv_erp_minus_one);
|
|
||||||
// let cfm = 1.0 / (dt * dt * stiffness + dt * damping);
|
|
||||||
// NOTE: This simplifies to cfm = cfm_coeff / projected_mass:
|
|
||||||
let cfm_coeff = inv_erp_minus_one * inv_erp_minus_one
|
|
||||||
/ ((1.0 + inv_erp_minus_one)
|
|
||||||
* 4.0
|
|
||||||
* self.contact_damping_ratio
|
|
||||||
* self.contact_damping_ratio);
|
|
||||||
|
|
||||||
// Furthermore, we use this coefficient inside of the impulse resolution.
|
|
||||||
// Surprisingly, several simplifications happen there.
|
|
||||||
// Let `m` the projected mass of the constraint.
|
|
||||||
// Let `m’` the projected mass that includes CFM: `m’ = 1 / (1 / m + cfm_coeff / m) = m / (1 + cfm_coeff)`
|
|
||||||
// We have:
|
|
||||||
// new_impulse = old_impulse - m’ (delta_vel - cfm * old_impulse)
|
|
||||||
// = old_impulse - m / (1 + cfm_coeff) * (delta_vel - cfm_coeff / m * old_impulse)
|
|
||||||
// = old_impulse * (1 - cfm_coeff / (1 + cfm_coeff)) - m / (1 + cfm_coeff) * delta_vel
|
|
||||||
// = old_impulse / (1 + cfm_coeff) - m * delta_vel / (1 + cfm_coeff)
|
|
||||||
// = 1 / (1 + cfm_coeff) * (old_impulse - m * delta_vel)
|
|
||||||
// So, setting cfm_factor = 1 / (1 + cfm_coeff).
|
|
||||||
// We obtain:
|
|
||||||
// new_impulse = cfm_factor * (old_impulse - m * delta_vel)
|
|
||||||
//
|
|
||||||
// The value returned by this function is this cfm_factor that can be used directly
|
|
||||||
// in the constraint solver.
|
|
||||||
1.0 / (1.0 + cfm_coeff)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The CFM (constraints force mixing) coefficient applied to all joints for constraints regularization.
|
|
||||||
///
|
|
||||||
/// This parameter is computed automatically from [`Self::joint_natural_frequency`],
|
|
||||||
/// [`Self::joint_damping_ratio`] and the substep length.
|
|
||||||
pub fn joint_cfm_coeff(&self) -> Real {
|
|
||||||
// Compute CFM assuming a critically damped spring multiplied by the damping ratio.
|
|
||||||
// The logic is similar to `Self::contact_cfm_factor`.
|
|
||||||
let joint_erp = self.joint_erp();
|
|
||||||
if joint_erp == 0.0 {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
let inv_erp_minus_one = 1.0 / joint_erp - 1.0;
|
|
||||||
inv_erp_minus_one * inv_erp_minus_one
|
|
||||||
/ ((1.0 + inv_erp_minus_one)
|
|
||||||
* 4.0
|
|
||||||
* self.joint_damping_ratio
|
|
||||||
* self.joint_damping_ratio)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Amount of penetration the engine won’t attempt to correct (default: `0.001` multiplied by
|
|
||||||
/// [`Self::length_unit`]).
|
/// [`Self::length_unit`]).
|
||||||
pub fn allowed_linear_error(&self) -> Real {
|
pub fn allowed_linear_error(&self) -> Real {
|
||||||
self.normalized_allowed_linear_error * self.length_unit
|
self.normalized_allowed_linear_error * self.length_unit
|
||||||
@@ -326,10 +304,7 @@ impl Default for IntegrationParameters {
|
|||||||
Self {
|
Self {
|
||||||
dt: 1.0 / 60.0,
|
dt: 1.0 / 60.0,
|
||||||
min_ccd_dt: 1.0 / 60.0 / 100.0,
|
min_ccd_dt: 1.0 / 60.0 / 100.0,
|
||||||
contact_natural_frequency: 30.0,
|
contact_softness: SpringCoefficients::contact_defaults(),
|
||||||
contact_damping_ratio: 5.0,
|
|
||||||
joint_natural_frequency: 1.0e6,
|
|
||||||
joint_damping_ratio: 1.0,
|
|
||||||
warmstart_coefficient: 1.0,
|
warmstart_coefficient: 1.0,
|
||||||
num_internal_pgs_iterations: 1,
|
num_internal_pgs_iterations: 1,
|
||||||
num_internal_stabilization_iterations: 1,
|
num_internal_stabilization_iterations: 1,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::dynamics::integration_parameters::SpringCoefficients;
|
||||||
use crate::dynamics::{GenericJoint, GenericJointBuilder, JointAxesMask};
|
use crate::dynamics::{GenericJoint, GenericJointBuilder, JointAxesMask};
|
||||||
use crate::math::{Isometry, Point, Real};
|
use crate::math::{Isometry, Point, Real};
|
||||||
|
|
||||||
@@ -91,6 +92,19 @@ impl FixedJoint {
|
|||||||
self.data.set_local_anchor2(anchor2);
|
self.data.set_local_anchor2(anchor2);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(&self) -> SpringCoefficients<Real> {
|
||||||
|
self.data.softness
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
|
||||||
|
self.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FixedJoint> for GenericJoint {
|
impl From<FixedJoint> for GenericJoint {
|
||||||
@@ -138,13 +152,20 @@ impl FixedJointBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the joint’s anchor, expressed in the local-space of the second rigid-body.
|
/// Sets the joint's anchor, expressed in the local-space of the second rigid-body.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn local_anchor2(mut self, anchor2: Point<Real>) -> Self {
|
pub fn local_anchor2(mut self, anchor2: Point<Real>) -> Self {
|
||||||
self.0.set_local_anchor2(anchor2);
|
self.0.set_local_anchor2(anchor2);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
|
||||||
|
self.0.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Build the fixed joint.
|
/// Build the fixed joint.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn build(self) -> FixedJoint {
|
pub fn build(self) -> FixedJoint {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#![allow(clippy::bad_bit_mask)] // Clippy will complain about the bitmasks due to JointAxesMask::FREE_FIXED_AXES being 0.
|
#![allow(clippy::bad_bit_mask)] // Clippy will complain about the bitmasks due to JointAxesMask::FREE_FIXED_AXES being 0.
|
||||||
|
#![allow(clippy::unnecessary_cast)] // Casts are needed for switching between f32/f64.
|
||||||
|
|
||||||
|
use crate::dynamics::integration_parameters::SpringCoefficients;
|
||||||
use crate::dynamics::solver::MotorParameters;
|
use crate::dynamics::solver::MotorParameters;
|
||||||
use crate::dynamics::{
|
use crate::dynamics::{
|
||||||
FixedJoint, MotorModel, PrismaticJoint, RevoluteJoint, RigidBody, RopeJoint,
|
FixedJoint, MotorModel, PrismaticJoint, RevoluteJoint, RigidBody, RopeJoint,
|
||||||
@@ -282,6 +284,8 @@ pub struct GenericJoint {
|
|||||||
/// For coupled degrees of freedoms (DoF), only the first linear (resp. angular) coupled DoF motor and `motor_axes`
|
/// For coupled degrees of freedoms (DoF), only the first linear (resp. angular) coupled DoF motor and `motor_axes`
|
||||||
/// bitmask is applied to the coupled linear (resp. angular) axes.
|
/// bitmask is applied to the coupled linear (resp. angular) axes.
|
||||||
pub motors: [JointMotor; SPATIAL_DIM],
|
pub motors: [JointMotor; SPATIAL_DIM],
|
||||||
|
/// The coefficients controlling the joint constraints’ softness.
|
||||||
|
pub softness: SpringCoefficients<Real>,
|
||||||
/// Are contacts between the attached rigid-bodies enabled?
|
/// Are contacts between the attached rigid-bodies enabled?
|
||||||
pub contacts_enabled: bool,
|
pub contacts_enabled: bool,
|
||||||
/// Whether the joint is enabled.
|
/// Whether the joint is enabled.
|
||||||
@@ -301,6 +305,7 @@ impl Default for GenericJoint {
|
|||||||
coupled_axes: JointAxesMask::empty(),
|
coupled_axes: JointAxesMask::empty(),
|
||||||
limits: [JointLimits::default(); SPATIAL_DIM],
|
limits: [JointLimits::default(); SPATIAL_DIM],
|
||||||
motors: [JointMotor::default(); SPATIAL_DIM],
|
motors: [JointMotor::default(); SPATIAL_DIM],
|
||||||
|
softness: SpringCoefficients::joint_defaults(),
|
||||||
contacts_enabled: true,
|
contacts_enabled: true,
|
||||||
enabled: JointEnabled::Enabled,
|
enabled: JointEnabled::Enabled,
|
||||||
user_data: 0,
|
user_data: 0,
|
||||||
@@ -440,6 +445,13 @@ impl GenericJoint {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the spring coefficients controlling this joint constraint’s softness.
|
||||||
|
#[must_use]
|
||||||
|
pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
|
||||||
|
self.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// The joint limits along the specified axis.
|
/// The joint limits along the specified axis.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn limits(&self, axis: JointAxis) -> Option<&JointLimits<Real>> {
|
pub fn limits(&self, axis: JointAxis) -> Option<&JointLimits<Real>> {
|
||||||
@@ -767,6 +779,13 @@ impl GenericJointBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
|
||||||
|
self.0.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// An arbitrary user-defined 128-bit integer associated to the joints built by this builder.
|
/// An arbitrary user-defined 128-bit integer associated to the joints built by this builder.
|
||||||
pub fn user_data(mut self, data: u128) -> Self {
|
pub fn user_data(mut self, data: u128) -> Self {
|
||||||
self.0.user_data = data;
|
self.0.user_data = data;
|
||||||
|
|||||||
@@ -314,6 +314,7 @@ impl MultibodyJoint {
|
|||||||
jacobians,
|
jacobians,
|
||||||
constraints,
|
constraints,
|
||||||
&mut num_constraints,
|
&mut num_constraints,
|
||||||
|
self.data.softness,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
curr_free_dof += 1;
|
curr_free_dof += 1;
|
||||||
@@ -349,6 +350,7 @@ impl MultibodyJoint {
|
|||||||
jacobians,
|
jacobians,
|
||||||
constraints,
|
constraints,
|
||||||
&mut num_constraints,
|
&mut num_constraints,
|
||||||
|
self.data.softness,
|
||||||
);
|
);
|
||||||
Some(limits)
|
Some(limits)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#![allow(missing_docs)] // For downcast.
|
#![allow(missing_docs)] // For downcast.
|
||||||
|
|
||||||
|
use crate::dynamics::integration_parameters::SpringCoefficients;
|
||||||
use crate::dynamics::joint::MultibodyLink;
|
use crate::dynamics::joint::MultibodyLink;
|
||||||
use crate::dynamics::solver::{GenericJointConstraint, WritebackId};
|
use crate::dynamics::solver::{GenericJointConstraint, WritebackId};
|
||||||
use crate::dynamics::{IntegrationParameters, JointMotor, Multibody};
|
use crate::dynamics::{IntegrationParameters, JointMotor, Multibody};
|
||||||
@@ -19,12 +20,16 @@ pub fn unit_joint_limit_constraint(
|
|||||||
jacobians: &mut DVector<Real>,
|
jacobians: &mut DVector<Real>,
|
||||||
constraints: &mut [GenericJointConstraint],
|
constraints: &mut [GenericJointConstraint],
|
||||||
insert_at: &mut usize,
|
insert_at: &mut usize,
|
||||||
|
softness: SpringCoefficients<Real>,
|
||||||
) {
|
) {
|
||||||
let ndofs = multibody.ndofs();
|
let ndofs = multibody.ndofs();
|
||||||
let min_enabled = curr_pos < limits[0];
|
let min_enabled = curr_pos < limits[0];
|
||||||
let max_enabled = limits[1] < curr_pos;
|
let max_enabled = limits[1] < curr_pos;
|
||||||
let erp_inv_dt = params.joint_erp_inv_dt();
|
|
||||||
let cfm_coeff = params.joint_cfm_coeff();
|
// Compute per-joint ERP and CFM
|
||||||
|
let erp_inv_dt = softness.erp_inv_dt(params.dt);
|
||||||
|
let cfm_coeff = softness.cfm_coeff(params.dt);
|
||||||
|
|
||||||
let rhs_bias = ((curr_pos - limits[1]).max(0.0) - (limits[0] - curr_pos).max(0.0)) * erp_inv_dt;
|
let rhs_bias = ((curr_pos - limits[1]).max(0.0) - (limits[0] - curr_pos).max(0.0)) * erp_inv_dt;
|
||||||
let rhs_wo_bias = 0.0;
|
let rhs_wo_bias = 0.0;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#[cfg(feature = "dim2")]
|
#[cfg(feature = "dim2")]
|
||||||
use crate::dynamics::joint::{GenericJointBuilder, JointAxesMask};
|
use crate::dynamics::joint::{GenericJointBuilder, JointAxesMask};
|
||||||
|
|
||||||
|
use crate::dynamics::integration_parameters::SpringCoefficients;
|
||||||
use crate::dynamics::joint::GenericJoint;
|
use crate::dynamics::joint::GenericJoint;
|
||||||
use crate::dynamics::{JointAxis, MotorModel};
|
use crate::dynamics::{JointAxis, MotorModel};
|
||||||
use crate::math::{Point, Real, UnitVector};
|
use crate::math::{Point, Real, UnitVector};
|
||||||
@@ -155,6 +156,19 @@ impl PinSlotJoint {
|
|||||||
self.data.set_limits(JointAxis::LinX, limits);
|
self.data.set_limits(JointAxis::LinX, limits);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(&self) -> SpringCoefficients<Real> {
|
||||||
|
self.data.softness
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
|
||||||
|
self.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PinSlotJoint> for GenericJoint {
|
impl From<PinSlotJoint> for GenericJoint {
|
||||||
@@ -255,13 +269,20 @@ impl PinSlotJointBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the `[min,max]` limit distances attached bodies can translate along the joint’s principal axis.
|
/// Sets the `[min,max]` limit distances attached bodies can translate along the joint's principal axis.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn limits(mut self, limits: [Real; 2]) -> Self {
|
pub fn limits(mut self, limits: [Real; 2]) -> Self {
|
||||||
self.0.set_limits(limits);
|
self.0.set_limits(limits);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
|
||||||
|
self.0.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds the pin slot joint.
|
/// Builds the pin slot joint.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn build(self) -> PinSlotJoint {
|
pub fn build(self) -> PinSlotJoint {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::dynamics::integration_parameters::SpringCoefficients;
|
||||||
use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
|
use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
|
||||||
use crate::dynamics::{JointAxis, MotorModel};
|
use crate::dynamics::{JointAxis, MotorModel};
|
||||||
use crate::math::{Point, Real, UnitVector};
|
use crate::math::{Point, Real, UnitVector};
|
||||||
@@ -176,6 +177,19 @@ impl PrismaticJoint {
|
|||||||
self.data.set_limits(JointAxis::LinX, limits);
|
self.data.set_limits(JointAxis::LinX, limits);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(&self) -> SpringCoefficients<Real> {
|
||||||
|
self.data.softness
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint.
|
||||||
|
#[must_use]
|
||||||
|
pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
|
||||||
|
self.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PrismaticJoint> for GenericJoint {
|
impl From<PrismaticJoint> for GenericJoint {
|
||||||
@@ -275,13 +289,20 @@ impl PrismaticJointBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the `[min,max]` limit distances attached bodies can translate along the joint’s principal axis.
|
/// Sets the `[min,max]` limit distances attached bodies can translate along the joint's principal axis.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn limits(mut self, limits: [Real; 2]) -> Self {
|
pub fn limits(mut self, limits: [Real; 2]) -> Self {
|
||||||
self.0.set_limits(limits);
|
self.0.set_limits(limits);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
|
||||||
|
self.0.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds the prismatic joint.
|
/// Builds the prismatic joint.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn build(self) -> PrismaticJoint {
|
pub fn build(self) -> PrismaticJoint {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::dynamics::integration_parameters::SpringCoefficients;
|
||||||
use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
|
use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
|
||||||
use crate::dynamics::{JointAxis, JointLimits, JointMotor, MotorModel};
|
use crate::dynamics::{JointAxis, JointLimits, JointMotor, MotorModel};
|
||||||
use crate::math::{Point, Real, Rotation};
|
use crate::math::{Point, Real, Rotation};
|
||||||
@@ -203,6 +204,19 @@ impl RevoluteJoint {
|
|||||||
self.data.set_limits(JointAxis::AngX, limits);
|
self.data.set_limits(JointAxis::AngX, limits);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(&self) -> SpringCoefficients<Real> {
|
||||||
|
self.data.softness
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
|
||||||
|
self.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RevoluteJoint> for GenericJoint {
|
impl From<RevoluteJoint> for GenericJoint {
|
||||||
@@ -296,13 +310,20 @@ impl RevoluteJointBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the `[min,max]` limit angles attached bodies can rotate along the joint’s principal axis.
|
/// Sets the `[min,max]` limit angles attached bodies can rotate along the joint's principal axis.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn limits(mut self, limits: [Real; 2]) -> Self {
|
pub fn limits(mut self, limits: [Real; 2]) -> Self {
|
||||||
self.0.set_limits(limits);
|
self.0.set_limits(limits);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
|
||||||
|
self.0.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds the revolute joint.
|
/// Builds the revolute joint.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn build(self) -> RevoluteJoint {
|
pub fn build(self) -> RevoluteJoint {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::dynamics::integration_parameters::SpringCoefficients;
|
||||||
use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
|
use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
|
||||||
use crate::dynamics::{JointAxis, MotorModel};
|
use crate::dynamics::{JointAxis, MotorModel};
|
||||||
use crate::math::{Point, Real};
|
use crate::math::{Point, Real};
|
||||||
@@ -153,6 +154,19 @@ impl RopeJoint {
|
|||||||
self.data.set_limits(JointAxis::LinX, [0.0, max_dist]);
|
self.data.set_limits(JointAxis::LinX, [0.0, max_dist]);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(&self) -> SpringCoefficients<Real> {
|
||||||
|
self.data.softness
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
|
||||||
|
self.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RopeJoint> for GenericJoint {
|
impl From<RopeJoint> for GenericJoint {
|
||||||
@@ -245,6 +259,13 @@ impl RopeJointBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
|
||||||
|
self.0.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds the rope joint.
|
/// Builds the rope joint.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn build(self) -> RopeJoint {
|
pub fn build(self) -> RopeJoint {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::dynamics::integration_parameters::SpringCoefficients;
|
||||||
use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
|
use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
|
||||||
use crate::dynamics::{JointAxis, JointMotor, MotorModel};
|
use crate::dynamics::{JointAxis, JointMotor, MotorModel};
|
||||||
use crate::math::{Isometry, Point, Real};
|
use crate::math::{Isometry, Point, Real};
|
||||||
@@ -192,6 +193,19 @@ impl SphericalJoint {
|
|||||||
self.data.set_limits(axis, limits);
|
self.data.set_limits(axis, limits);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(&self) -> SpringCoefficients<Real> {
|
||||||
|
self.data.softness
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
|
||||||
|
self.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SphericalJoint> for GenericJoint {
|
impl From<SphericalJoint> for GenericJoint {
|
||||||
@@ -305,6 +319,13 @@ impl SphericalJointBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the softness of this joint’s locked degrees of freedom.
|
||||||
|
#[must_use]
|
||||||
|
pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
|
||||||
|
self.0.data.softness = softness;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds the spherical joint.
|
/// Builds the spherical joint.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn build(self) -> SphericalJoint {
|
pub fn build(self) -> SphericalJoint {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
pub use self::ccd::CCDSolver;
|
pub use self::ccd::CCDSolver;
|
||||||
pub use self::coefficient_combine_rule::CoefficientCombineRule;
|
pub use self::coefficient_combine_rule::CoefficientCombineRule;
|
||||||
pub use self::integration_parameters::IntegrationParameters;
|
pub use self::integration_parameters::{IntegrationParameters, SpringCoefficients};
|
||||||
pub use self::island_manager::IslandManager;
|
pub use self::island_manager::IslandManager;
|
||||||
|
|
||||||
#[cfg(feature = "dim3")]
|
#[cfg(feature = "dim3")]
|
||||||
|
|||||||
@@ -254,10 +254,10 @@ impl ContactWithCoulombFrictionBuilder {
|
|||||||
_multibodies: &MultibodyJointSet,
|
_multibodies: &MultibodyJointSet,
|
||||||
constraint: &mut ContactWithCoulombFriction,
|
constraint: &mut ContactWithCoulombFriction,
|
||||||
) {
|
) {
|
||||||
let cfm_factor = SimdReal::splat(params.contact_cfm_factor());
|
let cfm_factor = SimdReal::splat(params.contact_softness.cfm_factor(params.dt));
|
||||||
let inv_dt = SimdReal::splat(params.inv_dt());
|
let inv_dt = SimdReal::splat(params.inv_dt());
|
||||||
let allowed_lin_err = SimdReal::splat(params.allowed_linear_error());
|
let allowed_lin_err = SimdReal::splat(params.allowed_linear_error());
|
||||||
let erp_inv_dt = SimdReal::splat(params.contact_erp_inv_dt());
|
let erp_inv_dt = SimdReal::splat(params.contact_softness.erp_inv_dt(params.dt));
|
||||||
let max_corrective_velocity = SimdReal::splat(params.max_corrective_velocity());
|
let max_corrective_velocity = SimdReal::splat(params.max_corrective_velocity());
|
||||||
let warmstart_coeff = SimdReal::splat(params.warmstart_coefficient);
|
let warmstart_coeff = SimdReal::splat(params.warmstart_coefficient);
|
||||||
|
|
||||||
|
|||||||
@@ -261,10 +261,10 @@ impl ContactWithTwistFrictionBuilder {
|
|||||||
_multibodies: &MultibodyJointSet,
|
_multibodies: &MultibodyJointSet,
|
||||||
constraint: &mut ContactWithTwistFriction,
|
constraint: &mut ContactWithTwistFriction,
|
||||||
) {
|
) {
|
||||||
let cfm_factor = SimdReal::splat(params.contact_cfm_factor());
|
let cfm_factor = SimdReal::splat(params.contact_softness.cfm_factor(params.dt));
|
||||||
let inv_dt = SimdReal::splat(params.inv_dt());
|
let inv_dt = SimdReal::splat(params.inv_dt());
|
||||||
let allowed_lin_err = SimdReal::splat(params.allowed_linear_error());
|
let allowed_lin_err = SimdReal::splat(params.allowed_linear_error());
|
||||||
let erp_inv_dt = SimdReal::splat(params.contact_erp_inv_dt());
|
let erp_inv_dt = SimdReal::splat(params.contact_softness.erp_inv_dt(params.dt));
|
||||||
let max_corrective_velocity = SimdReal::splat(params.max_corrective_velocity());
|
let max_corrective_velocity = SimdReal::splat(params.max_corrective_velocity());
|
||||||
let warmstart_coeff = SimdReal::splat(params.warmstart_coefficient);
|
let warmstart_coeff = SimdReal::splat(params.warmstart_coefficient);
|
||||||
|
|
||||||
|
|||||||
@@ -346,9 +346,9 @@ impl GenericContactConstraintBuilder {
|
|||||||
multibodies: &MultibodyJointSet,
|
multibodies: &MultibodyJointSet,
|
||||||
constraint: &mut GenericContactConstraint,
|
constraint: &mut GenericContactConstraint,
|
||||||
) {
|
) {
|
||||||
let cfm_factor = params.contact_cfm_factor();
|
let cfm_factor = params.contact_softness.cfm_factor(params.dt);
|
||||||
let inv_dt = params.inv_dt();
|
let inv_dt = params.inv_dt();
|
||||||
let erp_inv_dt = params.contact_erp_inv_dt();
|
let erp_inv_dt = params.contact_softness.erp_inv_dt(params.dt);
|
||||||
|
|
||||||
// We don’t update jacobians so the update is mostly identical to the non-generic velocity constraint.
|
// We don’t update jacobians so the update is mostly identical to the non-generic velocity constraint.
|
||||||
let pose1 = multibodies
|
let pose1 = multibodies
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ impl GenericJointConstraint {
|
|||||||
mb1,
|
mb1,
|
||||||
mb2,
|
mb2,
|
||||||
i - DIM,
|
i - DIM,
|
||||||
|
joint.softness,
|
||||||
WritebackId::Dof(i),
|
WritebackId::Dof(i),
|
||||||
);
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
@@ -157,6 +158,7 @@ impl GenericJointConstraint {
|
|||||||
mb1,
|
mb1,
|
||||||
mb2,
|
mb2,
|
||||||
i,
|
i,
|
||||||
|
joint.softness,
|
||||||
WritebackId::Dof(i),
|
WritebackId::Dof(i),
|
||||||
);
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
@@ -176,6 +178,7 @@ impl GenericJointConstraint {
|
|||||||
mb2,
|
mb2,
|
||||||
i - DIM,
|
i - DIM,
|
||||||
[joint.limits[i].min, joint.limits[i].max],
|
[joint.limits[i].min, joint.limits[i].max],
|
||||||
|
joint.softness,
|
||||||
WritebackId::Limit(i),
|
WritebackId::Limit(i),
|
||||||
);
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
@@ -194,6 +197,7 @@ impl GenericJointConstraint {
|
|||||||
mb2,
|
mb2,
|
||||||
i,
|
i,
|
||||||
[joint.limits[i].min, joint.limits[i].max],
|
[joint.limits[i].min, joint.limits[i].max],
|
||||||
|
joint.softness,
|
||||||
WritebackId::Limit(i),
|
WritebackId::Limit(i),
|
||||||
);
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use crate::utils;
|
|||||||
use crate::utils::IndexMut2;
|
use crate::utils::IndexMut2;
|
||||||
use na::{DVector, SVector};
|
use na::{DVector, SVector};
|
||||||
|
|
||||||
|
use crate::dynamics::integration_parameters::SpringCoefficients;
|
||||||
use crate::dynamics::solver::ConstraintsCounts;
|
use crate::dynamics::solver::ConstraintsCounts;
|
||||||
use crate::dynamics::solver::solver_body::SolverBodies;
|
use crate::dynamics::solver::solver_body::SolverBodies;
|
||||||
#[cfg(feature = "dim3")]
|
#[cfg(feature = "dim3")]
|
||||||
@@ -406,6 +407,7 @@ impl JointConstraintHelper<Real> {
|
|||||||
mb1: LinkOrBodyRef,
|
mb1: LinkOrBodyRef,
|
||||||
mb2: LinkOrBodyRef,
|
mb2: LinkOrBodyRef,
|
||||||
locked_axis: usize,
|
locked_axis: usize,
|
||||||
|
softness: SpringCoefficients<Real>,
|
||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
) -> GenericJointConstraint {
|
) -> GenericJointConstraint {
|
||||||
let lin_jac = self.basis.column(locked_axis).into_owned();
|
let lin_jac = self.basis.column(locked_axis).into_owned();
|
||||||
@@ -426,7 +428,7 @@ impl JointConstraintHelper<Real> {
|
|||||||
ang_jac2,
|
ang_jac2,
|
||||||
);
|
);
|
||||||
|
|
||||||
let erp_inv_dt = params.joint_erp_inv_dt();
|
let erp_inv_dt = softness.erp_inv_dt(params.dt);
|
||||||
let rhs_bias = lin_jac.dot(&self.lin_err) * erp_inv_dt;
|
let rhs_bias = lin_jac.dot(&self.lin_err) * erp_inv_dt;
|
||||||
c.rhs += rhs_bias;
|
c.rhs += rhs_bias;
|
||||||
c
|
c
|
||||||
@@ -444,6 +446,7 @@ impl JointConstraintHelper<Real> {
|
|||||||
mb2: LinkOrBodyRef,
|
mb2: LinkOrBodyRef,
|
||||||
limited_axis: usize,
|
limited_axis: usize,
|
||||||
limits: [Real; 2],
|
limits: [Real; 2],
|
||||||
|
softness: SpringCoefficients<Real>,
|
||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
) -> GenericJointConstraint {
|
) -> GenericJointConstraint {
|
||||||
let lin_jac = self.basis.column(limited_axis).into_owned();
|
let lin_jac = self.basis.column(limited_axis).into_owned();
|
||||||
@@ -468,7 +471,8 @@ impl JointConstraintHelper<Real> {
|
|||||||
let min_enabled = dist <= limits[0];
|
let min_enabled = dist <= limits[0];
|
||||||
let max_enabled = limits[1] <= dist;
|
let max_enabled = limits[1] <= dist;
|
||||||
|
|
||||||
let erp_inv_dt = params.joint_erp_inv_dt();
|
let erp_inv_dt = softness.erp_inv_dt(params.dt);
|
||||||
|
|
||||||
let rhs_bias = ((dist - limits[1]).max(0.0) - (limits[0] - dist).max(0.0)) * erp_inv_dt;
|
let rhs_bias = ((dist - limits[1]).max(0.0) - (limits[0] - dist).max(0.0)) * erp_inv_dt;
|
||||||
constraint.rhs += rhs_bias;
|
constraint.rhs += rhs_bias;
|
||||||
constraint.impulse_bounds = [
|
constraint.impulse_bounds = [
|
||||||
@@ -545,6 +549,7 @@ impl JointConstraintHelper<Real> {
|
|||||||
mb1: LinkOrBodyRef,
|
mb1: LinkOrBodyRef,
|
||||||
mb2: LinkOrBodyRef,
|
mb2: LinkOrBodyRef,
|
||||||
_locked_axis: usize,
|
_locked_axis: usize,
|
||||||
|
softness: SpringCoefficients<Real>,
|
||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
) -> GenericJointConstraint {
|
) -> GenericJointConstraint {
|
||||||
#[cfg(feature = "dim2")]
|
#[cfg(feature = "dim2")]
|
||||||
@@ -566,7 +571,7 @@ impl JointConstraintHelper<Real> {
|
|||||||
ang_jac,
|
ang_jac,
|
||||||
);
|
);
|
||||||
|
|
||||||
let erp_inv_dt = params.joint_erp_inv_dt();
|
let erp_inv_dt = softness.erp_inv_dt(params.dt);
|
||||||
#[cfg(feature = "dim2")]
|
#[cfg(feature = "dim2")]
|
||||||
let rhs_bias = self.ang_err.im * erp_inv_dt;
|
let rhs_bias = self.ang_err.im * erp_inv_dt;
|
||||||
#[cfg(feature = "dim3")]
|
#[cfg(feature = "dim3")]
|
||||||
@@ -587,6 +592,7 @@ impl JointConstraintHelper<Real> {
|
|||||||
mb2: LinkOrBodyRef,
|
mb2: LinkOrBodyRef,
|
||||||
_limited_axis: usize,
|
_limited_axis: usize,
|
||||||
limits: [Real; 2],
|
limits: [Real; 2],
|
||||||
|
softness: SpringCoefficients<Real>,
|
||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
) -> GenericJointConstraint {
|
) -> GenericJointConstraint {
|
||||||
#[cfg(feature = "dim2")]
|
#[cfg(feature = "dim2")]
|
||||||
@@ -620,7 +626,7 @@ impl JointConstraintHelper<Real> {
|
|||||||
max_enabled as u32 as Real * Real::MAX,
|
max_enabled as u32 as Real * Real::MAX,
|
||||||
];
|
];
|
||||||
|
|
||||||
let erp_inv_dt = params.joint_erp_inv_dt();
|
let erp_inv_dt = softness.erp_inv_dt(params.dt);
|
||||||
let rhs_bias =
|
let rhs_bias =
|
||||||
((s_ang - s_limits[1]).max(0.0) - (s_limits[0] - s_ang).max(0.0)) * erp_inv_dt;
|
((s_ang - s_limits[1]).max(0.0) - (s_limits[0] - s_ang).max(0.0)) * erp_inv_dt;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ use crate::utils::{SimdBasis, SimdQuat};
|
|||||||
use na::SMatrix;
|
use na::SMatrix;
|
||||||
|
|
||||||
#[cfg(feature = "simd-is-enabled")]
|
#[cfg(feature = "simd-is-enabled")]
|
||||||
use crate::math::{SIMD_WIDTH, SimdReal};
|
use {
|
||||||
|
crate::dynamics::SpringCoefficients,
|
||||||
|
crate::math::{SIMD_WIDTH, SimdReal},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct JointConstraintBuilder {
|
pub struct JointConstraintBuilder {
|
||||||
body1: u32,
|
body1: u32,
|
||||||
@@ -103,6 +106,7 @@ pub struct JointConstraintBuilderSimd {
|
|||||||
local_frame1: Isometry<SimdReal>,
|
local_frame1: Isometry<SimdReal>,
|
||||||
local_frame2: Isometry<SimdReal>,
|
local_frame2: Isometry<SimdReal>,
|
||||||
locked_axes: u8,
|
locked_axes: u8,
|
||||||
|
softness: SpringCoefficients<SimdReal>,
|
||||||
constraint_id: usize,
|
constraint_id: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +153,10 @@ impl JointConstraintBuilderSimd {
|
|||||||
local_frame1,
|
local_frame1,
|
||||||
local_frame2,
|
local_frame2,
|
||||||
locked_axes: joint[0].data.locked_axes.bits(),
|
locked_axes: joint[0].data.locked_axes.bits(),
|
||||||
|
softness: SpringCoefficients {
|
||||||
|
natural_frequency: array![|ii| joint[ii].data.softness.natural_frequency].into(),
|
||||||
|
damping_ratio: array![|ii| joint[ii].data.softness.damping_ratio].into(),
|
||||||
|
},
|
||||||
constraint_id: *out_constraint_id,
|
constraint_id: *out_constraint_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -191,6 +199,7 @@ impl JointConstraintBuilderSimd {
|
|||||||
&frame1,
|
&frame1,
|
||||||
&frame2,
|
&frame2,
|
||||||
self.locked_axes,
|
self.locked_axes,
|
||||||
|
self.softness,
|
||||||
&mut out[self.constraint_id..],
|
&mut out[self.constraint_id..],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -277,17 +286,25 @@ impl<N: SimdRealCopy> JointConstraintHelper<N> {
|
|||||||
limited_axis: usize,
|
limited_axis: usize,
|
||||||
limits: [N; 2],
|
limits: [N; 2],
|
||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
|
erp_inv_dt: N,
|
||||||
|
cfm_coeff: N,
|
||||||
) -> JointConstraint<N, LANES> {
|
) -> JointConstraint<N, LANES> {
|
||||||
let zero = N::zero();
|
let zero = N::zero();
|
||||||
let mut constraint =
|
let mut constraint = self.lock_linear(
|
||||||
self.lock_linear(params, joint_id, body1, body2, limited_axis, writeback_id);
|
params,
|
||||||
|
joint_id,
|
||||||
|
body1,
|
||||||
|
body2,
|
||||||
|
limited_axis,
|
||||||
|
writeback_id,
|
||||||
|
erp_inv_dt,
|
||||||
|
cfm_coeff,
|
||||||
|
);
|
||||||
|
|
||||||
let dist = self.lin_err.dot(&constraint.lin_jac);
|
let dist = self.lin_err.dot(&constraint.lin_jac);
|
||||||
let min_enabled = dist.simd_le(limits[0]);
|
let min_enabled = dist.simd_le(limits[0]);
|
||||||
let max_enabled = limits[1].simd_le(dist);
|
let max_enabled = limits[1].simd_le(dist);
|
||||||
|
|
||||||
let erp_inv_dt = N::splat(params.joint_erp_inv_dt());
|
|
||||||
let cfm_coeff = N::splat(params.joint_cfm_coeff());
|
|
||||||
let rhs_bias =
|
let rhs_bias =
|
||||||
((dist - limits[1]).simd_max(zero) - (limits[0] - dist).simd_max(zero)) * erp_inv_dt;
|
((dist - limits[1]).simd_max(zero) - (limits[0] - dist).simd_max(zero)) * erp_inv_dt;
|
||||||
constraint.rhs = constraint.rhs_wo_bias + rhs_bias;
|
constraint.rhs = constraint.rhs_wo_bias + rhs_bias;
|
||||||
@@ -309,6 +326,8 @@ impl<N: SimdRealCopy> JointConstraintHelper<N> {
|
|||||||
coupled_axes: u8,
|
coupled_axes: u8,
|
||||||
limits: [N; 2],
|
limits: [N; 2],
|
||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
|
erp_inv_dt: N,
|
||||||
|
cfm_coeff: N,
|
||||||
) -> JointConstraint<N, LANES> {
|
) -> JointConstraint<N, LANES> {
|
||||||
let zero = N::zero();
|
let zero = N::zero();
|
||||||
let mut lin_jac = Vector::zeros();
|
let mut lin_jac = Vector::zeros();
|
||||||
@@ -345,8 +364,6 @@ impl<N: SimdRealCopy> JointConstraintHelper<N> {
|
|||||||
let ii_ang_jac1 = body1.ii * ang_jac1;
|
let ii_ang_jac1 = body1.ii * ang_jac1;
|
||||||
let ii_ang_jac2 = body2.ii * ang_jac2;
|
let ii_ang_jac2 = body2.ii * ang_jac2;
|
||||||
|
|
||||||
let erp_inv_dt = N::splat(params.joint_erp_inv_dt());
|
|
||||||
let cfm_coeff = N::splat(params.joint_cfm_coeff());
|
|
||||||
let rhs_bias = (dist - limits[1]).simd_max(zero) * erp_inv_dt;
|
let rhs_bias = (dist - limits[1]).simd_max(zero) * erp_inv_dt;
|
||||||
let rhs = rhs_wo_bias + rhs_bias;
|
let rhs = rhs_wo_bias + rhs_bias;
|
||||||
let impulse_bounds = [N::zero(), N::splat(Real::INFINITY)];
|
let impulse_bounds = [N::zero(), N::splat(Real::INFINITY)];
|
||||||
@@ -385,8 +402,18 @@ impl<N: SimdRealCopy> JointConstraintHelper<N> {
|
|||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
) -> JointConstraint<N, LANES> {
|
) -> JointConstraint<N, LANES> {
|
||||||
let inv_dt = N::splat(params.inv_dt());
|
let inv_dt = N::splat(params.inv_dt());
|
||||||
let mut constraint =
|
let mut constraint = self.lock_linear(
|
||||||
self.lock_linear(params, joint_id, body1, body2, motor_axis, writeback_id);
|
params,
|
||||||
|
joint_id,
|
||||||
|
body1,
|
||||||
|
body2,
|
||||||
|
motor_axis,
|
||||||
|
writeback_id,
|
||||||
|
// Set regularization factors to zero.
|
||||||
|
// The motor impl. will overwrite them after.
|
||||||
|
N::zero(),
|
||||||
|
N::zero(),
|
||||||
|
);
|
||||||
|
|
||||||
let mut rhs_wo_bias = N::zero();
|
let mut rhs_wo_bias = N::zero();
|
||||||
if motor_params.erp_inv_dt != N::zero() {
|
if motor_params.erp_inv_dt != N::zero() {
|
||||||
@@ -491,12 +518,14 @@ impl<N: SimdRealCopy> JointConstraintHelper<N> {
|
|||||||
|
|
||||||
pub fn lock_linear<const LANES: usize>(
|
pub fn lock_linear<const LANES: usize>(
|
||||||
&self,
|
&self,
|
||||||
params: &IntegrationParameters,
|
_params: &IntegrationParameters,
|
||||||
joint_id: [JointIndex; LANES],
|
joint_id: [JointIndex; LANES],
|
||||||
body1: &JointSolverBody<N, LANES>,
|
body1: &JointSolverBody<N, LANES>,
|
||||||
body2: &JointSolverBody<N, LANES>,
|
body2: &JointSolverBody<N, LANES>,
|
||||||
locked_axis: usize,
|
locked_axis: usize,
|
||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
|
erp_inv_dt: N,
|
||||||
|
cfm_coeff: N,
|
||||||
) -> JointConstraint<N, LANES> {
|
) -> JointConstraint<N, LANES> {
|
||||||
let lin_jac = self.basis.column(locked_axis).into_owned();
|
let lin_jac = self.basis.column(locked_axis).into_owned();
|
||||||
#[cfg(feature = "dim2")]
|
#[cfg(feature = "dim2")]
|
||||||
@@ -509,8 +538,6 @@ impl<N: SimdRealCopy> JointConstraintHelper<N> {
|
|||||||
let ang_jac2 = self.cmat2_basis.column(locked_axis).into_owned();
|
let ang_jac2 = self.cmat2_basis.column(locked_axis).into_owned();
|
||||||
|
|
||||||
let rhs_wo_bias = N::zero();
|
let rhs_wo_bias = N::zero();
|
||||||
let erp_inv_dt = N::splat(params.joint_erp_inv_dt());
|
|
||||||
let cfm_coeff = N::splat(params.joint_cfm_coeff());
|
|
||||||
let rhs_bias = lin_jac.dot(&self.lin_err) * erp_inv_dt;
|
let rhs_bias = lin_jac.dot(&self.lin_err) * erp_inv_dt;
|
||||||
|
|
||||||
let ii_ang_jac1 = body1.ii * ang_jac1;
|
let ii_ang_jac1 = body1.ii * ang_jac1;
|
||||||
@@ -540,13 +567,15 @@ impl<N: SimdRealCopy> JointConstraintHelper<N> {
|
|||||||
|
|
||||||
pub fn limit_angular<const LANES: usize>(
|
pub fn limit_angular<const LANES: usize>(
|
||||||
&self,
|
&self,
|
||||||
params: &IntegrationParameters,
|
_params: &IntegrationParameters,
|
||||||
joint_id: [JointIndex; LANES],
|
joint_id: [JointIndex; LANES],
|
||||||
body1: &JointSolverBody<N, LANES>,
|
body1: &JointSolverBody<N, LANES>,
|
||||||
body2: &JointSolverBody<N, LANES>,
|
body2: &JointSolverBody<N, LANES>,
|
||||||
_limited_axis: usize,
|
_limited_axis: usize,
|
||||||
limits: [N; 2],
|
limits: [N; 2],
|
||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
|
erp_inv_dt: N,
|
||||||
|
cfm_coeff: N,
|
||||||
) -> JointConstraint<N, LANES> {
|
) -> JointConstraint<N, LANES> {
|
||||||
let zero = N::zero();
|
let zero = N::zero();
|
||||||
let half = N::splat(0.5);
|
let half = N::splat(0.5);
|
||||||
@@ -568,8 +597,6 @@ impl<N: SimdRealCopy> JointConstraintHelper<N> {
|
|||||||
#[cfg(feature = "dim3")]
|
#[cfg(feature = "dim3")]
|
||||||
let ang_jac = self.ang_basis.column(_limited_axis).into_owned();
|
let ang_jac = self.ang_basis.column(_limited_axis).into_owned();
|
||||||
let rhs_wo_bias = N::zero();
|
let rhs_wo_bias = N::zero();
|
||||||
let erp_inv_dt = N::splat(params.joint_erp_inv_dt());
|
|
||||||
let cfm_coeff = N::splat(params.joint_cfm_coeff());
|
|
||||||
let rhs_bias = ((s_ang - s_limits[1]).simd_max(zero)
|
let rhs_bias = ((s_ang - s_limits[1]).simd_max(zero)
|
||||||
- (s_limits[0] - s_ang).simd_max(zero))
|
- (s_limits[0] - s_ang).simd_max(zero))
|
||||||
* erp_inv_dt;
|
* erp_inv_dt;
|
||||||
@@ -663,12 +690,14 @@ impl<N: SimdRealCopy> JointConstraintHelper<N> {
|
|||||||
|
|
||||||
pub fn lock_angular<const LANES: usize>(
|
pub fn lock_angular<const LANES: usize>(
|
||||||
&self,
|
&self,
|
||||||
params: &IntegrationParameters,
|
_params: &IntegrationParameters,
|
||||||
joint_id: [JointIndex; LANES],
|
joint_id: [JointIndex; LANES],
|
||||||
body1: &JointSolverBody<N, LANES>,
|
body1: &JointSolverBody<N, LANES>,
|
||||||
body2: &JointSolverBody<N, LANES>,
|
body2: &JointSolverBody<N, LANES>,
|
||||||
_locked_axis: usize,
|
_locked_axis: usize,
|
||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
|
erp_inv_dt: N,
|
||||||
|
cfm_coeff: N,
|
||||||
) -> JointConstraint<N, LANES> {
|
) -> JointConstraint<N, LANES> {
|
||||||
#[cfg(feature = "dim2")]
|
#[cfg(feature = "dim2")]
|
||||||
let ang_jac = N::one();
|
let ang_jac = N::one();
|
||||||
@@ -676,8 +705,6 @@ impl<N: SimdRealCopy> JointConstraintHelper<N> {
|
|||||||
let ang_jac = self.ang_basis.column(_locked_axis).into_owned();
|
let ang_jac = self.ang_basis.column(_locked_axis).into_owned();
|
||||||
|
|
||||||
let rhs_wo_bias = N::zero();
|
let rhs_wo_bias = N::zero();
|
||||||
let erp_inv_dt = N::splat(params.joint_erp_inv_dt());
|
|
||||||
let cfm_coeff = N::splat(params.joint_cfm_coeff());
|
|
||||||
#[cfg(feature = "dim2")]
|
#[cfg(feature = "dim2")]
|
||||||
let rhs_bias = self.ang_err.im * erp_inv_dt;
|
let rhs_bias = self.ang_err.im * erp_inv_dt;
|
||||||
#[cfg(feature = "dim3")]
|
#[cfg(feature = "dim3")]
|
||||||
@@ -760,13 +787,15 @@ impl JointConstraintHelper<Real> {
|
|||||||
#[cfg(feature = "dim3")]
|
#[cfg(feature = "dim3")]
|
||||||
pub fn limit_angular_coupled(
|
pub fn limit_angular_coupled(
|
||||||
&self,
|
&self,
|
||||||
params: &IntegrationParameters,
|
_params: &IntegrationParameters,
|
||||||
joint_id: [JointIndex; 1],
|
joint_id: [JointIndex; 1],
|
||||||
body1: &JointSolverBody<Real, 1>,
|
body1: &JointSolverBody<Real, 1>,
|
||||||
body2: &JointSolverBody<Real, 1>,
|
body2: &JointSolverBody<Real, 1>,
|
||||||
coupled_axes: u8,
|
coupled_axes: u8,
|
||||||
limits: [Real; 2],
|
limits: [Real; 2],
|
||||||
writeback_id: WritebackId,
|
writeback_id: WritebackId,
|
||||||
|
erp_inv_dt: Real,
|
||||||
|
cfm_coeff: Real,
|
||||||
) -> JointConstraint<Real, 1> {
|
) -> JointConstraint<Real, 1> {
|
||||||
// NOTE: right now, this only supports exactly 2 coupled axes.
|
// NOTE: right now, this only supports exactly 2 coupled axes.
|
||||||
let ang_coupled_axes = coupled_axes >> DIM;
|
let ang_coupled_axes = coupled_axes >> DIM;
|
||||||
@@ -791,8 +820,6 @@ impl JointConstraintHelper<Real> {
|
|||||||
|
|
||||||
let rhs_wo_bias = 0.0;
|
let rhs_wo_bias = 0.0;
|
||||||
|
|
||||||
let erp_inv_dt = params.joint_erp_inv_dt();
|
|
||||||
let cfm_coeff = params.joint_cfm_coeff();
|
|
||||||
let rhs_bias = ((angle - limits[1]).max(0.0) - (limits[0] - angle).max(0.0)) * erp_inv_dt;
|
let rhs_bias = ((angle - limits[1]).max(0.0) - (limits[0] - angle).max(0.0)) * erp_inv_dt;
|
||||||
|
|
||||||
let ii_ang_jac1 = body1.ii * ang_jac;
|
let ii_ang_jac1 = body1.ii * ang_jac;
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ use crate::dynamics::solver::solver_body::SolverBodies;
|
|||||||
use crate::math::{SIMD_WIDTH, SimdReal};
|
use crate::math::{SIMD_WIDTH, SimdReal};
|
||||||
#[cfg(feature = "dim2")]
|
#[cfg(feature = "dim2")]
|
||||||
use crate::num::Zero;
|
use crate::num::Zero;
|
||||||
|
#[cfg(feature = "simd-is-enabled")]
|
||||||
|
use na::SimdValue;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub struct MotorParameters<N: SimdRealCopy> {
|
pub struct MotorParameters<N: SimdRealCopy> {
|
||||||
@@ -142,6 +144,10 @@ impl JointConstraint<Real, 1> {
|
|||||||
let limit_axes = joint.limit_axes.bits() & !locked_axes;
|
let limit_axes = joint.limit_axes.bits() & !locked_axes;
|
||||||
let coupled_axes = joint.coupled_axes.bits();
|
let coupled_axes = joint.coupled_axes.bits();
|
||||||
|
|
||||||
|
// Compute per-joint ERP and CFM coefficients
|
||||||
|
let erp_inv_dt = joint.softness.erp_inv_dt(params.dt);
|
||||||
|
let cfm_coeff = joint.softness.cfm_coeff(params.dt);
|
||||||
|
|
||||||
// The has_lin/ang_coupling test is needed to avoid shl overflow later.
|
// The has_lin/ang_coupling test is needed to avoid shl overflow later.
|
||||||
let has_lin_coupling = (coupled_axes & JointAxesMask::LIN_AXES.bits()) != 0;
|
let has_lin_coupling = (coupled_axes & JointAxesMask::LIN_AXES.bits()) != 0;
|
||||||
let first_coupled_lin_axis_id =
|
let first_coupled_lin_axis_id =
|
||||||
@@ -236,14 +242,24 @@ impl JointConstraint<Real, 1> {
|
|||||||
body2,
|
body2,
|
||||||
i - DIM,
|
i - DIM,
|
||||||
WritebackId::Dof(i),
|
WritebackId::Dof(i),
|
||||||
|
erp_inv_dt,
|
||||||
|
cfm_coeff,
|
||||||
);
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i in 0..DIM {
|
for i in 0..DIM {
|
||||||
if locked_axes & (1 << i) != 0 {
|
if locked_axes & (1 << i) != 0 {
|
||||||
out[len] =
|
out[len] = builder.lock_linear(
|
||||||
builder.lock_linear(params, [joint_id], body1, body2, i, WritebackId::Dof(i));
|
params,
|
||||||
|
[joint_id],
|
||||||
|
body1,
|
||||||
|
body2,
|
||||||
|
i,
|
||||||
|
WritebackId::Dof(i),
|
||||||
|
erp_inv_dt,
|
||||||
|
cfm_coeff,
|
||||||
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,6 +274,8 @@ impl JointConstraint<Real, 1> {
|
|||||||
i - DIM,
|
i - DIM,
|
||||||
[joint.limits[i].min, joint.limits[i].max],
|
[joint.limits[i].min, joint.limits[i].max],
|
||||||
WritebackId::Limit(i),
|
WritebackId::Limit(i),
|
||||||
|
erp_inv_dt,
|
||||||
|
cfm_coeff,
|
||||||
);
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
@@ -272,6 +290,8 @@ impl JointConstraint<Real, 1> {
|
|||||||
i,
|
i,
|
||||||
[joint.limits[i].min, joint.limits[i].max],
|
[joint.limits[i].min, joint.limits[i].max],
|
||||||
WritebackId::Limit(i),
|
WritebackId::Limit(i),
|
||||||
|
erp_inv_dt,
|
||||||
|
cfm_coeff,
|
||||||
);
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
@@ -290,6 +310,8 @@ impl JointConstraint<Real, 1> {
|
|||||||
joint.limits[first_coupled_ang_axis_id].max,
|
joint.limits[first_coupled_ang_axis_id].max,
|
||||||
],
|
],
|
||||||
WritebackId::Limit(first_coupled_ang_axis_id),
|
WritebackId::Limit(first_coupled_ang_axis_id),
|
||||||
|
erp_inv_dt,
|
||||||
|
cfm_coeff,
|
||||||
);
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
@@ -306,6 +328,8 @@ impl JointConstraint<Real, 1> {
|
|||||||
joint.limits[first_coupled_lin_axis_id].max,
|
joint.limits[first_coupled_lin_axis_id].max,
|
||||||
],
|
],
|
||||||
WritebackId::Limit(first_coupled_lin_axis_id),
|
WritebackId::Limit(first_coupled_lin_axis_id),
|
||||||
|
erp_inv_dt,
|
||||||
|
cfm_coeff,
|
||||||
);
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
@@ -344,8 +368,13 @@ impl JointConstraint<SimdReal, SIMD_WIDTH> {
|
|||||||
frame1: &Isometry<SimdReal>,
|
frame1: &Isometry<SimdReal>,
|
||||||
frame2: &Isometry<SimdReal>,
|
frame2: &Isometry<SimdReal>,
|
||||||
locked_axes: u8,
|
locked_axes: u8,
|
||||||
|
softness: crate::dynamics::SpringCoefficients<SimdReal>,
|
||||||
out: &mut [Self],
|
out: &mut [Self],
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
let dt = SimdReal::splat(params.dt);
|
||||||
|
let erp_inv_dt = softness.erp_inv_dt(dt);
|
||||||
|
let cfm_coeff = softness.cfm_coeff(dt);
|
||||||
|
|
||||||
let builder = JointConstraintHelper::new(
|
let builder = JointConstraintHelper::new(
|
||||||
frame1,
|
frame1,
|
||||||
frame2,
|
frame2,
|
||||||
@@ -357,8 +386,16 @@ impl JointConstraint<SimdReal, SIMD_WIDTH> {
|
|||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
for i in 0..DIM {
|
for i in 0..DIM {
|
||||||
if locked_axes & (1 << i) != 0 {
|
if locked_axes & (1 << i) != 0 {
|
||||||
out[len] =
|
out[len] = builder.lock_linear(
|
||||||
builder.lock_linear(params, joint_id, body1, body2, i, WritebackId::Dof(i));
|
params,
|
||||||
|
joint_id,
|
||||||
|
body1,
|
||||||
|
body2,
|
||||||
|
i,
|
||||||
|
WritebackId::Dof(i),
|
||||||
|
erp_inv_dt,
|
||||||
|
cfm_coeff,
|
||||||
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,6 +409,8 @@ impl JointConstraint<SimdReal, SIMD_WIDTH> {
|
|||||||
body2,
|
body2,
|
||||||
i - DIM,
|
i - DIM,
|
||||||
WritebackId::Dof(i),
|
WritebackId::Dof(i),
|
||||||
|
erp_inv_dt,
|
||||||
|
cfm_coeff,
|
||||||
);
|
);
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,33 +189,24 @@ pub(crate) fn update_ui(
|
|||||||
|
|
||||||
let mut substep_params = *integration_parameters;
|
let mut substep_params = *integration_parameters;
|
||||||
substep_params.dt /= substep_params.num_solver_iterations as Real;
|
substep_params.dt /= substep_params.num_solver_iterations as Real;
|
||||||
let curr_erp = substep_params.contact_erp();
|
let curr_erp = substep_params.contact_softness.erp(substep_params.dt);
|
||||||
let curr_cfm_factor = substep_params.contact_cfm_factor();
|
let curr_cfm_factor = substep_params
|
||||||
|
.contact_softness
|
||||||
|
.cfm_factor(substep_params.dt);
|
||||||
ui.add(
|
ui.add(
|
||||||
Slider::new(
|
Slider::new(
|
||||||
&mut integration_parameters.contact_natural_frequency,
|
&mut integration_parameters.contact_softness.natural_frequency,
|
||||||
0.01..=120.0,
|
0.01..=120.0,
|
||||||
)
|
)
|
||||||
.text(format!("contacts Hz (erp = {curr_erp:.3})")),
|
.text(format!("contacts Hz (erp = {curr_erp:.3})")),
|
||||||
);
|
);
|
||||||
ui.add(
|
ui.add(
|
||||||
Slider::new(
|
Slider::new(
|
||||||
&mut integration_parameters.contact_damping_ratio,
|
&mut integration_parameters.contact_softness.damping_ratio,
|
||||||
0.01..=20.0,
|
0.01..=20.0,
|
||||||
)
|
)
|
||||||
.text(format!("damping ratio (cfm-factor = {curr_cfm_factor:.3})",)),
|
.text(format!("damping ratio (cfm-factor = {curr_cfm_factor:.3})",)),
|
||||||
);
|
);
|
||||||
ui.add(
|
|
||||||
Slider::new(
|
|
||||||
&mut integration_parameters.joint_natural_frequency,
|
|
||||||
0.0..=1200000.0,
|
|
||||||
)
|
|
||||||
.text("joint erp"),
|
|
||||||
);
|
|
||||||
ui.add(
|
|
||||||
Slider::new(&mut integration_parameters.joint_damping_ratio, 0.0..=20.0)
|
|
||||||
.text("joint damping ratio"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
|
|||||||
Reference in New Issue
Block a user