From adb3a19257eeaa7ad94346058971299537faa61d Mon Sep 17 00:00:00 2001 From: Affinator Date: Tue, 30 Jan 2024 09:43:41 +0100 Subject: [PATCH 1/5] Implement cast_ray_predicate to allow filtering the colliders with a function (#297) As I was writing a xpbd backend for bevy_mod_picking, I found that a ray cast with a predicate was missing to implement the functionality efficiently. I was inspired by the predicate from bevy_rapier: https://github.com/dimforge/bevy_rapier/blob/c6bcce4695d596a7a9c8e91748d4dbb3d31f6d13/src/pipeline/query_filter.rs#L12 # Objective - Implement a ray cast function with the support for a predicate function to filter the colliders ## Solution - Added support for a predicate starting at SpatialQuery and down the pipeline - I added a small (and fun to play with) example to illustrate one possible use case - I chose not to change existing functionality. Therefore there is some code duplication in QueryPipelineAsCompositeShapeWithPredicate. This could be unified by introducing an Option in QueryPipelineAsCompositeShape, but that would create breaking changes at many places. --------- Co-authored-by: hendrikd Co-authored-by: Joona Aalto --- .../examples/cast_ray_predicate.rs | 200 ++++++++++++++++++ src/plugins/collision/mod.rs | 4 +- src/plugins/spatial_query/pipeline.rs | 98 +++++++++ src/plugins/spatial_query/system_param.rs | 62 ++++++ 4 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 crates/bevy_xpbd_3d/examples/cast_ray_predicate.rs diff --git a/crates/bevy_xpbd_3d/examples/cast_ray_predicate.rs b/crates/bevy_xpbd_3d/examples/cast_ray_predicate.rs new file mode 100644 index 00000000..95e61466 --- /dev/null +++ b/crates/bevy_xpbd_3d/examples/cast_ray_predicate.rs @@ -0,0 +1,200 @@ +#![allow(clippy::unnecessary_cast)] + +use bevy::{pbr::NotShadowReceiver, prelude::*}; +use bevy_xpbd_3d::{math::*, prelude::*}; +use examples_common_3d::XpbdExamplePlugin; + +fn main() { + App::new() + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) + .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) + .insert_resource(Msaa::Sample4) + .add_systems(Startup, setup) + .add_systems(Update, (movement, reset_colors, raycast).chain()) + .run(); +} + +/// The acceleration used for movement. +#[derive(Component)] +struct MovementAcceleration(Scalar); + +#[derive(Component)] +struct RayIndicator; + +/// If to be ignored by raycast +#[derive(Component)] +struct OutOfGlass(bool); + +const CUBE_COLOR: Color = Color::rgba(0.2, 0.7, 0.9, 1.0); +const CUBE_COLOR_GLASS: Color = Color::rgba(0.2, 0.7, 0.9, 0.5); + +fn setup( + mut commands: Commands, + mut materials: ResMut>, + mut meshes: ResMut>, +) { + let cube_mesh = meshes.add(Mesh::from(shape::Cube { size: 1.0 })); + + // Ground + commands.spawn(( + PbrBundle { + mesh: cube_mesh.clone(), + material: materials.add(Color::rgb(0.7, 0.7, 0.8).into()), + transform: Transform::from_xyz(0.0, -2.0, 0.0).with_scale(Vec3::new(100.0, 1.0, 100.0)), + ..default() + }, + RigidBody::Static, + Collider::cuboid(1.0, 1.0, 1.0), + )); + + let cube_size = 2.0; + + // Spawn cube stacks + for x in -1..2 { + for y in -1..2 { + for z in -1..2 { + let position = Vec3::new(x as f32, y as f32 + 5.0, z as f32) * (cube_size + 0.05); + let material: StandardMaterial = if x == -1 { + CUBE_COLOR_GLASS.into() + } else { + CUBE_COLOR.into() + }; + commands.spawn(( + PbrBundle { + mesh: cube_mesh.clone(), + material: materials.add(material.clone()), + transform: Transform::from_translation(position) + .with_scale(Vec3::splat(cube_size as f32)), + ..default() + }, + RigidBody::Dynamic, + Collider::cuboid(1.0, 1.0, 1.0), + MovementAcceleration(10.0), + OutOfGlass(x == -1), + )); + } + } + } + + // raycast indicator + commands.spawn(( + PbrBundle { + mesh: cube_mesh.clone(), + material: materials.add(Color::rgb(1.0, 0.0, 0.0).into()), + transform: Transform::from_xyz(-500.0, 2.0, 0.0) + .with_scale(Vec3::new(1000.0, 0.1, 0.1)), + ..default() + }, + RayIndicator, + NotShadowReceiver, + )); + + // Directional light + commands.spawn(DirectionalLightBundle { + directional_light: DirectionalLight { + illuminance: 20_000.0, + shadows_enabled: true, + ..default() + }, + transform: Transform::default().looking_at(Vec3::new(-1.0, -2.5, -1.5), Vec3::Y), + ..default() + }); + + // Camera + commands.spawn(Camera3dBundle { + transform: Transform::from_translation(Vec3::new(0.0, 12.0, 40.0)) + .looking_at(Vec3::Y * 5.0, Vec3::Y), + ..default() + }); +} + +fn movement( + time: Res