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 QueryPipeline::cast_shape.
This method has similar arguments as QueryPipeline::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
let shape = Cuboid::new(Vector::new(1.0, 2.0));
let shape_pos = Pose::new(Vector::new(0.0, 1.0), 0.2);
let shape_vel = Vector::new(0.1, 0.4);
let max_toi = 4.0;
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,
};
let query_pipeline = world.query_pipeline_with_filter(filter);
if let Some((handle, hit)) = query_pipeline.cast_shape(
&shape_pos, shape_vel, &shape, options
) {
// The first collider hit has the handle `handle`. The `hit` is a
// structure containing details about the hit configuration.
println!("Hit the collider {:?} with the configuration: {:?}", handle, hit);
}
let shape = Cuboid::new(Vector::new(1.0, 2.0, 3.0));
let shape_pos = Pose::new(Vector::new(0.0, 1.0, 0.0), Vector::new(0.2, 0.7, 0.1));
let shape_vel = Vector::new(0.1, 0.4, 0.2);
let max_toi = 4.0;
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,
};
let query_pipeline = world.query_pipeline_with_filter(filter);
if let Some((handle, hit)) = query_pipeline.cast_shape(
&shape_pos, shape_vel, &shape, options
) {
// The first collider hit has the handle `handle`. The `hit` is a
// structure containing details about the hit configuration.
println!("Hit the collider {:?} with the configuration: {:?}", handle, hit);
}
The result of the shape-casting includes the handle 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.
If the shape was already intersecting a collider at its initial position, the witness points and normals are only
reliable if ShapeCastOptions::compute_impact_geometry_on_penetration is set to true.
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, QueryPipeline::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 NonlinearRigidMotion 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 (with hit.witness1 and hit.normal1 in world-space, and hit.witness2 and
hit.normal2 in the local-space of the cast shape, whose pose at the time of impact is given by
NonlinearRigidMotion::position_at_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.