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