Skip to content

Commit

Permalink
Release 0.3.0 (#6)
Browse files Browse the repository at this point in the history
- Moves examples from README.md into library docs, now compiling correctly as doctests
- Adds SimplePosition struct for easy Isometry+Position usage
- Integrates Amethyst Transforms for usage with Position trait with feature amethyst_core
- Adds method apply_external_force for applying (cumulative) external forces to PhysicsBodys
- Adds synchronization of PhysicsBodys from physics world, renaming SyncPositionsFromPhysicsSystem to SyncBodiesFromPhysicsSystem to make this clear.
- Re-exports ncollide and nphysics while also cleaning up namespace and use statements.
  • Loading branch information
distransient authored Jun 23, 2019
1 parent d924441 commit 750547e
Show file tree
Hide file tree
Showing 18 changed files with 556 additions and 532 deletions.
14 changes: 10 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[package]
name = "specs-physics"
version = "0.2.2"
authors = ["Benjamin Amling <[email protected]>"]
repository = "https://github.com/bamling/specs-physics.git"
homepage = "https://github.com/bamling/specs-physics.git"
version = "0.3.0"
authors = ["Benjamin Amling <[email protected]>", "Kel <[email protected]>", "jojolepro <[email protected]>"]
repository = "https://github.com/amethyst/specs-physics.git"
homepage = "https://github.com/amethyst/specs-physics.git"
edition = "2018"

license = "MIT"
Expand All @@ -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"
Expand All @@ -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"
Expand Down
148 changes: 1 addition & 147 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<N>` - 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<f32>);

impl Component for Position {
type Storage = FlaggedStorage<Self, DenseVecStorage<Self>>;
}

impl Position<f32> for Position {
fn as_isometry(&self) -> Isometry3<f32> {
self.0
}

fn set_isometry(&mut self, isometry: &Isometry3<f32>) {
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::<f32, MyPosition>::default(),
"sync_bodies_to_physics_system",
&[],
)
.with(
SyncCollidersToPhysicsSystem::<f32, MyPosition>::default(),
"sync_colliders_to_physics_system",
&["sync_bodies_to_physics_system"],
)
.with(
SyncGravityToPhysicsSystem::<f32>::default(),
"sync_gravity_to_physics_system",
&[],
)
.with(
PhysicsStepperSystem::<f32>::default(),
"physics_stepper_system",
&[
"sync_bodies_to_physics_system",
"sync_colliders_to_physics_system",
"sync_gravity_to_physics_system",
],
)
.with(
SyncPositionsFromPhysicsSystem::<f32, MyPosition>::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

Expand All @@ -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
Expand Down
42 changes: 8 additions & 34 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -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<Self, DenseVecStorage<Self>>;
}

impl Position<f32> for SimpleTranslation {
fn as_isometry(&self) -> Isometry3<f32> {
Isometry3::translation(self.x, self.y, self.z)
}

fn set_isometry(&mut self, isometry: &Isometry3<f32>) {
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();
Expand All @@ -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::<f32, SimpleTranslation>();
let mut dispatcher = physics_dispatcher::<f32, SimplePosition<f32>>();
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::<f32>(Isometry3::<f32>::translation(
1.0, 1.0, 1.0,
)))
.with(PhysicsBodyBuilder::<f32>::from(BodyStatus::Dynamic).build())
.with(PhysicsColliderBuilder::<f32>::from(Shape::Rectangle(1.0, 1.0, 1.0)).build())
.build();
Expand Down
56 changes: 14 additions & 42 deletions examples/collision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, DenseVecStorage<Self>>;
}

impl Position<f32> for SimpleTranslation {
fn as_isometry(&self) -> Isometry3<f32> {
Isometry3::translation(self.x, self.y, self.z)
}

fn set_isometry(&mut self, isometry: &Isometry3<f32>) {
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();
Expand All @@ -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::<f32, SimpleTranslation>();
let mut dispatcher = physics_dispatcher::<f32, SimplePosition<f32>>();
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::<f32>(Isometry3::<f32>::translation(
1.0, 1.0, 1.0,
)))
.with(
PhysicsBodyBuilder::<f32>::from(BodyStatus::Dynamic)
.velocity(Vector3::new(1.0, 0.0, 0.0))
.velocity(Velocity3::linear(1.0, 0.0, 0.0))
.build(),
)
.with(PhysicsColliderBuilder::<f32>::from(Shape::Rectangle(2.0, 2.0, 1.0)).build())
Expand All @@ -69,11 +43,9 @@ fn main() {
// one
world
.create_entity()
.with(SimpleTranslation {
x: 3.0,
y: 1.0,
z: 1.0,
})
.with(SimplePosition::<f32>(Isometry3::<f32>::translation(
3.0, 1.0, 1.0,
)))
.with(PhysicsBodyBuilder::<f32>::from(BodyStatus::Static).build())
.with(PhysicsColliderBuilder::<f32>::from(Shape::Rectangle(2.0, 2.0, 1.0)).build())
.build();
Expand All @@ -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::<SimpleTranslation>();
let pos_storage = world.read_storage::<SimplePosition<f32>>();
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);
}
Loading

0 comments on commit 750547e

Please sign in to comment.