fyrox_impl/scene/dim2/
rigidbody.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Rigid body is a physics entity that responsible for the dynamics and kinematics of the solid.
22//!
23//! # Common problems
24//!
25//! **Q:** Rigid body is "stuck".
26//! **A:** Most likely the rigid body is "sleeping", in this case it must be activated manually, it is
27//! most common problem with rigid bodies that controlled manually from code. They must be activated
28//! using [`RigidBody::wake_up`]. By default any external action does **not** wakes up rigid body.
29//! You can also explicitly tell to rigid body that it cannot sleep, by calling
30//! [`RigidBody::set_can_sleep`] with `false` value.
31use crate::scene::node::constructor::NodeConstructor;
32use crate::{
33    core::{
34        algebra::{Matrix4, Vector2},
35        log::Log,
36        math::{aabb::AxisAlignedBoundingBox, m4x4_approx_eq},
37        parking_lot::Mutex,
38        pool::Handle,
39        reflect::prelude::*,
40        type_traits::prelude::*,
41        uuid::{uuid, Uuid},
42        variable::InheritableVariable,
43        visitor::prelude::*,
44    },
45    scene::{
46        base::{Base, BaseBuilder},
47        dim2::collider::Collider,
48        graph::Graph,
49        node::{Node, NodeTrait, SyncContext, UpdateContext},
50        rigidbody::RigidBodyType,
51        Scene,
52    },
53};
54use fyrox_graph::constructor::ConstructorProvider;
55use fyrox_graph::{BaseSceneGraph, SceneGraph};
56use rapier2d::prelude::RigidBodyHandle;
57use std::{
58    cell::Cell,
59    collections::VecDeque,
60    fmt::{Debug, Formatter},
61    ops::{Deref, DerefMut},
62};
63
64#[derive(Debug)]
65pub(crate) enum ApplyAction {
66    Force(Vector2<f32>),
67    Torque(f32),
68    ForceAtPoint {
69        force: Vector2<f32>,
70        point: Vector2<f32>,
71    },
72    Impulse(Vector2<f32>),
73    TorqueImpulse(f32),
74    ImpulseAtPoint {
75        impulse: Vector2<f32>,
76        point: Vector2<f32>,
77    },
78    WakeUp,
79}
80
81/// Rigid body is a physics entity that responsible for the dynamics and kinematics of the solid.
82/// Use this node when you need to simulate real-world physics in your game.
83///
84/// # Sleeping
85///
86/// Rigid body that does not move for some time will go asleep. This means that the body will not
87/// move unless it is woken up by some other moving body. This feature allows to save CPU resources.
88#[derive(Visit, Reflect, ComponentProvider)]
89pub struct RigidBody {
90    base: Base,
91
92    #[reflect(setter = "set_lin_vel")]
93    pub(crate) lin_vel: InheritableVariable<Vector2<f32>>,
94
95    #[reflect(setter = "set_ang_vel")]
96    pub(crate) ang_vel: InheritableVariable<f32>,
97
98    #[reflect(setter = "set_lin_damping")]
99    pub(crate) lin_damping: InheritableVariable<f32>,
100
101    #[reflect(setter = "set_ang_damping")]
102    pub(crate) ang_damping: InheritableVariable<f32>,
103
104    #[reflect(setter = "set_body_type")]
105    pub(crate) body_type: InheritableVariable<RigidBodyType>,
106
107    #[reflect(min_value = 0.0, step = 0.05)]
108    #[reflect(setter = "set_mass")]
109    pub(crate) mass: InheritableVariable<f32>,
110
111    #[reflect(setter = "lock_rotations")]
112    pub(crate) rotation_locked: InheritableVariable<bool>,
113
114    #[reflect(setter = "lock_translation")]
115    pub(crate) translation_locked: InheritableVariable<bool>,
116
117    #[reflect(setter = "enable_ccd")]
118    pub(crate) ccd_enabled: InheritableVariable<bool>,
119
120    #[reflect(setter = "set_can_sleep")]
121    pub(crate) can_sleep: InheritableVariable<bool>,
122
123    #[reflect(setter = "set_dominance")]
124    pub(crate) dominance: InheritableVariable<i8>,
125
126    #[reflect(setter = "set_gravity_scale")]
127    pub(crate) gravity_scale: InheritableVariable<f32>,
128
129    #[visit(skip)]
130    #[reflect(hidden)]
131    pub(crate) sleeping: bool,
132
133    #[visit(skip)]
134    #[reflect(hidden)]
135    pub(crate) reset_forces: Cell<bool>,
136
137    #[visit(skip)]
138    #[reflect(hidden)]
139    pub(crate) native: Cell<RigidBodyHandle>,
140
141    #[visit(skip)]
142    #[reflect(hidden)]
143    pub(crate) actions: Mutex<VecDeque<ApplyAction>>,
144}
145
146impl Debug for RigidBody {
147    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
148        write!(f, "RigidBody")
149    }
150}
151
152impl Default for RigidBody {
153    fn default() -> Self {
154        Self {
155            base: Default::default(),
156            lin_vel: Default::default(),
157            ang_vel: Default::default(),
158            lin_damping: Default::default(),
159            ang_damping: Default::default(),
160            sleeping: Default::default(),
161            body_type: InheritableVariable::new_modified(RigidBodyType::Dynamic),
162            mass: InheritableVariable::new_modified(1.0),
163            rotation_locked: Default::default(),
164            translation_locked: Default::default(),
165            ccd_enabled: Default::default(),
166            can_sleep: InheritableVariable::new_modified(true),
167            dominance: Default::default(),
168            gravity_scale: InheritableVariable::new_modified(1.0),
169            native: Cell::new(RigidBodyHandle::invalid()),
170            actions: Default::default(),
171            reset_forces: Default::default(),
172        }
173    }
174}
175
176impl Deref for RigidBody {
177    type Target = Base;
178
179    fn deref(&self) -> &Self::Target {
180        &self.base
181    }
182}
183
184impl DerefMut for RigidBody {
185    fn deref_mut(&mut self) -> &mut Self::Target {
186        &mut self.base
187    }
188}
189
190impl Clone for RigidBody {
191    fn clone(&self) -> Self {
192        Self {
193            base: self.base.clone(),
194            lin_vel: self.lin_vel.clone(),
195            ang_vel: self.ang_vel.clone(),
196            lin_damping: self.lin_damping.clone(),
197            ang_damping: self.ang_damping.clone(),
198            sleeping: self.sleeping,
199            body_type: self.body_type.clone(),
200            mass: self.mass.clone(),
201            rotation_locked: self.rotation_locked.clone(),
202            translation_locked: self.translation_locked.clone(),
203            ccd_enabled: self.ccd_enabled.clone(),
204            can_sleep: self.can_sleep.clone(),
205            dominance: self.dominance.clone(),
206            gravity_scale: self.gravity_scale.clone(),
207            // Do not copy. The copy will have its own native representation.
208            native: Cell::new(RigidBodyHandle::invalid()),
209            actions: Default::default(),
210            reset_forces: self.reset_forces.clone(),
211        }
212    }
213}
214
215impl TypeUuidProvider for RigidBody {
216    fn type_uuid() -> Uuid {
217        uuid!("0b242335-75a4-4c65-9685-3e82a8979047")
218    }
219}
220
221impl RigidBody {
222    /// Sets new linear velocity of the rigid body. Changing this parameter will wake up the rigid
223    /// body!
224    pub fn set_lin_vel(&mut self, lin_vel: Vector2<f32>) -> Vector2<f32> {
225        self.lin_vel.set_value_and_mark_modified(lin_vel)
226    }
227
228    /// Returns current linear velocity of the rigid body.
229    pub fn lin_vel(&self) -> Vector2<f32> {
230        *self.lin_vel
231    }
232
233    /// Sets new angular velocity of the rigid body. Changing this parameter will wake up the rigid
234    /// body!
235    pub fn set_ang_vel(&mut self, ang_vel: f32) -> f32 {
236        self.ang_vel.set_value_and_mark_modified(ang_vel)
237    }
238
239    /// Returns current angular velocity of the rigid body.
240    pub fn ang_vel(&self) -> f32 {
241        *self.ang_vel
242    }
243
244    /// Sets _additional_ mass of the rigid body. It is called additional because real mass is defined
245    /// by colliders attached to the body and their density and volume.
246    pub fn set_mass(&mut self, mass: f32) -> f32 {
247        self.mass.set_value_and_mark_modified(mass)
248    }
249
250    /// Returns _additional_ mass of the rigid body.
251    pub fn mass(&self) -> f32 {
252        *self.mass
253    }
254
255    /// Sets angular damping of the rigid body. Angular damping will decrease angular velocity over
256    /// time. Default is zero.
257    pub fn set_ang_damping(&mut self, damping: f32) -> f32 {
258        self.ang_damping.set_value_and_mark_modified(damping)
259    }
260
261    /// Returns current angular damping.
262    pub fn ang_damping(&self) -> f32 {
263        *self.ang_damping
264    }
265
266    /// Sets linear damping of the rigid body. Linear damping will decrease linear velocity over
267    /// time. Default is zero.
268    pub fn set_lin_damping(&mut self, damping: f32) -> f32 {
269        self.lin_damping.set_value_and_mark_modified(damping)
270    }
271
272    /// Returns current linear damping.
273    pub fn lin_damping(&self) -> f32 {
274        *self.lin_damping
275    }
276
277    /// Locks rotations
278    pub fn lock_rotations(&mut self, state: bool) -> bool {
279        self.rotation_locked.set_value_and_mark_modified(state)
280    }
281
282    /// Returns true if rotation is locked, false - otherwise.
283    pub fn is_rotation_locked(&self) -> bool {
284        *self.rotation_locked
285    }
286
287    /// Locks translation in world coordinates.
288    pub fn lock_translation(&mut self, state: bool) -> bool {
289        self.translation_locked.set_value_and_mark_modified(state)
290    }
291
292    /// Returns true if translation is locked, false - otherwise.    
293    pub fn is_translation_locked(&self) -> bool {
294        *self.translation_locked
295    }
296
297    /// Sets new body type. See [`RigidBodyType`] for more info.
298    pub fn set_body_type(&mut self, body_type: RigidBodyType) -> RigidBodyType {
299        self.body_type.set_value_and_mark_modified(body_type)
300    }
301
302    /// Returns current body type.
303    pub fn body_type(&self) -> RigidBodyType {
304        *self.body_type
305    }
306
307    /// Returns true if the rigid body is sleeping (temporarily excluded from simulation to save
308    /// resources), false - otherwise.
309    pub fn is_sleeping(&self) -> bool {
310        self.sleeping
311    }
312
313    /// Returns true if continuous collision detection is enabled, false - otherwise.
314    pub fn is_ccd_enabled(&self) -> bool {
315        *self.ccd_enabled
316    }
317
318    /// Enables or disables continuous collision detection. CCD is very useful for fast moving objects
319    /// to prevent accidental penetrations on high velocities.
320    pub fn enable_ccd(&mut self, enable: bool) -> bool {
321        self.ccd_enabled.set_value_and_mark_modified(enable)
322    }
323
324    /// Sets a gravity scale coefficient. Zero can be used to disable gravity.
325    pub fn set_gravity_scale(&mut self, scale: f32) -> f32 {
326        self.gravity_scale.set_value_and_mark_modified(scale)
327    }
328
329    /// Returns current gravity scale coefficient.
330    pub fn gravity_scale(&self) -> f32 {
331        *self.gravity_scale
332    }
333
334    /// Sets dominance group of the rigid body. A rigid body with higher dominance group will not
335    /// be affected by an object with lower dominance group (it will behave like it has an infinite
336    /// mass). This is very importance feature for character physics in games, you can set highest
337    /// dominance group to the player, and it won't be affected by any external forces.
338    pub fn set_dominance(&mut self, dominance: i8) -> i8 {
339        self.dominance.set_value_and_mark_modified(dominance)
340    }
341
342    /// Returns current dominance group.
343    pub fn dominance(&self) -> i8 {
344        *self.dominance
345    }
346
347    /// Applies a force at the center-of-mass of this rigid-body. The force will be applied in the
348    /// next simulation step. This does nothing on non-dynamic bodies.
349    pub fn apply_force(&mut self, force: Vector2<f32>) {
350        self.actions.get_mut().push_back(ApplyAction::Force(force))
351    }
352
353    /// Applies a torque at the center-of-mass of this rigid-body. The torque will be applied in
354    /// the next simulation step. This does nothing on non-dynamic bodies.
355    pub fn apply_torque(&mut self, torque: f32) {
356        self.actions
357            .get_mut()
358            .push_back(ApplyAction::Torque(torque))
359    }
360
361    /// Applies a force at the given world-space point of this rigid-body. The force will be applied
362    /// in the next simulation step. This does nothing on non-dynamic bodies.
363    pub fn apply_force_at_point(&mut self, force: Vector2<f32>, point: Vector2<f32>) {
364        self.actions
365            .get_mut()
366            .push_back(ApplyAction::ForceAtPoint { force, point })
367    }
368
369    /// Applies an impulse at the center-of-mass of this rigid-body. The impulse is applied right
370    /// away, changing the linear velocity. This does nothing on non-dynamic bodies.
371    pub fn apply_impulse(&mut self, impulse: Vector2<f32>) {
372        self.actions
373            .get_mut()
374            .push_back(ApplyAction::Impulse(impulse))
375    }
376
377    /// Applies an angular impulse at the center-of-mass of this rigid-body. The impulse is applied
378    /// right away, changing the angular velocity. This does nothing on non-dynamic bodies.
379    pub fn apply_torque_impulse(&mut self, torque_impulse: f32) {
380        self.actions
381            .get_mut()
382            .push_back(ApplyAction::TorqueImpulse(torque_impulse))
383    }
384
385    /// Applies an impulse at the given world-space point of this rigid-body. The impulse is applied
386    /// right away, changing the linear and/or angular velocities. This does nothing on non-dynamic
387    /// bodies.
388    pub fn apply_impulse_at_point(&mut self, impulse: Vector2<f32>, point: Vector2<f32>) {
389        self.actions
390            .get_mut()
391            .push_back(ApplyAction::ImpulseAtPoint { impulse, point })
392    }
393
394    /// Sets whether the rigid body can sleep or not. If `false` is passed, it _automatically_ wake
395    /// up rigid body.
396    pub fn set_can_sleep(&mut self, can_sleep: bool) -> bool {
397        self.can_sleep.set_value_and_mark_modified(can_sleep)
398    }
399
400    /// Returns true if the rigid body can sleep, false - otherwise.
401    pub fn is_can_sleep(&self) -> bool {
402        *self.can_sleep
403    }
404
405    /// Wakes up rigid body, forcing it to return to participate in the simulation.
406    pub fn wake_up(&mut self) {
407        self.actions.get_mut().push_back(ApplyAction::WakeUp)
408    }
409
410    pub(crate) fn need_sync_model(&self) -> bool {
411        self.lin_vel.need_sync()
412            || self.ang_vel.need_sync()
413            || self.lin_damping.need_sync()
414            || self.ang_damping.need_sync()
415            || self.body_type.need_sync()
416            || self.mass.need_sync()
417            || self.rotation_locked.need_sync()
418            || self.translation_locked.need_sync()
419            || self.ccd_enabled.need_sync()
420            || self.can_sleep.need_sync()
421            || self.dominance.need_sync()
422            || self.gravity_scale.need_sync()
423            || self.reset_forces.get()
424    }
425}
426
427impl ConstructorProvider<Node, Graph> for RigidBody {
428    fn constructor() -> NodeConstructor {
429        NodeConstructor::new::<Self>()
430            .with_variant("Rigid Body", |_| {
431                RigidBodyBuilder::new(BaseBuilder::new().with_name("Rigid Body 2D"))
432                    .build_node()
433                    .into()
434            })
435            .with_group("Physics 2D")
436    }
437}
438
439impl NodeTrait for RigidBody {
440    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
441        self.base.local_bounding_box()
442    }
443
444    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
445        self.base.world_bounding_box()
446    }
447
448    fn id(&self) -> Uuid {
449        Self::type_uuid()
450    }
451
452    fn on_removed_from_graph(&mut self, graph: &mut Graph) {
453        graph.physics2d.remove_body(self.native.get());
454        self.native.set(RigidBodyHandle::invalid());
455
456        Log::info(format!(
457            "Native rigid body was removed for node: {}",
458            self.name()
459        ));
460    }
461
462    fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
463        context.physics2d.sync_to_rigid_body_node(self_handle, self);
464    }
465
466    fn on_global_transform_changed(
467        &self,
468        new_global_transform: &Matrix4<f32>,
469        context: &mut SyncContext,
470    ) {
471        if !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
472            context
473                .physics2d
474                .set_rigid_body_position(self, new_global_transform);
475        }
476    }
477
478    fn update(&mut self, context: &mut UpdateContext) {
479        context.physics2d.sync_rigid_body_node(
480            self,
481            // Rigid body 2D can be root node of a scene, in this case it does not have a parent.
482            context
483                .nodes
484                .try_borrow(self.parent)
485                .map(|p| p.global_transform())
486                .unwrap_or_else(Matrix4::identity),
487        );
488    }
489
490    fn validate(&self, scene: &Scene) -> Result<(), String> {
491        for &child in self.children() {
492            if scene.graph.try_get_of_type::<Collider>(child).is_some() {
493                return Ok(());
494            }
495        }
496
497        Err("The 2D rigid body must have at least one 2D collider as a \
498        direct child node to work correctly!"
499            .to_string())
500    }
501}
502
503/// Allows you to create rigid body in declarative manner.
504pub struct RigidBodyBuilder {
505    base_builder: BaseBuilder,
506    lin_vel: Vector2<f32>,
507    ang_vel: f32,
508    lin_damping: f32,
509    ang_damping: f32,
510    sleeping: bool,
511    body_type: RigidBodyType,
512    mass: f32,
513    rotation_locked: bool,
514    translation_locked: bool,
515    ccd_enabled: bool,
516    can_sleep: bool,
517    dominance: i8,
518    gravity_scale: f32,
519}
520
521impl RigidBodyBuilder {
522    /// Creates new rigid body builder.
523    pub fn new(base_builder: BaseBuilder) -> Self {
524        Self {
525            base_builder,
526            lin_vel: Default::default(),
527            ang_vel: Default::default(),
528            lin_damping: 0.0,
529            ang_damping: 0.0,
530            sleeping: false,
531            body_type: RigidBodyType::Dynamic,
532            mass: 1.0,
533            rotation_locked: false,
534            translation_locked: false,
535            ccd_enabled: false,
536            can_sleep: true,
537            dominance: 0,
538            gravity_scale: 1.0,
539        }
540    }
541
542    /// Sets the desired body type.
543    pub fn with_body_type(mut self, body_type: RigidBodyType) -> Self {
544        self.body_type = body_type;
545        self
546    }
547
548    /// Sets the desired _additional_ mass of the body.
549    pub fn with_mass(mut self, mass: f32) -> Self {
550        self.mass = mass;
551        self
552    }
553
554    /// Sets whether continuous collision detection should be enabled or not.
555    pub fn with_ccd_enabled(mut self, enabled: bool) -> Self {
556        self.ccd_enabled = enabled;
557        self
558    }
559
560    /// Sets desired linear velocity.
561    pub fn with_lin_vel(mut self, lin_vel: Vector2<f32>) -> Self {
562        self.lin_vel = lin_vel;
563        self
564    }
565
566    /// Sets desired angular velocity.
567    pub fn with_ang_vel(mut self, ang_vel: f32) -> Self {
568        self.ang_vel = ang_vel;
569        self
570    }
571
572    /// Sets desired angular damping.
573    pub fn with_ang_damping(mut self, ang_damping: f32) -> Self {
574        self.ang_damping = ang_damping;
575        self
576    }
577
578    /// Sets desired linear damping.
579    pub fn with_lin_damping(mut self, lin_damping: f32) -> Self {
580        self.lin_damping = lin_damping;
581        self
582    }
583
584    /// Sets whether the rotation around X axis of the body should be locked or not.
585    pub fn with_rotation_locked(mut self, rotation_locked: bool) -> Self {
586        self.rotation_locked = rotation_locked;
587        self
588    }
589
590    /// Sets whether the translation of the body should be locked or not.
591    pub fn with_translation_locked(mut self, translation_locked: bool) -> Self {
592        self.translation_locked = translation_locked;
593        self
594    }
595
596    /// Sets desired dominance group.
597    pub fn with_dominance(mut self, dominance: i8) -> Self {
598        self.dominance = dominance;
599        self
600    }
601
602    /// Sets desired gravity scale.
603    pub fn with_gravity_scale(mut self, gravity_scale: f32) -> Self {
604        self.gravity_scale = gravity_scale;
605        self
606    }
607
608    /// Sets initial state of the body (sleeping or not).
609    pub fn with_sleeping(mut self, sleeping: bool) -> Self {
610        self.sleeping = sleeping;
611        self
612    }
613
614    /// Sets whether rigid body can sleep or not.
615    pub fn with_can_sleep(mut self, can_sleep: bool) -> Self {
616        self.can_sleep = can_sleep;
617        self
618    }
619
620    /// Creates RigidBody node but does not add it to the graph.
621    pub fn build_rigid_body(self) -> RigidBody {
622        RigidBody {
623            base: self.base_builder.build_base(),
624            lin_vel: self.lin_vel.into(),
625            ang_vel: self.ang_vel.into(),
626            lin_damping: self.lin_damping.into(),
627            ang_damping: self.ang_damping.into(),
628            sleeping: self.sleeping,
629            body_type: self.body_type.into(),
630            mass: self.mass.into(),
631            rotation_locked: self.rotation_locked.into(),
632            translation_locked: self.translation_locked.into(),
633            ccd_enabled: self.ccd_enabled.into(),
634            can_sleep: self.can_sleep.into(),
635            dominance: self.dominance.into(),
636            gravity_scale: self.gravity_scale.into(),
637            native: Cell::new(RigidBodyHandle::invalid()),
638            actions: Default::default(),
639            reset_forces: Default::default(),
640        }
641    }
642
643    /// Creates RigidBody node but does not add it to the graph.
644    pub fn build_node(self) -> Node {
645        Node::new(self.build_rigid_body())
646    }
647
648    /// Creates RigidBody node and adds it to the graph.
649    pub fn build(self, graph: &mut Graph) -> Handle<Node> {
650        graph.add_node(self.build_node())
651    }
652}