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 }