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
use na::{self, RealField};

/// Parameters for a time-step of the physics engine.
#[derive(Clone)]
pub struct IntegrationParameters<N: RealField> {
    /// The timestep (default: `1.0 / 60.0`)
    dt: N,
    /// The inverse of `dt`.
    inv_dt: N,
    /// 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 total elapsed time in the physics world.
    ///
    /// This is the accumulation of the `dt` of all the calls to `world.step()`.
    pub t: N,
    /// 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: N,
    /// 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: N,
    /// Contacts at points where the involved bodies have a relative
    /// velocity smaller than this threshold wont be affected by the restitution force (default: `1.0`).
    pub restitution_velocity_threshold: N,
    /// Ammount of penetration the engine wont attempt to correct (default: `0.001m`).
    pub allowed_linear_error: N,
    /// Ammount of angular drift of joint limits the engine wont
    /// attempt to correct (default: `0.001rad`).
    pub allowed_angular_error: N,
    /// Maximum linear correction during one step of the non-linear position solver (default: `0.2`).
    pub max_linear_correction: N,
    /// Maximum angular correction during one step of the non-linear position solver (default: `0.2`).
    pub max_angular_correction: N,
    /// 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: N,
    /// Maximum number of iterations performed by the velocity constraints solver (default: `8`).
    pub max_velocity_iterations: usize,
    /// Maximum number of iterations performed by the position-based constraints solver (default: `3`).
    pub max_position_iterations: 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 highest this number, the highest 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
    /// seperate 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<N: RealField> IntegrationParameters<N> {
    /// Creates a set of integration parameters with the given values.
    pub fn new(
        dt: N,
        erp: N,
        warmstart_coeff: N,
        restitution_velocity_threshold: N,
        allowed_linear_error: N,
        allowed_angular_error: N,
        max_linear_correction: N,
        max_angular_correction: N,
        max_stabilization_multiplier: N,
        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 {
            t: N::zero(),
            dt,
            inv_dt: if dt == N::zero() {
                N::zero()
            } else {
                N::one() / dt
            },
            erp,
            warmstart_coeff,
            restitution_velocity_threshold,
            allowed_linear_error,
            allowed_angular_error,
            max_linear_correction,
            max_angular_correction,
            max_stabilization_multiplier,
            max_velocity_iterations,
            max_position_iterations,
            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)]
    pub fn dt(&self) -> N {
        self.dt
    }

    /// The inverse of the time-stepping length.
    ///
    /// This is zero if `self.dt` is zero.
    #[inline(always)]
    pub fn inv_dt(&self) -> N {
        self.inv_dt
    }

    /// Sets the time-stepping length.
    ///
    /// This automatically recompute `self.inv_dt`.
    #[inline]
    pub fn set_dt(&mut self, dt: N) {
        assert!(
            dt >= N::zero(),
            "The time-stepping length cannot be negative."
        );
        self.dt = dt;
        if dt == N::zero() {
            self.inv_dt = N::zero()
        } else {
            self.inv_dt = N::one() / 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: N) {
        self.inv_dt = inv_dt;
        if inv_dt == N::zero() {
            self.dt = N::zero()
        } else {
            self.dt = N::one() / inv_dt
        }
    }
}

impl<N: RealField> Default for IntegrationParameters<N> {
    fn default() -> Self {
        Self::new(
            na::convert(1.0 / 60.0),
            na::convert(0.2),
            na::convert(1.0),
            na::convert(1.0),
            na::convert(0.001),
            na::convert(0.001),
            na::convert(0.2),
            na::convert(0.2),
            na::convert(0.2),
            8,
            3,
            10,
            1,
            false,
            false,
            false,
        )
    }
}