commit 057cb9aa6f714d8750fcd6690282b411e4d2b1b9
parent 12b96dbb72c6f873ad2086230469fc405836a03f
Author: bamlin <b.amling@tarent.de>
Date: Thu, 30 May 2019 17:10:08 +0200
added core components and updated README.md
Diffstat:
7 files changed, 408 insertions(+), 1 deletion(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -4,6 +4,15 @@ version = "0.0.1"
authors = ["Benjamin Amling <benjamin@amling.net>"]
repository = "https://github.com/bamling/specs-physics.git"
homepage = "https://github.com/bamling/specs-physics.git"
+edition = "2018"
[dependencies]
+log = "0.4.6"
+specs = "0.14.3"
+specs-hierarchy = "0.3.1"
+nalgebra = "0.18.0"
+ncollide3d = "0.19.1"
+nphysics3d = "0.11.1"
+[dev-dependencies]
+simple_logger = "1.2.0"+
\ No newline at end of file
diff --git a/README.md b/README.md
@@ -1,2 +1,32 @@
# specs-physics
-nphysics integration for the Specs entity component system
+
+**specs-physics** aims to be an easily usable and extendable [nphysics](https://www.nphysics.org/) physics engine integration for applications and games that utilise the [Specs ECS](https://slide-rs.github.io/specs/).
+
+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
+
+```toml
+[dependencies]
+specs-physics = "0.0.1"
+```
+
+### Example
+
+TBD
+
+## Contributing
+
+TBD
+
+## License
+
+Distributed under the MIT License. See [LICENSE](https://github.com/bamling/specs-physics/blob/master/LICENSE) for more information.
+
+## Acknowledgments
+
+This project is heavily inspired by [nphysics-ecs-dumb](https://github.com/distransient/nphysics-ecs-dumb); they did most of the heavy lifting, I'm just building up on what they have started!
+
+**Special thanks to:**
+- [distransient](https://github.com/distransient)
+- [jojolepro](https://github.com/jojolepro)+
\ No newline at end of file
diff --git a/rustfmt.toml b/rustfmt.toml
@@ -0,0 +1,7 @@
+imports_layout = "HorizontalVertical"
+condense_wildcard_suffixes = true
+#brace_style = "PreferSameLine"
+merge_imports = true
+reorder_impl_items = true
+use_field_init_shorthand = true
+wrap_comments = true+
\ No newline at end of file
diff --git a/src/body.rs b/src/body.rs
@@ -0,0 +1,115 @@
+use nalgebra::RealField;
+use nphysics::object::BodyHandle;
+pub use nphysics::object::BodyStatus;
+use specs::{Component, DenseVecStorage, FlaggedStorage};
+
+use crate::math::{Matrix3, Point3, Vector3};
+
+/// The `PhysicsBody` `Component` represents a `PhysicsWorld` `RigidBody` in
+/// Specs and contains all the data required for the synchronisation between
+/// both worlds.
+#[derive(Clone, Copy, Debug)]
+pub struct PhysicsBody<N: RealField> {
+ pub(crate) handle: Option<BodyHandle>,
+ pub gravity_enabled: bool,
+ pub body_status: BodyStatus,
+ pub velocity: Vector3<N>,
+ pub angular_inertia: Matrix3<N>,
+ pub mass: N,
+ pub local_center_of_mass: Point3<N>,
+}
+
+impl<N: RealField> Component for PhysicsBody<N> {
+ type Storage = FlaggedStorage<Self, DenseVecStorage<Self>>;
+}
+
+/// The `PhysicsBodyBuilder` implements the builder pattern for `PhysicsBody`s
+/// and is the recommended way of instantiating and customising new
+/// `PhysicsBody` instances.
+///
+/// # Example
+///
+/// ```rust
+/// use specs_physics::{
+/// body::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();
+/// ```
+pub struct PhysicsBodyBuilder<N: RealField> {
+ gravity_enabled: bool,
+ body_status: BodyStatus,
+ velocity: Vector3<N>,
+ angular_inertia: Matrix3<N>,
+ mass: N,
+ local_center_of_mass: Point3<N>,
+}
+
+impl<N: RealField> From<BodyStatus> for PhysicsBodyBuilder<N> {
+ /// Creates a new `PhysicsBodyBuilder` from the given `BodyStatus`. This
+ /// also populates the `PhysicsBody` with sane defaults.
+ fn from(body_status: BodyStatus) -> Self {
+ Self {
+ gravity_enabled: false,
+ body_status,
+ velocity: Vector3::new(N::zero(), N::zero(), 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()),
+ }
+ }
+}
+
+impl<N: RealField> PhysicsBodyBuilder<N> {
+ /// Sets the `gravity_enabled` value of the `PhysicsBodyBuilder`.
+ pub fn gravity_enabled(mut self, gravity_enabled: bool) -> Self {
+ self.gravity_enabled = gravity_enabled;
+ self
+ }
+
+ // Sets the `velocity` value of the `PhysicsBodyBuilder`.
+ pub fn velocity(mut self, velocity: Vector3<N>) -> Self {
+ self.velocity = velocity;
+ self
+ }
+
+ /// Sets the `angular_inertia` value of the `PhysicsBodyBuilder`.
+ pub fn angular_inertia(mut self, angular_inertia: Matrix3<N>) -> Self {
+ self.angular_inertia = angular_inertia;
+ self
+ }
+
+ /// Sets the `mass` value of the `PhysicsBodyBuilder`.
+ pub fn mass(mut self, mass: N) -> Self {
+ self.mass = mass;
+ self
+ }
+
+ /// Sets the `local_center_of_mass` value of the `PhysicsBodyBuilder`.
+ pub fn local_center_of_mass(mut self, local_center_of_mass: Point3<N>) -> Self {
+ self.local_center_of_mass = local_center_of_mass;
+ self
+ }
+
+ /// Builds the `PhysicsBody` from the values set in the `PhysicsBodyBuilder`
+ /// instance.
+ pub fn build(self) -> PhysicsBody<N> {
+ PhysicsBody {
+ handle: None,
+ gravity_enabled: self.gravity_enabled,
+ body_status: self.body_status,
+ velocity: self.velocity,
+ angular_inertia: self.angular_inertia,
+ mass: self.mass,
+ local_center_of_mass: self.local_center_of_mass,
+ }
+ }
+}
diff --git a/src/collider.rs b/src/collider.rs
@@ -0,0 +1,219 @@
+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};
+
+/// `Shape` serves as an abstraction over nphysics `ShapeHandle`s and makes it
+/// easier to configure and define said `ShapeHandle`s for the user without
+/// having to know the underlying nphysics API.
+#[derive(Clone, Copy, Debug)]
+pub enum Shape<N: RealField> {
+ Circle(N),
+ Rectangle(N, N, N),
+}
+
+impl<N: RealField> Shape<N> {
+ /// Converts a `Shape` and its values into its corresponding `ShapeHandle`
+ /// type. The `ShapeHandle` is used to define a `Collider` in the
+ /// `PhysicsWorld`.
+ fn handle(&self, margin: N) -> ShapeHandle<N> {
+ match *self {
+ Shape::Circle(radius) => ShapeHandle::new(Ball::new(radius)),
+ Shape::Rectangle(width, height, depth) => ShapeHandle::new(Cuboid::new(Vector3::new(
+ width / N::from_f32(2.0).unwrap() - margin,
+ height / N::from_f32(2.0).unwrap() - margin,
+ depth / N::from_f32(2.0).unwrap() - margin,
+ ))),
+ }
+ }
+}
+
+/// The `PhysicsCollider` `Component` represents a `Collider` in the physics
+/// world. A physics `Collider` is automatically created when this `Component`
+/// is added to an `Entity`. Value changes are automatically synchronised with
+/// the physic worlds `Collider`.
+#[derive(Clone)]
+pub struct PhysicsCollider<N: RealField> {
+ pub(crate) handle: Option<ColliderHandle>,
+ pub shape: Shape<N>,
+ pub offset_from_parent: Isometry3<N>,
+ pub density: N,
+ pub material: MaterialHandle<N>,
+ pub margin: N,
+ pub collision_groups: CollisionGroups,
+ pub linear_prediction: N,
+ pub angular_prediction: N,
+ pub sensor: bool,
+}
+
+impl<N: RealField> Component for PhysicsCollider<N> {
+ type Storage = FlaggedStorage<Self, DenseVecStorage<Self>>;
+}
+
+impl<N: RealField> fmt::Debug for PhysicsCollider<N> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "PhysicsCollider {{ \
+ handle: {:?}, \
+ offset_from_parent: {:?}, \
+ density: {}, \
+ margin: {}, \
+ collision_group: {:?}, \
+ linear_prediction: {}, \
+ angular_prediction: {}, \
+ sensor: {} \
+ }}",
+ self.handle,
+ self.offset_from_parent,
+ self.density,
+ self.margin,
+ self.collision_groups,
+ self.linear_prediction,
+ self.angular_prediction,
+ self.sensor,
+ )?;
+ Ok(())
+ }
+}
+
+impl<N: RealField> PhysicsCollider<N> {
+ /// Returns the `ShapeHandle` for `shape`, taking the `margin` into
+ /// consideration.
+ pub(crate) fn shape_handle(&self) -> ShapeHandle<N> {
+ self.shape.handle(self.margin)
+ }
+}
+
+/// The `PhysicsColliderBuilder` implements the builder pattern for
+/// `PhysicsCollider`s and is the recommended way of instantiating and
+/// customising new `PhysicsCollider` instances.
+///
+/// # Example
+///
+/// ```rust
+/// use specs_physics::{
+/// collider::{
+/// 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();
+/// ```
+pub struct PhysicsColliderBuilder<N: RealField> {
+ shape: Shape<N>,
+ offset_from_parent: Isometry3<N>,
+ density: N,
+ material: MaterialHandle<N>,
+ margin: N,
+ collision_groups: CollisionGroups,
+ linear_prediction: N,
+ angular_prediction: N,
+ sensor: bool,
+}
+
+impl<N: RealField> From<Shape<N>> for PhysicsColliderBuilder<N> {
+ /// Creates a new `PhysicsColliderBuilder` from the given `Shape`. This
+ // also populates the `PhysicsCollider` with sane defaults.
+ fn from(shape: Shape<N>) -> Self {
+ Self {
+ shape,
+ offset_from_parent: Isometry3::identity(),
+ density: N::from_f32(1.3).unwrap(),
+ material: MaterialHandle::new(BasicMaterial::default()),
+ margin: N::from_f32(0.2).unwrap(), // default was: 0.01
+ collision_groups: CollisionGroups::default(),
+ linear_prediction: N::from_f32(0.002).unwrap(),
+ angular_prediction: N::from_f32(PI / 180.0 * 5.0).unwrap(),
+ sensor: false,
+ }
+ }
+}
+
+impl<N: RealField> PhysicsColliderBuilder<N> {
+ /// Sets the `offset_from_parent` value of the `PhysicsColliderBuilder`.
+ pub fn offset_from_parent(mut self, offset_from_parent: Isometry3<N>) -> Self {
+ self.offset_from_parent = offset_from_parent;
+ self
+ }
+
+ /// Sets the `density` value of the `PhysicsColliderBuilder`.
+ pub fn density(mut self, density: N) -> Self {
+ self.density = density;
+ self
+ }
+
+ /// Sets the `material` value of the `PhysicsColliderBuilder`.
+ pub fn material(mut self, material: MaterialHandle<N>) -> Self {
+ self.material = material;
+ self
+ }
+
+ /// Sets the `margin` value of the `PhysicsColliderBuilder`.
+ pub fn margin(mut self, margin: N) -> Self {
+ self.margin = margin;
+ self
+ }
+
+ /// Sets the `collision_groups` value of the `PhysicsColliderBuilder`.
+ pub fn collision_groups(mut self, collision_groups: CollisionGroups) -> Self {
+ self.collision_groups = collision_groups;
+ self
+ }
+
+ /// Sets the `linear_prediction` value of the `PhysicsColliderBuilder`.
+ pub fn linear_prediction(mut self, linear_prediction: N) -> Self {
+ self.linear_prediction = linear_prediction;
+ self
+ }
+
+ /// Sets the `angular_prediction` value of the `PhysicsColliderBuilder`.
+ pub fn angular_prediction(mut self, angular_prediction: N) -> Self {
+ self.angular_prediction = angular_prediction;
+ self
+ }
+
+ /// Sets the `sensor` value of the `PhysicsColliderBuilder`.
+ pub fn sensor(mut self, sensor: bool) -> Self {
+ self.sensor = sensor;
+ self
+ }
+
+ /// Builds the `PhysicsCollider` from the values set in the
+ /// `PhysicsColliderBuilder` instance.
+ pub fn build(self) -> PhysicsCollider<N> {
+ PhysicsCollider {
+ handle: None,
+ shape: self.shape,
+ offset_from_parent: self.offset_from_parent,
+ density: self.density,
+ material: self.material,
+ margin: self.margin,
+ collision_groups: self.collision_groups,
+ linear_prediction: self.linear_prediction,
+ angular_prediction: self.angular_prediction,
+ sensor: self.sensor,
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
@@ -0,0 +1,24 @@
+#[macro_use]
+extern crate log;
+extern crate ncollide3d as ncollide;
+extern crate nphysics3d as nphysics;
+
+pub use nalgebra as math;
+use nphysics::world::World;
+
+use self::math::Vector3;
+pub use self::{
+ body::{PhysicsBody, PhysicsBodyBuilder},
+ collider::{PhysicsCollider, PhysicsColliderBuilder, Shape},
+};
+
+pub mod body;
+pub mod collider;
+mod systems;
+
+///// The `PhysicsWorld` containing all physical objects.
+//pub type PhysicsWorld<N> = World<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>;
diff --git a/src/lib.rs b/src/systems/mod.rs