specs-physics

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

sync_bodies_to_physics.rs (7566B)


      1 use std::marker::PhantomData;
      2 
      3 use specs::{
      4     storage::ComponentEvent,
      5     world::Index,
      6     BitSet,
      7     Join,
      8     ReadStorage,
      9     ReaderId,
     10     System,
     11     SystemData,
     12     World,
     13     WriteExpect,
     14     WriteStorage,
     15 };
     16 
     17 use crate::{
     18     bodies::{PhysicsBody, Position},
     19     nalgebra::RealField,
     20     Physics,
     21 };
     22 
     23 use super::iterate_component_events;
     24 
     25 /// The `SyncBodiesToPhysicsSystem` handles the synchronisation of `PhysicsBody`
     26 /// `Component`s into the physics `World`.
     27 pub struct SyncBodiesToPhysicsSystem<N, P> {
     28     positions_reader_id: Option<ReaderId<ComponentEvent>>,
     29     physics_bodies_reader_id: Option<ReaderId<ComponentEvent>>,
     30 
     31     n_marker: PhantomData<N>,
     32     p_marker: PhantomData<P>,
     33 }
     34 
     35 impl<'s, N, P> System<'s> for SyncBodiesToPhysicsSystem<N, P>
     36 where
     37     N: RealField,
     38     P: Position<N>,
     39 {
     40     type SystemData = (
     41         ReadStorage<'s, P>,
     42         WriteExpect<'s, Physics<N>>,
     43         WriteStorage<'s, PhysicsBody<N>>,
     44     );
     45 
     46     fn run(&mut self, data: Self::SystemData) {
     47         let (positions, mut physics, mut physics_bodies) = data;
     48 
     49         // collect all ComponentEvents for the Position storage
     50         let (inserted_positions, modified_positions, removed_positions) =
     51             iterate_component_events(&positions, self.positions_reader_id.as_mut().unwrap());
     52 
     53         // collect all ComponentEvents for the PhysicsBody storage
     54         let (inserted_physics_bodies, modified_physics_bodies, removed_physics_bodies) =
     55             iterate_component_events(
     56                 &physics_bodies,
     57                 self.physics_bodies_reader_id.as_mut().unwrap(),
     58             );
     59 
     60         // handle removed events
     61         for id in &removed_physics_bodies | &removed_positions {
     62             remove_rigid_body::<N, P>(id, &mut physics);
     63         }
     64 
     65         // iterate over PhysicsBody and Position components with an id/Index that
     66         // exists in either of the collected ComponentEvent BitSets
     67         for (position, mut physics_body, id) in (
     68             &positions,
     69             &mut physics_bodies,
     70             &inserted_positions
     71                 | &modified_positions
     72                 | &removed_positions
     73                 | &inserted_physics_bodies
     74                 | &modified_physics_bodies
     75                 | &removed_physics_bodies,
     76         )
     77             .join()
     78         {
     79             // handle inserted events
     80             if inserted_positions.contains(id) || inserted_physics_bodies.contains(id) {
     81                 debug!("Inserted PhysicsBody with id: {}", id);
     82                 add_rigid_body::<N, P>(id, &position, &mut physics, &mut physics_body);
     83             }
     84 
     85             // handle modified events
     86             if modified_positions.contains(id) || modified_physics_bodies.contains(id) {
     87                 debug!("Modified PhysicsBody with id: {}", id);
     88                 update_rigid_body::<N, P>(
     89                     id,
     90                     &position,
     91                     &mut physics,
     92                     &mut physics_body,
     93                     &modified_positions,
     94                     &modified_physics_bodies,
     95                 );
     96             }
     97         }
     98     }
     99 
    100     fn setup(&mut self, res: &mut World) {
    101         info!("SyncBodiesToPhysicsSystem.setup");
    102         Self::SystemData::setup(res);
    103 
    104         // initialise required resources
    105         res.entry::<Physics<N>>().or_insert_with(Physics::default);
    106 
    107         // register reader id for the Position storage
    108         let mut position_storage: WriteStorage<P> = SystemData::fetch(&res);
    109         self.positions_reader_id = Some(position_storage.register_reader());
    110 
    111         // register reader id for the PhysicsBody storage
    112         let mut physics_body_storage: WriteStorage<PhysicsBody<N>> = SystemData::fetch(&res);
    113         self.physics_bodies_reader_id = Some(physics_body_storage.register_reader());
    114     }
    115 }
    116 
    117 impl<N, P> Default for SyncBodiesToPhysicsSystem<N, P>
    118 where
    119     N: RealField,
    120     P: Position<N>,
    121 {
    122     fn default() -> Self {
    123         Self {
    124             positions_reader_id: None,
    125             physics_bodies_reader_id: None,
    126             n_marker: PhantomData,
    127             p_marker: PhantomData,
    128         }
    129     }
    130 }
    131 
    132 fn add_rigid_body<N, P>(
    133     id: Index,
    134     position: &P,
    135     physics: &mut Physics<N>,
    136     physics_body: &mut PhysicsBody<N>,
    137 ) where
    138     N: RealField,
    139     P: Position<N>,
    140 {
    141     // remove already existing bodies for this inserted component;
    142     // this technically should never happen but we need to keep the list of body
    143     // handles clean
    144     if let Some(body_handle) = physics.body_handles.remove(&id) {
    145         warn!("Removing orphaned body handle: {:?}", body_handle);
    146         physics.bodies.remove(body_handle);
    147     }
    148 
    149     // create a new RigidBody in the PhysicsWorld and store its
    150     // handle for later usage
    151     let handle = physics.bodies.insert(
    152         physics_body
    153             .to_rigid_body_desc()
    154             .position(*position.isometry())
    155             .user_data(id)
    156             .build(),
    157     );
    158 
    159     physics_body.handle = Some(handle);
    160     physics.body_handles.insert(id, handle);
    161 
    162     info!(
    163         "Inserted rigid body to world with values: {:?}",
    164         physics_body
    165     );
    166 }
    167 
    168 fn update_rigid_body<N, P>(
    169     id: Index,
    170     position: &P,
    171     physics: &mut Physics<N>,
    172     physics_body: &mut PhysicsBody<N>,
    173     modified_positions: &BitSet,
    174     modified_physics_bodies: &BitSet,
    175 ) where
    176     N: RealField,
    177     P: Position<N>,
    178 {
    179     if let Some(rigid_body) = physics.bodies.rigid_body_mut(physics_body.handle.unwrap()) {
    180         // the PhysicsBody was modified, update everything but the position
    181         if modified_physics_bodies.contains(id) {
    182             physics_body.apply_to_physics_world(rigid_body);
    183         }
    184 
    185         // the Position was modified, update the position directly
    186         if modified_positions.contains(id) {
    187             rigid_body.set_position(*position.isometry());
    188         }
    189 
    190         trace!(
    191             "Updated rigid body in world with values: {:?}",
    192             physics_body
    193         );
    194     }
    195 }
    196 
    197 fn remove_rigid_body<N, P>(id: Index, physics: &mut Physics<N>)
    198 where
    199     N: RealField,
    200     P: Position<N>,
    201 {
    202     if let Some(handle) = physics.body_handles.remove(&id) {
    203         // remove body if it still exists in the PhysicsWorld
    204         physics.bodies.remove(handle);
    205         info!("Removed rigid body from world with id: {}", id);
    206     }
    207 }
    208 
    209 #[cfg(test)]
    210 mod tests {
    211     use crate::{
    212         nalgebra::Isometry3,
    213         nphysics::object::BodyStatus,
    214         systems::SyncBodiesToPhysicsSystem,
    215         Physics,
    216         PhysicsBodyBuilder,
    217         SimplePosition,
    218     };
    219 
    220     use specs::prelude::*;
    221 
    222     #[test]
    223     fn add_rigid_body() {
    224         let mut world = World::new();
    225         let mut dispatcher = DispatcherBuilder::new()
    226             .with(
    227                 SyncBodiesToPhysicsSystem::<f32, SimplePosition<f32>>::default(),
    228                 "sync_bodies_to_physics_system",
    229                 &[],
    230             )
    231             .build();
    232         dispatcher.setup(&mut world);
    233 
    234         // create an Entity with the PhysicsBody component and execute the dispatcher
    235         world
    236             .create_entity()
    237             .with(SimplePosition::<f32>(Isometry3::<f32>::translation(
    238                 1.0, 1.0, 1.0,
    239             )))
    240             .with(PhysicsBodyBuilder::<f32>::from(BodyStatus::Dynamic).build())
    241             .build();
    242         dispatcher.dispatch(&world);
    243 
    244         // fetch the Physics instance and check for new bodies
    245         let physics = world.read_resource::<Physics<f32>>();
    246         assert_eq!(physics.body_handles.len(), 1);
    247         assert_eq!(physics.bodies.iter().count(), 2);
    248     }
    249 }