nphysics
nphysics copied to clipboard
Until `MechanicalWorld::step` is called, `GeometricalWorld::interferences_with_point` uses old collider positions
For context, I am writing a level editor that uses interferences_with_point
to determine the entities the user are clicking on. When the user moves an entity, the underlying body's position is moved. However, the call to interferences_with_point
still seems to be using the old object's position until the next time step
is called. I have tried both updating the collider position along with the body and using GeometricalWorld::sync_colliders
, which has had no effect. Reading through the source of step
, I have taken a few stabs in the dark on what will finally allow interferences_with_point
to work (without actually calling step
), culminating with this:
mech: DefaultMechanicalWorld<f32>,
geo: DefaultGeometricalWorld<f32>,
bodies: DefaultBodySet<f32>,
colliders: DefaultColliderSet<f32>,
joints: DefaultJointConstraintSet<f32>,
forces: DefaultForceGeneratorSet<f32>,
...
self.mech.maintain(&mut self.geo, &mut self.bodies, &mut self.colliders, &mut self.joints);
self.geo.clear_events();
self.geo.sync_colliders(&self.bodies, &mut self.colliders);
self.geo.perform_broad_phase(&self.colliders);
self.geo.perform_narrow_phase(&self.colliders);
self.colliders.foreach_mut(|_, c| c.clear_update_flags());
Still, this did not work. In the end, my workaround was this:
let old_timestep = self.mech.timestep();
self.mech.set_timestep(0.0);
self.mech.step(
&mut self.geo,
&mut self.bodies,
&mut self.colliders,
&mut self.joints,
&mut self.forces,
);
self.mech.set_timestep(old_timestep);
This feels like a bit of a hack, especially in the presence of all those other functions which I expected to work, having read the source, but I am all out of leads.
Is there a specific algorithm that users of nphysics are supposed to use to ensure interferences_with_point
is querying an up to date world, or is this perhaps a bug in the library?
My workaround of using a timestep of 0.0
ended up not being so good because it would cause dynamic bodies that are temporarily colliding with static bodies to be forced apart, which is not viable behavior for an editor. Therefore I took another look at what made step
work for interferences_with_point
by removing chunks of MechanicalWorld::step
until the editor exhibited the original issue.
After some false positives, I narrowed it to this chunk:
bodies.foreach_mut(&mut |_, b: &mut dyn Body<N>| {
b.clear_forces();
b.validate_advancement();
});
I transplanted this into my sync with step function and it works! My theory is that even though I was setting the body and collider position to the new position, the old body position is temporarily cached and used to reset the attached collider position before the broad phase based point interference is run. The call to Body::validate_advancement
clears out the old position allowing the broad phase to work as expected with the new collider position. After simplifying my sync
function looks like this:
pub fn sync(&mut self) {
self.mech.maintain(&mut self.geo, &mut self.bodies, &mut self.colliders, &mut self.joints);
self.bodies.foreach_mut(&mut |_, b| {
b.validate_advancement();
});
self.geo.sync_colliders(&self.bodies, &mut self.colliders);
self.geo.perform_broad_phase(&self.colliders);
}
I'm not sure if this is a supported use case or if it will continue to work, so it would be nice to get some input from a maintainer, but I'm basically happy with my solution. Thanks for taking a look.