specs-physics

[Fork] Integration of Amethyst, SPECS and Nphysics together.
git clone https://git.jojolepro.com/specs-physics.git
Log | Files | Refs | README | LICENSE

commit e70b07560de2dc0c8ab9f4ea3981341b4d24f008
parent 7fae74c61e3b79da7e8afcd1ed862f408f48d424
Author: Joël Lupien (Jojolepro) <jojolepromain@gmail.com>
Date:   Fri,  7 Dec 2018 21:01:42 -0500

Fix Collider bugs and update example.

Diffstat:
Mexamples/amethyst.rs | 81++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/colliders.rs | 3++-
Msrc/systems/mod.rs | 2+-
Msrc/systems/sync_bodies_from_physics.rs | 135++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/systems/sync_bodies_to_physics.rs | 16+++++++---------
Msrc/systems/sync_colliders_to_physics.rs | 75+++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
6 files changed, 207 insertions(+), 105 deletions(-)

diff --git a/examples/amethyst.rs b/examples/amethyst.rs @@ -8,9 +8,13 @@ use amethyst::renderer::{ Stage, Texture, }; use amethyst::{Application, GameData, GameDataBuilder, SimpleState, StateData}; -use nphysics_ecs_dumb::bodies::DynamicBody; +use nalgebra::Isometry3; +use nphysics_ecs_dumb::ncollide::shape::{Ball, Cuboid, ShapeHandle}; use nphysics_ecs_dumb::nphysics::math::{Point, Velocity}; -use nphysics_ecs_dumb::systems::PhysicsBundle; +use nphysics_ecs_dumb::nphysics::object::BodyHandle; +use nphysics_ecs_dumb::nphysics::object::Material as PhysicsMaterial; +use nphysics_ecs_dumb::nphysics::volumetric::Volumetric; +use nphysics_ecs_dumb::*; use num_traits::identities::One; struct GameState; @@ -52,11 +56,11 @@ impl SimpleState for GameState { .build(); // Add Light - data.world.add_resource(AmbientColor(Rgba::from([0.5; 3]))); + data.world.add_resource(AmbientColor(Rgba::from([0.2; 3]))); data.world .create_entity() .with(Light::Point(PointLight { - intensity: 3.0, + intensity: 50.0, color: Rgba::white(), radius: 5.0, smoothness: 4.0, @@ -71,20 +75,79 @@ impl SimpleState for GameState { &data.world.read_resource(), ); + let ball = ShapeHandle::new(Ball::new(1.0)); + // Add Sphere (todo: add many, add rigidbodies and colliders) data.world .create_entity() - .with(sphere_handle) - .with(material) - .with(Transform::from(Vector3::new(0.0, 0.0, -10.0))) + .with(sphere_handle.clone()) + .with(material.clone()) + .with(Transform::from(Vector3::new(0.0, 3.0, -10.0))) .with(GlobalTransform::default()) .with(DynamicBody::new_rigidbody_with_velocity( - Velocity::linear(0.0, 10.0, 0.0), + Velocity::linear(0.0, 1.0, 0.0), 10.0, Matrix3::one(), - Point::new(0.0, 0.0, 0.0), + ball.center_of_mass(), )) + .with( + ColliderBuilder::from(ball.clone()) + .collision_group(0) + .physics_material(PhysicsMaterial::default()) + .build() + .unwrap(), + ) .build(); + + // Add ground + data.world + .create_entity() + .with(sphere_handle) + .with(material) + .with(Transform::from(Vector3::new(0.0, 0.0, -10.0))) + .with(GlobalTransform::default()) + .with( + //ColliderBuilder::from(ShapeHandle::new(Cuboid::new(Vector3::new(5.0, 1.0, 5.0)))) + ColliderBuilder::from(ball) + .collision_group(0) + .physics_material(PhysicsMaterial::default()) + .build() + .unwrap(), + ) + .build(); + + //---------------------------------------------------- nphysics's ball3.rs adapted + + /*let mut physics_world = data.world.write_resource::<PhysicsWorld>(); + physics_world.set_gravity(Vector3::new(0.0, -9.81, 0.0)); + + // Material for all objects. + let material = PhysicsMaterial::default(); + let ground_shape = + ShapeHandle::new(Cuboid::new(Vector3::repeat(1.0 - 0.01))); + let ground_pos = Isometry3::new(Vector3::new(0.0, -0.5, -15.0), nalgebra::zero()); + + physics_world.add_collider( + 0.01, + ground_shape, + BodyHandle::ground(), + ground_pos, + material.clone(), + ); + let geom = ShapeHandle::new(Ball::new(1.0 - 0.01)); + let inertia = geom.inertia(1.0); + let center_of_mass = geom.center_of_mass(); + + + let pos = Isometry3::new(Vector3::new(0.0, 5.0, -15.0), nalgebra::zero()); + let handle = physics_world.add_rigid_body(pos, inertia, center_of_mass); + physics_world.add_collider( + 0.01, + geom.clone(), + handle, + Isometry3::identity(), + material.clone(), + );*/ } } diff --git a/src/colliders.rs b/src/colliders.rs @@ -31,11 +31,12 @@ impl ColliderType { } } -#[derive(new, Clone, Builder)] +#[derive(new, Clone, Builder, Debug)] #[builder(pattern = "owned")] pub struct Collider { #[new(default)] //#[serde(skip)] + #[builder(default)] pub(crate) handle: Option<ColliderHandle>, /// Warning: Changing the margin after inserting the entity will have no effect. pub margin: f32, diff --git a/src/systems/mod.rs b/src/systems/mod.rs @@ -51,7 +51,7 @@ impl<'a, 'b, 'c> SystemBundle<'a, 'b> for PhysicsBundle<'c> { builder.add( SyncCollidersToPhysicsSystem::new(), SYNC_COLLIDERS_TO_PHYSICS_SYSTEM, - &[], + &[SYNC_BODIES_TO_PHYSICS_SYSTEM], ); builder.add( diff --git a/src/systems/sync_bodies_from_physics.rs b/src/systems/sync_bodies_from_physics.rs @@ -31,7 +31,7 @@ impl<'a> System<'a> for SyncBodiesFromPhysicsSystem { Write<'a, EventChannel<EntityProximityEvent>>, WriteStorage<'a, GlobalTransform>, WriteStorage<'a, DynamicBody>, - ReadStorage<'a, Transform>, + WriteStorage<'a, Transform>, ReadStorage<'a, Collider>, ); @@ -43,7 +43,7 @@ impl<'a> System<'a> for SyncBodiesFromPhysicsSystem { mut proximity_events, mut global_transforms, mut physics_bodies, - local_transforms, + mut local_transforms, colliders, ) = data; @@ -51,55 +51,83 @@ impl<'a> System<'a> for SyncBodiesFromPhysicsSystem { // Apply the updated values of the simulated world to our Components #[allow(unused_mut)] - for (mut global_transform, mut body, local_transform) in ( + for (mut global_transform, mut body, mut local_transform) in ( &mut global_transforms, &mut physics_bodies, - local_transforms.maybe(), + (&mut local_transforms).maybe(), ) .join() { - let updated_body = physical_world.body(body.handle().unwrap()); + if let Some(updated_body_handle) = body.handle() { + let updated_body = physical_world.body(updated_body_handle); + + if updated_body.is_ground() || !updated_body.is_active() || updated_body.is_static() + { + trace!( + "Skipping synchronizing data from non-dynamic body: {:?}", + updated_body.handle() + ); + continue; + } - if updated_body.is_ground() || !updated_body.is_active() || updated_body.is_static() { - trace!("Skipping synchronizing data from non-dynamic body: {:?}", updated_body.handle()); - continue; - } + match (body, updated_body) { + ( + DynamicBody::RigidBody(ref mut rigid_body), + Body::RigidBody(ref updated_rigid_body), + ) => { + trace!( + "Synchronizing RigidBody from handle: {:?}", + updated_rigid_body.handle() + ); - match (body, updated_body) { - ( - DynamicBody::RigidBody(ref mut rigid_body), - Body::RigidBody(ref updated_rigid_body), - ) => { - trace!("Synchronizing RigidBody from handle: {:?}", updated_rigid_body.handle()); - - trace!("Synchronized RigidBody's updated position: {}", updated_rigid_body.position()); - // TODO: Might get rid of the scale!!! - global_transform.0 = updated_rigid_body - .position() - .to_homogeneous() - .prepend_nonuniform_scaling( - local_transform - .map(|tr| tr.scale()) - .unwrap_or(&Vector3::new(1.0, 1.0, 1.0)), + trace!( + "Synchronized RigidBody's updated position: {:?}", + updated_rigid_body.position() ); - trace!("Synchronized RigidBody's updated velocity: {:?}", updated_rigid_body.velocity()); - rigid_body.velocity = *updated_rigid_body.velocity(); + global_transform.0 = updated_rigid_body + .position() + .to_homogeneous() + .prepend_nonuniform_scaling( + &local_transform + .as_ref() + .map(|tr| *tr.scale()) + .unwrap_or_else(|| Vector3::new(1.0, 1.0, 1.0)), + ); + + if let Some(ref mut local_transform) = local_transform { + *local_transform.isometry_mut() = updated_rigid_body.position(); + } + + trace!( + "Synchronized RigidBody's updated velocity: {:?}", + updated_rigid_body.velocity() + ); + rigid_body.velocity = *updated_rigid_body.velocity(); - trace!("Synchronized RigidBody's updated inertia: {:?}", updated_rigid_body.inertia()); - let inertia = updated_rigid_body.inertia(); - rigid_body.mass = inertia.linear; - rigid_body.angular_mass = inertia.angular; + trace!( + "Synchronized RigidBody's updated inertia: {:?}", + updated_rigid_body.inertia() + ); + let inertia = updated_rigid_body.inertia(); + rigid_body.mass = inertia.linear; + rigid_body.angular_mass = inertia.angular; - trace!("Synchronized RigidBody's updated center of mass: {}", updated_rigid_body.center_of_mass()); - rigid_body.center_of_mass = updated_rigid_body.center_of_mass(); - } - (DynamicBody::Multibody(_multibody), Body::Multibody(_updated_multibody)) => { - error!("Multibody found; not implemented currently, sorry!") - } - // TODO: Add data to unexpected pair message. Not straightforward. - _ => error!("Unexpected dynamics body pair!"), - }; + trace!( + "Synchronized RigidBody's updated center of mass: {:?}", + updated_rigid_body.center_of_mass() + ); + rigid_body.center_of_mass = updated_rigid_body.center_of_mass(); + } + (DynamicBody::Multibody(_multibody), Body::Multibody(_updated_multibody)) => { + error!("Multibody found; not implemented currently, sorry!") + } + // TODO: Add data to unexpected pair message. Not straightforward. + _ => error!("Unexpected dynamics body pair!"), + }; + } else { + warn!("Found body in physics world not registered in the ECS world!"); + } } trace!("Iterating collision events."); @@ -107,16 +135,16 @@ impl<'a> System<'a> for SyncBodiesFromPhysicsSystem { let collision_world = physical_world.collision_world(); contact_events.iter_write(collision_world.contact_events().iter().cloned().map(|ev| { - trace!("Emitting contact event: {}", ev); + trace!("Emitting contact event: {:?}", ev); let (handle1, handle2) = match ev { ContactEvent::Started(h1, h2) => (h1, h2), ContactEvent::Stopped(h1, h2) => (h1, h2), }; - let e1 = entity_from_handle(&entities, &colliders, &handle1) + let e1 = entity_from_handle(&entities, &colliders, handle1) .expect("Failed to find entity for collider."); - let e2 = entity_from_handle(&entities, &colliders, &handle2) + let e2 = entity_from_handle(&entities, &colliders, handle2) .expect("Failed to find entity for collider."); (e1, e2, ev) })); @@ -127,40 +155,29 @@ impl<'a> System<'a> for SyncBodiesFromPhysicsSystem { .iter() .cloned() .map(|ev| { - trace!("Emitting proximity event: {}", ev); + trace!("Emitting proximity event: {:?}", ev); - let e1 = entity_from_handle(&entities, &colliders, &ev.collider1) + let e1 = entity_from_handle(&entities, &colliders, ev.collider1) .expect("Failed to find entity for collider."); - let e2 = entity_from_handle(&entities, &colliders, &ev.collider2) + let e2 = entity_from_handle(&entities, &colliders, ev.collider2) .expect("Failed to find entity for collider."); (e1, e2, ev) }), ); - - // TODO: reader id from other system? - // Now that we changed them all, let's remove all those pesky events that were generated. - // global_transforms - // .channel() - // .read(&mut self.transforms_reader_id.as_mut().unwrap()) - // .for_each(|_| ()); - // physics_bodies - // .channel() - // .read(&mut self.physics_bodies_reader_id.as_mut().unwrap()) - // .for_each(|_| ()); } } pub fn entity_from_handle( entities: &EntitiesRes, colliders: &ReadStorage<Collider>, - handle: &ColliderHandle, + handle: ColliderHandle, ) -> Option<Entity> { (&*entities, colliders) .join() .find(|(_, c)| { c.handle .expect("Collider has no handle and wasn't removed.") - == *handle + == handle }) .map(|(e, _)| e) } diff --git a/src/systems/sync_bodies_to_physics.rs b/src/systems/sync_bodies_to_physics.rs @@ -8,7 +8,7 @@ use amethyst::ecs::{ }; use core::ops::Deref; use nalgebra::try_convert; -use nphysics3d::math::{Inertia, Force, Isometry}; +use nphysics3d::math::{Force, Inertia, Isometry}; #[derive(Default)] pub struct SyncBodiesToPhysicsSystem { @@ -115,19 +115,17 @@ impl<'a> System<'a> for SyncBodiesToPhysicsSystem { trace!("Detected changed dynamics body with id {}", id); match body { DynamicBody::RigidBody(ref mut rigid_body) => { - match physical_world.rigid_body_mut(rigid_body.handle.unwrap()) { - Some(physical_body) => { + if let Some(physical_body) = physical_world.rigid_body_mut(rigid_body.handle.unwrap()) { let position: Isometry<f32> = try_convert(transform.0).unwrap(); - trace!("Updating rigid body in physics world with isometry: {}", position); + trace!( + "Updating rigid body in physics world with isometry: {}", + position + ); physical_body.set_position(position); physical_body.set_velocity(rigid_body.velocity); physical_body.apply_force(&rigid_body.external_forces); // if you changed the mass properties at all... too bad! - }, - None => { - - } } } DynamicBody::Multibody(_) => { @@ -150,7 +148,7 @@ impl<'a> System<'a> for SyncBodiesToPhysicsSystem { } } -fn iterate_events<'a, T, D, S>( +fn iterate_events<T, D, S>( tracked_storage: &Storage<T, D>, reader: &mut ReaderId<ComponentEvent>, inserted: &mut BitSet, diff --git a/src/systems/sync_colliders_to_physics.rs b/src/systems/sync_colliders_to_physics.rs @@ -1,7 +1,7 @@ use crate::bodies::DynamicBody; use crate::Collider; use crate::PhysicsWorld; -use amethyst::core::GlobalTransform; +use amethyst::core::Transform; use amethyst::ecs::storage::{ComponentEvent, GenericReadStorage, MaskedStorage}; use amethyst::ecs::{ BitSet, Component, Entities, Join, ReadStorage, ReaderId, Resources, Storage, System, @@ -20,7 +20,7 @@ impl<'a> System<'a> for SyncCollidersToPhysicsSystem { type SystemData = ( WriteExpect<'a, PhysicsWorld>, Entities<'a>, - ReadStorage<'a, GlobalTransform>, + ReadStorage<'a, Transform>, ReadStorage<'a, DynamicBody>, WriteStorage<'a, Collider>, ); @@ -32,7 +32,7 @@ impl<'a> System<'a> for SyncCollidersToPhysicsSystem { let mut modified_colliders = BitSet::new(); iterate_events( - &transforms, + &colliders, self.colliders_reader_id.as_mut().unwrap(), &mut inserted_colliders, &mut modified_colliders, @@ -41,46 +41,47 @@ impl<'a> System<'a> for SyncCollidersToPhysicsSystem { &colliders, ); - for (entity, mut collider, id) in ( + for (entity, mut collider, id, tr) in ( &entities, &mut colliders, &inserted_colliders | &modified_colliders, + &transforms, ) .join() { if inserted_colliders.contains(id) { - trace!("Detected inserted collider with id {}", id); - + println!("Detected inserted collider with id {:?}", id); // Just inserted. Remove old one and insert new. - if let Some(handle) = collider.handle { - if physical_world.collider(handle).is_some() { - trace!("Removing collider marked as inserted that already exists with handle: {:?}", handle); - - physical_world.remove_colliders(&[handle]); - } + if collider.handle.is_some() + && physical_world.collider(collider.handle.unwrap()).is_some() + { + physical_world.remove_colliders(&[collider.handle.unwrap()]); } let parent = if let Some(rb) = rigid_bodies.get(entity) { - trace!("Attaching inserted collider to rigid body: {}", entity); + trace!("Attaching inserted collider to rigid body: {:?}", entity); rb.handle().expect( "You should normally have a body handle at this point. This is a bug.", ) } else { - trace!("Attaching inserted collider to ground."); - BodyHandle::ground() }; + let position = if parent.is_ground() { + tr.isometry() * collider.offset_from_parent + } else { + collider.offset_from_parent + }; collider.handle = Some(physical_world.add_collider( collider.margin, collider.shape.clone(), parent, - collider.offset_from_parent, + position, collider.physics_material.clone(), )); - trace!("Inserted collider to world with values: {}", collider); + trace!("Inserted collider to world with values: {:?}", collider); let prediction = physical_world.prediction(); let angular_prediction = physical_world.angular_prediction(); @@ -91,15 +92,15 @@ impl<'a> System<'a> for SyncCollidersToPhysicsSystem { .collision_object_mut(collider.handle.unwrap()) .unwrap(); - let collider_handle = collider_object.handle().clone(); - - collision_world.set_collision_group(collider_handle, collider.collision_group); - collider_object.set_query_type(collider.query_type.to_geometric_query_type( collider.margin, prediction, angular_prediction, )); + + let collider_handle = collider_object.handle(); + + collision_world.set_collision_group(collider_handle, collider.collision_group); } else if modified_colliders.contains(id) || modified_colliders.contains(id) { println!("Detected changed collider with id {:?}", id); @@ -110,8 +111,7 @@ impl<'a> System<'a> for SyncCollidersToPhysicsSystem { let collider_handle = collision_world .collision_object(collider.handle.unwrap()) .unwrap() - .handle() - .clone(); + .handle(); collision_world.set_collision_group(collider_handle, collider.collision_group); collision_world.set_shape(collider_handle, collider.shape.clone()); @@ -119,8 +119,26 @@ impl<'a> System<'a> for SyncCollidersToPhysicsSystem { let collider_object = collision_world .collision_object_mut(collider.handle.unwrap()) .unwrap(); - //collider_handle.set_shape(collider_handle.shape); - collider_object.set_position(collider.offset_from_parent.clone()); + + let parent = if let Some(rb) = rigid_bodies.get(entity) { + trace!("Updating collider to rigid body: {:?}", entity); + + rb.handle().expect( + "You should normally have a body handle at this point. This is a bug.", + ) + } else { + trace!("Updating collider to ground."); + + BodyHandle::ground() + }; + + let position = if parent.is_ground() { + tr.isometry() * collider.offset_from_parent + } else { + collider.offset_from_parent + }; + + collider_object.set_position(position); collider_object.set_query_type(collider.query_type.to_geometric_query_type( collider.margin, prediction, @@ -131,6 +149,11 @@ impl<'a> System<'a> for SyncCollidersToPhysicsSystem { .set_material(collider.physics_material.clone()); } } + + colliders + .channel() + .read(&mut self.colliders_reader_id.as_mut().unwrap()) + .for_each(|_| ()); } fn setup(&mut self, res: &mut Resources) { @@ -141,7 +164,7 @@ impl<'a> System<'a> for SyncCollidersToPhysicsSystem { } } -fn iterate_events<'a, T, D, S>( +fn iterate_events<T, D, S>( tracked_storage: &Storage<T, D>, reader: &mut ReaderId<ComponentEvent>, inserted: &mut BitSet,