nphysics2d/solver/integration_parameters.rs
1use na::{self, RealField};
2
3/// Parameters for a time-step of the physics engine.
4#[derive(Clone)]
5pub struct IntegrationParameters<N: RealField + Copy> {
6 /// The timestep (default: `1.0 / 60.0`)
7 dt: N,
8 /// The inverse of `dt`.
9 inv_dt: N,
10 /// If `true`, the world's `step` method will stop right after resolving exactly one CCD event (default: `false`).
11 /// This allows the user to take action during a timestep, in-between two CCD events.
12 pub return_after_ccd_substep: bool,
13 /// The total elapsed time in the physics world.
14 ///
15 /// This is the accumulation of the `dt` of all the calls to `world.step()`.
16 pub t: N,
17 /// The Error Reduction Parameter in `[0, 1]` is the proportion of
18 /// the positional error to be corrected at each time step (default: `0.2`).
19 pub erp: N,
20 /// Each cached impulse are multiplied by this coefficient in `[0, 1]`
21 /// when they are re-used to initialize the solver (default `1.0`).
22 pub warmstart_coeff: N,
23 /// Contacts at points where the involved bodies have a relative
24 /// velocity smaller than this threshold wont be affected by the restitution force (default: `1.0`).
25 pub restitution_velocity_threshold: N,
26 /// Ammount of penetration the engine wont attempt to correct (default: `0.001m`).
27 pub allowed_linear_error: N,
28 /// Ammount of angular drift of joint limits the engine wont
29 /// attempt to correct (default: `0.001rad`).
30 pub allowed_angular_error: N,
31 /// Maximum linear correction during one step of the non-linear position solver (default: `0.2`).
32 pub max_linear_correction: N,
33 /// Maximum angular correction during one step of the non-linear position solver (default: `0.2`).
34 pub max_angular_correction: N,
35 /// Maximum nonlinear SOR-prox scaling parameter when the constraint
36 /// correction direction is close to the kernel of the involved multibody's
37 /// jacobian (default: `0.2`).
38 pub max_stabilization_multiplier: N,
39 /// Maximum number of iterations performed by the velocity constraints solver (default: `8`).
40 pub max_velocity_iterations: usize,
41 /// Maximum number of iterations performed by the position-based constraints solver (default: `3`).
42 pub max_position_iterations: usize,
43 /// Maximum number of iterations performed by the position-based constraints solver for CCD steps (default: `10`).
44 ///
45 /// This should be sufficiently high so all penetration get resolved. For example, if CCD cause your
46 /// objects to stutter, that may be because the number of CCD position iterations is too low, causing
47 /// them to remain stuck in a penetration configuration for a few frames.
48 ///
49 /// The highest this number, the highest its computational cost.
50 pub max_ccd_position_iterations: usize,
51 /// Maximum number of substeps performed by the solver (default: `1`).
52 pub max_ccd_substeps: usize,
53 /// Controls the number of Proximity::Intersecting events generated by a trigger during CCD resolution (default: `false`).
54 ///
55 /// If false, triggers will only generate one Proximity::Intersecting event during a step, even
56 /// if another colliders repeatedly enters and leaves the triggers during multiple CCD substeps.
57 ///
58 /// If true, triggers will generate as many Proximity::Intersecting and Proximity::Disjoint/Proximity::WithinMargin
59 /// events as the number of times a collider repeatedly enters and leaves the triggers during multiple CCD substeps.
60 /// This is more computationally intensive.
61 pub multiple_ccd_substep_sensor_events_enabled: bool,
62 /// Whether penetration are taken into account in CCD resolution (default: `false`).
63 ///
64 /// If this is set to `false` two penetrating colliders will not be considered to have any time of impact
65 /// while they are penetrating. This may end up allowing some tunelling, but will avoid stuttering effect
66 /// when the constraints solver fails to completely separate two colliders after a CCD contact.
67 ///
68 /// If this is set to `true`, two penetrating colliders will be considered to have a time of impact
69 /// equal to 0 until the constraints solver manages to separate them. This will prevent tunnelling
70 /// almost completely, but may introduce stuttering effects when the constraints solver fails to completely
71 /// seperate two colliders after a CCD contact.
72 // FIXME: this is a very binary way of handling penetration.
73 // We should provide a more flexible solution by letting the user choose some
74 // minimal amount of movement applied to an object that get stuck.
75 pub ccd_on_penetration_enabled: bool,
76}
77
78impl<N: RealField + Copy> IntegrationParameters<N> {
79 /// Creates a set of integration parameters with the given values.
80 pub fn new(
81 dt: N,
82 erp: N,
83 warmstart_coeff: N,
84 restitution_velocity_threshold: N,
85 allowed_linear_error: N,
86 allowed_angular_error: N,
87 max_linear_correction: N,
88 max_angular_correction: N,
89 max_stabilization_multiplier: N,
90 max_velocity_iterations: usize,
91 max_position_iterations: usize,
92 max_ccd_position_iterations: usize,
93 max_ccd_substeps: usize,
94 return_after_ccd_substep: bool,
95 multiple_ccd_substep_sensor_events_enabled: bool,
96 ccd_on_penetration_enabled: bool,
97 ) -> Self {
98 IntegrationParameters {
99 t: N::zero(),
100 dt,
101 inv_dt: if dt == N::zero() {
102 N::zero()
103 } else {
104 N::one() / dt
105 },
106 erp,
107 warmstart_coeff,
108 restitution_velocity_threshold,
109 allowed_linear_error,
110 allowed_angular_error,
111 max_linear_correction,
112 max_angular_correction,
113 max_stabilization_multiplier,
114 max_velocity_iterations,
115 max_position_iterations,
116 max_ccd_position_iterations,
117 max_ccd_substeps,
118 return_after_ccd_substep,
119 multiple_ccd_substep_sensor_events_enabled,
120 ccd_on_penetration_enabled,
121 }
122 }
123
124 /// The current time-stepping length.
125 #[inline(always)]
126 pub fn dt(&self) -> N {
127 self.dt
128 }
129
130 /// The inverse of the time-stepping length.
131 ///
132 /// This is zero if `self.dt` is zero.
133 #[inline(always)]
134 pub fn inv_dt(&self) -> N {
135 self.inv_dt
136 }
137
138 /// Sets the time-stepping length.
139 ///
140 /// This automatically recompute `self.inv_dt`.
141 #[inline]
142 pub fn set_dt(&mut self, dt: N) {
143 assert!(
144 dt >= N::zero(),
145 "The time-stepping length cannot be negative."
146 );
147 self.dt = dt;
148 if dt == N::zero() {
149 self.inv_dt = N::zero()
150 } else {
151 self.inv_dt = N::one() / dt
152 }
153 }
154
155 /// Sets the inverse time-stepping length (i.e. the frequency).
156 ///
157 /// This automatically recompute `self.dt`.
158 #[inline]
159 pub fn set_inv_dt(&mut self, inv_dt: N) {
160 self.inv_dt = inv_dt;
161 if inv_dt == N::zero() {
162 self.dt = N::zero()
163 } else {
164 self.dt = N::one() / inv_dt
165 }
166 }
167}
168
169impl<N: RealField + Copy> Default for IntegrationParameters<N> {
170 fn default() -> Self {
171 Self::new(
172 na::convert(1.0 / 60.0),
173 na::convert(0.2),
174 na::convert(1.0),
175 na::convert(1.0),
176 na::convert(0.001),
177 na::convert(0.001),
178 na::convert(0.2),
179 na::convert(0.2),
180 na::convert(0.2),
181 8,
182 3,
183 10,
184 1,
185 false,
186 false,
187 false,
188 )
189 }
190}