specs-physics

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

lib.rs (16268B)


      1 //! ## Usage
      2 //!
      3 //! To use **specs-physics**, add the following dependency to your projects
      4 //! *Cargo.toml*:
      5 //!
      6 //! ```toml
      7 //! [dependencies]
      8 //! specs-physics = "0.3.0"
      9 //! ```
     10 //!
     11 //! **specs-physics** defines a set of [Specs][] `System`s and `Component`s to
     12 //! handle the creation, modification and removal of [nphysics][] objects
     13 //! ([RigidBody][], [Collider][]) and the synchronisation of object positions
     14 //! and global gravity between both worlds.
     15 //!
     16 //! ### Generic types
     17 //!
     18 //! All `System`s and `Component`s provided by this crate require between one
     19 //! and two type parameters to function properly. These were explicitly
     20 //! introduced to keep this integration as generic as possible and allow
     21 //! compatibility with as many external crates and game engines as possible.
     22 //!
     23 //! #### `N: RealField`
     24 //!
     25 //! [nphysics][] is built upon [nalgebra][] and uses various types and
     26 //! structures from this crate. **specs-physics** builds up on this even further
     27 //! and utilises the same structures, which all work with any type that
     28 //! implements `nalgebra::RealField`. `nalgebra::RealField` is by default
     29 //! implemented for various standard types, such as `f32` and`f64`. `nalgebra`
     30 //! is re-exported under `specs_physics::nalgebra`.
     31 //!
     32 //! #### `P: Position<N>`
     33 //!
     34 //! a type parameter which implements the `specs_physics::bodies::Position`
     35 //! *trait*, requiring also a `Component` implementation with a
     36 //! `FlaggedStorage`. This `Position` `Component` is used to initially place a
     37 //! [RigidBody][] in the [nphysics][] world and later used to synchronise the
     38 //! updated translation and rotation of these bodies back into the [Specs][]
     39 //! world.
     40 //!
     41 //! Example for a `Position` `Component`, simply using the "Isometry" type (aka
     42 //! combined translation and rotation structure) directly:
     43 //!
     44 //! ```rust
     45 //! use specs::{Component, DenseVecStorage, FlaggedStorage};
     46 //! use specs_physics::{bodies::Position, nalgebra::Isometry3};
     47 //!
     48 //! struct Pos(pub Isometry3<f32>);
     49 //!
     50 //! impl Component for Pos {
     51 //!     type Storage = FlaggedStorage<Self, DenseVecStorage<Self>>;
     52 //! }
     53 //!
     54 //! impl Position<f32> for Pos {
     55 //!     fn isometry(&self) -> &Isometry3<f32> {
     56 //!         &self.0
     57 //!     }
     58 //!
     59 //!     fn isometry_mut(&mut self) -> &mut Isometry3<f32> {
     60 //!         &mut self.0
     61 //!     }
     62 //!
     63 //!     fn set_isometry(&mut self, isometry: &Isometry3<f32>) -> &mut Pos {
     64 //!         self.0 = *isometry;
     65 //!         self
     66 //!     }
     67 //! }
     68 //! ```
     69 //!
     70 //! If you're using [Amethyst][], you can enable the "amethyst" feature for this
     71 //! crate which provides a `Position<Float>` impl for `Transform`.
     72 //!
     73 //! ```toml
     74 //! [dependencies]
     75 //! specs-physics = { version = "0.3", features = ["amethyst"] }
     76 //! ```
     77 //!
     78 //! ### Components
     79 //!
     80 //! ##### PhysicsBody
     81 //!
     82 //! The `specs_physics::PhysicsBody` `Component` is used to define [RigidBody][]
     83 //! from the comforts of your [Specs][] world. Changes to the `PhysicsBody` will
     84 //! automatically be synchronised with [nphysics][].
     85 //!
     86 //! Example:
     87 //!
     88 //! ```rust
     89 //! use specs_physics::{
     90 //!     nalgebra::{Matrix3, Point3},
     91 //!     nphysics::{algebra::Velocity3, object::BodyStatus},
     92 //!     PhysicsBodyBuilder,
     93 //! };
     94 //!
     95 //! let physics_body = PhysicsBodyBuilder::from(BodyStatus::Dynamic)
     96 //!     .gravity_enabled(true)
     97 //!     .velocity(Velocity3::linear(1.0, 1.0, 1.0))
     98 //!     .angular_inertia(Matrix3::from_diagonal_element(3.0))
     99 //!     .mass(1.3)
    100 //!     .local_center_of_mass(Point3::new(0.0, 0.0, 0.0))
    101 //!     .build();
    102 //! ```
    103 //!
    104 //! ##### PhysicsCollider
    105 //!
    106 //! `specs_physics::PhysicsCollider`s are the counterpart to `PhysicsBody`s.
    107 //! They can exist on their own or as a part of a `PhysicsBody`
    108 //! `PhysicsCollider`s are used to define and create [Collider][]'s in
    109 //! [nphysics][].
    110 //!
    111 //! Example:
    112 //!
    113 //! ```rust
    114 //! use specs_physics::{
    115 //!     colliders::Shape,
    116 //!     nalgebra::{Isometry3, Vector3},
    117 //!     ncollide::pipeline::CollisionGroups,
    118 //!     nphysics::material::{BasicMaterial, MaterialHandle},
    119 //!     PhysicsColliderBuilder,
    120 //! };
    121 //!
    122 //! let physics_collider = PhysicsColliderBuilder::from(
    123 //!         Shape::Cuboid{ half_extents: Vector3::new(10.0, 10.0, 1.0) })
    124 //!     .offset_from_parent(Isometry3::identity())
    125 //!     .density(1.2)
    126 //!     .material(MaterialHandle::new(BasicMaterial::default()))
    127 //!     .margin(0.02)
    128 //!     .collision_groups(CollisionGroups::default())
    129 //!     .linear_prediction(0.001)
    130 //!     .angular_prediction(0.0)
    131 //!     .sensor(true)
    132 //!     .build();
    133 //! ```
    134 //!
    135 //! To assign multiple [Collider][]'s the the same body, [Entity hierarchy][]
    136 //! can be used. This utilises [specs-hierarchy][].
    137 //!
    138 //! ### Systems
    139 //!
    140 //! The following `System`s currently exist and should be added to your
    141 //! `Dispatcher` in order:
    142 //!
    143 //! 1. `specs_physics::systems::SyncBodiesToPhysicsSystem` - handles the
    144 //! creation, modification and removal of [RigidBody][]'s based on the
    145 //! `PhysicsBody` `Component` and an implementation of the `Position`
    146 //! *trait*.
    147 //!
    148 //! 2. `specs_physics::systems::SyncCollidersToPhysicsSystem` - handles
    149 //! the creation, modification and removal of [Collider][]'s based on the
    150 //! `PhysicsCollider` `Component`. This `System` depends on
    151 //! `SyncBodiesToPhysicsSystem` as [Collider][] can depend on [RigidBody][].
    152 //!
    153 //! 3. `specs_physics::systems::SyncParametersToPhysicsSystem` - handles the
    154 //! modification of the [nphysics][] `DefaultMechanicalWorld`s parameters.
    155 //!
    156 //! 4. `specs_physics::systems::PhysicsStepperSystem` - handles the progression
    157 //! of the [nphysics][] `DefaultMechanicalWorld` and causes objects to actually
    158 //! move and change their position. This `System` is the backbone for collision
    159 //! detection.
    160 //!
    161 //! 5. `specs_physics::systems::SyncBodiesFromPhysicsSystem` -
    162 //! handles the synchronisation of [RigidBody][] positions and dynamics back
    163 //! into the [Specs][] `Component`s. This `System` also utilises the
    164 //! `Position` *trait* implementation.
    165 //!
    166 //! An example `Dispatcher` with all required `System`s:
    167 //!
    168 //! ```rust
    169 //! use specs::DispatcherBuilder;
    170 //! use specs_physics::{
    171 //!     systems::{
    172 //!         PhysicsStepperSystem,
    173 //!         SyncBodiesFromPhysicsSystem,
    174 //!         SyncBodiesToPhysicsSystem,
    175 //!         SyncCollidersToPhysicsSystem,
    176 //!         SyncParametersToPhysicsSystem,
    177 //!     },
    178 //!     SimplePosition,
    179 //! };
    180 //!
    181 //! let dispatcher = DispatcherBuilder::new()
    182 //!     .with(
    183 //!         SyncBodiesToPhysicsSystem::<f32, SimplePosition<f32>>::default(),
    184 //!         "sync_bodies_to_physics_system",
    185 //!         &[],
    186 //!     )
    187 //!     .with(
    188 //!         SyncCollidersToPhysicsSystem::<f32, SimplePosition<f32>>::default(),
    189 //!         "sync_colliders_to_physics_system",
    190 //!         &["sync_bodies_to_physics_system"],
    191 //!     )
    192 //!     .with(
    193 //!         SyncParametersToPhysicsSystem::<f32>::default(),
    194 //!         "sync_gravity_to_physics_system",
    195 //!         &[],
    196 //!     )
    197 //!     .with(
    198 //!         PhysicsStepperSystem::<f32>::default(),
    199 //!         "physics_stepper_system",
    200 //!         &[
    201 //!             "sync_bodies_to_physics_system",
    202 //!             "sync_colliders_to_physics_system",
    203 //!             "sync_gravity_to_physics_system",
    204 //!         ],
    205 //!     )
    206 //!     .with(
    207 //!         SyncBodiesFromPhysicsSystem::<f32, SimplePosition<f32>>::default(),
    208 //!         "sync_bodies_from_physics_system",
    209 //!         &["physics_stepper_system"],
    210 //!     )
    211 //!     .build();
    212 //! ```
    213 //!
    214 //! If you're using [Amethyst][] Transforms directly, you'd pass the generic
    215 //! arguments like so:
    216 //!
    217 //! ```
    218 //! # #[cfg(feature = "amethyst")]
    219 //! # {
    220 //! use amethyst_core::Transform;
    221 //! use specs_physics::systems::SyncBodiesToPhysicsSystem;
    222 //! SyncBodiesToPhysicsSystem::<f32, Transform>::default();
    223 //! # }
    224 //! ```
    225 //!
    226 //! Alternatively to building your own `Dispatcher`, you can always fall back on
    227 //! the convenience function `specs_physics::physics_dispatcher()`, which
    228 //! returns a configured *default* `Dispatcher` for you or
    229 //! `specs_physics::register_physics_systems()` which takes a
    230 //! `DispatcherBuilder` as an argument and registers the required `System`s for
    231 //! you.
    232 //!
    233 //! [Specs]: https://slide-rs.github.io/specs/
    234 //! [nphysics]: https://www.nphysics.org/
    235 //! [nalgebra]: https://nalgebra.org/
    236 //! [RigidBody]: https://www.nphysics.org/rigid_body_simulations_with_contacts/#rigid-bodies
    237 //! [Collider]: https://www.nphysics.org/rigid_body_simulations_with_contacts/#colliders
    238 //! [Amethyst]: https://amethyst.rs/
    239 //! [Entity hierarchy]: https://github.com/bamling/specs-physics/blob/master/examples/hierarchy.rs
    240 //! [specs-hierarchy]: https://github.com/rustgd/specs-hierarchy
    241 
    242 #[macro_use]
    243 extern crate log;
    244 
    245 pub use nalgebra;
    246 pub use ncollide3d as ncollide;
    247 pub use nphysics3d as nphysics;
    248 pub use shrev;
    249 
    250 use std::collections::HashMap;
    251 
    252 use specs::{
    253     world::Index,
    254     Component,
    255     DenseVecStorage,
    256     Dispatcher,
    257     DispatcherBuilder,
    258     Entity,
    259     FlaggedStorage,
    260 };
    261 use specs_hierarchy::Parent;
    262 
    263 pub use self::{
    264     bodies::{util::SimplePosition, PhysicsBody, PhysicsBodyBuilder},
    265     colliders::{PhysicsCollider, PhysicsColliderBuilder},
    266 };
    267 
    268 use self::{
    269     bodies::Position,
    270     nalgebra::{RealField, Vector3},
    271     nphysics::{
    272         counters::Counters,
    273         force_generator::DefaultForceGeneratorSet,
    274         joint::DefaultJointConstraintSet,
    275         material::MaterialsCoefficientsTable,
    276         object::{
    277             DefaultBodyHandle,
    278             DefaultBodySet,
    279             DefaultColliderHandle,
    280             DefaultColliderSet,
    281             Ground,
    282         },
    283         solver::IntegrationParameters,
    284         world::{DefaultGeometricalWorld, DefaultMechanicalWorld},
    285     },
    286     systems::{
    287         PhysicsStepperSystem,
    288         SyncBodiesFromPhysicsSystem,
    289         SyncBodiesToPhysicsSystem,
    290         SyncCollidersToPhysicsSystem,
    291         SyncParametersToPhysicsSystem,
    292     },
    293 };
    294 
    295 #[cfg(feature = "amethyst")]
    296 pub mod amethyst;
    297 pub mod bodies;
    298 pub mod colliders;
    299 pub mod events;
    300 pub mod parameters;
    301 pub mod systems;
    302 /// Resource holding the internal fields where physics computation occurs.
    303 /// Some inspection methods are exposed to allow debugging.
    304 pub struct Physics<N: RealField> {
    305     /// Core structure where physics computation and synchronization occurs.
    306     pub(crate) mechanical_world: DefaultMechanicalWorld<N>,
    307     pub(crate) geometrical_world: DefaultGeometricalWorld<N>,
    308     pub(crate) bodies: DefaultBodySet<N>,
    309     pub(crate) colliders: DefaultColliderSet<N>,
    310     pub(crate) joint_constraints: DefaultJointConstraintSet<N>,
    311     pub(crate) force_generators: DefaultForceGeneratorSet<N>,
    312     pub(crate) ground: DefaultBodyHandle,
    313 
    314     /// Hashmap of Entities to internal Physics bodies.
    315     /// Necessary for reacting to removed Components.
    316     pub(crate) body_handles: HashMap<Index, DefaultBodyHandle>,
    317     /// Hashmap of Entities to internal Collider handles.
    318     /// Necessary for reacting to removed Components.
    319     pub(crate) collider_handles: HashMap<Index, DefaultColliderHandle>,
    320 }
    321 
    322 // Some non-mutating methods for diagnostics and testing
    323 impl<N: RealField> Physics<N> {
    324     /// Creates a new instance of the physics structure.
    325     pub fn new() -> Self {
    326         Self::default()
    327     }
    328 
    329     /// Reports the internal value for the timestep.
    330     /// See also `TimeStep` for setting this value.
    331     pub fn timestep(&self) -> N {
    332         self.mechanical_world.timestep()
    333     }
    334 
    335     /// Reports the internal value for the gravity.
    336     /// See also `Gravity` for setting this value.
    337     pub fn gravity(&self) -> &Vector3<N> {
    338         &self.mechanical_world.gravity
    339     }
    340 
    341     /// Retrieves the performance statistics for the last simulated timestep.
    342     /// Profiling is disabled by default.
    343     /// See also `PhysicsProfilingEnabled` for enabling performance counters.
    344     pub fn performance_counters(&self) -> &Counters {
    345         &self.mechanical_world.counters
    346     }
    347 
    348     /// Retrieves the internal parameters for integration.
    349     /// See also `PhysicsIntegrationParameters` for setting these parameters.
    350     pub fn integration_parameters(&self) -> &IntegrationParameters<N> {
    351         &self.mechanical_world.integration_parameters
    352     }
    353 
    354     /// Retrieves the internal lookup table for friction and restitution
    355     /// constants. Exposing this for modification is TODO.
    356     pub fn materials_coefficients_table(&self) -> &MaterialsCoefficientsTable<N> {
    357         &self.mechanical_world.material_coefficients
    358     }
    359 }
    360 
    361 impl<N: RealField> Default for Physics<N> {
    362     fn default() -> Self {
    363         let mut bodies = DefaultBodySet::new();
    364         let ground = bodies.insert(Ground::new());
    365         Self {
    366             mechanical_world: DefaultMechanicalWorld::new(Vector3::zeros()),
    367             geometrical_world: DefaultGeometricalWorld::new(),
    368             bodies,
    369             ground,
    370             colliders: DefaultColliderSet::new(),
    371             joint_constraints: DefaultJointConstraintSet::new(),
    372             force_generators: DefaultForceGeneratorSet::new(),
    373             body_handles: HashMap::new(),
    374             collider_handles: HashMap::new(),
    375         }
    376     }
    377 }
    378 
    379 /// The `PhysicsParent` `Component` is used to represent a parent/child
    380 /// relationship between physics based `Entity`s.
    381 #[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
    382 pub struct PhysicsParent {
    383     pub entity: Entity,
    384 }
    385 
    386 impl Component for PhysicsParent {
    387     type Storage = FlaggedStorage<Self, DenseVecStorage<Self>>;
    388 }
    389 
    390 impl Parent for PhysicsParent {
    391     fn parent_entity(&self) -> Entity {
    392         self.entity
    393     }
    394 }
    395 
    396 /// Convenience function for configuring and building a `Dispatcher` with all
    397 /// required physics related `System`s.
    398 ///
    399 /// # Examples
    400 /// ```
    401 /// use specs_physics::bodies::util::SimplePosition;
    402 /// let dispatcher = specs_physics::physics_dispatcher::<f32, SimplePosition<f32>>();
    403 /// ```
    404 pub fn physics_dispatcher<'a, 'b, N, P>() -> Dispatcher<'a, 'b>
    405 where
    406     N: RealField,
    407     P: Position<N>,
    408 {
    409     let mut dispatcher_builder = DispatcherBuilder::new();
    410     register_physics_systems::<N, P>(&mut dispatcher_builder);
    411 
    412     dispatcher_builder.build()
    413 }
    414 
    415 /// Convenience function for registering all required physics related `System`s
    416 /// to the given `DispatcherBuilder`. This also serves as a blueprint on how
    417 ///// to properly set up the `System`s and have them depend on each other.
    418 pub fn register_physics_systems<N, P>(dispatcher_builder: &mut DispatcherBuilder)
    419 where
    420     N: RealField,
    421     P: Position<N>,
    422 {
    423     // add SyncBodiesToPhysicsSystem first since we have to start with bodies;
    424     // colliders can exist without a body but in most cases have a body parent
    425     dispatcher_builder.add(
    426         SyncBodiesToPhysicsSystem::<N, P>::default(),
    427         "sync_bodies_to_physics_system",
    428         &[],
    429     );
    430 
    431     // add SyncCollidersToPhysicsSystem next with SyncBodiesToPhysicsSystem as its
    432     // dependency
    433     dispatcher_builder.add(
    434         SyncCollidersToPhysicsSystem::<N, P>::default(),
    435         "sync_colliders_to_physics_system",
    436         &["sync_bodies_to_physics_system"],
    437     );
    438 
    439     // add SyncParametersToPhysicsSystem; this System can be added at any point in
    440     // time as it merely synchronizes the simulation parameters of the world,
    441     // thus it has no other dependencies.
    442     dispatcher_builder.add(
    443         SyncParametersToPhysicsSystem::<N>::default(),
    444         "sync_parameters_to_physics_system",
    445         &[],
    446     );
    447 
    448     // add PhysicsStepperSystem after all other Systems that write data to the
    449     // nphysics DefaultMechanicalWorld and has to depend on them; this System is
    450     // used to progress the nphysics DefaultMechanicalWorld for all existing
    451     // objects
    452     dispatcher_builder.add(
    453         PhysicsStepperSystem::<N>::default(),
    454         "physics_stepper_system",
    455         &[
    456             "sync_bodies_to_physics_system",
    457             "sync_colliders_to_physics_system",
    458             "sync_parameters_to_physics_system",
    459         ],
    460     );
    461 
    462     // add SyncBodiesFromPhysicsSystem last as it handles the
    463     // synchronisation between nphysics DefaultMechanicalWorld bodies and the
    464     // Position components; this depends on the PhysicsStepperSystem
    465     dispatcher_builder.add(
    466         SyncBodiesFromPhysicsSystem::<N, P>::default(),
    467         "sync_bodies_from_physics_system",
    468         &["physics_stepper_system"],
    469     );
    470 }