mod common;
use std::sync::mpsc::channel;
use bevy::prelude::*;
use bevy_mod_physx::prelude::{self as bpx, *};
use bevy_mod_physx::types::OnCollision;
use bevy_mod_physx::utils::{get_actor_entity_from_ptr, get_shape_entity_from_ptr};
use physx::scene::FilterShaderDescriptor;
use physx_sys::{
FilterShaderCallbackInfo,
PxContactPairFlags,
PxContactPair_extractContacts,
PxFilterFlags,
PxPairFlags,
};
#[derive(Resource)]
struct DemoMaterials {
normal: Handle<StandardMaterial>,
highlighted: Handle<StandardMaterial>,
}
#[derive(Component)]
struct Highlightable;
#[derive(Component)]
#[component(storage = "SparseSet")]
struct Highlighted;
unsafe extern "C" fn simulation_filter_shader(s: *mut FilterShaderCallbackInfo) -> PxFilterFlags {
unsafe {
let s = &mut *s as &mut FilterShaderCallbackInfo;
let pair_flags = &mut *(s.pairFlags) as &mut PxPairFlags;
*pair_flags = PxPairFlags::SolveContact |
PxPairFlags::DetectDiscreteContact |
PxPairFlags::NotifyTouchFound |
PxPairFlags::NotifyTouchLost |
PxPairFlags::NotifyContactPoints;
PxFilterFlags::empty()
}
}
#[derive(Message)]
pub struct CollisionEvent {
actor0: Entity,
actor1: Entity,
}
fn main() {
let (mpsc_sender, mpsc_receiver) = channel();
let on_collision = OnCollision::new(move |header, pairs| {
assert!(!header.flags.contains(physx_sys::PxContactPairHeaderFlags::RemovedActor0));
let actor0 = unsafe { get_actor_entity_from_ptr(header.actors[0] as *const _) };
assert!(!header.flags.contains(physx_sys::PxContactPairHeaderFlags::RemovedActor1));
let actor1 = unsafe { get_actor_entity_from_ptr(header.actors[1] as *const _) };
for pair in pairs {
assert!(!pair.flags.contains(PxContactPairFlags::RemovedShape0));
let shape0 = unsafe { get_shape_entity_from_ptr(pair.shapes[0] as *const _) };
assert!(!pair.flags.contains(PxContactPairFlags::RemovedShape0));
let shape1 = unsafe { get_shape_entity_from_ptr(pair.shapes[1] as *const _) };
let contacts = unsafe {
let mut buffer = Vec::with_capacity(pairs.len());
PxContactPair_extractContacts(pair, buffer.as_mut_ptr(), pairs.len() as u32);
buffer.set_len(pairs.len());
buffer
};
let mut status = "Contact";
if pair.flags.contains(PxContactPairFlags::ActorPairHasFirstTouch) {
status = "New contact";
}
if pair.flags.contains(PxContactPairFlags::ActorPairLostTouch) {
status = "Lost contact";
}
println!("{status} between {shape0:?} and {shape1:?}:");
for contact in contacts {
println!(
"position: {:?}, separation: {:?}, normal: {:?}, impulse: {:?}",
contact.position.to_bevy(),
contact.separation,
contact.normal.to_bevy(),
contact.impulse.to_bevy(),
);
}
println!("----------");
}
mpsc_sender.send(CollisionEvent { actor0, actor1 }).unwrap();
});
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(PhysicsPlugins.set(
PhysicsCore {
scene: bpx::SceneDescriptor {
simulation_filter_shader: FilterShaderDescriptor::CallDefaultFirst(simulation_filter_shader),
on_collision: Some(on_collision),
..default()
},
..default()
}.with_pvd()
))
.add_plugins(common::DemoUtils) .add_physics_event_channel(mpsc_receiver)
.add_systems(Startup, (
init_materials,
ApplyDeferred,
(
spawn_plane,
spawn_tiles,
spawn_dynamic,
spawn_camera_and_light,
),
).chain())
.add_systems(Update, highlight_on_hit)
.run();
}
fn init_materials(
mut commands: Commands,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.insert_resource(DemoMaterials {
normal: materials.add(Color::srgb(0.8, 0.7, 0.6)),
highlighted: materials.add(Color::srgb(0.3, 0.4, 0.9)),
});
}
fn spawn_plane(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut physics: ResMut<Physics>,
mut px_geometries: ResMut<Assets<bpx::Geometry>>,
mut px_materials: ResMut<Assets<bpx::Material>>,
) {
let primitive = Plane3d::default();
let mesh = meshes.add(primitive.mesh().size(500., 500.));
let material = materials.add(Color::srgb(0.3, 0.5, 0.3));
let px_geometry = px_geometries.add(primitive);
let px_material = px_materials.add(bpx::Material::new(&mut physics, 0.5, 0.5, 0.6));
commands.spawn_empty()
.insert((
Mesh3d::from(mesh.clone()),
MeshMaterial3d::from(material.clone()),
))
.insert(bpx::RigidBody::Static)
.insert(bpx::Shape {
geometry: px_geometry,
material: px_material,
..default()
})
.insert(Name::new("Plane"));
}
fn spawn_tiles(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
materials: Res<DemoMaterials>,
mut px_geometries: ResMut<Assets<bpx::Geometry>>,
) {
let num = 32;
let rad = 1.0;
let height = 0.1;
let primitive = Cuboid::new(rad * 2., height * 2., rad * 2.);
let px_geometry = px_geometries.add(primitive);
let mesh = meshes.add(primitive);
let material = materials.normal.clone();
let shift = rad * 2.5;
let centerx = shift * (num / 2) as f32;
let centerz = shift * (num / 2) as f32;
for i in 0..num {
for j in 0..num {
let x = i as f32 * shift - centerx;
let y = height / 2.;
let z = j as f32 * shift - centerz;
commands.spawn((
Mesh3d::from(mesh.clone()),
MeshMaterial3d::from(material.clone()),
Transform::from_xyz(x, y, z),
RigidBody::Static,
bpx::Shape {
geometry: px_geometry.clone(),
..default()
},
Highlightable,
));
}
}
}
fn spawn_dynamic(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut physics: ResMut<Physics>,
mut px_geometries: ResMut<Assets<bpx::Geometry>>,
mut px_materials: ResMut<Assets<bpx::Material>>,
) {
let primitive = Sphere::new(1.25);
let mesh = meshes.add(primitive);
let material = materials.add(Color::srgb(0.8, 0.7, 0.6));
let px_geometry = px_geometries.add(primitive);
let px_material = px_materials.add(bpx::Material::new(&mut physics, 0., 0., 1.));
let transform = Transform::from_xyz(0., 5., 32.5);
commands.spawn_empty()
.insert((
Mesh3d::from(mesh.clone()),
MeshMaterial3d::from(material.clone()),
transform,
))
.insert(bpx::RigidBody::Dynamic)
.insert(MassProperties::density(10.))
.insert(bpx::Shape {
material: px_material,
geometry: px_geometry,
..default()
})
.insert(Velocity::linear(Vec3::new(2.5, -5., -10.)))
.insert(Name::new("Ball"));
}
fn spawn_camera_and_light(mut commands: Commands) {
commands
.spawn((
Name::new("Camera"),
Transform::from_xyz(-21., 0., 0.),
Visibility::default(),
))
.with_children(|builder| {
builder.spawn((
Camera3d::default(),
Transform::from_translation(Vec3::new(-41.7, 33., 0.)).looking_at(Vec3::ZERO, Vec3::Y),
));
});
commands.spawn((
Name::new("Light"),
DirectionalLight::default(),
Transform::from_rotation(Quat::from_euler(EulerRot::XYZ, -1.2, -0.2, 0.)),
));
}
fn highlight_on_hit(
mut commands: Commands,
materials: Res<DemoMaterials>,
mut events: MessageReader<CollisionEvent>,
highlighable: Query<(), With<Highlightable>>,
) {
for event in events.read() {
for entity in [ event.actor0, event.actor1 ] {
if highlighable.get(entity).is_ok() {
commands.entity(entity)
.insert(MeshMaterial3d::from(materials.highlighted.clone()))
.insert(Highlighted);
}
}
}
}