feat: solver improvements + release v0.29.0 (#876)

* feat: solver improvements

* feat: add function to get/set whether gyroscopic forces are enabled on a rigid-body

* chore: switch to released versions of parry and wide instead of local patches

* fix cargo doc

* chore: typo fixes

* chore: clippy fix

* Release v0.29.0

* chore: more clippy fixes
This commit is contained in:
Sébastien Crozet
2025-09-05 19:31:58 +02:00
committed by GitHub
parent 317322b31b
commit 134f433903
94 changed files with 5066 additions and 8136 deletions

View File

@@ -1,6 +1,5 @@
use crate::math::Real;
use na::RealField;
use std::num::NonZeroUsize;
#[cfg(doc)]
use super::RigidBodyActivation;
@@ -9,6 +8,28 @@ use super::RigidBodyActivation;
// the 3D domino demo. So for now we dont enable it in 3D.
pub(crate) static BLOCK_SOLVER_ENABLED: bool = cfg!(feature = "dim2");
/// Friction models used for all contact constraints between two rigid-bodies.
///
/// This selection does not apply to multibodies that always rely on the [`FrictionModel::Coulomb`].
#[cfg(feature = "dim3")]
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub enum FrictionModel {
/// A simplified friction model significantly faster to solve than [`Self::Coulomb`]
/// but less accurate.
///
/// Instead of solving one Coulomb friction constraint per contact in a contact manifold,
/// this approximation only solves one Coulomb friction constraint per group of 4 contacts
/// in a contact manifold, plus one "twist" constraint. The "twist" constraint is purely
/// rotational and aims to eliminate angular movement in the manifolds tangent plane.
#[default]
Simplified,
/// The coulomb friction model.
///
/// This results in one Coulomb friction constraint per contact point.
Coulomb,
}
/// Parameters for a time-step of the physics engine.
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
@@ -92,24 +113,25 @@ pub struct IntegrationParameters {
/// This value is implicitly scaled by [`IntegrationParameters::length_unit`].
pub normalized_prediction_distance: Real,
/// The number of solver iterations run by the constraints solver for calculating forces (default: `4`).
pub num_solver_iterations: NonZeroUsize,
/// Number of addition friction resolution iteration run during the last solver sub-step (default: `0`).
pub num_additional_friction_iterations: usize,
pub num_solver_iterations: usize,
/// Number of internal Project Gauss Seidel (PGS) iterations run at each solver iteration (default: `1`).
pub num_internal_pgs_iterations: usize,
/// The number of stabilization iterations run at each solver iterations (default: `2`).
/// The number of stabilization iterations run at each solver iterations (default: `1`).
pub num_internal_stabilization_iterations: usize,
/// Minimum number of dynamic bodies in each active island (default: `128`).
/// Minimum number of dynamic bodies on each active island (default: `128`).
pub min_island_size: usize,
/// Maximum number of substeps performed by the solver (default: `1`).
pub max_ccd_substeps: usize,
/// The type of friction constraints used in the simulation.
#[cfg(feature = "dim3")]
pub friction_model: FrictionModel,
}
impl IntegrationParameters {
/// The inverse of the time-stepping length, i.e. the steps per seconds (Hz).
///
/// This is zero if `self.dt` is zero.
#[inline(always)]
#[inline]
pub fn inv_dt(&self) -> Real {
if self.dt == 0.0 { 0.0 } else { 1.0 / self.dt }
}
@@ -260,12 +282,10 @@ impl IntegrationParameters {
pub fn prediction_distance(&self) -> Real {
self.normalized_prediction_distance * self.length_unit
}
}
/// Initialize the simulation parameters with settings matching the TGS-soft solver
/// with warmstarting.
///
/// This is the default configuration, equivalent to [`IntegrationParameters::default()`].
pub fn tgs_soft() -> Self {
impl Default for IntegrationParameters {
fn default() -> Self {
Self {
dt: 1.0 / 60.0,
min_ccd_dt: 1.0 / 60.0 / 100.0,
@@ -275,9 +295,8 @@ impl IntegrationParameters {
joint_damping_ratio: 1.0,
warmstart_coefficient: 1.0,
num_internal_pgs_iterations: 1,
num_internal_stabilization_iterations: 2,
num_additional_friction_iterations: 0,
num_solver_iterations: NonZeroUsize::new(4).unwrap(),
num_internal_stabilization_iterations: 1,
num_solver_iterations: 4,
// TODO: what is the optimal value for min_island_size?
// It should not be too big so that we don't end up with
// huge islands that don't fit in cache.
@@ -289,37 +308,8 @@ impl IntegrationParameters {
normalized_prediction_distance: 0.002,
max_ccd_substeps: 1,
length_unit: 1.0,
}
}
/// Initialize the simulation parameters with settings matching the TGS-soft solver
/// **without** warmstarting.
///
/// The [`IntegrationParameters::tgs_soft()`] configuration should be preferred unless
/// warmstarting proves to be undesirable for your use-case.
pub fn tgs_soft_without_warmstart() -> Self {
Self {
contact_damping_ratio: 0.25,
warmstart_coefficient: 0.0,
num_additional_friction_iterations: 4,
..Self::tgs_soft()
}
}
/// Initializes the integration parameters to match the legacy PGS solver from Rapier version <= 0.17.
///
/// This exists mainly for testing and comparison purpose.
pub fn pgs_legacy() -> Self {
Self {
num_solver_iterations: NonZeroUsize::new(1).unwrap(),
num_internal_pgs_iterations: 4,
..Self::tgs_soft_without_warmstart()
#[cfg(feature = "dim3")]
friction_model: FrictionModel::default(),
}
}
}
impl Default for IntegrationParameters {
fn default() -> Self {
Self::tgs_soft()
}
}