Skip to main content

oxygengine_physics_2d/
resource.rs

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                            // TODO: test if test for effective (deep) filtering is needed.
236                            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                            // TODO: test if test for effective (deep) filtering is needed.
246                            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                        // TODO: test if test for effective (deep) filtering is needed.
261                        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                        // TODO: test if test for effective (deep) filtering is needed.
267                        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}