Skip to main content

fyrox_impl/scene/
ragdoll.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//! Ragdoll is a set of rigid bodies linked with various joints, which can control a set of bones
22//! of a mesh. Ragdolls are used mostly for body physics. See [`Ragdoll`] docs for more info and
23//! usage examples.
24
25use crate::{
26    core::{
27        algebra::{Matrix4, UnitQuaternion, Vector3},
28        log::Log,
29        math::{aabb::AxisAlignedBoundingBox, Matrix4Ext},
30        pool::{Handle, PoolError},
31        reflect::prelude::*,
32        type_traits::prelude::*,
33        uuid::{uuid, Uuid},
34        uuid_provider,
35        variable::InheritableVariable,
36        visitor::prelude::*,
37    },
38    graph::SceneGraph,
39    scene::{
40        base::{Base, BaseBuilder},
41        collider::Collider,
42        graph::Graph,
43        node::{constructor::NodeConstructor, Node, NodeTrait, UpdateContext},
44        rigidbody::{RigidBody, RigidBodyType},
45    },
46};
47use fyrox_graph::constructor::ConstructorProvider;
48use std::ops::{Deref, DerefMut};
49
50/// A part of ragdoll, that has a physical rigid body, a bone and zero or more children limbs.
51/// Multiple limbs together forms a ragdoll.
52#[derive(Clone, Debug, PartialEq, Default, Visit, Reflect)]
53pub struct Limb {
54    /// A handle of a scene node, that is used as a bone in some other scene node (mesh).
55    pub bone: Handle<Node>,
56    /// A handle to a rigid body scene node.
57    pub physical_bone: Handle<RigidBody>,
58    /// A set of children limbs.
59    pub children: Vec<Limb>,
60}
61
62uuid_provider!(Limb = "6d5bc2f7-8acc-4b64-8e4b-65d4551150bf");
63
64impl Limb {
65    /// Iterates recursively across the entire tree of descendant limbs and does the specified action
66    /// with every limb along the way.
67    pub fn iterate_recursive<F>(&self, func: &mut F) -> Result<(), PoolError>
68    where
69        F: FnMut(&Self) -> Result<(), PoolError>,
70    {
71        func(self)?;
72
73        for child in self.children.iter() {
74            child.iterate_recursive(func)?
75        }
76
77        Ok(())
78    }
79}
80
81/// Ragdoll is a set of rigid bodies linked with various joints, which can control a set of bones
82/// of a mesh. Ragdolls are used mostly for body physics.
83///
84/// ## How to create
85///
86/// Usually, bodies have quite complex hierarchy of bones and total count of the bones could be 30+.
87/// Manual creation of such ragdoll is very tedious and counterproductive. That's why the best way
88/// to create a ragdoll is to use the editor, and the ragdoll wizard in particular. However, if
89/// you're brave enough you can read this code <https://github.com/FyroxEngine/Fyrox/blob/master/editor/src/utils/ragdoll.rs> -
90/// it creates a ragdoll using a humanoid skeleton.  
91#[derive(Clone, Reflect, Visit, Debug, Default, ComponentProvider)]
92#[reflect(derived_type = "Node")]
93#[visit(optional)]
94pub struct Ragdoll {
95    base: Base,
96    /// A handle to a main rigid body of the character to which this ragdoll belongs to. If set, the
97    /// ragdoll will take control over the collider and will move it together with the root limb.
98    pub character_rigid_body: InheritableVariable<Handle<RigidBody>>,
99    /// A flag, that defines whether the ragdoll is active or not. Active ragdoll enables limb rigid
100    /// bodies and takes control over `character_rigid_body` (if set).
101    pub is_active: InheritableVariable<bool>,
102    /// Root limb of the ragdoll. Usually it is hips of the body and rest of the limbs are forming
103    /// the rest of the hierarchy.
104    pub root_limb: InheritableVariable<Limb>,
105    /// A flag, that defines whether the ragdoll will deactivate colliders when it is not active or not.
106    /// This option could be useful if you want to disable physics of limbs while the ragdoll is active.
107    pub deactivate_colliders: InheritableVariable<bool>,
108    #[reflect(hidden)]
109    prev_enabled: bool,
110}
111
112impl Deref for Ragdoll {
113    type Target = Base;
114
115    fn deref(&self) -> &Self::Target {
116        &self.base
117    }
118}
119
120impl DerefMut for Ragdoll {
121    fn deref_mut(&mut self) -> &mut Self::Target {
122        &mut self.base
123    }
124}
125
126impl TypeUuidProvider for Ragdoll {
127    fn type_uuid() -> Uuid {
128        uuid!("f4441683-dcef-472d-9d7d-4adca4579107")
129    }
130}
131
132impl ConstructorProvider<Node, Graph> for Ragdoll {
133    fn constructor() -> NodeConstructor {
134        NodeConstructor::new::<Self>()
135            .with_variant("Ragdoll", |_| {
136                RagdollBuilder::new(BaseBuilder::new().with_name("Ragdoll"))
137                    .build_node()
138                    .into()
139            })
140            .with_group("Physics")
141    }
142}
143
144impl NodeTrait for Ragdoll {
145    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
146        self.base.local_bounding_box()
147    }
148
149    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
150        self.base.world_bounding_box()
151    }
152
153    fn id(&self) -> Uuid {
154        Self::type_uuid()
155    }
156
157    fn update(&mut self, ctx: &mut UpdateContext) {
158        // Get linear and angular velocities of the character rigid body and transfer it onto rag doll bodies when it is just activated.
159        let mut new_lin_vel = None;
160        let mut new_ang_vel = None;
161        if *self.is_active && !self.prev_enabled {
162            if let Ok(character_rigid_body) = ctx.nodes.try_get(*self.character_rigid_body) {
163                new_lin_vel = Some(character_rigid_body.lin_vel());
164                new_ang_vel = Some(character_rigid_body.ang_vel());
165            }
166        }
167        self.prev_enabled = *self.is_active;
168
169        Log::verify(self.root_limb.iterate_recursive(&mut |limb| {
170            let mbc = ctx.nodes.begin_multi_borrow();
171
172            let mut need_update_transform = false;
173
174            let mut limb_body = mbc.try_get_mut(limb.physical_bone)?;
175            if *self.is_active {
176                // Transfer linear and angular velocities to rag doll bodies.
177                if let Some(lin_vel) = new_lin_vel {
178                    limb_body.set_lin_vel(lin_vel);
179                }
180                if let Some(ang_vel) = new_ang_vel {
181                    limb_body.set_ang_vel(ang_vel);
182                }
183
184                if limb_body.body_type() != RigidBodyType::Dynamic {
185                    limb_body.set_body_type(RigidBodyType::Dynamic);
186                }
187
188                if *self.deactivate_colliders {
189                    for child in limb_body.children() {
190                        if let Ok(mut collider) =
191                            mbc.try_get_component_of_type_mut::<Collider>(*child)
192                        {
193                            collider.set_is_sensor(false);
194                        }
195                    }
196                }
197
198                let body_transform = limb_body.global_transform();
199
200                // Sync transform of the bone with respective body.
201                let bone_parent = mbc.try_get(limb.bone)?.parent();
202                let transform: Matrix4<f32> = mbc
203                    .try_get(bone_parent)?
204                    .global_transform()
205                    .try_inverse()
206                    .unwrap_or_else(Matrix4::identity)
207                    * body_transform;
208
209                mbc.try_get_mut(limb.bone)?
210                    .local_transform_mut()
211                    .set_position(Vector3::new(transform[12], transform[13], transform[14]))
212                    .set_pre_rotation(UnitQuaternion::identity())
213                    .set_post_rotation(UnitQuaternion::identity())
214                    .set_rotation(UnitQuaternion::from_matrix_eps(
215                        &transform.basis(),
216                        f32::EPSILON,
217                        16,
218                        Default::default(),
219                    ));
220
221                need_update_transform = true;
222            } else {
223                limb_body.set_body_type(RigidBodyType::KinematicPositionBased);
224                limb_body.set_lin_vel(Default::default());
225                limb_body.set_ang_vel(Default::default());
226
227                if *self.deactivate_colliders {
228                    for child in limb_body.children() {
229                        if let Ok(mut collider) =
230                            mbc.try_get_component_of_type_mut::<Collider>(*child)
231                        {
232                            collider.set_is_sensor(true);
233                        }
234                    }
235                }
236
237                let self_transform_inverse =
238                    self.global_transform().try_inverse().unwrap_or_default();
239
240                // Sync transform of the physical body with respective bone.
241                let bone = mbc.try_get(limb.bone)?;
242                let relative_transform = self_transform_inverse * bone.global_transform();
243
244                let position = Vector3::new(
245                    relative_transform[12],
246                    relative_transform[13],
247                    relative_transform[14],
248                );
249                let rotation = UnitQuaternion::from_matrix_eps(
250                    &relative_transform.basis(),
251                    f32::EPSILON,
252                    16,
253                    Default::default(),
254                );
255                limb_body
256                    .local_transform_mut()
257                    .set_position(position)
258                    .set_rotation(rotation);
259            }
260
261            drop(limb_body);
262            drop(mbc);
263
264            if need_update_transform {
265                // Calculate transform of the descendants explicitly, so the next bones in hierarchy will have new transform
266                // that can be used to calculate relative transform.
267                Graph::update_hierarchical_data_recursively(
268                    ctx.nodes,
269                    ctx.sound_context,
270                    ctx.physics,
271                    ctx.physics2d,
272                    limb.bone,
273                );
274            }
275
276            Ok(())
277        }));
278
279        if let Ok(root_limb_body) = ctx.nodes.try_borrow(self.root_limb.bone) {
280            let position = root_limb_body.global_position();
281            if let Ok(character_rigid_body) = ctx.nodes.try_get_mut(*self.character_rigid_body) {
282                if *self.is_active {
283                    character_rigid_body.set_lin_vel(Default::default());
284                    character_rigid_body.set_ang_vel(Default::default());
285                    character_rigid_body
286                        .local_transform_mut()
287                        .set_position(position);
288                    character_rigid_body.set_body_type(RigidBodyType::KinematicPositionBased);
289                } else {
290                    character_rigid_body.set_body_type(RigidBodyType::Dynamic);
291                }
292            }
293        }
294    }
295}
296
297/// Ragdoll builder creates [`Ragdoll`] scene nodes.
298pub struct RagdollBuilder {
299    base_builder: BaseBuilder,
300    character_rigid_body: Handle<RigidBody>,
301    is_active: bool,
302    deactivate_colliders: bool,
303    root_limb: Limb,
304}
305
306impl RagdollBuilder {
307    /// Creates a new ragdoll builder.
308    pub fn new(base_builder: BaseBuilder) -> Self {
309        Self {
310            base_builder,
311            character_rigid_body: Default::default(),
312            is_active: true,
313            deactivate_colliders: false,
314            root_limb: Default::default(),
315        }
316    }
317
318    /// Sets the desired character rigid body.
319    pub fn with_character_rigid_body(mut self, handle: Handle<RigidBody>) -> Self {
320        self.character_rigid_body = handle;
321        self
322    }
323
324    /// Sets whether the ragdoll is active or not.
325    pub fn with_active(mut self, active: bool) -> Self {
326        self.is_active = active;
327        self
328    }
329
330    /// Sets the desired root limb.
331    pub fn with_root_limb(mut self, root_limb: Limb) -> Self {
332        self.root_limb = root_limb;
333        self
334    }
335
336    /// Sets whether the ragdoll should deactivate colliders of its limbs when it is not active or not.
337    pub fn with_deactivate_colliders(mut self, value: bool) -> Self {
338        self.deactivate_colliders = value;
339        self
340    }
341
342    /// Builds the ragdoll.
343    pub fn build_ragdoll(self) -> Ragdoll {
344        Ragdoll {
345            base: self.base_builder.build_base(),
346            character_rigid_body: self.character_rigid_body.into(),
347            is_active: self.is_active.into(),
348            root_limb: self.root_limb.into(),
349            deactivate_colliders: self.deactivate_colliders.into(),
350            prev_enabled: self.is_active,
351        }
352    }
353
354    /// Creates ragdoll node, but does not add it to a graph.
355    pub fn build_node(self) -> Node {
356        Node::new(self.build_ragdoll())
357    }
358
359    /// Creates the ragdoll node and adds it to the given graph.
360    pub fn build(self, graph: &mut Graph) -> Handle<Ragdoll> {
361        graph.add_node(self.build_node()).to_variant()
362    }
363}