Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to modify the contact points seen by the constraints solver #120

Merged
merged 10 commits into from
Feb 24, 2021
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