Merge pull request #30 from robert-hrusecky/infinite_fall_memory
Infinite fall memory fix for #14
This commit is contained in:
@@ -14,6 +14,7 @@ mod add_remove2;
|
||||
mod collision_groups2;
|
||||
mod damping2;
|
||||
mod debug_box_ball2;
|
||||
mod debug_infinite_fall;
|
||||
mod heightfield2;
|
||||
mod joints2;
|
||||
mod platform2;
|
||||
@@ -64,6 +65,7 @@ pub fn main() {
|
||||
("Restitution", restitution2::init_world),
|
||||
("Sensor", sensor2::init_world),
|
||||
("(Debug) box ball", debug_box_ball2::init_world),
|
||||
("(Debug) infinite fall", debug_infinite_fall::init_world),
|
||||
];
|
||||
|
||||
// Lexicographic sort, with stress tests moved at the end of the list.
|
||||
|
||||
33
examples2d/debug_infinite_fall.rs
Normal file
33
examples2d/debug_infinite_fall.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use rapier2d::dynamics::{JointSet, RigidBodyBuilder, RigidBodySet};
|
||||
use rapier2d::geometry::{ColliderBuilder, ColliderSet};
|
||||
use rapier_testbed2d::Testbed;
|
||||
|
||||
pub fn init_world(testbed: &mut Testbed) {
|
||||
/*
|
||||
* World
|
||||
*/
|
||||
let mut bodies = RigidBodySet::new();
|
||||
let mut colliders = ColliderSet::new();
|
||||
let joints = JointSet::new();
|
||||
|
||||
let rad = 1.0;
|
||||
// Build the dynamic box rigid body.
|
||||
let rigid_body = RigidBodyBuilder::new_dynamic()
|
||||
.translation(0.0, 3.0 * rad)
|
||||
.can_sleep(false)
|
||||
.build();
|
||||
let handle = bodies.insert(rigid_body);
|
||||
let collider = ColliderBuilder::ball(rad).build();
|
||||
colliders.insert(collider, handle, &mut bodies);
|
||||
|
||||
/*
|
||||
* Set up the testbed.
|
||||
*/
|
||||
testbed.set_world(bodies, colliders, joints);
|
||||
// testbed.look_at(Point2::new(10.0, 10.0, 10.0), Point2::origin());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
|
||||
testbed.run()
|
||||
}
|
||||
@@ -238,13 +238,14 @@ impl SAPAxis {
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_out_of_bounds_proxies(&self, existing_proxies: &mut BitVec) -> bool {
|
||||
let mut deleted_any = false;
|
||||
fn delete_out_of_bounds_proxies(&self, existing_proxies: &mut BitVec) -> usize {
|
||||
let mut deleted = 0;
|
||||
for endpoint in &self.endpoints {
|
||||
if endpoint.value < self.min_bound {
|
||||
if endpoint.is_end() {
|
||||
existing_proxies.set(endpoint.proxy() as usize, false);
|
||||
deleted_any = true;
|
||||
let proxy_idx = endpoint.proxy() as usize;
|
||||
if endpoint.is_end() && existing_proxies[proxy_idx] {
|
||||
existing_proxies.set(proxy_idx, false);
|
||||
deleted += 1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
@@ -253,16 +254,17 @@ impl SAPAxis {
|
||||
|
||||
for endpoint in self.endpoints.iter().rev() {
|
||||
if endpoint.value > self.max_bound {
|
||||
if endpoint.is_start() {
|
||||
let proxy_idx = endpoint.proxy() as usize;
|
||||
if endpoint.is_start() && existing_proxies[proxy_idx] {
|
||||
existing_proxies.set(endpoint.proxy() as usize, false);
|
||||
deleted_any = true;
|
||||
deleted += 1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
deleted_any
|
||||
deleted
|
||||
}
|
||||
|
||||
fn delete_out_of_bounds_endpoints(&mut self, existing_proxies: &BitVec) {
|
||||
@@ -338,7 +340,8 @@ struct SAPRegion {
|
||||
existing_proxies: BitVec,
|
||||
#[cfg_attr(feature = "serde-serialize", serde(skip))]
|
||||
to_insert: Vec<usize>, // Workspace
|
||||
need_update: bool,
|
||||
update_count: usize,
|
||||
proxy_count: usize,
|
||||
}
|
||||
|
||||
impl SAPRegion {
|
||||
@@ -353,7 +356,38 @@ impl SAPRegion {
|
||||
axes,
|
||||
existing_proxies: BitVec::new(),
|
||||
to_insert: Vec::new(),
|
||||
need_update: false,
|
||||
update_count: 0,
|
||||
proxy_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recycle(bounds: AABB<f32>, mut old: Self) -> Self {
|
||||
// Correct the bounds
|
||||
for (axis, &bound) in old.axes.iter_mut().zip(bounds.mins.iter()) {
|
||||
axis.min_bound = bound;
|
||||
}
|
||||
for (axis, &bound) in old.axes.iter_mut().zip(bounds.maxs.iter()) {
|
||||
axis.max_bound = bound;
|
||||
}
|
||||
|
||||
old.update_count = 0;
|
||||
|
||||
// The rest of the fields should be "empty"
|
||||
assert_eq!(old.proxy_count, 0);
|
||||
assert!(old.to_insert.is_empty());
|
||||
debug_assert!(!old.existing_proxies.any());
|
||||
for axis in old.axes.iter() {
|
||||
assert!(axis.endpoints.len() == 2); // Account for sentinels
|
||||
}
|
||||
|
||||
old
|
||||
}
|
||||
|
||||
pub fn recycle_or_new(bounds: AABB<f32>, pool: &mut Vec<Self>) -> Self {
|
||||
if let Some(old) = pool.pop() {
|
||||
Self::recycle(bounds, old)
|
||||
} else {
|
||||
Self::new(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +395,7 @@ impl SAPRegion {
|
||||
// We keep the proxy_id as argument for uniformity with the "preupdate"
|
||||
// method. However we don't actually need it because the deletion will be
|
||||
// handled transparently during the next update.
|
||||
self.need_update = true;
|
||||
self.update_count = 1;
|
||||
}
|
||||
|
||||
pub fn preupdate_proxy(&mut self, proxy_id: usize) -> bool {
|
||||
@@ -373,31 +407,34 @@ impl SAPRegion {
|
||||
if !self.existing_proxies[proxy_id] {
|
||||
self.to_insert.push(proxy_id);
|
||||
self.existing_proxies.set(proxy_id, true);
|
||||
self.proxy_count += 1;
|
||||
false
|
||||
} else {
|
||||
self.need_update = true;
|
||||
// Here we need a second update if all proxies exit this region. In this case, we need
|
||||
// to delete the final proxy, but the region may not have AABBs overlapping it, so it
|
||||
// wouldn't get an update otherwise.
|
||||
self.update_count = 2;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, proxies: &Proxies, reporting: &mut HashMap<(u32, u32), bool>) {
|
||||
if self.need_update {
|
||||
if self.update_count > 0 {
|
||||
// Update endpoints.
|
||||
let mut deleted_any = false;
|
||||
let mut deleted = 0;
|
||||
for dim in 0..DIM {
|
||||
self.axes[dim].update_endpoints(dim, proxies, reporting);
|
||||
deleted_any = self.axes[dim]
|
||||
.delete_out_of_bounds_proxies(&mut self.existing_proxies)
|
||||
|| deleted_any;
|
||||
deleted += self.axes[dim].delete_out_of_bounds_proxies(&mut self.existing_proxies);
|
||||
}
|
||||
|
||||
if deleted_any {
|
||||
if deleted > 0 {
|
||||
self.proxy_count -= deleted;
|
||||
for dim in 0..DIM {
|
||||
self.axes[dim].delete_out_of_bounds_endpoints(&self.existing_proxies);
|
||||
}
|
||||
}
|
||||
|
||||
self.need_update = false;
|
||||
self.update_count -= 1;
|
||||
}
|
||||
|
||||
if !self.to_insert.is_empty() {
|
||||
@@ -407,6 +444,10 @@ impl SAPRegion {
|
||||
}
|
||||
self.axes[0].batch_insert(0, &self.to_insert, proxies, Some(reporting));
|
||||
self.to_insert.clear();
|
||||
|
||||
// In the rare event that all proxies leave this region in the next step, we need an
|
||||
// update to remove them.
|
||||
self.update_count = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,6 +460,8 @@ pub struct BroadPhase {
|
||||
regions: HashMap<Point<i32>, SAPRegion>,
|
||||
removed_colliders: Option<Subscription<RemovedCollider>>,
|
||||
deleted_any: bool,
|
||||
region_pool: Vec<SAPRegion>,
|
||||
points_to_remove: Vec<Point<i32>>, // Workspace
|
||||
// We could think serializing this workspace is useless.
|
||||
// It turns out is is important to serialize at least its capacity
|
||||
// and restore this capacity when deserializing the hashmap.
|
||||
@@ -511,6 +554,8 @@ impl BroadPhase {
|
||||
removed_colliders: None,
|
||||
proxies: Proxies::new(),
|
||||
regions: HashMap::default(),
|
||||
region_pool: Vec::new(),
|
||||
points_to_remove: Vec::new(),
|
||||
reporting: HashMap::default(),
|
||||
deleted_any: false,
|
||||
}
|
||||
@@ -611,15 +656,16 @@ impl BroadPhase {
|
||||
let start = point_key(aabb.mins);
|
||||
let end = point_key(aabb.maxs);
|
||||
|
||||
let regions = &mut self.regions;
|
||||
let pool = &mut self.region_pool;
|
||||
#[cfg(feature = "dim2")]
|
||||
for i in start.x..=end.x {
|
||||
for j in start.y..=end.y {
|
||||
let region_key = Point::new(i, j);
|
||||
let region_bounds = region_aabb(region_key);
|
||||
let region = self
|
||||
.regions
|
||||
let region = regions
|
||||
.entry(region_key)
|
||||
.or_insert_with(|| SAPRegion::new(region_bounds));
|
||||
.or_insert_with(|| SAPRegion::recycle_or_new(region_bounds, pool));
|
||||
let _ = region.preupdate_proxy(proxy_id);
|
||||
}
|
||||
}
|
||||
@@ -630,10 +676,9 @@ impl BroadPhase {
|
||||
for k in start.z..=end.z {
|
||||
let region_key = Point::new(i, j, k);
|
||||
let region_bounds = region_aabb(region_key);
|
||||
let region = self
|
||||
.regions
|
||||
let region = regions
|
||||
.entry(region_key)
|
||||
.or_insert_with(|| SAPRegion::new(region_bounds));
|
||||
.or_insert_with(|| SAPRegion::recycle_or_new(region_bounds, pool));
|
||||
let _ = region.preupdate_proxy(proxy_id);
|
||||
}
|
||||
}
|
||||
@@ -642,11 +687,26 @@ impl BroadPhase {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_regions(&mut self) {
|
||||
for (point, region) in &mut self.regions {
|
||||
region.update(&self.proxies, &mut self.reporting);
|
||||
if region.proxy_count == 0 {
|
||||
self.points_to_remove.push(*point);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all the empty regions and store them in the region pool
|
||||
let regions = &mut self.regions;
|
||||
self.region_pool.extend(
|
||||
self.points_to_remove
|
||||
.drain(..)
|
||||
.map(|p| regions.remove(&p).unwrap()),
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn complete_removals(&mut self) {
|
||||
if self.deleted_any {
|
||||
for (_, region) in &mut self.regions {
|
||||
region.update(&self.proxies, &mut self.reporting);
|
||||
}
|
||||
self.update_regions();
|
||||
|
||||
// NOTE: we don't care about reporting pairs.
|
||||
self.reporting.clear();
|
||||
@@ -658,9 +718,7 @@ impl BroadPhase {
|
||||
// println!("num regions: {}", self.regions.len());
|
||||
|
||||
self.reporting.clear();
|
||||
for (_, region) in &mut self.regions {
|
||||
region.update(&self.proxies, &mut self.reporting)
|
||||
}
|
||||
self.update_regions();
|
||||
|
||||
// Convert reports to broad phase events.
|
||||
// let t = instant::now();
|
||||
|
||||
Reference in New Issue
Block a user