Skip to main content

oxygengine_physics_2d/
component.rs

1use core::{
2    ecs::Entity,
3    prefab::{Prefab, PrefabError, PrefabProxy},
4    state::StateToken,
5    Scalar,
6};
7use ncollide2d::{
8    pipeline::CollisionGroups,
9    shape::{Ball, Capsule, ConvexPolygon, Cuboid, HeightField, Plane, Segment, ShapeHandle},
10};
11use nphysics2d::{
12    math::{Inertia, Isometry, Point, Vector, Velocity},
13    object::{
14        ActivationStatus, BodyStatus, ColliderDesc, DefaultBodyHandle, DefaultColliderHandle,
15        RigidBodyDesc,
16    },
17};
18use serde::{Deserialize, Serialize};
19use std::collections::HashMap;
20#[cfg(not(feature = "scalar64"))]
21use std::f32::{consts::PI as SCALAR_PI, MAX as SCALAR_MAX};
22#[cfg(feature = "scalar64")]
23use std::f64::{consts::PI as SCALAR_PI, MAX as SCALAR_MAX};
24
25pub(crate) enum RigidBody2dInner {
26    None,
27    Description(RigidBodyDesc<Scalar>),
28    Handle(DefaultBodyHandle),
29}
30
31pub struct RigidBody2d(pub(crate) RigidBody2dInner);
32
33impl RigidBody2d {
34    pub fn new(desc: RigidBodyDesc<Scalar>) -> Self {
35        Self(RigidBody2dInner::Description(desc))
36    }
37
38    pub fn is_created(&self) -> bool {
39        matches!(&self.0, RigidBody2dInner::Handle(_))
40    }
41
42    pub fn handle(&self) -> Option<DefaultBodyHandle> {
43        if let RigidBody2dInner::Handle(handle) = &self.0 {
44            Some(*handle)
45        } else {
46            None
47        }
48    }
49
50    pub(crate) fn take_description(&mut self) -> Option<RigidBodyDesc<Scalar>> {
51        if self.is_created() {
52            return None;
53        }
54        let inner = std::mem::replace(&mut self.0, RigidBody2dInner::None);
55        if let RigidBody2dInner::Description(desc) = inner {
56            Some(desc)
57        } else {
58            None
59        }
60    }
61}
62
63#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
64pub enum RigidBody2dPrefabProxyBodyStatus {
65    Disabled,
66    Static,
67    Dynamic,
68    Kinematic,
69}
70
71impl Default for RigidBody2dPrefabProxyBodyStatus {
72    fn default() -> Self {
73        Self::Dynamic
74    }
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct RigidBody2dPrefabProxy {
79    #[serde(default = "RigidBody2dPrefabProxy::default_gravity_enabled")]
80    pub gravity_enabled: bool,
81    #[serde(default = "RigidBody2dPrefabProxy::default_linear_motion_interpolation_enabled")]
82    pub linear_motion_interpolation_enabled: bool,
83    #[serde(default = "RigidBody2dPrefabProxy::default_position")]
84    pub position: Vector<Scalar>,
85    #[serde(default = "RigidBody2dPrefabProxy::default_rotation")]
86    pub rotation: Scalar,
87    #[serde(default = "RigidBody2dPrefabProxy::default_velocity")]
88    pub velocity: (Vector<Scalar>, Scalar),
89    #[serde(default = "RigidBody2dPrefabProxy::default_linear_damping")]
90    pub linear_damping: Scalar,
91    #[serde(default = "RigidBody2dPrefabProxy::default_angular_damping")]
92    pub angular_damping: Scalar,
93    #[serde(default = "RigidBody2dPrefabProxy::default_max_linear_velocity")]
94    pub max_linear_velocity: Scalar,
95    #[serde(default = "RigidBody2dPrefabProxy::default_max_angular_velocity")]
96    pub max_angular_velocity: Scalar,
97    #[serde(default = "RigidBody2dPrefabProxy::default_local_inertia")]
98    pub local_inertia: (Scalar, Scalar),
99    #[serde(default = "RigidBody2dPrefabProxy::default_local_center_of_mass")]
100    pub local_center_of_mass: Point<Scalar>,
101    #[serde(default = "RigidBody2dPrefabProxy::default_status")]
102    pub status: RigidBody2dPrefabProxyBodyStatus,
103    #[serde(default = "RigidBody2dPrefabProxy::default_sleep_threshold")]
104    pub sleep_threshold: Option<Scalar>,
105    #[serde(default = "RigidBody2dPrefabProxy::default_kinematic_translations")]
106    pub kinematic_translations: Vector<bool>,
107    #[serde(default = "RigidBody2dPrefabProxy::default_kinematic_rotations")]
108    pub kinematic_rotations: bool,
109}
110
111impl Default for RigidBody2dPrefabProxy {
112    fn default() -> Self {
113        Self {
114            gravity_enabled: Self::default_gravity_enabled(),
115            linear_motion_interpolation_enabled: Self::default_linear_motion_interpolation_enabled(
116            ),
117            position: Self::default_position(),
118            rotation: Self::default_rotation(),
119            velocity: Self::default_velocity(),
120            linear_damping: Self::default_linear_damping(),
121            angular_damping: Self::default_angular_damping(),
122            max_linear_velocity: Self::default_max_linear_velocity(),
123            max_angular_velocity: Self::default_max_angular_velocity(),
124            local_inertia: Self::default_local_inertia(),
125            local_center_of_mass: Self::default_local_center_of_mass(),
126            status: Self::default_status(),
127            sleep_threshold: Self::default_sleep_threshold(),
128            kinematic_translations: Self::default_kinematic_translations(),
129            kinematic_rotations: Self::default_kinematic_rotations(),
130        }
131    }
132}
133
134impl RigidBody2dPrefabProxy {
135    fn default_gravity_enabled() -> bool {
136        true
137    }
138
139    fn default_linear_motion_interpolation_enabled() -> bool {
140        false
141    }
142
143    fn default_position() -> Vector<Scalar> {
144        Vector::new(0.0, 0.0)
145    }
146
147    fn default_rotation() -> Scalar {
148        0.0
149    }
150
151    fn default_velocity() -> (Vector<Scalar>, Scalar) {
152        (Vector::new(0.0, 0.0), 0.0)
153    }
154
155    fn default_linear_damping() -> Scalar {
156        0.0
157    }
158
159    fn default_angular_damping() -> Scalar {
160        0.0
161    }
162
163    fn default_max_linear_velocity() -> Scalar {
164        SCALAR_MAX
165    }
166
167    fn default_max_angular_velocity() -> Scalar {
168        SCALAR_MAX
169    }
170
171    fn default_local_inertia() -> (Scalar, Scalar) {
172        (0.0, 0.0)
173    }
174
175    fn default_local_center_of_mass() -> Point<Scalar> {
176        Point::origin()
177    }
178
179    fn default_status() -> RigidBody2dPrefabProxyBodyStatus {
180        RigidBody2dPrefabProxyBodyStatus::Dynamic
181    }
182
183    #[allow(clippy::unnecessary_wraps)]
184    fn default_sleep_threshold() -> Option<Scalar> {
185        Some(ActivationStatus::default_threshold())
186    }
187
188    fn default_kinematic_translations() -> Vector<bool> {
189        Vector::repeat(false)
190    }
191
192    fn default_kinematic_rotations() -> bool {
193        false
194    }
195}
196
197impl Prefab for RigidBody2dPrefabProxy {}
198
199impl PrefabProxy<RigidBody2dPrefabProxy> for RigidBody2d {
200    fn from_proxy_with_extras(
201        proxy: RigidBody2dPrefabProxy,
202        _: &HashMap<String, Entity>,
203        _: StateToken,
204    ) -> Result<Self, PrefabError> {
205        let desc = RigidBodyDesc::new()
206            .gravity_enabled(proxy.gravity_enabled)
207            .linear_motion_interpolation_enabled(proxy.linear_motion_interpolation_enabled)
208            .position(Isometry::new(proxy.position, proxy.rotation))
209            .velocity(Velocity::new(proxy.velocity.0, proxy.velocity.1))
210            .linear_damping(proxy.linear_damping)
211            .angular_damping(proxy.angular_damping)
212            .max_linear_velocity(proxy.max_linear_velocity)
213            .max_angular_velocity(proxy.max_angular_velocity)
214            .local_inertia(Inertia::new(proxy.local_inertia.0, proxy.local_inertia.1))
215            .local_center_of_mass(proxy.local_center_of_mass)
216            .status(match proxy.status {
217                RigidBody2dPrefabProxyBodyStatus::Disabled => BodyStatus::Disabled,
218                RigidBody2dPrefabProxyBodyStatus::Static => BodyStatus::Static,
219                RigidBody2dPrefabProxyBodyStatus::Dynamic => BodyStatus::Dynamic,
220                RigidBody2dPrefabProxyBodyStatus::Kinematic => BodyStatus::Kinematic,
221            })
222            .sleep_threshold(proxy.sleep_threshold)
223            .kinematic_translations(proxy.kinematic_translations)
224            .kinematic_rotations(proxy.kinematic_rotations);
225        Ok(Self::new(desc))
226    }
227}
228
229pub(crate) enum Collider2dInner {
230    None,
231    Description(ColliderDesc<Scalar>),
232    Handle(DefaultColliderHandle),
233}
234
235pub struct Collider2d(pub(crate) Collider2dInner);
236
237impl Collider2d {
238    pub fn new(desc: ColliderDesc<Scalar>) -> Self {
239        Self(Collider2dInner::Description(desc))
240    }
241
242    pub fn is_created(&self) -> bool {
243        matches!(&self.0, Collider2dInner::Handle(_))
244    }
245
246    pub fn handle(&self) -> Option<DefaultColliderHandle> {
247        if let Collider2dInner::Handle(handle) = &self.0 {
248            Some(*handle)
249        } else {
250            None
251        }
252    }
253
254    pub(crate) fn take_description(&mut self) -> Option<ColliderDesc<Scalar>> {
255        if self.is_created() {
256            return None;
257        }
258        let inner = std::mem::replace(&mut self.0, Collider2dInner::None);
259        if let Collider2dInner::Description(desc) = inner {
260            Some(desc)
261        } else {
262            None
263        }
264    }
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub enum Collider2dPrefabProxyShape {
269    Ball(Ball<Scalar>),
270    Capsule(Capsule<Scalar>),
271    ConvexPolygon(ConvexPolygon<Scalar>),
272    Cuboid(Cuboid<Scalar>),
273    HeightField(HeightField<Scalar>),
274    Plane(Plane<Scalar>),
275    Segment(Segment<Scalar>),
276}
277
278impl Default for Collider2dPrefabProxyShape {
279    fn default() -> Self {
280        Self::Ball(Ball::new(1.0))
281    }
282}
283
284#[derive(Debug, Default, Clone, Serialize, Deserialize)]
285pub struct Collider2dPrefabProxyCollisionGroups {
286    pub membership: Option<Vec<usize>>,
287    pub whitelist: Option<Vec<usize>>,
288    pub blacklist: Option<Vec<usize>>,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct Collider2dPrefabProxy {
293    #[serde(default = "Collider2dPrefabProxy::default_margin")]
294    pub margin: Scalar,
295    #[serde(default)]
296    pub collision_groups: Collider2dPrefabProxyCollisionGroups,
297    #[serde(default)]
298    pub shape: Collider2dPrefabProxyShape,
299    #[serde(default = "Collider2dPrefabProxy::default_position")]
300    pub position: Vector<Scalar>,
301    #[serde(default = "Collider2dPrefabProxy::default_rotation")]
302    pub rotation: Scalar,
303    #[serde(default = "Collider2dPrefabProxy::default_density")]
304    pub density: Scalar,
305    #[serde(default = "Collider2dPrefabProxy::default_linear_prediction")]
306    pub linear_prediction: Scalar,
307    #[serde(default = "Collider2dPrefabProxy::default_angular_prediction")]
308    pub angular_prediction: Scalar,
309    #[serde(default = "Collider2dPrefabProxy::default_is_sensor")]
310    pub is_sensor: bool,
311    #[serde(default = "Collider2dPrefabProxy::default_ccd_enabled")]
312    pub ccd_enabled: bool,
313}
314
315impl Default for Collider2dPrefabProxy {
316    fn default() -> Self {
317        Self {
318            margin: Self::default_margin(),
319            collision_groups: Collider2dPrefabProxyCollisionGroups::default(),
320            shape: Collider2dPrefabProxyShape::default(),
321            position: Self::default_position(),
322            rotation: Self::default_rotation(),
323            density: Self::default_density(),
324            linear_prediction: Self::default_linear_prediction(),
325            angular_prediction: Self::default_angular_prediction(),
326            is_sensor: Self::default_is_sensor(),
327            ccd_enabled: Self::default_ccd_enabled(),
328        }
329    }
330}
331
332impl Collider2dPrefabProxy {
333    fn default_margin() -> Scalar {
334        0.01
335    }
336
337    fn default_position() -> Vector<Scalar> {
338        Vector::new(0.0, 0.0)
339    }
340
341    fn default_rotation() -> Scalar {
342        0.0
343    }
344
345    fn default_density() -> Scalar {
346        0.0
347    }
348
349    fn default_linear_prediction() -> Scalar {
350        0.001
351    }
352
353    fn default_angular_prediction() -> Scalar {
354        SCALAR_PI / 180.0 * 5.0
355    }
356
357    fn default_is_sensor() -> bool {
358        false
359    }
360
361    fn default_ccd_enabled() -> bool {
362        false
363    }
364}
365
366impl Prefab for Collider2dPrefabProxy {}
367
368impl PrefabProxy<Collider2dPrefabProxy> for Collider2d {
369    fn from_proxy_with_extras(
370        proxy: Collider2dPrefabProxy,
371        _: &HashMap<String, Entity>,
372        _: StateToken,
373    ) -> Result<Self, PrefabError> {
374        let shape = match proxy.shape {
375            Collider2dPrefabProxyShape::Ball(shape) => ShapeHandle::new(shape),
376            Collider2dPrefabProxyShape::Capsule(shape) => ShapeHandle::new(shape),
377            Collider2dPrefabProxyShape::ConvexPolygon(shape) => ShapeHandle::new(shape),
378            Collider2dPrefabProxyShape::Cuboid(shape) => ShapeHandle::new(shape),
379            Collider2dPrefabProxyShape::HeightField(shape) => ShapeHandle::new(shape),
380            Collider2dPrefabProxyShape::Plane(shape) => ShapeHandle::new(shape),
381            Collider2dPrefabProxyShape::Segment(shape) => ShapeHandle::new(shape),
382        };
383        let desc = ColliderDesc::new(shape)
384            .margin(proxy.margin)
385            .collision_groups({
386                let mut result = CollisionGroups::new();
387                if let Some(list) = proxy.collision_groups.membership {
388                    result = result.with_membership(&list)
389                }
390                if let Some(list) = proxy.collision_groups.whitelist {
391                    result = result.with_whitelist(&list)
392                }
393                if let Some(list) = proxy.collision_groups.blacklist {
394                    result = result.with_blacklist(&list)
395                }
396                result
397            })
398            .position(Isometry::new(proxy.position, proxy.rotation))
399            .density(proxy.density)
400            .linear_prediction(proxy.linear_prediction)
401            .angular_prediction(proxy.angular_prediction)
402            .sensor(proxy.is_sensor)
403            .ccd_enabled(proxy.ccd_enabled);
404        Ok(Self::new(desc))
405    }
406}
407
408#[derive(Debug, Copy, Clone)]
409pub enum Collider2dBody {
410    Me,
411    Entity(Entity),
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
415pub enum Collider2dBodyPrefabProxy {
416    Me,
417    Entity(String),
418}
419
420impl Prefab for Collider2dBodyPrefabProxy {}
421
422impl PrefabProxy<Collider2dBodyPrefabProxy> for Collider2dBody {
423    fn from_proxy_with_extras(
424        proxy: Collider2dBodyPrefabProxy,
425        named_entities: &HashMap<String, Entity>,
426        _: StateToken,
427    ) -> Result<Self, PrefabError> {
428        match proxy {
429            Collider2dBodyPrefabProxy::Me => Ok(Self::Me),
430            Collider2dBodyPrefabProxy::Entity(name) => {
431                if let Some(entity) = named_entities.get(&name) {
432                    Ok(Self::Entity(*entity))
433                } else {
434                    Err(PrefabError::Custom(format!(
435                        "Could not find entity named: {}",
436                        name
437                    )))
438                }
439            }
440        }
441    }
442}