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 3e1c4906aa2f024e4e1bb5e1654f1131dcebb6dd
parent 12a2119f665af696f39da207c5dfc62fc2c01a50
Author: bamlin <b.amling@tarent.de>
Date:   Fri, 31 May 2019 14:57:22 +0200

added gravity resource and system for synchronisation; the gravity resource is optional

Diffstat:
MCargo.toml | 6+++---
Aexamples/basic.rs | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dexamples/main.rs | 63---------------------------------------------------------------
Msrc/body.rs | 2+-
Msrc/lib.rs | 26++++++++++++++++++++++----
Msrc/systems/mod.rs | 1+
Asrc/systems/sync_gravity_to_physics.rs | 46++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 136 insertions(+), 71 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -18,5 +18,5 @@ nphysics3d = "0.11.1" simple_logger = "1.2.0" [[example]] -name = "example" -path = "examples/main.rs"- \ No newline at end of file +name = "basic" +path = "examples/basic.rs"+ \ No newline at end of file diff --git a/examples/basic.rs b/examples/basic.rs @@ -0,0 +1,63 @@ +extern crate log; +extern crate simple_logger; + +use specs::{world::Builder, Component, DenseVecStorage, FlaggedStorage, World}; +use specs_physics::{ + body::{BodyStatus, Position}, + dispatcher, + PhysicsBodyBuilder, + PhysicsColliderBuilder, + Shape, +}; + +/// `Pos` struct for synchronisation of the position between the ECS and +/// nphysics; this has to implement both `Component` and `Position` +struct Pos { + x: f32, + y: f32, + z: f32, +} + +impl Component for Pos { + type Storage = FlaggedStorage<Self, DenseVecStorage<Self>>; +} + +impl Position<f32> for Pos { + fn position(&self) -> (f32, f32, f32) { + (self.x, self.y, self.z) + } + + fn set_position(&mut self, x: f32, y: f32, z: f32) { + self.x = x; + self.y = y; + self.z = z; + } +} + +fn main() { + // initialise the logger for system logs + simple_logger::init().unwrap(); + + // initialise the Specs world; this will contain our Resources and Entities + let mut world = World::new(); + + // create the dispatcher containing all relevant Systems; alternatively to using + // the convenience function you can add all required Systems by hand + let mut dispatcher = dispatcher::<f32, Pos>(); + dispatcher.setup(&mut world.res); + + // create an Entity containing the required Components + world + .create_entity() + .with(Pos { + x: 1.0, + y: 1.0, + z: 1.0, + }) + .with(PhysicsBodyBuilder::<f32>::from(BodyStatus::Dynamic).build()) + .with(PhysicsColliderBuilder::<f32>::from(Shape::Rectangle(1.0, 1.0, 1.0)).build()) + .build(); + + // execute the dispatcher + dispatcher.dispatch(&world.res); +} diff --git a/examples/main.rs b/examples/main.rs @@ -1,63 +0,0 @@ -extern crate log; -extern crate simple_logger; - -use specs::{world::Builder, Component, DenseVecStorage, FlaggedStorage, World}; -use specs_physics::{ - body::{BodyStatus, Position}, - dispatcher, - PhysicsBodyBuilder, - PhysicsColliderBuilder, - Shape, -}; - -/// `Pos` struct for synchronisation of the position between the ECS and -/// nphysics; this has to implement both `Component` and `Position` -struct Pos { - x: f32, - y: f32, - z: f32, -} - -impl Component for Pos { - type Storage = FlaggedStorage<Self, DenseVecStorage<Self>>; -} - -impl Position<f32> for Pos { - fn position(&self) -> (f32, f32, f32) { - (self.x, self.y, self.z) - } - - fn set_position(&mut self, x: f32, y: f32, z: f32) { - self.x = x; - self.y = y; - self.z = z; - } -} - -fn main() { - // initialise the logger for system logs - simple_logger::init().unwrap(); - - // initialise the Specs world; this will contain our Resources and Entities - let mut world = World::new(); - - // create the dispatcher containing all relevant Systems; alternatively to using - // the convenience function you can add all required Systems by hand - let mut dispatcher = dispatcher::<f32, Pos>(); - dispatcher.setup(&mut world.res); - - // create an Entity containing the required Components - world - .create_entity() - .with(Pos { - x: 1.0, - y: 1.0, - z: 1.0, - }) - .with(PhysicsBodyBuilder::<f32>::from(BodyStatus::Static).build()) - .with(PhysicsColliderBuilder::<f32>::from(Shape::Rectangle(1.0, 1.0, 1.0)).build()) - .build(); - - // execute the dispatcher - dispatcher.dispatch(&world.res); -} diff --git a/src/body.rs b/src/body.rs @@ -71,7 +71,7 @@ impl<N: RealField> From<BodyStatus> for PhysicsBodyBuilder<N> { Self { gravity_enabled: false, body_status, - velocity: Vector3::new(N::zero(), N::zero(), N::zero()), + velocity: Vector3::repeat(N::zero()), angular_inertia: Matrix3::zeros(), mass: N::from_f32(1.2).unwrap(), local_center_of_mass: Point3::new(N::zero(), N::zero(), N::zero()), diff --git a/src/lib.rs b/src/lib.rs @@ -6,7 +6,7 @@ extern crate nphysics3d as nphysics; use std::collections::HashMap; pub use nalgebra as math; -use nalgebra::RealField; +use nalgebra::{RealField, Scalar}; use nphysics::{ object::{BodyHandle, ColliderHandle}, world::World, @@ -26,6 +26,7 @@ use self::{ systems::{ sync_bodies_to_physics::SyncBodiesToPhysicsSystem, sync_colliders_to_physics::SyncCollidersToPhysicsSystem, + sync_gravity_to_physics::SyncGravityToPhysicsSystem, }, }; @@ -54,9 +55,17 @@ impl<N: RealField> Default for Physics<N> { } } -/// `Gravity` is a type alias for `Vector3<f32>`. It represents a constant -/// acceleration affecting all physical objects in the scene. -pub type Gravity<N> = Vector3<N>; +///// `Gravity` is a type alias for `Vector3`. It represents a constant +///// acceleration affecting all physical objects in the scene. +//pub type Gravity<N> = Vector3<N>; + +pub struct Gravity<N: RealField + Scalar>(Vector3<N>); + +impl<N: RealField + Scalar> Default for Gravity<N> { + fn default() -> Self { + Self(Vector3::repeat(N::zero())) + } +} /// The `PhysicsParent` `Component` is used to represent a parent/child /// relationship between physics based `Entity`s. @@ -101,5 +110,14 @@ where &["sync_bodies_to_physics_system"], ); + // add SyncGravityToPhysicsSystem; this System can be added at any point in time + // as it merely handles the gravity value of the nphysics World, thus it has no + // other dependencies + dispatcher_builder.add( + SyncGravityToPhysicsSystem::<N>::default(), + "sync_gravity_to_physics_system", + &[], + ); + dispatcher_builder.build() } diff --git a/src/systems/mod.rs b/src/systems/mod.rs @@ -10,6 +10,7 @@ use std::ops::Deref; pub mod sync_bodies_to_physics; pub mod sync_colliders_to_physics; +pub mod sync_gravity_to_physics; /// Iterated over the `ComponentEvent::Inserted`s of a given, tracked `Storage` /// and returns the results in a `BitSet`. diff --git a/src/systems/sync_gravity_to_physics.rs b/src/systems/sync_gravity_to_physics.rs @@ -0,0 +1,46 @@ +use nalgebra::RealField; +use specs::{Read, Resources, System, SystemData, WriteExpect}; + +use crate::{Gravity, Physics}; +use std::marker::PhantomData; + +/// The `SyncGravityToPhysicsSystem` synchronises the `Gravity` values with the +/// nphysics `World`s gravity. This way the gravity can be affected on demand. +/// +/// The `Gravity` `Resource` is treated as *optional* and needs to be registered +/// in order to change affect the `World`s gravity. +pub struct SyncGravityToPhysicsSystem<N> { + n_marker: PhantomData<N>, +} + +impl<'s, N: RealField> System<'s> for SyncGravityToPhysicsSystem<N> { + type SystemData = (Option<Read<'s, Gravity<N>>>, WriteExpect<'s, Physics<N>>); + + fn run(&mut self, data: Self::SystemData) { + let (gravity, mut physics) = data; + + // if a Gravity resource exists, synchronise its values with the nphysics World + if let Some(gravity) = gravity { + physics.world.set_gravity(gravity.0); + } + } + + fn setup(&mut self, res: &mut Resources) { + info!("SyncGravityToPhysicsSystem.setup"); + Self::SystemData::setup(res); + + // initialise required resources + res.entry::<Physics<N>>().or_insert_with(Physics::default); + } +} + +impl<N> Default for SyncGravityToPhysicsSystem<N> +where + N: RealField, +{ + fn default() -> Self { + Self { + n_marker: PhantomData, + } + } +}