use crate::math::Real; /// Parameters for a time-step of the physics engine. #[derive(Copy, Clone)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct IntegrationParameters { /// The timestep length (default: `1.0 / 60.0`) pub dt: Real, /// Minimum timestep size when using CCD with multiple substeps (default `1.0 / 60.0 / 100.0`) /// /// When CCD with multiple substeps is enabled, the timestep is subdivided /// into smaller pieces. This timestep subdivision won't generate timestep /// lengths smaller than `min_ccd_dt`. /// /// Setting this to a large value will reduce the opportunity to performing /// CCD substepping, resulting in potentially more time dropped by the /// motion-clamping mechanism. Setting this to an very small value may lead /// to numerical instabilities. pub min_ccd_dt: Real, /// 0-1: how much of the velocity to dampen out in the constraint solver? /// (default `1.0`). pub velocity_solve_fraction: Real, /// 0-1: multiplier for how much of the constraint violation (e.g. contact penetration) /// will be compensated for during the velocity solve. /// If zero, you need to enable the positional solver. /// If non-zero, you do not need the positional solver. /// A good non-zero value is around `0.2`. /// (default `0.0`). pub erp: Real, /// Amount of penetration the engine wont attempt to correct (default: `0.001m`). pub allowed_linear_error: Real, /// The maximal distance separating two objects that will generate predictive contacts (default: `0.002`). pub prediction_distance: Real, /// Maximum number of iterations performed to solve non-penetration and joint constraints (default: `4`). pub max_velocity_iterations: usize, /// Maximum number of iterations performed to solve friction constraints (default: `8`). pub max_velocity_friction_iterations: usize, /// Maximum number of iterations performed to remove the energy introduced by penetration corrections (default: `1`). pub max_stabilization_iterations: usize, /// If `false`, friction and non-penetration constraints will be solved in the same loop. Otherwise, /// non-penetration constraints are solved first, and friction constraints are solved after (default: `true`). pub interleave_restitution_and_friction_resolution: bool, /// Minimum number of dynamic bodies in 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, } 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)] pub fn inv_dt(&self) -> Real { if self.dt == 0.0 { 0.0 } else { 1.0 / self.dt } } /// Sets the time-stepping length. #[inline] #[deprecated = "You can just set the `IntegrationParams::dt` value directly"] pub fn set_dt(&mut self, dt: Real) { assert!(dt >= 0.0, "The time-stepping length cannot be negative."); self.dt = dt; } /// Sets the inverse time-stepping length (i.e. the frequency). /// /// This automatically recompute `self.dt`. #[inline] pub fn set_inv_dt(&mut self, inv_dt: Real) { if inv_dt == 0.0 { self.dt = 0.0 } else { self.dt = 1.0 / inv_dt } } /// Convenience: `erp / dt` #[inline] pub(crate) fn erp_inv_dt(&self) -> Real { self.erp * self.inv_dt() } } impl Default for IntegrationParameters { fn default() -> Self { Self { dt: 1.0 / 60.0, min_ccd_dt: 1.0 / 60.0 / 100.0, velocity_solve_fraction: 1.0, erp: 0.8, allowed_linear_error: 0.001, // 0.005 prediction_distance: 0.002, max_velocity_iterations: 4, max_velocity_friction_iterations: 8, max_stabilization_iterations: 1, interleave_restitution_and_friction_resolution: true, // Enabling this makes a big difference for 2D stability. // FIXME: 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. // However we don't want it to be too small and end up with // tons of islands, reducing SIMD parallelism opportunities. min_island_size: 128, max_ccd_substeps: 1, } } }