diff --git a/Cargo.toml b/Cargo.toml index 6eb98b9..162dca9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "specs-physics" -version = "0.2.2" -authors = ["Benjamin Amling "] -repository = "https://github.com/bamling/specs-physics.git" -homepage = "https://github.com/bamling/specs-physics.git" +version = "0.3.0" +authors = ["Benjamin Amling ", "Kel ", "jojolepro "] +repository = "https://github.com/amethyst/specs-physics.git" +homepage = "https://github.com/amethyst/specs-physics.git" edition = "2018" license = "MIT" @@ -13,6 +13,11 @@ description = "nphysics integration for the Specs entity component system" keywords = ["specs", "nphysics", "nphysics3d"] +[features] +default = [] + +amethyst = ["amethyst_core"] + [dependencies] log = "0.4.6" specs = "0.14.3" @@ -21,6 +26,7 @@ shrev = "1.1.1" nalgebra = "0.18.0" ncollide3d = "0.19.1" nphysics3d = "0.11.1" +amethyst_core = { version = "0.6.0", optional = true } [dev-dependencies] simple_logger = "1.2.0" diff --git a/README.md b/README.md index 861f285..84be3be 100644 --- a/README.md +++ b/README.md @@ -17,152 +17,6 @@ The dream is to *simply* create `Entity`s with a set of configurable `Component`s and have most of your physics covered, be it collision/proximity detection, velocity and acceleration or gravity. -## Usage - -To use **specs-physics**, add the following dependency to your projects *Cargo.toml*: - -```toml -[dependencies] -specs-physics = "0.2.2" -``` - -**specs-physics** defines a set of [Specs](https://slide-rs.github.io/specs/) `System`s and `Component`s to handle the creation, modification and removal of [nphysics](https://www.nphysics.org/) objects ([RigidBody](https://www.nphysics.org/rigid_body_simulations_with_contacts/#rigid-bodies), [Collider](https://www.nphysics.org/rigid_body_simulations_with_contacts/#colliders)) and the synchronisation of object positions and global gravity between both worlds. - -### Generic types - -All `System`s and `Component`s provided by this crate require between one and two type parameters to function properly. These were explicitly introduced to keep this integration as generic as possible and allow compatibility with as many external crates and game engines as possible. - -`N: RealField` - [nphysics](https://www.nphysics.org/) is built upon [nalgebra](https://nalgebra.org/) and uses various types and structures from this crate. **specs-physics** builds up on this even further and utilises the same structures, which all work with any type that implements `nalgebra::RealField`. `nalgebra::RealField` is by default implemented for various standard types, such as `f32` and`f64`. `nalgebra` is re-exported under `specs_physics::math`. - -`P: Position` - a type parameter which implements the `specs_physics::bodies::Position` *trait*, requiring also a `Component` implementation with a `FlaggedStorage`. This `Position` `Component` is used to initially place a [RigidBody](https://www.nphysics.org/rigid_body_simulations_with_contacts/#rigid-bodies) in the [nphysics](https://www.nphysics.org/) world and later used to synchronise the updated translation and rotation of these bodies back into the [Specs](https://slide-rs.github.io/specs/) world. - -Example for a `Position` `Component`, simply using the "Isometry" type (aka combined translation and rotation structure) directly: -```rust -use specs::{Component, DenseVecStorage, FlaggedStorage}; -use specs_physics::{ - bodies::Position, - math::Isometry3, -}; - -struct Position(Isometry3); - -impl Component for Position { - type Storage = FlaggedStorage>; -} - -impl Position for Position { - fn as_isometry(&self) -> Isometry3 { - self.0 - } - - fn set_isometry(&mut self, isometry: &Isometry3) { - self.0 = isometry; - } -} -``` - -### Components - -##### PhysicsBody - -The `specs_physics::PhysicsBody` `Component` is used to define [RigidBody](https://www.nphysics.org/rigid_body_simulations_with_contacts/#rigid-bodies) from the comforts of your [Specs](https://slide-rs.github.io/specs/) world. Changes to the `PhysicsBody` will automatically be synchronised with [nphysics](https://www.nphysics.org/). - -Example: - -```rust -use specs_physics::{ - bodies::BodyStatus, - math::{Matrix3, Point3, Vector3}, - PhysicsBodyBuilder, -}; - -let physics_body = PhysicsBodyBuilder::from(BodyStatus::Dynamic) - .gravity_enabled(true) - .velocity(Vector3::new(1.0, 1.0, 1.0)) - .angular_inertia(Matrix3::from_diagonal_element(3.0)) - .mass(1.3) - .local_center_of_mass(Point3::new(0.0, 0.0, 0.0)) - .build(); -``` - -##### PhysicsCollider - -`specs_physics::PhysicsCollider`s are the counterpart to `PhysicsBody`s. They can exist on their own or as a part of a `PhysicsBody` `PhysicsCollider`s are used to define and create [Colliders](https://www.nphysics.org/rigid_body_simulations_with_contacts/#colliders) in [nphysics](https://www.nphysics.org/). - -Example: - -```rust -use specs_physics::{ - colliders::{ - material::{BasicMaterial, MaterialHandle}, - CollisionGroups, - }, - math::Isometry3, - PhysicsColliderBuilder, - Shape, -}; - -let physics_collider = PhysicsColliderBuilder::from(Shape::Rectangle(10.0, 10.0, 1.0)) - .offset_from_parent(Isometry3::identity()) - .density(1.2) - .material(MaterialHandle::new(BasicMaterial::default())) - .margin(0.02) - .collision_groups(CollisionGroups::default()) - .linear_prediction(0.001) - .angular_prediction(0.0) - .sensor(true) - .build(); -``` - -To assign multiple [Colliders](https://www.nphysics.org/rigid_body_simulations_with_contacts/#colliders) the the same body, [Entity hierarchy](https://github.com/bamling/specs-physics/blob/master/examples/hierarchy.rs) can be used. This utilises [specs-hierarchy](https://github.com/rustgd/specs-hierarchy). - -### Systems - -The following `System`s currently exist and should be added to your `Dispatcher` in order: - -1. `specs_physics::systems::SyncBodiesToPhysicsSystem` - handles the creation, modification and removal of [RigidBodies](https://www.nphysics.org/rigid_body_simulations_with_contacts/#rigid-bodies) based on the `PhysicsBody` `Component` and an implementation of the `Position` *trait*. -2. `specs_physics::systems::SyncCollidersToPhysicsSystem` - handles the creation, modification and removal of [Colliders](https://www.nphysics.org/rigid_body_simulations_with_contacts/#colliders) based on the `PhysicsCollider` `Component`. This `System` depends on `SyncBodiesToPhysicsSystem` as [Colliders](https://www.nphysics.org/rigid_body_simulations_with_contacts/#colliders) can depend on [RigidBodies](https://www.nphysics.org/rigid_body_simulations_with_contacts/#rigid-bodies). -3. `specs_physics::systems::SyncGravityToPhysicsSystem` - handles the modification of the [nphysics](https://www.nphysics.org/) `World`s gravity. -4. `specs_physics::systems::PhysicsStepperSystem` - handles the progression of the [nphysics](https://www.nphysics.org/) `World` and causes objects to actually move and change their position. This `System` is the backbone for collision detection. -5. `specs_physics::systems::SyncPositionsFromPhysicsSystem` - handles the synchronisation of [RigidBodies](https://www.nphysics.org/rigid_body_simulations_with_contacts/#rigid-bodies) positions back into the [Specs](https://slide-rs.github.io/specs/) `Component`s. This `System` also utilises the `Position` *trait* implementation. - -An example `Dispatcher` with all required `System`s: - -```rust -let dispatcher = DispatcherBuilder::new() - .with( - SyncBodiesToPhysicsSystem::::default(), - "sync_bodies_to_physics_system", - &[], - ) - .with( - SyncCollidersToPhysicsSystem::::default(), - "sync_colliders_to_physics_system", - &["sync_bodies_to_physics_system"], - ) - .with( - SyncGravityToPhysicsSystem::::default(), - "sync_gravity_to_physics_system", - &[], - ) - .with( - PhysicsStepperSystem::::default(), - "physics_stepper_system", - &[ - "sync_bodies_to_physics_system", - "sync_colliders_to_physics_system", - "sync_gravity_to_physics_system", - ], - ) - .with( - SyncPositionsFromPhysicsSystem::::default(), - "sync_positions_from_physics_system", - &["physics_stepper_system"], - ) - .build(); -``` - -Alternatively to building your own `Dispatcher`, you can always fall back on the convenience function `specs_physics::physics_dispatcher()`, which returns a configured *default* `Dispatcher` for you or `specs_physics::register_physics_systems()` which takes a `DispatcherBuilder` as an argument and registers the required `System`s for you . ### Examples @@ -181,7 +35,7 @@ Full *TODO* sheet can be found in [this nphysics issue][todo] - [x] RigidBody Components - [x] Collider Components - [x] Proximity and Contact EventChannels -- [ ] External force property +- [x] External force property - [x] `log` based logging - [ ] Handling Body Activation & Sleeping - [ ] Multibody-based Component Joints diff --git a/examples/basic.rs b/examples/basic.rs index 091938d..caf1a7d 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,41 +1,17 @@ extern crate log; extern crate simple_logger; -use specs::{world::Builder, Component, DenseVecStorage, FlaggedStorage, World}; +use specs::world::{Builder, World}; use specs_physics::{ - bodies::{BodyStatus, Position}, colliders::Shape, - math::Isometry3, + nalgebra::Isometry3, + nphysics::object::BodyStatus, physics_dispatcher, PhysicsBodyBuilder, PhysicsColliderBuilder, + SimplePosition, }; -/// `SimpleTranslation` struct for synchronisation of the position between the -/// ECS and nphysics; this has to implement both `Component` and `Position` -struct SimpleTranslation { - x: f32, - y: f32, - z: f32, -} - -impl Component for SimpleTranslation { - type Storage = FlaggedStorage>; -} - -impl Position for SimpleTranslation { - fn as_isometry(&self) -> Isometry3 { - Isometry3::translation(self.x, self.y, self.z) - } - - fn set_isometry(&mut self, isometry: &Isometry3) { - let translation = isometry.translation.vector; - self.x = translation.x; - self.y = translation.y; - self.z = translation.z; - } -} - fn main() { // initialise the logger for system logs simple_logger::init().unwrap(); @@ -45,17 +21,15 @@ fn main() { // create the dispatcher containing all relevant Systems; alternatively to using // the convenience function you can add all required Systems by hand - let mut dispatcher = physics_dispatcher::(); + let mut dispatcher = physics_dispatcher::>(); dispatcher.setup(&mut world.res); // create an Entity containing the required Components world .create_entity() - .with(SimpleTranslation { - x: 1.0, - y: 1.0, - z: 1.0, - }) + .with(SimplePosition::(Isometry3::::translation( + 1.0, 1.0, 1.0, + ))) .with(PhysicsBodyBuilder::::from(BodyStatus::Dynamic).build()) .with(PhysicsColliderBuilder::::from(Shape::Rectangle(1.0, 1.0, 1.0)).build()) .build(); diff --git a/examples/collision.rs b/examples/collision.rs index b8da6f6..cb19d8c 100644 --- a/examples/collision.rs +++ b/examples/collision.rs @@ -2,41 +2,17 @@ extern crate log; extern crate simple_logger; -use specs::{world::Builder, Component, DenseVecStorage, FlaggedStorage, World}; +use specs::world::{Builder, World}; use specs_physics::{ - bodies::{BodyStatus, Position}, colliders::Shape, - math::{Isometry3, Vector3}, + nalgebra::Isometry3, + nphysics::{algebra::Velocity3, object::BodyStatus}, physics_dispatcher, PhysicsBodyBuilder, PhysicsColliderBuilder, + SimplePosition, }; -/// `SimpleTranslation` struct for synchronisation of the position between the -/// ECS and nphysics; this has to implement both `Component` and `Position` -struct SimpleTranslation { - x: f32, - y: f32, - z: f32, -} - -impl Component for SimpleTranslation { - type Storage = FlaggedStorage>; -} - -impl Position for SimpleTranslation { - fn as_isometry(&self) -> Isometry3 { - Isometry3::translation(self.x, self.y, self.z) - } - - fn set_isometry(&mut self, isometry: &Isometry3) { - let translation = isometry.translation.vector; - self.x = translation.x; - self.y = translation.y; - self.z = translation.z; - } -} - fn main() { // initialise the logger for system logs simple_logger::init().unwrap(); @@ -46,20 +22,18 @@ fn main() { // create the dispatcher containing all relevant Systems; alternatively to using // the convenience function you can add all required Systems by hand - let mut dispatcher = physics_dispatcher::(); + let mut dispatcher = physics_dispatcher::>(); dispatcher.setup(&mut world.res); // create an Entity with a dynamic PhysicsBody component and a velocity let entity = world .create_entity() - .with(SimpleTranslation { - x: 1.0, - y: 1.0, - z: 1.0, - }) + .with(SimplePosition::(Isometry3::::translation( + 1.0, 1.0, 1.0, + ))) .with( PhysicsBodyBuilder::::from(BodyStatus::Dynamic) - .velocity(Vector3::new(1.0, 0.0, 0.0)) + .velocity(Velocity3::linear(1.0, 0.0, 0.0)) .build(), ) .with(PhysicsColliderBuilder::::from(Shape::Rectangle(2.0, 2.0, 1.0)).build()) @@ -69,11 +43,9 @@ fn main() { // one world .create_entity() - .with(SimpleTranslation { - x: 3.0, - y: 1.0, - z: 1.0, - }) + .with(SimplePosition::(Isometry3::::translation( + 3.0, 1.0, 1.0, + ))) .with(PhysicsBodyBuilder::::from(BodyStatus::Static).build()) .with(PhysicsColliderBuilder::::from(Shape::Rectangle(2.0, 2.0, 1.0)).build()) .build(); @@ -83,8 +55,8 @@ fn main() { // fetch the translation component for the Entity with the dynamic body; the // position should still be approx the same - let pos_storage = world.read_storage::(); + let pos_storage = world.read_storage::>(); let pos = pos_storage.get(entity).unwrap(); - info!("updated position: x={}, y={}, z={},", pos.x, pos.y, pos.z); + info!("updated position: {}", pos.0.translation); } diff --git a/examples/events.rs b/examples/events.rs index a05e1c1..b6b34c9 100644 --- a/examples/events.rs +++ b/examples/events.rs @@ -2,42 +2,18 @@ extern crate log; extern crate simple_logger; -use specs::{world::Builder, Component, DenseVecStorage, FlaggedStorage, World}; +use specs::world::{Builder, World}; use specs_physics::{ - bodies::{BodyStatus, Position}, colliders::Shape, events::ContactEvents, - math::{Isometry3, Vector3}, + nalgebra::Isometry3, + nphysics::{algebra::Velocity3, object::BodyStatus}, physics_dispatcher, PhysicsBodyBuilder, PhysicsColliderBuilder, + SimplePosition, }; -/// `SimpleTranslation` struct for synchronisation of the position between the -/// ECS and nphysics; this has to implement both `Component` and `Position` -struct SimpleTranslation { - x: f32, - y: f32, - z: f32, -} - -impl Component for SimpleTranslation { - type Storage = FlaggedStorage>; -} - -impl Position for SimpleTranslation { - fn as_isometry(&self) -> Isometry3 { - Isometry3::translation(self.x, self.y, self.z) - } - - fn set_isometry(&mut self, isometry: &Isometry3) { - let translation = isometry.translation.vector; - self.x = translation.x; - self.y = translation.y; - self.z = translation.z; - } -} - fn main() { // initialise the logger for system logs simple_logger::init().unwrap(); @@ -47,21 +23,19 @@ fn main() { // create the dispatcher containing all relevant Systems; alternatively to using // the convenience function you can add all required Systems by hand - let mut dispatcher = physics_dispatcher::(); + let mut dispatcher = physics_dispatcher::>(); dispatcher.setup(&mut world.res); let mut contact_event_reader = world.res.fetch_mut::().register_reader(); // create an Entity with a dynamic PhysicsBody component and a velocity world .create_entity() - .with(SimpleTranslation { - x: 1.0, - y: 1.0, - z: 1.0, - }) + .with(SimplePosition::(Isometry3::::translation( + 1.0, 1.0, 1.0, + ))) .with( PhysicsBodyBuilder::::from(BodyStatus::Dynamic) - .velocity(Vector3::new(1.0, 0.0, 0.0)) + .velocity(Velocity3::linear(1.0, 0.0, 0.0)) .build(), ) .with(PhysicsColliderBuilder::::from(Shape::Rectangle(2.0, 2.0, 1.0)).build()) @@ -71,11 +45,9 @@ fn main() { // one world .create_entity() - .with(SimpleTranslation { - x: 3.0, - y: 1.0, - z: 1.0, - }) + .with(SimplePosition::(Isometry3::::translation( + 3.0, 1.0, 1.0, + ))) .with(PhysicsBodyBuilder::::from(BodyStatus::Static).build()) .with(PhysicsColliderBuilder::::from(Shape::Rectangle(2.0, 2.0, 1.0)).build()) .build(); diff --git a/examples/hierarchy.rs b/examples/hierarchy.rs index 496d593..1b57f85 100644 --- a/examples/hierarchy.rs +++ b/examples/hierarchy.rs @@ -1,42 +1,18 @@ extern crate log; extern crate simple_logger; -use specs::{world::Builder, Component, DenseVecStorage, FlaggedStorage, World}; +use specs::world::{Builder, World}; use specs_physics::{ - bodies::{BodyStatus, Position}, colliders::Shape, - math::Isometry3, + nalgebra::Isometry3, + nphysics::object::BodyStatus, physics_dispatcher, PhysicsBodyBuilder, PhysicsColliderBuilder, PhysicsParent, + SimplePosition, }; -/// `SimpleTranslation` struct for synchronisation of the position between the -/// ECS and nphysics; this has to implement both `Component` and `Position` -struct SimpleTranslation { - x: f32, - y: f32, - z: f32, -} - -impl Component for SimpleTranslation { - type Storage = FlaggedStorage>; -} - -impl Position for SimpleTranslation { - fn as_isometry(&self) -> Isometry3 { - Isometry3::translation(self.x, self.y, self.z) - } - - fn set_isometry(&mut self, isometry: &Isometry3) { - let translation = isometry.translation.vector; - self.x = translation.x; - self.y = translation.y; - self.z = translation.z; - } -} - fn main() { // initialise the logger for system logs simple_logger::init().unwrap(); @@ -46,18 +22,16 @@ fn main() { // create the dispatcher containing all relevant Systems; alternatively to using // the convenience function you can add all required Systems by hand - let mut dispatcher = physics_dispatcher::(); + let mut dispatcher = physics_dispatcher::>(); dispatcher.setup(&mut world.res); // create an Entity containing the required Components; this Entity will be the // parent let parent = world .create_entity() - .with(SimpleTranslation { - x: 1.0, - y: 1.0, - z: 1.0, - }) + .with(SimplePosition::(Isometry3::::translation( + 1.0, 1.0, 1.0, + ))) .with(PhysicsBodyBuilder::::from(BodyStatus::Dynamic).build()) .with(PhysicsColliderBuilder::::from(Shape::Rectangle(1.0, 1.0, 1.0)).build()) .build(); @@ -68,11 +42,9 @@ fn main() { // collider will be attached to the parent let _child = world .create_entity() - .with(SimpleTranslation { - x: 1.0, - y: 1.0, - z: 1.0, - }) + .with(SimplePosition::(Isometry3::::translation( + 1.0, 1.0, 1.0, + ))) .with( PhysicsColliderBuilder::::from(Shape::Rectangle(1.0, 1.0, 1.0)) .sensor(true) diff --git a/examples/positions.rs b/examples/positions.rs index 8a1716d..45801e6 100644 --- a/examples/positions.rs +++ b/examples/positions.rs @@ -2,41 +2,17 @@ extern crate log; extern crate simple_logger; -use specs::{world::Builder, Component, DenseVecStorage, FlaggedStorage, World}; +use specs::world::{Builder, World}; use specs_physics::{ - bodies::{BodyStatus, Position}, colliders::Shape, - math::{Isometry3, Vector3}, + nalgebra::Isometry3, + nphysics::{algebra::Velocity3, object::BodyStatus}, physics_dispatcher, PhysicsBodyBuilder, PhysicsColliderBuilder, + SimplePosition, }; -/// `SimpleTranslation` struct for synchronisation of the position between the -/// ECS and nphysics; this has to implement both `Component` and `Position` -struct SimpleTranslation { - x: f32, - y: f32, - z: f32, -} - -impl Component for SimpleTranslation { - type Storage = FlaggedStorage>; -} - -impl Position for SimpleTranslation { - fn as_isometry(&self) -> Isometry3 { - Isometry3::translation(self.x, self.y, self.z) - } - - fn set_isometry(&mut self, isometry: &Isometry3) { - let translation = isometry.translation.vector; - self.x = translation.x; - self.y = translation.y; - self.z = translation.z; - } -} - fn main() { // initialise the logger for system logs simple_logger::init().unwrap(); @@ -46,21 +22,19 @@ fn main() { // create the dispatcher containing all relevant Systems; alternatively to using // the convenience function you can add all required Systems by hand - let mut dispatcher = physics_dispatcher::(); + let mut dispatcher = physics_dispatcher::>(); dispatcher.setup(&mut world.res); // create an Entity containing the required Components; for this examples sake // we'll give the PhysicsBody a velocity let entity = world .create_entity() - .with(SimpleTranslation { - x: 1.0, - y: 1.0, - z: 1.0, - }) + .with(SimplePosition::(Isometry3::::translation( + 1.0, 1.0, 1.0, + ))) .with( PhysicsBodyBuilder::::from(BodyStatus::Dynamic) - .velocity(Vector3::new(1.0, 0.0, 0.0)) + .velocity(Velocity3::linear(1.0, 0.0, 0.0)) .build(), ) .with(PhysicsColliderBuilder::::from(Shape::Rectangle(1.0, 1.0, 1.0)).build()) @@ -70,8 +44,8 @@ fn main() { dispatcher.dispatch(&world.res); // fetch the SimpleTranslation component for the created Entity - let pos_storage = world.read_storage::(); + let pos_storage = world.read_storage::>(); let pos = pos_storage.get(entity).unwrap(); - info!("updated position: x={}, y={}, z={},", pos.x, pos.y, pos.z); + info!("updated position: {}", pos.0.translation); } diff --git a/src/bodies.rs b/src/bodies.rs index 40ddace..ee4458c 100644 --- a/src/bodies.rs +++ b/src/bodies.rs @@ -1,9 +1,43 @@ -use nalgebra::RealField; -use nphysics::object::BodyHandle; -pub use nphysics::object::BodyStatus; use specs::{Component, DenseVecStorage, FlaggedStorage}; -use crate::math::{Isometry3, Matrix3, Point3, Vector3}; +use crate::{ + nalgebra::{Isometry3, Matrix3, Point3, RealField}, + nphysics::{ + algebra::{Force3, ForceType, Velocity3}, + object::{Body, BodyHandle, BodyPart, BodyStatus, RigidBody, RigidBodyDesc}, + }, +}; + +pub mod util { + use specs::{Component, DenseVecStorage, FlaggedStorage}; + + use crate::{ + bodies::Position, + nalgebra::{Isometry3, RealField}, + }; + + pub struct SimplePosition(pub Isometry3); + + impl Position for SimplePosition { + fn isometry(&self) -> &Isometry3 { + &self.0 + } + + fn isometry_mut(&mut self) -> &mut Isometry3 { + &mut self.0 + } + + fn set_isometry(&mut self, isometry: &Isometry3) -> &mut Self { + self.0.rotation = isometry.rotation; + self.0.translation = isometry.translation; + self + } + } + + impl Component for SimplePosition { + type Storage = FlaggedStorage>; + } +} /// An implementation of the `Position` trait is required for the /// synchronisation of the position of Specs and nphysics objects. @@ -14,8 +48,24 @@ use crate::math::{Isometry3, Matrix3, Point3, Vector3}; pub trait Position: Component>> + Send + Sync { - fn as_isometry(&self) -> Isometry3; - fn set_isometry(&mut self, isometry: &Isometry3); + fn isometry(&self) -> &Isometry3; + fn isometry_mut(&mut self) -> &mut Isometry3; + fn set_isometry(&mut self, isometry: &Isometry3) -> &mut Self; +} + +#[cfg(feature = "amethyst")] +impl Position for amethyst_core::Transform { + fn isometry(&self) -> &Isometry3 { + self.isometry() + } + + fn isometry_mut(&mut self) -> &mut Isometry3 { + self.isometry_mut() + } + + fn set_isometry(&mut self, isometry: &Isometry3) -> &mut Self { + self.set_isometry(*isometry) + } } /// The `PhysicsBody` `Component` represents a `PhysicsWorld` `RigidBody` in @@ -26,16 +76,70 @@ pub struct PhysicsBody { pub(crate) handle: Option, pub gravity_enabled: bool, pub body_status: BodyStatus, - pub velocity: Vector3, + pub velocity: Velocity3, pub angular_inertia: Matrix3, pub mass: N, pub local_center_of_mass: Point3, + external_forces: Force3, } impl Component for PhysicsBody { type Storage = FlaggedStorage>; } +impl PhysicsBody { + pub fn check_external_force(&self) -> &Force3 { + &self.external_forces + } + + pub fn apply_external_force(&mut self, force: &Force3) -> &mut Self { + self.external_forces += *force; + self + } + + /// For creating new rigid body from this component's values + pub(crate) fn to_rigid_body_desc(&self) -> RigidBodyDesc { + RigidBodyDesc::new() + .gravity_enabled(self.gravity_enabled) + .status(self.body_status) + .velocity(self.velocity) + .angular_inertia(self.angular_inertia) + .mass(self.mass) + .local_center_of_mass(self.local_center_of_mass) + } + + /// Note: applies forces by draining external force property + pub(crate) fn apply_to_physics_world(&mut self, rigid_body: &mut RigidBody) -> &mut Self { + rigid_body.enable_gravity(self.gravity_enabled); + rigid_body.set_status(self.body_status); + rigid_body.set_velocity(self.velocity); + rigid_body.set_angular_inertia(self.angular_inertia); + rigid_body.set_mass(self.mass); + rigid_body.set_local_center_of_mass(self.local_center_of_mass); + rigid_body.apply_force(0, &self.drain_external_force(), ForceType::Force, true); + self + } + + pub(crate) fn update_from_physics_world(&mut self, rigid_body: &RigidBody) -> &mut Self { + // These two probably won't be modified but hey + self.gravity_enabled = rigid_body.gravity_enabled(); + self.body_status = rigid_body.status(); + + self.velocity = *rigid_body.velocity(); + + let local_inertia = rigid_body.local_inertia(); + self.angular_inertia = local_inertia.angular; + self.mass = local_inertia.linear; + self + } + + fn drain_external_force(&mut self) -> Force3 { + let value = self.external_forces; + self.external_forces = Force3::::zero(); + value + } +} + /// The `PhysicsBodyBuilder` implements the builder pattern for `PhysicsBody`s /// and is the recommended way of instantiating and customising new /// `PhysicsBody` instances. @@ -44,14 +148,14 @@ impl Component for PhysicsBody { /// /// ```rust /// use specs_physics::{ -/// bodies::BodyStatus, -/// math::{Matrix3, Point3, Vector3}, +/// nalgebra::{Matrix3, Point3}, +/// nphysics::{algebra::Velocity3, object::BodyStatus}, /// PhysicsBodyBuilder, /// }; /// /// let physics_body = PhysicsBodyBuilder::from(BodyStatus::Dynamic) /// .gravity_enabled(true) -/// .velocity(Vector3::new(1.0, 1.0, 1.0)) +/// .velocity(Velocity3::linear(1.0, 1.0, 1.0)) /// .angular_inertia(Matrix3::from_diagonal_element(3.0)) /// .mass(1.3) /// .local_center_of_mass(Point3::new(0.0, 0.0, 0.0)) @@ -60,7 +164,7 @@ impl Component for PhysicsBody { pub struct PhysicsBodyBuilder { gravity_enabled: bool, body_status: BodyStatus, - velocity: Vector3, + velocity: Velocity3, angular_inertia: Matrix3, mass: N, local_center_of_mass: Point3, @@ -73,10 +177,10 @@ impl From for PhysicsBodyBuilder { Self { gravity_enabled: false, body_status, - velocity: Vector3::repeat(N::zero()), + velocity: Velocity3::zero(), angular_inertia: Matrix3::zeros(), mass: N::from_f32(1.2).unwrap(), - local_center_of_mass: Point3::new(N::zero(), N::zero(), N::zero()), + local_center_of_mass: Point3::origin(), } } } @@ -89,7 +193,7 @@ impl PhysicsBodyBuilder { } // Sets the `velocity` value of the `PhysicsBodyBuilder`. - pub fn velocity(mut self, velocity: Vector3) -> Self { + pub fn velocity(mut self, velocity: Velocity3) -> Self { self.velocity = velocity; self } @@ -123,6 +227,7 @@ impl PhysicsBodyBuilder { angular_inertia: self.angular_inertia, mass: self.mass, local_center_of_mass: self.local_center_of_mass, + external_forces: Force3::zero(), } } } diff --git a/src/colliders.rs b/src/colliders.rs index 5bba087..c6ae363 100644 --- a/src/colliders.rs +++ b/src/colliders.rs @@ -1,15 +1,18 @@ use std::{f32::consts::PI, fmt}; -use nalgebra::RealField; -use ncollide::shape::{Ball, Cuboid, ShapeHandle}; -pub use ncollide::world::CollisionGroups; -pub use nphysics::material; -use nphysics::object::ColliderHandle; use specs::{Component, DenseVecStorage, FlaggedStorage}; -use crate::math::{Isometry3, Vector3}; - -use self::material::{BasicMaterial, MaterialHandle}; +use crate::{ + nalgebra::{Isometry3, RealField, Vector3}, + ncollide::{ + shape::{Ball, Cuboid, ShapeHandle}, + world::CollisionGroups, + }, + nphysics::{ + material::{BasicMaterial, MaterialHandle}, + object::ColliderHandle, + }, +}; /// `Shape` serves as an abstraction over nphysics `ShapeHandle`s and makes it /// easier to configure and define said `ShapeHandle`s for the user without @@ -101,12 +104,10 @@ impl PhysicsCollider { /// /// ```rust /// use specs_physics::{ -/// colliders::{ -/// material::{BasicMaterial, MaterialHandle}, -/// CollisionGroups, -/// Shape, -/// }, -/// math::Isometry3, +/// colliders::Shape, +/// nalgebra::Isometry3, +/// ncollide::world::CollisionGroups, +/// nphysics::material::{BasicMaterial, MaterialHandle}, /// PhysicsColliderBuilder, /// }; /// diff --git a/src/events.rs b/src/events.rs index 04c2a0d..c32cfb3 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,7 +1,6 @@ -use shrev::EventChannel; use specs::Entity; -pub use ncollide::query::Proximity; +use crate::{ncollide::query::Proximity, shrev::EventChannel}; /// The `ContactType` is set accordingly to whether a contact began or ended. #[derive(Debug)] diff --git a/src/lib.rs b/src/lib.rs index 25e3c94..831aca8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,37 +1,282 @@ +//! ## Usage +//! +//! To use **specs-physics**, add the following dependency to your projects +//! *Cargo.toml*: +//! +//! ```toml +//! [dependencies] +//! specs-physics = "0.3.0" +//! ``` +//! +//! **specs-physics** defines a set of [Specs][] `System`s and `Component`s to +//! handle the creation, modification and removal of [nphysics][] objects +//! ([RigidBody][], [Collider][]) and the synchronisation of object positions +//! and global gravity between both worlds. +//! +//! ### Generic types +//! +//! All `System`s and `Component`s provided by this crate require between one +//! and two type parameters to function properly. These were explicitly +//! introduced to keep this integration as generic as possible and allow +//! compatibility with as many external crates and game engines as possible. +//! +//! #### `N: RealField` +//! +//! [nphysics][] is built upon [nalgebra][] and uses various types and +//! structures from this crate. **specs-physics** builds up on this even further +//! and utilises the same structures, which all work with any type that +//! implements `nalgebra::RealField`. `nalgebra::RealField` is by default +//! implemented for various standard types, such as `f32` and`f64`. `nalgebra` +//! is re-exported under `specs_physics::nalgebra`. +//! +//! #### `P: Position` +//! +//! a type parameter which implements the `specs_physics::bodies::Position` +//! *trait*, requiring also a `Component` implementation with a +//! `FlaggedStorage`. This `Position` `Component` is used to initially place a +//! [RigidBody][] in the [nphysics][] world and later used to synchronise the +//! updated translation and rotation of these bodies back into the [Specs][] +//! world. +//! +//! Example for a `Position` `Component`, simply using the "Isometry" type (aka +//! combined translation and rotation structure) directly: +//! +//! ```rust +//! use specs::{Component, DenseVecStorage, FlaggedStorage}; +//! use specs_physics::{bodies::Position, nalgebra::Isometry3}; +//! +//! struct Pos(pub Isometry3); +//! +//! impl Component for Pos { +//! type Storage = FlaggedStorage>; +//! } +//! +//! impl Position for Pos { +//! fn isometry(&self) -> &Isometry3 { +//! &self.0 +//! } +//! +//! fn isometry_mut(&mut self) -> &mut Isometry3 { +//! &mut self.0 +//! } +//! +//! fn set_isometry(&mut self, isometry: &Isometry3) -> &mut Pos { +//! self.0 = *isometry; +//! self +//! } +//! } +//! ``` +//! +//! If you're using [Amethyst][], you can enable the "amethyst" feature for this +//! crate which provides a `Position` impl for `Transform`. +//! +//! ```toml +//! [dependencies] +//! specs-physics = { version = "0.3", features = ["amethyst"] } +//! ``` +//! +//! ### Components +//! +//! ##### PhysicsBody +//! +//! The `specs_physics::PhysicsBody` `Component` is used to define [RigidBody][] +//! from the comforts of your [Specs][] world. Changes to the `PhysicsBody` will +//! automatically be synchronised with [nphysics][]. +//! +//! Example: +//! +//! ```rust +//! use specs_physics::{ +//! nalgebra::{Matrix3, Point3}, +//! nphysics::{algebra::Velocity3, object::BodyStatus}, +//! PhysicsBodyBuilder, +//! }; +//! +//! let physics_body = PhysicsBodyBuilder::from(BodyStatus::Dynamic) +//! .gravity_enabled(true) +//! .velocity(Velocity3::linear(1.0, 1.0, 1.0)) +//! .angular_inertia(Matrix3::from_diagonal_element(3.0)) +//! .mass(1.3) +//! .local_center_of_mass(Point3::new(0.0, 0.0, 0.0)) +//! .build(); +//! ``` +//! +//! ##### PhysicsCollider +//! +//! `specs_physics::PhysicsCollider`s are the counterpart to `PhysicsBody`s. +//! They can exist on their own or as a part of a `PhysicsBody` +//! `PhysicsCollider`s are used to define and create [Collider][]'s in +//! [nphysics][]. +//! +//! Example: +//! +//! ```rust +//! use specs_physics::{ +//! colliders::Shape, +//! nalgebra::Isometry3, +//! ncollide::world::CollisionGroups, +//! nphysics::material::{BasicMaterial, MaterialHandle}, +//! PhysicsColliderBuilder, +//! }; +//! +//! let physics_collider = PhysicsColliderBuilder::from(Shape::Rectangle(10.0, 10.0, 1.0)) +//! .offset_from_parent(Isometry3::identity()) +//! .density(1.2) +//! .material(MaterialHandle::new(BasicMaterial::default())) +//! .margin(0.02) +//! .collision_groups(CollisionGroups::default()) +//! .linear_prediction(0.001) +//! .angular_prediction(0.0) +//! .sensor(true) +//! .build(); +//! ``` +//! +//! To assign multiple [Collider][]'s the the same body, [Entity hierarchy][] +//! can be used. This utilises [specs-hierarchy][]. +//! +//! ### Systems +//! +//! The following `System`s currently exist and should be added to your +//! `Dispatcher` in order: +//! +//! 1. `specs_physics::systems::SyncBodiesToPhysicsSystem` - handles the +//! creation, modification and removal of [RigidBody][]'s based on the +//! `PhysicsBody` `Component` and an implementation of the `Position` +//! *trait*. +//! +//! 2. `specs_physics::systems::SyncCollidersToPhysicsSystem` - handles +//! the creation, modification and removal of [Collider][]'s based on the +//! `PhysicsCollider` `Component`. This `System` depends on +//! `SyncBodiesToPhysicsSystem` as [Collider][] can depend on [RigidBody][]. +//! +//! 3. `specs_physics::systems::SyncParametersToPhysicsSystem` - handles the +//! modification of the [nphysics][] `World`s parameters. +//! +//! 4. `specs_physics::systems::PhysicsStepperSystem` - handles the progression +//! of the [nphysics][] `World` and causes objects to actually move and +//! change their position. This `System` is the backbone for collision +//! detection. +//! +//! 5. `specs_physics::systems::SyncBodiesFromPhysicsSystem` - +//! handles the synchronisation of [RigidBody][] positions and dynamics back +//! into the [Specs][] `Component`s. This `System` also utilises the +//! `Position` *trait* implementation. +//! +//! An example `Dispatcher` with all required `System`s: +//! +//! ```rust +//! use specs::DispatcherBuilder; +//! use specs_physics::{ +//! systems::{ +//! PhysicsStepperSystem, +//! SyncBodiesFromPhysicsSystem, +//! SyncBodiesToPhysicsSystem, +//! SyncCollidersToPhysicsSystem, +//! SyncParametersToPhysicsSystem, +//! }, +//! SimplePosition, +//! }; +//! +//! let dispatcher = DispatcherBuilder::new() +//! .with( +//! SyncBodiesToPhysicsSystem::>::default(), +//! "sync_bodies_to_physics_system", +//! &[], +//! ) +//! .with( +//! SyncCollidersToPhysicsSystem::>::default(), +//! "sync_colliders_to_physics_system", +//! &["sync_bodies_to_physics_system"], +//! ) +//! .with( +//! SyncParametersToPhysicsSystem::::default(), +//! "sync_gravity_to_physics_system", +//! &[], +//! ) +//! .with( +//! PhysicsStepperSystem::::default(), +//! "physics_stepper_system", +//! &[ +//! "sync_bodies_to_physics_system", +//! "sync_colliders_to_physics_system", +//! "sync_gravity_to_physics_system", +//! ], +//! ) +//! .with( +//! SyncBodiesFromPhysicsSystem::>::default(), +//! "sync_bodies_from_physics_system", +//! &["physics_stepper_system"], +//! ) +//! .build(); +//! ``` +//! +//! If you're using [Amethyst][] Transforms directly, you'd pass the generic +//! arguments like so: +//! +//! ``` +//! use amethyst_core::{Float, Transform}; +//! use specs_physics::systems::SyncBodiesToPhysicsSystem; +//! SyncBodiesToPhysicsSystem::::default(); +//! ``` +//! +//! Alternatively to building your own `Dispatcher`, you can always fall back on +//! the convenience function `specs_physics::physics_dispatcher()`, which +//! returns a configured *default* `Dispatcher` for you or +//! `specs_physics::register_physics_systems()` which takes a +//! `DispatcherBuilder` as an argument and registers the required `System`s for +//! you. +//! +//! [Specs]: https://slide-rs.github.io/specs/ +//! [nphysics]: https://www.nphysics.org/ +//! [nalgebra]: https://nalgebra.org/ +//! [RigidBody]: https://www.nphysics.org/rigid_body_simulations_with_contacts/#rigid-bodies +//! [Collider]: https://www.nphysics.org/rigid_body_simulations_with_contacts/#colliders +//! [Amethyst]: https://amethyst.rs/ +//! [Entity hierarchy]: https://github.com/bamling/specs-physics/blob/master/examples/hierarchy.rs +//! [specs-hierarchy]: https://github.com/rustgd/specs-hierarchy + #[macro_use] extern crate log; -extern crate ncollide3d as ncollide; -extern crate nphysics3d as nphysics; + +pub use nalgebra; +pub use ncollide3d as ncollide; +pub use nphysics3d as nphysics; +pub use shrev; use std::collections::HashMap; -pub use nalgebra as math; -use nphysics::{ - counters::Counters, - material::MaterialsCoefficientsTable, - object::{BodyHandle, ColliderHandle}, - solver::IntegrationParameters, - world::World, +use specs::{ + world::Index, + Component, + DenseVecStorage, + Dispatcher, + DispatcherBuilder, + Entity, + FlaggedStorage, }; -pub use shrev; -use specs::world::Index; +use specs_hierarchy::Parent; pub use self::{ - bodies::{PhysicsBody, PhysicsBodyBuilder}, + bodies::{util::SimplePosition, PhysicsBody, PhysicsBodyBuilder}, colliders::{PhysicsCollider, PhysicsColliderBuilder}, }; -use specs::{Component, DenseVecStorage, Dispatcher, DispatcherBuilder, Entity, FlaggedStorage}; -use specs_hierarchy::Parent; use self::{ bodies::Position, - math::{RealField, Vector3}, + nalgebra::{RealField, Vector3}, + nphysics::{ + counters::Counters, + material::MaterialsCoefficientsTable, + object::{BodyHandle, ColliderHandle}, + solver::IntegrationParameters, + world::World, + }, systems::{ PhysicsStepperSystem, + SyncBodiesFromPhysicsSystem, SyncBodiesToPhysicsSystem, SyncCollidersToPhysicsSystem, SyncParametersToPhysicsSystem, - SyncPositionsFromPhysicsSystem, }, }; @@ -130,6 +375,12 @@ impl Parent for PhysicsParent { /// Convenience function for configuring and building a `Dispatcher` with all /// required physics related `System`s. +/// +/// # Examples +/// ``` +/// use specs_physics::bodies::util::SimplePosition; +/// let dispatcher = specs_physics::physics_dispatcher::>(); +/// ``` pub fn physics_dispatcher<'a, 'b, N, P>() -> Dispatcher<'a, 'b> where N: RealField, @@ -183,16 +434,16 @@ where &[ "sync_bodies_to_physics_system", "sync_colliders_to_physics_system", - "sync_gravity_to_physics_system", + "sync_parameters_to_physics_system", ], ); - // add SyncPositionsFromPhysicsSystem last as it handles the + // add SyncBodiesFromPhysicsSystem last as it handles the // synchronisation between nphysics World bodies and the Position // components; this depends on the PhysicsStepperSystem dispatcher_builder.add( - SyncPositionsFromPhysicsSystem::::default(), - "sync_positions_from_physics_system", + SyncBodiesFromPhysicsSystem::::default(), + "sync_bodies_from_physics_system", &["physics_stepper_system"], ); } diff --git a/src/parameters.rs b/src/parameters.rs index 9ca59ac..df8c58a 100644 --- a/src/parameters.rs +++ b/src/parameters.rs @@ -2,10 +2,13 @@ //! Resources for modifying the various simulation parameters of the //! nphysics World. -use crate::math::{self as na, RealField, Scalar, Vector3}; -use nphysics::solver::IntegrationParameters; use std::ops::{Deref, DerefMut}; +use crate::{ + nalgebra::{self as na, RealField, Scalar, Vector3}, + nphysics::solver::IntegrationParameters, +}; + /// The `TimeStep` is used to set the timestep of the nphysics integration, see /// `nphysics::world::World::set_timestep(..)`. /// diff --git a/src/systems/mod.rs b/src/systems/mod.rs index c575aca..e051ab0 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -1,3 +1,5 @@ +use std::ops::Deref; + use specs::{ storage::{ComponentEvent, MaskedStorage}, BitSet, @@ -6,21 +8,20 @@ use specs::{ Storage, Tracked, }; -use std::ops::Deref; pub use self::{ physics_stepper::PhysicsStepperSystem, + sync_bodies_from_physics::SyncBodiesFromPhysicsSystem, sync_bodies_to_physics::SyncBodiesToPhysicsSystem, sync_colliders_to_physics::SyncCollidersToPhysicsSystem, sync_parameters_to_physics::SyncParametersToPhysicsSystem, - sync_positions_from_physics::SyncPositionsFromPhysicsSystem, }; mod physics_stepper; +mod sync_bodies_from_physics; mod sync_bodies_to_physics; mod sync_colliders_to_physics; mod sync_parameters_to_physics; -mod sync_positions_from_physics; /// Iterated over the `ComponentEvent::Inserted`s of a given, tracked `Storage` /// and returns the results in a `BitSet`. diff --git a/src/systems/physics_stepper.rs b/src/systems/physics_stepper.rs index 1714ecf..0dc283c 100644 --- a/src/systems/physics_stepper.rs +++ b/src/systems/physics_stepper.rs @@ -1,6 +1,5 @@ -use nalgebra::RealField; -use ncollide::{events::ContactEvent as NContactEvent, world::CollisionObjectHandle}; -use nphysics::world::ColliderWorld; +use std::marker::PhantomData; + use specs::{ world::Index, Entities, @@ -12,10 +11,12 @@ use specs::{ Write, WriteExpect, }; -use std::marker::PhantomData; use crate::{ events::{ContactEvent, ContactEvents, ContactType, ProximityEvent, ProximityEvents}, + nalgebra::RealField, + ncollide::{events::ContactEvent as NContactEvent, world::CollisionObjectHandle}, + nphysics::world::ColliderWorld, parameters::TimeStep, Physics, }; diff --git a/src/systems/sync_positions_from_physics.rs b/src/systems/sync_bodies_from_physics.rs similarity index 65% rename from src/systems/sync_positions_from_physics.rs rename to src/systems/sync_bodies_from_physics.rs index 1d74814..dfd203e 100644 --- a/src/systems/sync_positions_from_physics.rs +++ b/src/systems/sync_bodies_from_physics.rs @@ -1,46 +1,48 @@ -use nalgebra::RealField; -use specs::{Join, ReadExpect, ReadStorage, Resources, System, SystemData, WriteStorage}; use std::marker::PhantomData; +use specs::{Join, ReadExpect, Resources, System, SystemData, WriteStorage}; + use crate::{ bodies::{PhysicsBody, Position}, + nalgebra::RealField, Physics, }; -/// The `SyncPositionsFromPhysicsSystem` synchronised the updated position of +/// The `SyncBodiesFromPhysicsSystem` synchronised the updated position of /// the `RigidBody`s in the nphysics `World` with their Specs counterparts. This /// affects the `Position` `Component` related to the `Entity`. -pub struct SyncPositionsFromPhysicsSystem { +pub struct SyncBodiesFromPhysicsSystem { n_marker: PhantomData, p_marker: PhantomData

