1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
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, // /// If `true` and if rapier is compiled with the `parallel` feature, this will enable rayon-based multithreading (default: `true`). // /// // /// This parameter is ignored if rapier is not compiled with is `parallel` feature. // /// Refer to rayon's documentation regarding how to configure the number of threads with either // /// `rayon::ThreadPoolBuilder::new().num_threads(4).build_global().unwrap()` or `ThreadPool::install`. // /// Note that using only one thread with `multithreading_enabled` set to `true` will result on a slower // /// simulation than setting `multithreading_enabled` to `false`. // pub multithreading_enabled: bool, /// If `true`, the world's `step` method will stop right after resolving exactly one CCD event (default: `false`). /// This allows the user to take action during a timestep, in-between two CCD events. pub return_after_ccd_substep: bool, /// The Error Reduction Parameter in `[0, 1]` is the proportion of /// the positional error to be corrected at each time step (default: `0.2`). pub erp: Real, /// The Error Reduction Parameter for joints in `[0, 1]` is the proportion of /// the positional error to be corrected at each time step (default: `0.2`). pub joint_erp: Real, /// Each cached impulse are multiplied by this coefficient in `[0, 1]` /// when they are re-used to initialize the solver (default `1.0`). pub warmstart_coeff: Real, /// Amount of penetration the engine wont attempt to correct (default: `0.005m`). pub allowed_linear_error: Real, /// The maximal distance separating two objects that will generate predictive contacts (default: `0.002`). pub prediction_distance: Real, /// Amount of angular drift of joint limits the engine wont /// attempt to correct (default: `0.001rad`). pub allowed_angular_error: Real, /// Maximum linear correction during one step of the non-linear position solver (default: `0.2`). pub max_linear_correction: Real, /// Maximum angular correction during one step of the non-linear position solver (default: `0.2`). pub max_angular_correction: Real, /// Maximum nonlinear SOR-prox scaling parameter when the constraint /// correction direction is close to the kernel of the involved multibody's /// jacobian (default: `0.2`). pub max_stabilization_multiplier: Real, /// Maximum number of iterations performed by the velocity constraints solver (default: `4`). pub max_velocity_iterations: usize, /// Maximum number of iterations performed by the position-based constraints solver (default: `1`). pub max_position_iterations: usize, /// Minimum number of dynamic bodies in each active island (default: `128`). pub min_island_size: usize, /// Maximum number of iterations performed by the position-based constraints solver for CCD steps (default: `10`). /// /// This should be sufficiently high so all penetration get resolved. For example, if CCD cause your /// objects to stutter, that may be because the number of CCD position iterations is too low, causing /// them to remain stuck in a penetration configuration for a few frames. /// /// The higher this number, the higher its computational cost. pub max_ccd_position_iterations: usize, /// Maximum number of substeps performed by the solver (default: `1`). pub max_ccd_substeps: usize, /// Controls the number of Proximity::Intersecting events generated by a trigger during CCD resolution (default: `false`). /// /// If false, triggers will only generate one Proximity::Intersecting event during a step, even /// if another colliders repeatedly enters and leaves the triggers during multiple CCD substeps. /// /// If true, triggers will generate as many Proximity::Intersecting and Proximity::Disjoint/Proximity::WithinMargin /// events as the number of times a collider repeatedly enters and leaves the triggers during multiple CCD substeps. /// This is more computationally intensive. pub multiple_ccd_substep_sensor_events_enabled: bool, /// Whether penetration are taken into account in CCD resolution (default: `false`). /// /// If this is set to `false` two penetrating colliders will not be considered to have any time of impact /// while they are penetrating. This may end up allowing some tunelling, but will avoid stuttering effect /// when the constraints solver fails to completely separate two colliders after a CCD contact. /// /// If this is set to `true`, two penetrating colliders will be considered to have a time of impact /// equal to 0 until the constraints solver manages to separate them. This will prevent tunnelling /// almost completely, but may introduce stuttering effects when the constraints solver fails to completely /// separate two colliders after a CCD contact. // FIXME: this is a very binary way of handling penetration. // We should provide a more flexible solution by letting the user choose some // minimal amount of movement applied to an object that get stuck. pub ccd_on_penetration_enabled: bool, } impl IntegrationParameters { /// Creates a set of integration parameters with the given values. #[deprecated = "Use `IntegrationParameters { dt: 60.0, ..Default::default() }` instead"] pub fn new( dt: Real, // multithreading_enabled: bool, erp: Real, joint_erp: Real, warmstart_coeff: Real, _restitution_velocity_threshold: Real, allowed_linear_error: Real, allowed_angular_error: Real, max_linear_correction: Real, max_angular_correction: Real, prediction_distance: Real, max_stabilization_multiplier: Real, max_velocity_iterations: usize, max_position_iterations: usize, max_ccd_position_iterations: usize, max_ccd_substeps: usize, return_after_ccd_substep: bool, multiple_ccd_substep_sensor_events_enabled: bool, ccd_on_penetration_enabled: bool, ) -> Self { IntegrationParameters { dt, // multithreading_enabled, erp, joint_erp, warmstart_coeff, allowed_linear_error, allowed_angular_error, max_linear_correction, max_angular_correction, prediction_distance, max_stabilization_multiplier, max_velocity_iterations, max_position_iterations, // 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_position_iterations, max_ccd_substeps, return_after_ccd_substep, multiple_ccd_substep_sensor_events_enabled, ccd_on_penetration_enabled, } } /// The current time-stepping length. #[inline(always)] #[deprecated = "You can just read the `IntegrationParams::dt` value directly"] pub fn dt(&self) -> Real { self.dt } /// 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 } } } impl Default for IntegrationParameters { fn default() -> Self { Self { dt: 1.0 / 60.0, // multithreading_enabled: true, return_after_ccd_substep: false, erp: 0.2, joint_erp: 0.2, warmstart_coeff: 1.0, allowed_linear_error: 0.005, prediction_distance: 0.002, allowed_angular_error: 0.001, max_linear_correction: 0.2, max_angular_correction: 0.2, max_stabilization_multiplier: 0.2, max_velocity_iterations: 4, max_position_iterations: 1, // 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_position_iterations: 10, max_ccd_substeps: 1, multiple_ccd_substep_sensor_events_enabled: false, ccd_on_penetration_enabled: false, } } }