Skip to content

Commit

Permalink
Merge pull request #120 from dimforge/contact_modification
Browse files Browse the repository at this point in the history
Add the ability to modify the contact points seen by the constraints solver
  • Loading branch information
sebcrozet committed Feb 24, 2021
2 parents d31a327 + 3cc2738 commit 649eba1
Show file tree
Hide file tree
Showing 24 changed files with 739 additions and 195 deletions.
2 changes: 2 additions & 0 deletions examples2d/all_examples2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod debug_box_ball2;
mod heightfield2;
mod joints2;
mod locked_rotations2;
mod one_way_platforms2;
mod platform2;
mod polyline2;
mod pyramid2;
Expand Down Expand Up @@ -65,6 +66,7 @@ pub fn main() {
("Heightfield", heightfield2::init_world),
("Joints", joints2::init_world),
("Locked rotations", locked_rotations2::init_world),
("One-way platforms", one_way_platforms2::init_world),
("Platform", platform2::init_world),
("Polyline", polyline2::init_world),
("Pyramid", pyramid2::init_world),
Expand Down
2 changes: 1 addition & 1 deletion examples2d/damping2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ pub fn init_world(testbed: &mut Testbed) {
/*
* Set up the testbed.
*/
testbed.set_world_with_gravity(bodies, colliders, joints, Vector2::zeros());
testbed.set_world_with_params(bodies, colliders, joints, Vector2::zeros(), ());
testbed.look_at(Point2::new(3.0, 2.0), 50.0);
}
143 changes: 143 additions & 0 deletions examples2d/one_way_platforms2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use na::{Point2, Vector2};
use rapier2d::dynamics::{JointSet, RigidBodyBuilder, RigidBodySet};
use rapier2d::geometry::{ColliderBuilder, ColliderHandle, ColliderSet};
use rapier2d::pipeline::{ContactModificationContext, PhysicsHooks, PhysicsHooksFlags};
use rapier_testbed2d::Testbed;

struct OneWayPlatformHook {
platform1: ColliderHandle,
platform2: ColliderHandle,
}

impl PhysicsHooks for OneWayPlatformHook {
fn active_hooks(&self) -> PhysicsHooksFlags {
PhysicsHooksFlags::MODIFY_SOLVER_CONTACTS
}

fn modify_solver_contacts(&self, context: &mut ContactModificationContext) {
// The allowed normal for the first platform is its local +y axis, and the
// allowed normal for the second platform is its local -y axis.
//
// Now we have to be careful because the `manifold.local_n1` normal points
// toward the outside of the shape of `context.co1`. So we need to flip the
// allowed normal direction if the platform is in `context.collider_handle2`.
//
// Therefore:
// - If context.collider_handle1 == self.platform1 then the allowed normal is +y.
// - If context.collider_handle2 == self.platform1 then the allowed normal is -y.
// - If context.collider_handle1 == self.platform2 then its allowed normal +y needs to be flipped to -y.
// - If context.collider_handle2 == self.platform2 then the allowed normal -y needs to be flipped to +y.
let mut allowed_local_n1 = Vector2::zeros();

if context.collider_handle1 == self.platform1 {
allowed_local_n1 = Vector2::y();
} else if context.collider_handle2 == self.platform1 {
// Flip the allowed direction.
allowed_local_n1 = -Vector2::y();
}

if context.collider_handle1 == self.platform2 {
allowed_local_n1 = -Vector2::y();
} else if context.collider_handle2 == self.platform2 {
// Flip the allowed direction.
allowed_local_n1 = -Vector2::y();
}

// Call the helper function that simulates one-way platforms.
context.update_as_oneway_platform(&allowed_local_n1, 0.1);

// Set the surface velocity of the accepted contacts.
let tangent_velocity = if context.collider_handle1 == self.platform1
|| context.collider_handle2 == self.platform2
{
-12.0
} else {
12.0
};

for contact in context.solver_contacts.iter_mut() {
contact.tangent_velocity.x = tangent_velocity;
}
}
}

pub fn init_world(testbed: &mut Testbed) {
/*
* World
*/
let mut bodies = RigidBodySet::new();
let mut colliders = ColliderSet::new();
let joints = JointSet::new();

/*
* Ground
*/
let rigid_body = RigidBodyBuilder::new_static().build();
let handle = bodies.insert(rigid_body);

let collider = ColliderBuilder::cuboid(25.0, 0.5)
.translation(30.0, 2.0)
.modify_solver_contacts(true)
.build();
let platform1 = colliders.insert(collider, handle, &mut bodies);
let collider = ColliderBuilder::cuboid(25.0, 0.5)
.translation(-30.0, -2.0)
.modify_solver_contacts(true)
.build();
let platform2 = colliders.insert(collider, handle, &mut bodies);

/*
* Setup the one-way platform hook.
*/
let physics_hooks = OneWayPlatformHook {
platform1,
platform2,
};

/*
* Spawn cubes at regular intervals and apply a custom gravity
* depending on their position.
*/
testbed.add_callback(move |mut window, mut graphics, physics, _, run_state| {
if run_state.timestep_id % 50 == 0 && physics.bodies.len() <= 7 {
// Spawn a new cube.
let collider = ColliderBuilder::cuboid(1.5, 2.0).build();
let body = RigidBodyBuilder::new_dynamic()
.translation(20.0, 10.0)
.build();
let handle = physics.bodies.insert(body);
physics
.colliders
.insert(collider, handle, &mut physics.bodies);

if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) {
graphics.add(window, handle, &physics.bodies, &physics.colliders);
}
}

physics.bodies.foreach_active_dynamic_body_mut(|_, body| {
if body.position().translation.y > 1.0 {
body.set_gravity_scale(1.0, false);
} else if body.position().translation.y < -1.0 {
body.set_gravity_scale(-1.0, false);
}
});
});

/*
* Set up the testbed.
*/
testbed.set_world_with_params(
bodies,
colliders,
joints,
Vector2::new(0.0, -9.81),
physics_hooks,
);
testbed.look_at(Point2::new(0.0, 0.0), 20.0);
}

fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}
2 changes: 2 additions & 0 deletions examples3d/all_examples3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mod heightfield3;
mod joints3;
mod keva3;
mod locked_rotations3;
mod one_way_platforms3;
mod platform3;
mod primitives3;
mod restitution3;
Expand Down Expand Up @@ -83,6 +84,7 @@ pub fn main() {
("Heightfield", heightfield3::init_world),
("Joints", joints3::init_world),
("Locked rotations", locked_rotations3::init_world),
("One-way platforms", one_way_platforms3::init_world),
("Platform", platform3::init_world),
("Restitution", restitution3::init_world),
("Sensor", sensor3::init_world),
Expand Down
2 changes: 1 addition & 1 deletion examples3d/damping3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ pub fn init_world(testbed: &mut Testbed) {
/*
* Set up the testbed.
*/
testbed.set_world_with_gravity(bodies, colliders, joints, Vector3::zeros());
testbed.set_world_with_params(bodies, colliders, joints, Vector3::zeros(), ());
testbed.look_at(Point3::new(2.0, 2.5, 20.0), Point3::new(2.0, 2.5, 0.0));
}
6 changes: 2 additions & 4 deletions examples3d/fountain3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,14 @@ pub fn init_world(testbed: &mut Testbed) {
let handle = bodies.insert(rigid_body);
let collider = ColliderBuilder::cuboid(ground_size, ground_height, ground_size).build();
colliders.insert(collider, handle, &mut bodies);
let mut k = 0;

// Callback that will be executed on the main loop to handle proximities.
testbed.add_callback(move |mut window, mut graphics, physics, _, _| {
k += 1;
testbed.add_callback(move |mut window, mut graphics, physics, _, run_state| {
let rigid_body = RigidBodyBuilder::new_dynamic()
.translation(0.0, 10.0, 0.0)
.build();
let handle = physics.bodies.insert(rigid_body);
let collider = match k % 3 {
let collider = match run_state.timestep_id % 3 {
0 => ColliderBuilder::round_cylinder(rad, rad, rad / 10.0).build(),
1 => ColliderBuilder::cone(rad, rad).build(),
_ => ColliderBuilder::cuboid(rad, rad, rad).build(),
Expand Down
143 changes: 143 additions & 0 deletions examples3d/one_way_platforms3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use na::{Point3, Vector3};
use rapier3d::dynamics::{JointSet, RigidBodyBuilder, RigidBodySet};
use rapier3d::geometry::{ColliderBuilder, ColliderHandle, ColliderSet};
use rapier3d::pipeline::{ContactModificationContext, PhysicsHooks, PhysicsHooksFlags};
use rapier_testbed3d::Testbed;

struct OneWayPlatformHook {
platform1: ColliderHandle,
platform2: ColliderHandle,
}

impl PhysicsHooks for OneWayPlatformHook {
fn active_hooks(&self) -> PhysicsHooksFlags {
PhysicsHooksFlags::MODIFY_SOLVER_CONTACTS
}

fn modify_solver_contacts(&self, context: &mut ContactModificationContext) {
// The allowed normal for the first platform is its local +y axis, and the
// allowed normal for the second platform is its local -y axis.
//
// Now we have to be careful because the `manifold.local_n1` normal points
// toward the outside of the shape of `context.co1`. So we need to flip the
// allowed normal direction if the platform is in `context.collider_handle2`.
//
// Therefore:
// - If context.collider_handle1 == self.platform1 then the allowed normal is +y.
// - If context.collider_handle2 == self.platform1 then the allowed normal is -y.
// - If context.collider_handle1 == self.platform2 then its allowed normal +y needs to be flipped to -y.
// - If context.collider_handle2 == self.platform2 then the allowed normal -y needs to be flipped to +y.
let mut allowed_local_n1 = Vector3::zeros();

if context.collider_handle1 == self.platform1 {
allowed_local_n1 = Vector3::y();
} else if context.collider_handle2 == self.platform1 {
// Flip the allowed direction.
allowed_local_n1 = -Vector3::y();
}

if context.collider_handle1 == self.platform2 {
allowed_local_n1 = -Vector3::y();
} else if context.collider_handle2 == self.platform2 {
// Flip the allowed direction.
allowed_local_n1 = -Vector3::y();
}

// Call the helper function that simulates one-way platforms.
context.update_as_oneway_platform(&allowed_local_n1, 0.1);

// Set the surface velocity of the accepted contacts.
let tangent_velocity = if context.collider_handle1 == self.platform1
|| context.collider_handle2 == self.platform2
{
-12.0
} else {
12.0
};

for contact in context.solver_contacts.iter_mut() {
contact.tangent_velocity.z = tangent_velocity;
}
}
}

pub fn init_world(testbed: &mut Testbed) {
/*
* World
*/
let mut bodies = RigidBodySet::new();
let mut colliders = ColliderSet::new();
let joints = JointSet::new();

/*
* Ground
*/
let rigid_body = RigidBodyBuilder::new_static().build();
let handle = bodies.insert(rigid_body);

let collider = ColliderBuilder::cuboid(9.0, 0.5, 25.0)
.translation(0.0, 2.0, 30.0)
.modify_solver_contacts(true)
.build();
let platform1 = colliders.insert(collider, handle, &mut bodies);
let collider = ColliderBuilder::cuboid(9.0, 0.5, 25.0)
.translation(0.0, -2.0, -30.0)
.modify_solver_contacts(true)
.build();
let platform2 = colliders.insert(collider, handle, &mut bodies);

/*
* Setup the one-way platform hook.
*/
let physics_hooks = OneWayPlatformHook {
platform1,
platform2,
};

/*
* Spawn cubes at regular intervals and apply a custom gravity
* depending on their position.
*/
testbed.add_callback(move |mut window, mut graphics, physics, _, run_state| {
if run_state.timestep_id % 50 == 0 && physics.bodies.len() <= 7 {
// Spawn a new cube.
let collider = ColliderBuilder::cuboid(1.0, 2.0, 1.5).build();
let body = RigidBodyBuilder::new_dynamic()
.translation(0.0, 6.0, 20.0)
.build();
let handle = physics.bodies.insert(body);
physics
.colliders
.insert(collider, handle, &mut physics.bodies);

if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) {
graphics.add(window, handle, &physics.bodies, &physics.colliders);
}
}

physics.bodies.foreach_active_dynamic_body_mut(|_, body| {
if body.position().translation.y > 1.0 {
body.set_gravity_scale(1.0, false);
} else if body.position().translation.y < -1.0 {
body.set_gravity_scale(-1.0, false);
}
});
});

/*
* Set up the testbed.
*/
testbed.set_world_with_params(
bodies,
colliders,
joints,
Vector3::new(0.0, -9.81, 0.0),
physics_hooks,
);
testbed.look_at(Point3::new(-100.0, 0.0, 0.0), Point3::origin());
}

fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}
Loading

0 comments on commit 649eba1

Please sign in to comment.