scene_queries_shape_casting
Shape-casting (aka. sweep tests) is the big brother of ray-casting. The only difference with ray-cast is that instead of being a point travelling along a straight line, we have a complete shape travelling along a straight line. This is typically used for character controllers in games to determine by how much the player can move before it hits the environment.
Just like ray-casting, it is possible to control the behavior of the shape-casting like limiting the distance
travelled by the shape cast, and ignoring some colliders. See the details about the
max_toi and filter arguments in the ray-casting section.
The shape-casting along a straight line is performed by RapierContext::cast_shape.
This method has similar arguments as RapierContext::cast_ray except
that the ray is replaced by three arguments: the shape being cast, the initial position of the shape (this is analog to ray.origin) and
the linear velocity the shape is travelling at (this is analog to ray.dir):
- Example 2D
- Example 3D
/* Cast a shape inside of a system. */
fn cast_shape(rapier_context: ReadRapierContext) {
let rapier_context = rapier_context.single().unwrap();
let shape = Collider::cuboid(1.0, 2.0);
let shape_pos = Vec2::new(1.0, 2.0);
let shape_rot = 0.8;
let shape_vel = Vec2::new(0.1, 0.4);
let filter = QueryFilter::default();
let options = ShapeCastOptions {
max_time_of_impact: 4.0,
target_distance: 0.0,
stop_at_penetration: false,
compute_impact_geometry_on_penetration: false,
};
if let Some((entity, hit)) =
rapier_context.cast_shape(shape_pos, shape_rot, shape_vel, &shape, options, filter)
{
// The first collider hit has the entity `entity`. The `hit` is a
// structure containing details about the hit configuration.
println!(
"Hit the entity {:?} with the configuration: {:?}",
entity, hit
);
}
}
/* Cast a shape inside of a system. */
fn cast_shape(rapier_context: ReadRapierContext) {
let rapier_context = rapier_context.single().unwrap();
let shape = Collider::cuboid(1.0, 2.0, 3.0);
let shape_pos = Vec3::new(1.0, 2.0, 3.0);
let shape_rot = Quat::from_rotation_z(0.8);
let shape_vel = Vec3::new(0.1, 0.4, 0.2);
let filter = QueryFilter::default();
let options = ShapeCastOptions {
max_time_of_impact: 4.0,
target_distance: 0.0,
stop_at_penetration: false,
compute_impact_geometry_on_penetration: false,
};
if let Some((entity, hit)) =
rapier_context.cast_shape(shape_pos, shape_rot, shape_vel, &shape, options, filter)
{
// The first collider hit has the entity `entity`. The `hit` is a
// structure containing details about the hit configuration.
println!(
"Hit the entity {:?} with the configuration: {:?}",
entity, hit
);
}
}
The result of the shape-casting includes the entity of the first collider being hit, as well as detailed information about the geometry of the hit:
hit.time_of_impact: indicates the time of impact between the shape and the collider hit. This means that after travelling a distance ofshape_vel * hit.time_of_impactthe collider and the cast shape are exactly touching. Ifhit.time_of_impact == 0.0then the shape is already intersecting a collider at its initial position.hit.witness1: indicates the contact point on the collider hit when the cast shape and the collider are touching, expressed in world-space.hit.witness2: indicates the contact point on the cast shape when the cast shape and the collider are touching, expressed in the local-space of the cast shape.hit.normal1: indicates the outward normal of the collider hit at the contact pointhit.witness1, expressed in world-space.hit.normal2: indicates the outward normal of the cast shape at the contact pointhit.witness2, expressed in the local-space of the cast shape.
Because the cast shape moved, hit.witness2 and hit.normal2 can be converted to world-space by applying the pose of
the cast shape at the time of impact, i.e., its initial pose translated by shape_vel * hit.time_of_impact.
The witness points and normals are grouped into hit.details (a ShapeCastHitDetails). These details are None if
the shape was already intersecting a collider at its initial position (hit.status is then
ShapeCastStatus::PenetratingOrWithinTargetDist) unless ShapeCastOptions::compute_impact_geometry_on_penetration is
set to true. Finally, hit.subshape1 is the index of the part of the collider that was hit if its shape is composed
of several pieces (compound shapes, triangle meshes, etc.)
Note that the frames are different for Collider::cast_shape and Collider::cast_shape_nonlinear, which cast a
collider against another one outside of any physics context: there, every witness point and normal is expressed in the
local-space of its own shape.
Nonlinear shape-casting
The shape-casting above only moves the shape along a straight line: its orientation doesn't change during the cast.
If the rotation of the shape matters, RapierContext::cast_shape_nonlinear performs a
nonlinear shape-casting: the shape follows a rigid motion combining a constant linear velocity and a constant
angular velocity. This motion is described by a NonlinearMotion which contains the initial pose of the
shape, its linear and angular velocities, and the local-space point around which the shape rotates. At time , the
shape is rotated by the angular velocity times around that point, and translated by the linear velocity times .
The first impact is searched for between the start_time and end_time arguments. This is typically useful to
predict if a rotating object (e.g. a spinning blade, a swinging door, or the collider of a rigid-body with a non-zero
angular velocity) will hit something during a timestep.
If the shape is already intersecting a collider at start_time, setting stop_at_penetration to true makes the cast
report that collider with a time of impact equal to start_time. If it is false, that penetration is ignored when the
motion is separating the shapes, and the cast searches for a later impact that would result in tunnelling. The result
has the same form as for cast_shape (the details are None if the hit reported is a penetration at
start_time). Nonlinear shape-casting is more expensive than the linear one, so it is recommended to
use cast_shape whenever the shape doesn't rotate.