1use core::{ecs::Entity, Scalar};
2use ncollide2d::{pipeline::narrow_phase::ContactEvent, query::Proximity};
3use nphysics2d::{
4 force_generator::DefaultForceGeneratorSet,
5 joint::DefaultJointConstraintSet,
6 math::*,
7 object::{
8 Collider, DefaultBodyHandle, DefaultBodySet, DefaultColliderHandle, DefaultColliderSet,
9 RigidBody,
10 },
11 world::{DefaultGeometricalWorld, DefaultMechanicalWorld},
12};
13use std::collections::{HashMap, HashSet};
14
15#[derive(Debug, Copy, Clone)]
16pub enum Physics2dWorldSimulationMode {
17 FixedTimestepMaxIterations(usize),
18 DynamicTimestep,
19}
20
21#[derive(Debug, Clone)]
22pub enum Physics2dContact {
23 Started(Entity, Entity),
24 Stopped(Entity, Entity),
25}
26
27#[derive(Debug, Clone)]
28pub enum Physics2dProximity {
29 Started(Entity, Entity),
30 Stopped(Entity, Entity),
31}
32
33pub struct Physics2dWorld {
34 geometrical_world: DefaultGeometricalWorld<Scalar>,
35 mechanical_world: DefaultMechanicalWorld<Scalar>,
36 body_set: DefaultBodySet<Scalar>,
37 collider_set: DefaultColliderSet<Scalar>,
38 constraint_set: DefaultJointConstraintSet<Scalar>,
39 force_generator_set: DefaultForceGeneratorSet<Scalar>,
40 remaining_time_step: Scalar,
41 simulation_mode: Physics2dWorldSimulationMode,
42 collider_map: HashMap<DefaultColliderHandle, Entity>,
43 last_contacts: Vec<Physics2dContact>,
44 last_proximities: Vec<Physics2dProximity>,
45 active_contacts: HashSet<(Entity, Entity)>,
46 active_proximities: HashSet<(Entity, Entity)>,
47 paused: bool,
48}
49
50impl Default for Physics2dWorld {
51 fn default() -> Self {
52 Self {
53 geometrical_world: DefaultGeometricalWorld::new(),
54 mechanical_world: DefaultMechanicalWorld::new(Vector::y() * 9.81),
55 body_set: DefaultBodySet::new(),
56 collider_set: DefaultColliderSet::new(),
57 constraint_set: DefaultJointConstraintSet::new(),
58 force_generator_set: DefaultForceGeneratorSet::new(),
59 remaining_time_step: 0.0,
60 simulation_mode: Physics2dWorldSimulationMode::FixedTimestepMaxIterations(3),
61 collider_map: Default::default(),
62 last_contacts: vec![],
63 last_proximities: vec![],
64 active_contacts: Default::default(),
65 active_proximities: Default::default(),
66 paused: false,
67 }
68 }
69}
70
71impl Physics2dWorld {
72 pub fn new(gravity: Vector<Scalar>, mode: Physics2dWorldSimulationMode) -> Self {
73 let mut result = Self::default();
74 result.set_gravity(gravity);
75 result.set_simulation_mode(mode);
76 result
77 }
78
79 pub fn geometrical_world(&self) -> &DefaultGeometricalWorld<Scalar> {
80 &self.geometrical_world
81 }
82
83 pub fn mechanical_world(&self) -> &DefaultMechanicalWorld<Scalar> {
84 &self.mechanical_world
85 }
86
87 pub fn gravity(&self) -> Vector<Scalar> {
88 self.mechanical_world.gravity
89 }
90
91 pub fn set_gravity(&mut self, value: Vector<Scalar>) {
92 self.mechanical_world.gravity = value;
93 }
94
95 pub fn simulation_mode(&self) -> Physics2dWorldSimulationMode {
96 self.simulation_mode
97 }
98
99 pub fn set_simulation_mode(&mut self, simulation_mode: Physics2dWorldSimulationMode) {
100 self.simulation_mode = simulation_mode;
101 }
102
103 pub fn time_step(&self) -> Scalar {
104 self.mechanical_world.timestep()
105 }
106
107 pub fn set_time_step(&mut self, value: Scalar) {
108 self.mechanical_world.set_timestep(value);
109 self.remaining_time_step = 0.0;
110 }
111
112 pub fn paused(&self) -> bool {
113 self.paused
114 }
115
116 pub fn set_paused(&mut self, value: bool) {
117 self.paused = value;
118 }
119
120 pub fn reset_timestep_accumulator(&mut self) {
121 self.remaining_time_step = 0.0;
122 }
123
124 pub(crate) fn insert_body(&mut self, body: RigidBody<Scalar>) -> DefaultBodyHandle {
125 self.body_set.insert(body)
126 }
127
128 pub(crate) fn destroy_body(&mut self, handle: DefaultBodyHandle) {
129 self.body_set.remove(handle);
130 }
131
132 pub fn body(&self, handle: DefaultBodyHandle) -> Option<&RigidBody<Scalar>> {
133 self.body_set.rigid_body(handle)
134 }
135
136 pub fn body_mut(&mut self, handle: DefaultBodyHandle) -> Option<&mut RigidBody<Scalar>> {
137 self.body_set.rigid_body_mut(handle)
138 }
139
140 pub fn last_contacts(&self) -> impl Iterator<Item = &Physics2dContact> {
141 self.last_contacts.iter()
142 }
143
144 pub fn last_proximities(&self) -> impl Iterator<Item = &Physics2dProximity> {
145 self.last_proximities.iter()
146 }
147
148 pub fn active_contacts(&self) -> impl Iterator<Item = &(Entity, Entity)> {
149 self.active_contacts.iter()
150 }
151
152 pub fn active_proximities(&self) -> impl Iterator<Item = &(Entity, Entity)> {
153 self.active_proximities.iter()
154 }
155
156 pub(crate) fn insert_collider(
157 &mut self,
158 collider: Collider<Scalar, DefaultBodyHandle>,
159 entity: Entity,
160 ) -> DefaultColliderHandle {
161 let handle = self.collider_set.insert(collider);
162 self.collider_map.insert(handle, entity);
163 handle
164 }
165
166 pub(crate) fn destroy_collider(&mut self, handle: DefaultColliderHandle) {
167 self.collider_set.remove(handle);
168 }
169
170 pub fn collider(
171 &self,
172 handle: DefaultColliderHandle,
173 ) -> Option<&Collider<Scalar, DefaultBodyHandle>> {
174 self.collider_set.get(handle)
175 }
176
177 pub fn collider_mut(
178 &mut self,
179 handle: DefaultColliderHandle,
180 ) -> Option<&mut Collider<Scalar, DefaultBodyHandle>> {
181 self.collider_set.get_mut(handle)
182 }
183
184 pub fn are_in_contact(&self, first: Entity, second: Entity) -> bool {
185 self.active_contacts.contains(&(first, second))
186 || self.active_contacts.contains(&(second, first))
187 }
188
189 pub fn are_in_proximity(&self, first: Entity, second: Entity) -> bool {
190 self.active_proximities.contains(&(first, second))
191 || self.active_proximities.contains(&(second, first))
192 }
193
194 pub fn process(&mut self, mut delta_time: Scalar) {
195 if self.paused {
196 return;
197 }
198 self.last_contacts.clear();
199 self.last_proximities.clear();
200 self.active_contacts.clear();
201 self.active_proximities.clear();
202 match self.simulation_mode {
203 Physics2dWorldSimulationMode::FixedTimestepMaxIterations(iterations) => {
204 let time_step = self.mechanical_world.timestep();
205 delta_time += self.remaining_time_step;
206 let mut i = 0;
207 while delta_time >= time_step && i < iterations {
208 self.step();
209 delta_time -= time_step;
210 i += 1;
211 }
212 self.remaining_time_step = delta_time % time_step;
213 }
214 Physics2dWorldSimulationMode::DynamicTimestep => {
215 self.mechanical_world.set_timestep(delta_time);
216 self.step();
217 self.remaining_time_step = 0.0;
218 }
219 }
220 }
221
222 fn step(&mut self) {
223 self.mechanical_world.step(
224 &mut self.geometrical_world,
225 &mut self.body_set,
226 &mut self.collider_set,
227 &mut self.constraint_set,
228 &mut self.force_generator_set,
229 );
230 for contact in self.geometrical_world.contact_events() {
231 match contact {
232 ContactEvent::Started(a, b) => {
233 if let Some(a) = self.collider_map.get(a) {
234 if let Some(b) = self.collider_map.get(b) {
235 self.last_contacts.push(Physics2dContact::Started(*a, *b));
237 self.active_contacts.insert((*a, *b));
238 self.active_contacts.insert((*b, *a));
239 }
240 }
241 }
242 ContactEvent::Stopped(a, b) => {
243 if let Some(a) = self.collider_map.get(a) {
244 if let Some(b) = self.collider_map.get(b) {
245 self.last_contacts.push(Physics2dContact::Stopped(*a, *b));
247 self.active_contacts.remove(&(*a, *b));
248 self.active_contacts.remove(&(*b, *a));
249 }
250 }
251 }
252 }
253 }
254 for proximity in self.geometrical_world.proximity_events() {
255 if let Some(a) = self.collider_map.get(&proximity.collider1) {
256 if let Some(b) = self.collider_map.get(&proximity.collider2) {
257 let p = proximity.prev_status == Proximity::Intersecting;
258 let n = proximity.new_status == Proximity::Intersecting;
259 if !p && n {
260 self.last_proximities
262 .push(Physics2dProximity::Started(*a, *b));
263 self.active_proximities.insert((*a, *b));
264 self.active_proximities.insert((*b, *a));
265 } else if p && !n {
266 self.last_proximities
268 .push(Physics2dProximity::Stopped(*a, *b));
269 self.active_proximities.remove(&(*a, *b));
270 self.active_proximities.remove(&(*b, *a));
271 }
272 }
273 }
274 }
275 }
276}