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 }