diff options
| -rw-r--r-- | examples3d/debug_thin_cube_on_mesh3.rs | 4 | ||||
| -rw-r--r-- | src/dynamics/rigid_body.rs | 88 | ||||
| -rw-r--r-- | src/dynamics/rigid_body_components.rs | 6 | ||||
| -rw-r--r-- | src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs | 7 | ||||
| -rw-r--r-- | src/geometry/narrow_phase.rs | 29 |
5 files changed, 90 insertions, 44 deletions
diff --git a/examples3d/debug_thin_cube_on_mesh3.rs b/examples3d/debug_thin_cube_on_mesh3.rs index 801e0cd..edf6664 100644 --- a/examples3d/debug_thin_cube_on_mesh3.rs +++ b/examples3d/debug_thin_cube_on_mesh3.rs @@ -46,9 +46,9 @@ pub fn init_world(testbed: &mut Testbed) { .translation(vector![0.0, 5.0, 0.0]) .rotation(vector![0.5, 0.0, 0.5]) .linvel(vector![0.0, -100.0, 0.0]) - .soft_ccd_enabled(true); + .soft_ccd_prediction(10.0); let handle = bodies.insert(rigid_body); - let collider = ColliderBuilder::cuboid(0.01, 0.015, 5.0); + let collider = ColliderBuilder::cuboid(5.0, 0.015, 5.0); colliders.insert_with_parent(collider, handle, &mut bodies); /* diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs index e1075e6..d4043c2 100644 --- a/src/dynamics/rigid_body.rs +++ b/src/dynamics/rigid_body.rs @@ -448,17 +448,26 @@ impl RigidBody { self.ccd.ccd_enabled } - /// Enables of disable soft CCD (soft Continuous Collision-Detection) for this rigid-body. + /// Sets the maximum prediction distance Soft Continuous Collision-Detection. /// - /// Soft-CCD helps prevent tunneling, but may still let tunnelling happen depending on solver - /// convergence. This is cheaper than the full ccd enabled by [`RigidBody::enable_ccd`]. - pub fn enable_soft_ccd(&mut self, enabled: bool) { - self.ccd.soft_ccd_enabled = enabled; + /// When set to 0, soft-CCD is disabled. Soft-CCD helps prevent tunneling especially of + /// slow-but-thin to moderately fast objects. The soft CCD prediction distance indicates how + /// far in the object’s path the CCD algorithm is allowed to inspect. Large values can impact + /// performance badly by increasing the work needed from the broad-phase. + /// + /// It is a generally cheaper variant of regular CCD (that can be enabled with + /// [`RigidBody::enable_ccd`] since it relies on predictive constraints instead of + /// shape-cast and substeps. + pub fn set_soft_ccd_prediction(&mut self, prediction_distance: Real) { + self.ccd.soft_ccd_prediction = prediction_distance; } - /// Is Soft-CCD (Soft Continous Collision-Detection) enabled for this rigid-body? - pub fn is_soft_ccd_enabled(&self) -> bool { - self.ccd.soft_ccd_enabled + /// The soft-CCD prediction distance for this rigid-body. + /// + /// See the documentation of [`RigidBody::set_soft_ccd_prediction`] for additional details on + /// soft-CCD. + pub fn soft_ccd_prediction(&self) -> Real { + self.ccd.soft_ccd_prediction } // This is different from `is_ccd_enabled`. This checks that CCD @@ -880,6 +889,25 @@ impl RigidBody { /// Predicts the next position of this rigid-body, by integrating its velocity and forces /// by a time of `dt`. + pub(crate) fn predict_position_using_velocity_and_forces_with_max_dist( + &self, + dt: Real, + max_dist: Real, + ) -> Isometry<Real> { + let new_vels = self.forces.integrate(dt, &self.vels, &self.mprops); + // Compute the clamped dt such that the body doesn’t travel more than `max_dist`. + let linvel_norm = new_vels.linvel.norm(); + let clamped_linvel = linvel_norm.min(max_dist * crate::utils::inv(dt)); + let clamped_dt = dt * clamped_linvel * crate::utils::inv(linvel_norm); + new_vels.integrate( + clamped_dt, + &self.pos.position, + &self.mprops.local_mprops.local_com, + ) + } + + /// Predicts the next position of this rigid-body, by integrating its velocity and forces + /// by a time of `dt`. pub fn predict_position_using_velocity_and_forces(&self, dt: Real) -> Isometry<Real> { self.pos .integrate_forces_and_velocities(dt, &self.forces, &self.vels, &self.mprops) @@ -1110,19 +1138,25 @@ pub struct RigidBodyBuilder { mprops_flags: LockedAxes, /// The additional mass-properties of the rigid-body being built. See [`RigidBodyBuilder::additional_mass_properties`] for more information. additional_mass_properties: RigidBodyAdditionalMassProps, - /// Whether or not the rigid-body to be created can sleep if it reaches a dynamic equilibrium. + /// Whether the rigid-body to be created can sleep if it reaches a dynamic equilibrium. pub can_sleep: bool, - /// Whether or not the rigid-body is to be created asleep. + /// Whether the rigid-body is to be created asleep. pub sleeping: bool, /// Whether Continuous Collision-Detection is enabled for the rigid-body to be built. /// /// CCD prevents tunneling, but may still allow limited interpenetration of colliders. pub ccd_enabled: bool, - /// Whether Soft Continuous Collision-Detection is enabled for the rigid-body to be built. + /// The maximum prediction distance Soft Continuous Collision-Detection. /// - /// Soft-CCD helps prevent tunneling, but may still let tunnelling happen depending on solver - /// convergence. This is cheaper than the full ccd enabled by [`RigidBodyBuilder::ccd_enabled`]. - pub soft_ccd_enabled: bool, + /// When set to 0, soft CCD is disabled. Soft-CCD helps prevent tunneling especially of + /// slow-but-thin to moderately fast objects. The soft CCD prediction distance indicates how + /// far in the object’s path the CCD algorithm is allowed to inspect. Large values can impact + /// performance badly by increasing the work needed from the broad-phase. + /// + /// It is a generally cheaper variant of regular CCD (that can be enabled with + /// [`RigidBodyBuilder::ccd_enabled`] since it relies on predictive constraints instead of + /// shape-cast and substeps. + pub soft_ccd_prediction: Real, /// The dominance group of the rigid-body to be built. pub dominance_group: i8, /// Will the rigid-body being built be enabled? @@ -1152,7 +1186,7 @@ impl RigidBodyBuilder { can_sleep: true, sleeping: false, ccd_enabled: false, - soft_ccd_enabled: false, + soft_ccd_prediction: 0.0, dominance_group: 0, enabled: true, user_data: 0, @@ -1391,13 +1425,13 @@ impl RigidBodyBuilder { self } - /// Sets whether or not the rigid-body to be created can sleep if it reaches a dynamic equilibrium. + /// Sets whether the rigid-body to be created can sleep if it reaches a dynamic equilibrium. pub fn can_sleep(mut self, can_sleep: bool) -> Self { self.can_sleep = can_sleep; self } - /// Sets whether or not Continuous Collision-Detection is enabled for this rigid-body. + /// Sets whether Continuous Collision-Detection is enabled for this rigid-body. /// /// CCD prevents tunneling, but may still allow limited interpenetration of colliders. pub fn ccd_enabled(mut self, enabled: bool) -> Self { @@ -1405,16 +1439,22 @@ impl RigidBodyBuilder { self } - /// Sets whether or not Soft Continuous Collision-Detection is enabled for this rigid-body. + /// Sets the maximum prediction distance Soft Continuous Collision-Detection. + /// + /// When set to 0, soft-CCD is disabled. Soft-CCD helps prevent tunneling especially of + /// slow-but-thin to moderately fast objects. The soft CCD prediction distance indicates how + /// far in the object’s path the CCD algorithm is allowed to inspect. Large values can impact + /// performance badly by increasing the work needed from the broad-phase. /// - /// Soft-CCD helps prevent tunneling, but may still let tunnelling happen depending on solver - /// convergence. This is cheaper than the full ccd enabled by [`RigidBodyBuilder::ccd_enabled`]. - pub fn soft_ccd_enabled(mut self, enabled: bool) -> Self { - self.soft_ccd_enabled = enabled; + /// It is a generally cheaper variant of regular CCD (that can be enabled with + /// [`RigidBodyBuilder::ccd_enabled`] since it relies on predictive constraints instead of + /// shape-cast and substeps. + pub fn soft_ccd_prediction(mut self, prediction_distance: Real) -> Self { + self.soft_ccd_prediction = prediction_distance; self } - /// Sets whether or not the rigid-body is to be created asleep. + /// Sets whether the rigid-body is to be created asleep. pub fn sleeping(mut self, sleeping: bool) -> Self { self.sleeping = sleeping; self @@ -1451,7 +1491,7 @@ impl RigidBodyBuilder { rb.dominance = RigidBodyDominance(self.dominance_group); rb.enabled = self.enabled; rb.enable_ccd(self.ccd_enabled); - rb.enable_soft_ccd(self.soft_ccd_enabled); + rb.set_soft_ccd_prediction(self.soft_ccd_prediction); if self.can_sleep && self.sleeping { rb.sleep(); diff --git a/src/dynamics/rigid_body_components.rs b/src/dynamics/rigid_body_components.rs index 3eea73c..998253c 100644 --- a/src/dynamics/rigid_body_components.rs +++ b/src/dynamics/rigid_body_components.rs @@ -821,8 +821,8 @@ pub struct RigidBodyCcd { pub ccd_active: bool, /// Is CCD enabled for this rigid-body? pub ccd_enabled: bool, - /// Is soft-CCD enabled for this rigid-body? - pub soft_ccd_enabled: bool, + /// The soft-CCD prediction distance for this rigid-body. + pub soft_ccd_prediction: Real, } impl Default for RigidBodyCcd { @@ -832,7 +832,7 @@ impl Default for RigidBodyCcd { ccd_max_dist: 0.0, ccd_active: false, ccd_enabled: false, - soft_ccd_enabled: false, + soft_ccd_prediction: 0.0, } } } diff --git a/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs b/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs index bc09b57..fb5d407 100644 --- a/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs +++ b/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs @@ -597,8 +597,11 @@ impl BroadPhase for BroadPhaseMultiSap { let next_pos = co.parent.and_then(|p| { let parent = bodies.get(p.handle)?; - parent.is_soft_ccd_enabled().then(|| { - parent.predict_position_using_velocity_and_forces(dt) * p.pos_wrt_parent + (parent.soft_ccd_prediction() > 0.0).then(|| { + parent.predict_position_using_velocity_and_forces_with_max_dist( + dt, + parent.soft_ccd_prediction(), + ) * p.pos_wrt_parent }) }); diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs index a711117..39ed253 100644 --- a/src/geometry/narrow_phase.rs +++ b/src/geometry/narrow_phase.rs @@ -898,19 +898,22 @@ impl NarrowPhase { let pos12 = co1.pos.inv_mul(&co2.pos); - let effective_prediction_distance = if rb1.map(|rb| rb.is_soft_ccd_enabled()) == Some(true) || - rb2.map(|rb| rb.is_soft_ccd_enabled()) == Some(true) { - - let aabb1 = co1.compute_aabb(); - let aabb2 = co2.compute_aabb(); - - let linvel1 = rb1.map(|rb| *rb.linvel()).unwrap_or_default(); - let linvel2 = rb2.map(|rb| *rb.linvel()).unwrap_or_default(); - - if !aabb1.intersects(&aabb2) && !aabb1.intersects_moving_aabb(&aabb2, linvel2 - linvel1) { - pair.clear(); - break 'emit_events; - } + let soft_ccd_prediction1 = rb1.map(|rb| rb.soft_ccd_prediction()).unwrap_or(0.0); + let soft_ccd_prediction2 = rb2.map(|rb| rb.soft_ccd_prediction()).unwrap_or(0.0); + let effective_prediction_distance = if soft_ccd_prediction1 > 0.0 || soft_ccd_prediction2 > 0.0 { + let aabb1 = co1.compute_aabb(); + let aabb2 = co2.compute_aabb(); + let inv_dt = crate::utils::inv(dt); + + let linvel1 = rb1.map(|rb| rb.linvel() + .cap_magnitude(soft_ccd_prediction1 * inv_dt)).unwrap_or_default(); + let linvel2 = rb2.map(|rb| rb.linvel() + .cap_magnitude(soft_ccd_prediction2 * inv_dt)).unwrap_or_default(); + + if !aabb1.intersects(&aabb2) && !aabb1.intersects_moving_aabb(&aabb2, linvel2 - linvel1) { + pair.clear(); + break 'emit_events; + } prediction_distance.max( |