, } -impl<'s, N, P> System<'s> for SyncPositionsFromPhysicsSystem +impl<'s, N, P> System<'s> for SyncBodiesFromPhysicsSystem where N: RealField, P: Position, { type SystemData = ( ReadExpect<'s, Physics>, - ReadStorage<'s, PhysicsBody>, + WriteStorage<'s, PhysicsBody>, WriteStorage<'s, P>, ); fn run(&mut self, data: Self::SystemData) { - let (physics, physics_bodies, mut positions) = data; + let (physics, mut physics_bodies, mut positions) = data; // iterate over all PhysicBody components joined with their Positions - for (physics_body, position) in (&physics_bodies, &mut positions).join() { + for (physics_body, position) in (&mut physics_bodies, &mut positions).join() { // if a RigidBody exists in the nphysics World we fetch it and update the // Position component accordingly if let Some(rigid_body) = physics.world.rigid_body(physics_body.handle.unwrap()) { position.set_isometry(rigid_body.position()); + physics_body.update_from_physics_world(rigid_body); } } } fn setup(&mut self, res: &mut Resources) { - info!("SyncPositionsFromPhysicsSystem.setup"); + info!("SyncBodiesFromPhysicsSystem.setup"); Self::SystemData::setup(res); // initialise required resources @@ -48,7 +50,7 @@ where } } -impl Default for SyncPositionsFromPhysicsSystem +impl Default for SyncBodiesFromPhysicsSystem where N: RealField, P: Position, diff --git a/src/systems/sync_bodies_to_physics.rs b/src/systems/sync_bodies_to_physics.rs index a1b52b9..a2dfbc5 100644 --- a/src/systems/sync_bodies_to_physics.rs +++ b/src/systems/sync_bodies_to_physics.rs @@ -1,10 +1,5 @@ use std::marker::PhantomData; -use nalgebra::RealField; -use nphysics::{ - math::Velocity, - object::{Body, RigidBodyDesc}, -}; use specs::{ storage::ComponentEvent, world::Index, @@ -21,6 +16,7 @@ use specs::{ use crate::{ bodies::{PhysicsBody, Position}, + nalgebra::RealField, Physics, }; @@ -151,22 +147,11 @@ fn add_rigid_body( physics.world.remove_bodies(&[body_handle]); } - let delta_time = physics.world.timestep(); - // create a new RigidBody in the PhysicsWorld and store its // handle for later usage - let handle = RigidBodyDesc::new() - .position(position.as_isometry()) - .gravity_enabled(physics_body.gravity_enabled) - .status(physics_body.body_status) - .velocity(Velocity::linear( - physics_body.velocity.x / delta_time, - physics_body.velocity.y / delta_time, - physics_body.velocity.z / delta_time, - )) - .angular_inertia(physics_body.angular_inertia) - .mass(physics_body.mass) - .local_center_of_mass(physics_body.local_center_of_mass) + let handle = physics_body + .to_rigid_body_desc() + .position(*position.isometry()) .user_data(id) .build(&mut physics.world) .handle(); @@ -191,26 +176,15 @@ fn update_rigid_body( N: RealField, P: Position, { - let delta_time = physics.world.timestep(); - if let Some(rigid_body) = physics.world.rigid_body_mut(physics_body.handle.unwrap()) { // the PhysicsBody was modified, update everything but the position if modified_physics_bodies.contains(id) { - rigid_body.enable_gravity(physics_body.gravity_enabled); - rigid_body.set_status(physics_body.body_status); - rigid_body.set_velocity(Velocity::linear( - physics_body.velocity.x / delta_time, - physics_body.velocity.y / delta_time, - physics_body.velocity.z / delta_time, - )); - rigid_body.set_angular_inertia(physics_body.angular_inertia); - rigid_body.set_mass(physics_body.mass); - rigid_body.set_local_center_of_mass(physics_body.local_center_of_mass); + physics_body.apply_to_physics_world(rigid_body); } // the Position was modified, update the position directly if modified_positions.contains(id) { - rigid_body.set_position(position.as_isometry()); + rigid_body.set_position(*position.isometry()); } trace!( @@ -234,46 +208,23 @@ where #[cfg(test)] mod tests { - use super::*; - use crate::{bodies::BodyStatus, math::Isometry3, PhysicsBodyBuilder}; - use specs::{ - world::Builder, - Component, - DenseVecStorage, - DispatcherBuilder, - FlaggedStorage, - World, + use crate::{ + nalgebra::Isometry3, + nphysics::object::BodyStatus, + systems::SyncBodiesToPhysicsSystem, + Physics, + PhysicsBodyBuilder, + SimplePosition, }; - struct SimpleTranslation { - x: f32, - y: f32, - z: f32, - } - - impl Component for SimpleTranslation { - type Storage = FlaggedStorage>; - } - - impl Position for SimpleTranslation { - fn as_isometry(&self) -> Isometry3 { - Isometry3::translation(self.x, self.y, self.z) - } - - fn set_isometry(&mut self, isometry: &Isometry3) { - let translation = isometry.translation.vector; - self.x = translation.x; - self.y = translation.y; - self.z = translation.z; - } - } + use specs::{world::Builder, DispatcherBuilder, World}; #[test] fn add_rigid_body() { let mut world = World::new(); let mut dispatcher = DispatcherBuilder::new() .with( - SyncBodiesToPhysicsSystem::::default(), + SyncBodiesToPhysicsSystem::>::default(), "sync_bodies_to_physics_system", &[], ) @@ -283,11 +234,9 @@ mod tests { // create an Entity with the PhysicsBody component and execute the dispatcher world .create_entity() - .with(SimpleTranslation { - x: 1.0, - y: 1.0, - z: 1.0, - }) + .with(SimplePosition::(Isometry3::::translation( + 1.0, 1.0, 1.0, + ))) .with(PhysicsBodyBuilder::::from(BodyStatus::Dynamic).build()) .build(); dispatcher.dispatch(&mut world.res); diff --git a/src/systems/sync_colliders_to_physics.rs b/src/systems/sync_colliders_to_physics.rs index 2c11b1e..ea2ae6b 100644 --- a/src/systems/sync_colliders_to_physics.rs +++ b/src/systems/sync_colliders_to_physics.rs @@ -1,4 +1,5 @@ -use nalgebra::RealField; +use std::marker::PhantomData; + use specs::{ storage::ComponentEvent, world::Index, @@ -11,11 +12,15 @@ use specs::{ WriteExpect, WriteStorage, }; -use std::marker::PhantomData; - -use nphysics::object::{BodyPartHandle, ColliderDesc}; -use crate::{bodies::Position, colliders::PhysicsCollider, Physics, PhysicsParent}; +use crate::{ + bodies::Position, + colliders::PhysicsCollider, + nalgebra::RealField, + nphysics::object::{BodyPartHandle, ColliderDesc}, + Physics, + PhysicsParent, +}; use super::iterate_component_events; @@ -177,11 +182,11 @@ fn add_collider( // Position into consideration let translation = if parent_part_handle.is_ground() { // let scale = 1.0; may be added later - let mut iso = position.as_isometry(); + let iso = &mut position.isometry().clone(); iso.translation.vector += iso.rotation * physics_collider.offset_from_parent.translation.vector; //.component_mul(scale); iso.rotation *= physics_collider.offset_from_parent.rotation; - iso + *iso } else { physics_collider.offset_from_parent }; @@ -249,46 +254,23 @@ where #[cfg(test)] mod tests { - use super::*; - use crate::{colliders::Shape, math::Isometry3, PhysicsColliderBuilder}; - use specs::{ - world::Builder, - Component, - DenseVecStorage, - DispatcherBuilder, - FlaggedStorage, - World, + use specs::{world::Builder, DispatcherBuilder, World}; + + use crate::{ + colliders::Shape, + nalgebra::Isometry3, + systems::SyncCollidersToPhysicsSystem, + Physics, + PhysicsColliderBuilder, + SimplePosition, }; - struct SimpleTranslation { - x: f32, - y: f32, - z: f32, - } - - impl Component for SimpleTranslation { - type Storage = FlaggedStorage>; - } - - impl Position for SimpleTranslation { - fn as_isometry(&self) -> Isometry3 { - Isometry3::translation(self.x, self.y, self.z) - } - - fn set_isometry(&mut self, isometry: &Isometry3) { - let translation = isometry.translation.vector; - self.x = translation.x; - self.y = translation.y; - self.z = translation.z; - } - } - #[test] fn add_collider() { let mut world = World::new(); let mut dispatcher = DispatcherBuilder::new() .with( - SyncCollidersToPhysicsSystem::::default(), + SyncCollidersToPhysicsSystem::>::default(), "sync_colliders_to_physics_system", &[], ) @@ -299,11 +281,9 @@ mod tests { // dispatcher world .create_entity() - .with(SimpleTranslation { - x: 1.0, - y: 1.0, - z: 1.0, - }) + .with(SimplePosition::(Isometry3::::translation( + 1.0, 1.0, 1.0, + ))) .with(PhysicsColliderBuilder::::from(Shape::Circle(5.0)).build()) .build(); dispatcher.dispatch(&mut world.res); diff --git a/src/systems/sync_parameters_to_physics.rs b/src/systems/sync_parameters_to_physics.rs index 634f607..677c44a 100644 --- a/src/systems/sync_parameters_to_physics.rs +++ b/src/systems/sync_parameters_to_physics.rs @@ -1,8 +1,12 @@ -use nalgebra::RealField; +use std::marker::PhantomData; + use specs::{Read, Resources, System, SystemData, WriteExpect}; -use crate::{parameters::*, Physics}; -use std::marker::PhantomData; +use crate::{ + nalgebra::RealField, + parameters::{Gravity, PhysicsIntegrationParameters, PhysicsProfilingEnabled}, + Physics, +}; /// The `SyncParametersToPhysicsSystem` synchronises the simulation parameters /// with the nphysics `World`. @@ -75,11 +79,15 @@ where #[cfg(test)] mod tests { - use super::*; - use crate::Physics; - use nalgebra::Vector3; use specs::{DispatcherBuilder, World}; + use crate::{ + nalgebra::Vector3, + parameters::Gravity, + systems::SyncParametersToPhysicsSystem, + Physics, + }; + #[test] fn update_gravity() { let mut world = World::new();