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
use std::marker::PhantomData;
use specs::{
world::Index,
Entities,
Entity,
Read,
Resources,
System,
SystemData,
Write,
WriteExpect,
};
use crate::{
events::{ContactEvent, ContactEvents, ContactType, ProximityEvent, ProximityEvents},
nalgebra::RealField,
ncollide::{events::ContactEvent as NContactEvent, world::CollisionObjectHandle},
nphysics::world::ColliderWorld,
parameters::TimeStep,
Physics,
};
/// The `PhysicsStepperSystem` progresses the nphysics `World`.
pub struct PhysicsStepperSystem<N> {
n_marker: PhantomData<N>,
}
impl<'s, N: RealField> System<'s> for PhysicsStepperSystem<N> {
type SystemData = (
Entities<'s>,
Option<Read<'s, TimeStep<N>>>,
Write<'s, ContactEvents>,
Write<'s, ProximityEvents>,
WriteExpect<'s, Physics<N>>,
);
fn run(&mut self, data: Self::SystemData) {
let (entities, time_step, mut contact_events, mut proximity_events, mut physics) = data;
// if a TimeStep resource exits, set the timestep for the nphysics integration
// accordingly; this should not be required if the Systems are executed in a
// fixed interval
if let Some(time_step) = time_step {
// only update timestep if it actually differs from the current nphysics World
// one; keep in mind that changing the Resource will destabilize the simulation
if physics.world.timestep() != time_step.0 {
warn!(
"TimeStep and world.timestep() differ, changing worlds timestep from {} to: {:?}",
physics.world.timestep(),
time_step.0
);
physics.world.set_timestep(time_step.0);
}
}
physics.world.step();
let collider_world = physics.world.collider_world();
// map occurred ncollide ContactEvents to a custom ContactEvent type; this
// custom type contains data that is more relevant for Specs users than
// CollisionObjectHandles, such as the Entities that took part in the collision
contact_events.iter_write(collider_world.contact_events().iter().map(|contact_event| {
debug!("Got ContactEvent: {:?}", contact_event);
// retrieve CollisionObjectHandles from ContactEvent and map the ContactEvent
// type to our own custom ContactType
let (handle1, handle2, contact_type) = match contact_event {
NContactEvent::Started(handle1, handle2) => {
(*handle1, *handle2, ContactType::Started)
}
NContactEvent::Stopped(handle1, handle2) => {
(*handle1, *handle2, ContactType::Stopped)
}
};
// create our own ContactEvent from the extracted data; mapping the
// CollisionObjectHandles to Entities is error prone but should work as intended
// as long as we're the only ones working directly with the nphysics World
ContactEvent {
collider1: entity_from_collision_object_handle(&entities, handle1, &collider_world),
collider2: entity_from_collision_object_handle(&entities, handle2, &collider_world),
contact_type,
}
}));
// map occurred ncollide ProximityEvents to a custom ProximityEvent type; see
// ContactEvents for reasoning
proximity_events.iter_write(collider_world.proximity_events().iter().map(
|proximity_event| {
debug!("Got ProximityEvent: {:?}", proximity_event);
// retrieve CollisionObjectHandles and Proximity statuses from the ncollide
// ProximityEvent
let (handle1, handle2, prev_status, new_status) = (
proximity_event.collider1,
proximity_event.collider2,
proximity_event.prev_status,
proximity_event.new_status,
);
// create our own ProximityEvent from the extracted data; mapping
// CollisionObjectHandles to Entities is once again error prone, but yeah...
// ncollides Proximity types are mapped to our own types
ProximityEvent {
collider1: entity_from_collision_object_handle(
&entities,
handle1,
&collider_world,
),
collider2: entity_from_collision_object_handle(
&entities,
handle2,
&collider_world,
),
prev_status,
new_status,
}
},
));
}
fn setup(&mut self, res: &mut Resources) {
info!("PhysicsStepperSystem.setup");
Self::SystemData::setup(res);
// initialise required resources
res.entry::<Physics<N>>().or_insert_with(Physics::default);
}
}
impl<N> Default for PhysicsStepperSystem<N>
where
N: RealField,
{
fn default() -> Self {
Self {
n_marker: PhantomData,
}
}
}
fn entity_from_collision_object_handle<N: RealField>(
entities: &Entities,
collision_object_handle: CollisionObjectHandle,
collider_world: &ColliderWorld<N>,
) -> Entity {
entities.entity(
*collider_world
.collider(collision_object_handle)
.unwrap()
.user_data()
.unwrap()
.downcast_ref::<Index>()
.unwrap(),
)
}