Fix crash caused by the removal of a kinematic body.

This commit is contained in:
Crozet Sébastien
2020-09-14 22:22:55 +02:00
parent a60e1e030d
commit e16b7722be
3 changed files with 87 additions and 30 deletions

View File

@@ -65,7 +65,7 @@ pub fn init_world(testbed: &mut Testbed) {
* Setup a callback to control the platform. * Setup a callback to control the platform.
*/ */
testbed.add_callback(move |_, physics, _, _, time| { testbed.add_callback(move |_, physics, _, _, time| {
let mut platform = physics.bodies.get_mut(platform_handle).unwrap(); if let Some(mut platform) = physics.bodies.get_mut(platform_handle) {
let mut next_pos = platform.position; let mut next_pos = platform.position;
let dt = 0.016; let dt = 0.016;
@@ -80,6 +80,7 @@ pub fn init_world(testbed: &mut Testbed) {
} }
platform.set_next_kinematic_position(next_pos); platform.set_next_kinematic_position(next_pos);
}
}); });
/* /*

View File

@@ -2,7 +2,7 @@
use rayon::prelude::*; use rayon::prelude::*;
use crate::data::arena::Arena; use crate::data::arena::Arena;
use crate::dynamics::{Joint, RigidBody}; use crate::dynamics::{BodyStatus, Joint, RigidBody};
use crate::geometry::{ColliderSet, ContactPair, InteractionGraph}; use crate::geometry::{ColliderSet, ContactPair, InteractionGraph};
use crossbeam::channel::{Receiver, Sender}; use crossbeam::channel::{Receiver, Sender};
use std::ops::{Deref, DerefMut, Index, IndexMut}; use std::ops::{Deref, DerefMut, Index, IndexMut};
@@ -128,11 +128,25 @@ impl RigidBodySet {
pub(crate) fn activate(&mut self, handle: RigidBodyHandle) { pub(crate) fn activate(&mut self, handle: RigidBodyHandle) {
let mut rb = &mut self.bodies[handle]; let mut rb = &mut self.bodies[handle];
match rb.body_status {
// XXX: this case should only concern the dynamic bodies.
// For static bodies we should use the modified_inactive_set, or something
// similar. Right now we do this for static bodies as well so the broad-phase
// takes them into account the first time they are inserted.
BodyStatus::Dynamic | BodyStatus::Static => {
if self.active_dynamic_set.get(rb.active_set_id) != Some(&handle) { if self.active_dynamic_set.get(rb.active_set_id) != Some(&handle) {
rb.active_set_id = self.active_dynamic_set.len(); rb.active_set_id = self.active_dynamic_set.len();
self.active_dynamic_set.push(handle); self.active_dynamic_set.push(handle);
} }
} }
BodyStatus::Kinematic => {
if self.active_kinematic_set.get(rb.active_set_id) != Some(&handle) {
rb.active_set_id = self.active_kinematic_set.len();
self.active_kinematic_set.push(handle);
}
}
}
}
/// Is the given body handle valid? /// Is the given body handle valid?
pub fn contains(&self, handle: RigidBodyHandle) -> bool { pub fn contains(&self, handle: RigidBodyHandle) -> bool {
@@ -143,13 +157,14 @@ impl RigidBodySet {
pub fn insert(&mut self, rb: RigidBody) -> RigidBodyHandle { pub fn insert(&mut self, rb: RigidBody) -> RigidBodyHandle {
let handle = self.bodies.insert(rb); let handle = self.bodies.insert(rb);
let rb = &mut self.bodies[handle]; let rb = &mut self.bodies[handle];
rb.active_set_id = self.active_dynamic_set.len();
if !rb.is_sleeping() && rb.is_dynamic() { if !rb.is_sleeping() && rb.is_dynamic() {
rb.active_set_id = self.active_dynamic_set.len();
self.active_dynamic_set.push(handle); self.active_dynamic_set.push(handle);
} }
if rb.is_kinematic() { if rb.is_kinematic() {
rb.active_set_id = self.active_kinematic_set.len();
self.active_kinematic_set.push(handle); self.active_kinematic_set.push(handle);
} }
@@ -166,14 +181,17 @@ impl RigidBodySet {
pub(crate) fn remove_internal(&mut self, handle: RigidBodyHandle) -> Option<RigidBody> { pub(crate) fn remove_internal(&mut self, handle: RigidBodyHandle) -> Option<RigidBody> {
let rb = self.bodies.remove(handle)?; let rb = self.bodies.remove(handle)?;
// Remove this body from the active dynamic set. let mut active_sets = [&mut self.active_kinematic_set, &mut self.active_dynamic_set];
if self.active_dynamic_set.get(rb.active_set_id) == Some(&handle) {
self.active_dynamic_set.swap_remove(rb.active_set_id);
if let Some(replacement) = self.active_dynamic_set.get(rb.active_set_id) { for active_set in &mut active_sets {
if active_set.get(rb.active_set_id) == Some(&handle) {
active_set.swap_remove(rb.active_set_id);
if let Some(replacement) = active_set.get(rb.active_set_id) {
self.bodies[*replacement].active_set_id = rb.active_set_id; self.bodies[*replacement].active_set_id = rb.active_set_id;
} }
} }
}
Some(rb) Some(rb)
} }
@@ -181,6 +199,7 @@ impl RigidBodySet {
/// Forces the specified rigid-body to wake up if it is dynamic. /// Forces the specified rigid-body to wake up if it is dynamic.
pub fn wake_up(&mut self, handle: RigidBodyHandle) { pub fn wake_up(&mut self, handle: RigidBodyHandle) {
if let Some(rb) = self.bodies.get_mut(handle) { if let Some(rb) = self.bodies.get_mut(handle) {
// TODO: what about kinematic bodies?
if rb.is_dynamic() { if rb.is_dynamic() {
rb.wake_up(); rb.wake_up();
@@ -214,8 +233,11 @@ impl RigidBodySet {
/// ///
/// Using this is discouraged in favor of `self.get_mut(handle)` which does not /// Using this is discouraged in favor of `self.get_mut(handle)` which does not
/// suffer form the ABA problem. /// suffer form the ABA problem.
pub fn get_unknown_gen_mut(&mut self, i: usize) -> Option<(&mut RigidBody, RigidBodyHandle)> { pub fn get_unknown_gen_mut(&mut self, i: usize) -> Option<(RigidBodyMut, RigidBodyHandle)> {
self.bodies.get_unknown_gen_mut(i) let sender = &self.activation_channel.0;
self.bodies
.get_unknown_gen_mut(i)
.map(|(rb, handle)| (RigidBodyMut::new(handle, rb, sender), handle))
} }
/// Gets the rigid-body with the given handle. /// Gets the rigid-body with the given handle.

View File

@@ -298,8 +298,9 @@ impl PhysicsPipeline {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::dynamics::{JointSet, RigidBodyBuilder, RigidBodySet}; use crate::dynamics::{IntegrationParameters, JointSet, RigidBodyBuilder, RigidBodySet};
use crate::geometry::{BroadPhase, ColliderSet, NarrowPhase}; use crate::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase};
use crate::math::Vector;
use crate::pipeline::PhysicsPipeline; use crate::pipeline::PhysicsPipeline;
#[test] #[test]
@@ -310,12 +311,45 @@ mod test {
let mut bf = BroadPhase::new(); let mut bf = BroadPhase::new();
let mut nf = NarrowPhase::new(); let mut nf = NarrowPhase::new();
let mut set = RigidBodySet::new(); let mut bodies = RigidBodySet::new();
let rb = RigidBodyBuilder::new_dynamic().build();
// Check that removing the body right after inserting it works. // Check that removing the body right after inserting it works.
let h1 = set.insert(rb.clone()); // We add two dynamic bodies, one kinematic body and one static body before removing
pipeline.remove_rigid_body(h1, &mut bf, &mut nf, &mut set, &mut colliders, &mut joints); // them. This include a non-regression test where deleting a kimenatic body crashes.
let rb = RigidBodyBuilder::new_dynamic().build();
let h1 = bodies.insert(rb.clone());
let h2 = bodies.insert(rb.clone());
// The same but with a kinematic body.
let rb = RigidBodyBuilder::new_kinematic().build();
let h3 = bodies.insert(rb.clone());
// The same but with a static body.
let rb = RigidBodyBuilder::new_static().build();
let h4 = bodies.insert(rb.clone());
let to_delete = [h1, h2, h3, h4];
for h in &to_delete {
pipeline.remove_rigid_body(
*h,
&mut bf,
&mut nf,
&mut bodies,
&mut colliders,
&mut joints,
);
}
pipeline.step(
&Vector::zeros(),
&IntegrationParameters::default(),
&mut bf,
&mut nf,
&mut bodies,
&mut colliders,
&mut joints,
&(),
);
} }
#[test] #[test]