scene_queries_filters
It is common to exclude some colliders from being considered by a scene query. For example, a ray-cast performed for a
character controller will usually want to skip the character itself. Sometimes, we may even want it to ignore both the
character and any collider attached to a dynamic rigid-body, and ignore all sensors. To allow this filtering, most
scene queries take QueryFilter argument that lets you describe what needs to be excluded. In particular
its fields:R3QueryOptions argument that lets you describe what needs to be excluded. In particular the fields of its filter (an R3QueryFilter), and its predicate:filter argument, a QueryFilter that lets you describe what needs to be excluded. In particular the keyword arguments of its constructor:
flagsallows you to discard whole families of colliders based on their types or their parent types (e.g. exclude all sensors and all the colliders attached to a dynamic rigid-body).groupsis used to apply the collision group rules for the scene query. The scene query will only consider hits with colliders with collision groups compatible with this collision group (using the bitwise test described in the collision groups section).exclude_collideris the handle of one collider the query must ignore.exclude_rigid_bodyis the handle of one rigid-body with attached colliders the query must ignore.predicateis a user-definedclosure callback to apply any filtering rule. This can be used if the other filtering options above are not flexible enough.
The query options are initialized by r3DefaultQueryOptions, which doesn't exclude any collider. The flags are a
combination of the R3_QUERY_EXCLUDE_* constants (e.g. R3_QUERY_EXCLUDE_SENSORS), or one of the shortcuts
R3_QUERY_ONLY_DYNAMIC, R3_QUERY_ONLY_KINEMATIC, and R3_QUERY_ONLY_FIXED. The groups are only applied if the
use_groups field is set to 1. The exclude_collider and exclude_rigid_body fields are set to an invalid handle
(e.g. R3_INVALID_COLLIDER_HANDLE) to exclude nothing. Finally, the predicate is a callback called for each collider
that passed the other filtering rules: it returns 0 to exclude that collider. It is given the userData field of the
query options, a read-only access to the world (an R3ReadContext to be given to the r3ReadCollider_* and
r3ReadRigidBody_* functions), and the handle of the collider. Other scene queries can be performed from this
callback, but the world cannot be modified until the outer query returns.
The exclude_collider and exclude_rigid_body fields are set to the entity of the collider or rigid-body to
exclude (instead of its handle). The predicate is given the entity of each collider as well as its Rapier collider
(rapier::geometry::Collider), so its shape, position, or parent can be read without any additional ECS query. Since
the filter only holds a reference to the predicate closure, that closure can borrow other system parameters, e.g., a
Query for reading the components of the collider's entity.
QueryFilter() doesn't exclude any collider. The flags are a combination (with the | operator) of the
QueryFilterFlags constants, e.g., QueryFilterFlags.EXCLUDE_SENSORS or QueryFilterFlags.ONLY_DYNAMIC. The filters
are generally built with the static methods of QueryFilter setting its flags (e.g. QueryFilter.exclude_dynamic
or QueryFilter.only_fixed), followed by its builder methods (exclude_sensors, exclude_solids, groups,
exclude_collider, exclude_rigid_body, and predicate) which return a new filter so that they can be chained.
Finally, the predicate is a function called for each collider that passed the other filtering rules: it is given the
handle of the collider and a view of the Collider itself, and returns False to exclude that collider. It can read
any property of the collider (e.g. its user_data), but must not modify the world. An exception raised by the
predicate is raised again by the scene query.
Here is an an example of usage of the query filters with ray-casting:
let ray = Ray::new(Vector::new(1.0, 2.0), Vector::new(0.0, 1.0));
let max_toi = 4.0;
let solid = true;
let filter = QueryFilter::exclude_dynamic()
.exclude_sensors()
.exclude_rigid_body(player_handle)
.groups(InteractionGroups::new(
Group::GROUP_1 | Group::GROUP_2,
Group::GROUP_1,
InteractionTestMode::And,
))
.predicate(&|handle, collider| collider.user_data == 10);
let query_pipeline = world.query_pipeline_with_filter(filter);
if let Some((handle, toi)) = query_pipeline.cast_ray(&ray, max_toi, solid) {
// Handle the hit.
}
/* Cast a ray inside of a system. */
fn cast_ray_filtered(
rapier_context: ReadRapierContext,
player_query: Query<Entity, With<Player>>,
custom_data_query: Query<&CustomData>,
) {
let rapier_context = rapier_context.single().unwrap();
let player_handle = player_query.single().unwrap();
let ray_pos = Vec2::new(1.0, 2.0);
let ray_dir = Vec2::new(0.0, 1.0);
let max_toi = 4.0;
let solid = true;
let predicate = |entity, _collider: &_| {
// We can use a query to bevy inside the predicate.
custom_data_query
.get(entity)
.is_ok_and(|custom_data| custom_data.data == 10)
};
let filter = QueryFilter::exclude_dynamic()
.exclude_sensors()
.exclude_rigid_body(player_handle)
.groups(CollisionGroups::new(
Group::GROUP_1 | Group::GROUP_2,
Group::GROUP_1,
))
.predicate(&predicate);
if let Some((entity, toi)) = rapier_context.cast_ray(ray_pos, ray_dir, max_toi, solid, filter) {
// Handle the hit.
}
}
let ray = new RAPIER.Ray({ x: 1.0, y: 2.0 }, { x: 0.0, y: 1.0 });
let maxToi = 4.0;
let solid = true;
let filterFlags = QueryFilterFlags.EXCLUDE_DYNAMIC;
let filterGroups = 0x000b0001;
let filterExcludeRigidBody = player_rigid_body;
let filterPredicate = (collider: Collider) => data.get(collider.handle) == 10.0;
let hit = world.castRay(ray, maxToi, solid, filterFlags, filterGroups, null, filterExcludeRigidBody, filterPredicate);
if (hit != null) {
// Handle the hit.
}
// The predicate is called for each collider that passed the other filtering rules.
// Returning 0 excludes the collider from the scene query.
static R2Bool RAPIER_CALL user_data_predicate(void *user_data, const R2ReadContext *read,
R2ColliderHandle handle) {
(void)user_data;
return r2ReadCollider_UserData(read, handle).low == 10;
}
static void query_filter_section(const R2World *world, R2RigidBodyHandle player_handle) {
R2Vector ray_origin = r2Vector(1.0, 2.0);
R2Vector ray_dir = r2Vector(0.0, 1.0);
R2Real max_toi = 4.0;
R2Bool solid = 1;
R2QueryOptions options = r2DefaultQueryOptions();
options.filter.flags = R2_QUERY_EXCLUDE_DYNAMIC | R2_QUERY_EXCLUDE_SENSORS;
options.filter.exclude_rigid_body = player_handle;
options.filter.use_groups = 1;
options.filter.groups.memberships = 0x0001 | 0x0002; // Groups 1 and 2.
options.filter.groups.filter = 0x0001; // Group 1.
options.filter.groups.test_mode = R2_GROUPS_AND;
options.predicate = user_data_predicate;
options.userData = NULL; // Given to the predicate as its first argument.
R2RayToi toi = r2CastRayToi(world, &options, ray_origin, ray_dir, max_toi, solid);
if (toi.found) {
// Handle the hit.
}
}
ray = rp.Ray(origin=(1.0, 2.0, 3.0), dir=(0.0, 1.0, 0.0))
max_toi = 4.0
solid = True
query_filter = (
rp.QueryFilter.exclude_dynamic()
.exclude_sensors()
.exclude_rigid_body(player_handle)
.groups(
rp.InteractionGroups(
memberships=rp.Group.GROUP_1 | rp.Group.GROUP_2,
filter=rp.Group.GROUP_1,
test_mode=rp.InteractionTestMode.AND,
)
)
.predicate(lambda handle, collider: collider.user_data == 10)
)
query_pipeline = world.query_pipeline
hit = query_pipeline.cast_ray(ray, max_toi, solid, filter=query_filter)
if hit is not None:
handle, toi = hit
# Handle the hit.