fyrox_impl/scene/
joint.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//! Joint is used to restrict motion of two rigid bodies.
22
23use crate::scene::node::constructor::NodeConstructor;
24use crate::{
25    core::{
26        algebra::Matrix4,
27        log::Log,
28        math::{aabb::AxisAlignedBoundingBox, m4x4_approx_eq},
29        pool::Handle,
30        reflect::prelude::*,
31        type_traits::prelude::*,
32        uuid::{uuid, Uuid},
33        variable::InheritableVariable,
34        visitor::prelude::*,
35    },
36    scene::{
37        base::{Base, BaseBuilder},
38        graph::Graph,
39        node::{Node, NodeTrait, SyncContext},
40        rigidbody::RigidBody,
41        Scene,
42    },
43};
44use fyrox_core::algebra::{Isometry3, Vector3};
45use fyrox_core::uuid_provider;
46use fyrox_graph::constructor::ConstructorProvider;
47use fyrox_graph::{BaseSceneGraph, SceneGraphNode};
48use rapier2d::na::UnitQuaternion;
49use rapier3d::dynamics::ImpulseJointHandle;
50use std::cell::RefCell;
51use std::{
52    cell::Cell,
53    ops::{Deref, DerefMut, Range},
54};
55use strum_macros::{AsRefStr, EnumString, VariantNames};
56
57/// Ball joint locks any translational moves between two objects on the axis between objects, but
58/// allows rigid bodies to perform relative rotations. The real world example is a human shoulder,
59/// pendulum, etc.
60#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
61pub struct BallJoint {
62    /// Whether X angular limits are enabled or not. Default is `false`
63    #[reflect(description = "Whether X angular limits are enabled or not.")]
64    #[visit(optional)] // Backward compatibility
65    pub x_limits_enabled: bool,
66
67    /// Allowed angle range around local X axis of the joint (in radians).
68    #[reflect(description = "Allowed angle range around local X axis of the joint (in radians).")]
69    #[visit(optional)] // Backward compatibility
70    pub x_limits_angles: Range<f32>,
71
72    /// Whether Y angular limits are enabled or not. Default is `false`
73    #[reflect(description = "Whether Y angular limits are enabled or not.")]
74    #[visit(optional)] // Backward compatibility
75    pub y_limits_enabled: bool,
76
77    /// Allowed angle range around local Y axis of the joint (in radians).
78    #[reflect(description = "Allowed angle range around local Y axis of the joint (in radians).")]
79    #[visit(optional)] // Backward compatibility
80    pub y_limits_angles: Range<f32>,
81
82    /// Whether Z angular limits are enabled or not. Default is `false`
83    #[reflect(description = "Whether Z angular limits are enabled or not.")]
84    #[visit(optional)] // Backward compatibility
85    pub z_limits_enabled: bool,
86
87    /// Allowed angle range around local Z axis of the joint (in radians).
88    #[reflect(description = "Allowed angle range around local Z axis of the joint (in radians).")]
89    #[visit(optional)] // Backward compatibility
90    pub z_limits_angles: Range<f32>,
91}
92
93impl Default for BallJoint {
94    fn default() -> Self {
95        Self {
96            x_limits_enabled: false,
97            x_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
98            y_limits_enabled: false,
99            y_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
100            z_limits_enabled: false,
101            z_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
102        }
103    }
104}
105
106/// A fixed joint ensures that two rigid bodies does not move relative to each other. There is no
107/// straightforward real-world example, but it can be thought as two bodies were "welded" together.
108#[derive(Clone, Debug, Visit, PartialEq, Reflect, Default, Eq)]
109pub struct FixedJoint;
110
111/// Prismatic joint prevents any relative movement between two rigid-bodies, except for relative
112/// translations along one axis. The real world example is a sliders that used to support drawers.
113#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
114pub struct PrismaticJoint {
115    /// Whether linear limits along local joint X axis are enabled or not. Default is `false`
116    #[reflect(description = "Whether linear limits along local joint X axis are enabled or not.")]
117    #[visit(optional)] // Backward compatibility
118    pub limits_enabled: bool,
119
120    /// The min an max relative position of the attached bodies along local X axis of the joint.
121    #[reflect(
122        description = "The min an max relative position of the attached bodies along local X axis of the joint."
123    )]
124    #[visit(optional)] // Backward compatibility
125    pub limits: Range<f32>,
126}
127
128impl Default for PrismaticJoint {
129    fn default() -> Self {
130        Self {
131            limits_enabled: false,
132            limits: -std::f32::consts::PI..std::f32::consts::PI,
133        }
134    }
135}
136
137/// Revolute joint prevents any relative movement between two rigid bodies, except relative rotation
138/// along one axis. The real world example is wheels, fans, etc. It can also be used to simulate door
139/// hinge.
140#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
141pub struct RevoluteJoint {
142    /// Whether angular limits around local X axis of the joint are enabled or not. Default is `false`
143    #[reflect(
144        description = "Whether angular limits around local X axis of the joint are enabled or not."
145    )]
146    #[visit(optional)] // Backward compatibility
147    pub limits_enabled: bool,
148
149    /// Allowed angle range around local X axis of the joint (in radians).
150    #[reflect(description = "Allowed angle range around local X axis of the joint (in radians).")]
151    #[visit(optional)] // Backward compatibility
152    pub limits: Range<f32>,
153}
154
155impl Default for RevoluteJoint {
156    fn default() -> Self {
157        Self {
158            limits_enabled: false,
159            limits: -std::f32::consts::PI..std::f32::consts::PI,
160        }
161    }
162}
163
164/// The exact kind of the joint.
165#[derive(Clone, Debug, PartialEq, Visit, Reflect, AsRefStr, EnumString, VariantNames)]
166pub enum JointParams {
167    /// See [`BallJoint`] for more info.
168    BallJoint(BallJoint),
169    /// See [`FixedJoint`] for more info.
170    FixedJoint(FixedJoint),
171    /// See [`PrismaticJoint`] for more info.
172    PrismaticJoint(PrismaticJoint),
173    /// See [`RevoluteJoint`] for more info.
174    RevoluteJoint(RevoluteJoint),
175}
176
177uuid_provider!(JointParams = "a3e09303-9de4-4123-9492-05e27f29aaa3");
178
179impl Default for JointParams {
180    fn default() -> Self {
181        Self::BallJoint(Default::default())
182    }
183}
184
185#[derive(Visit, Reflect, Debug, Clone, Default)]
186pub(crate) struct LocalFrame {
187    pub position: Vector3<f32>,
188    pub rotation: UnitQuaternion<f32>,
189}
190
191impl LocalFrame {
192    pub fn new(isometry: &Isometry3<f32>) -> Self {
193        Self {
194            position: isometry.translation.vector,
195            rotation: isometry.rotation,
196        }
197    }
198}
199
200#[derive(Visit, Reflect, Debug, Clone, Default)]
201pub(crate) struct JointLocalFrames {
202    pub body1: LocalFrame,
203    pub body2: LocalFrame,
204}
205
206impl JointLocalFrames {
207    pub fn new(isometry1: &Isometry3<f32>, isometry2: &Isometry3<f32>) -> Self {
208        Self {
209            body1: LocalFrame::new(isometry1),
210            body2: LocalFrame::new(isometry2),
211        }
212    }
213}
214
215/// Joint is used to restrict motion of two rigid bodies. There are numerous examples of joints in
216/// real life: door hinge, ball joints in human arms, etc.
217#[derive(Visit, Reflect, Debug, ComponentProvider)]
218pub struct Joint {
219    base: Base,
220
221    #[reflect(setter = "set_params")]
222    pub(crate) params: InheritableVariable<JointParams>,
223
224    #[reflect(setter = "set_body1")]
225    pub(crate) body1: InheritableVariable<Handle<Node>>,
226
227    #[reflect(setter = "set_body2")]
228    pub(crate) body2: InheritableVariable<Handle<Node>>,
229
230    #[reflect(setter = "set_contacts_enabled")]
231    #[visit(optional)] // Backward compatibility
232    pub(crate) contacts_enabled: InheritableVariable<bool>,
233
234    #[reflect(setter = "set_auto_rebinding")]
235    #[visit(optional)] // Backward compatibility
236    pub(crate) auto_rebind: InheritableVariable<bool>,
237
238    #[visit(optional)]
239    #[reflect(hidden)]
240    pub(crate) local_frames: RefCell<Option<JointLocalFrames>>,
241
242    #[visit(skip)]
243    #[reflect(hidden)]
244    pub(crate) native: Cell<ImpulseJointHandle>,
245}
246
247impl Default for Joint {
248    fn default() -> Self {
249        Self {
250            base: Default::default(),
251            params: Default::default(),
252            body1: Default::default(),
253            body2: Default::default(),
254            contacts_enabled: InheritableVariable::new_modified(true),
255            auto_rebind: true.into(),
256            local_frames: Default::default(),
257            native: Cell::new(ImpulseJointHandle::invalid()),
258        }
259    }
260}
261
262impl Deref for Joint {
263    type Target = Base;
264
265    fn deref(&self) -> &Self::Target {
266        &self.base
267    }
268}
269
270impl DerefMut for Joint {
271    fn deref_mut(&mut self) -> &mut Self::Target {
272        &mut self.base
273    }
274}
275
276impl Clone for Joint {
277    fn clone(&self) -> Self {
278        Self {
279            base: self.base.clone(),
280            params: self.params.clone(),
281            body1: self.body1.clone(),
282            body2: self.body2.clone(),
283            contacts_enabled: self.contacts_enabled.clone(),
284            local_frames: self.local_frames.clone(),
285            // Do not copy. The copy will have its own native representation.
286            auto_rebind: self.auto_rebind.clone(),
287            native: Cell::new(ImpulseJointHandle::invalid()),
288        }
289    }
290}
291
292impl TypeUuidProvider for Joint {
293    fn type_uuid() -> Uuid {
294        uuid!("439d48f5-e3a3-4255-aa08-353c1ca42e3b")
295    }
296}
297
298impl Joint {
299    /// Returns a shared reference to the current joint parameters.
300    pub fn params(&self) -> &JointParams {
301        &self.params
302    }
303
304    /// Returns a mutable reference to the current joint parameters. Obtaining the mutable reference
305    /// will force the engine to do additional calculations to reflect changes to the physics engine.
306    pub fn params_mut(&mut self) -> &mut JointParams {
307        self.params.get_value_mut_and_mark_modified()
308    }
309
310    /// Sets new joint parameters.
311    pub fn set_params(&mut self, params: JointParams) -> JointParams {
312        self.params.set_value_and_mark_modified(params)
313    }
314
315    /// Sets the first body of the joint. The handle should point to the RigidBody node, otherwise
316    /// the joint will have no effect!
317    pub fn set_body1(&mut self, handle: Handle<Node>) -> Handle<Node> {
318        self.body1.set_value_and_mark_modified(handle)
319    }
320
321    /// Returns current first body of the joint.
322    pub fn body1(&self) -> Handle<Node> {
323        *self.body1
324    }
325
326    /// Sets the second body of the joint. The handle should point to the RigidBody node, otherwise
327    /// the joint will have no effect!
328    pub fn set_body2(&mut self, handle: Handle<Node>) -> Handle<Node> {
329        self.body2.set_value_and_mark_modified(handle)
330    }
331
332    /// Returns current second body of the joint.
333    pub fn body2(&self) -> Handle<Node> {
334        *self.body2
335    }
336
337    /// Sets whether the connected bodies should ignore collisions with each other or not.  
338    pub fn set_contacts_enabled(&mut self, enabled: bool) -> bool {
339        self.contacts_enabled.set_value_and_mark_modified(enabled)
340    }
341
342    /// Returns true if contacts between connected bodies is enabled, false - otherwise.
343    pub fn is_contacts_enabled(&self) -> bool {
344        *self.contacts_enabled
345    }
346
347    /// Sets whether the joint should automatically rebind two rigid bodies if the joint has changed its
348    /// global position.
349    pub fn set_auto_rebinding(&mut self, enabled: bool) -> bool {
350        self.auto_rebind.set_value_and_mark_modified(enabled)
351    }
352
353    /// Returns true if automatic rebinding of the joint is enabled or not.
354    pub fn is_auto_rebinding_enabled(&self) -> bool {
355        *self.auto_rebind
356    }
357}
358
359impl ConstructorProvider<Node, Graph> for Joint {
360    fn constructor() -> NodeConstructor {
361        NodeConstructor::new::<Self>()
362            .with_variant("Revolute Joint", |_| {
363                JointBuilder::new(BaseBuilder::new().with_name("Revolute Joint"))
364                    .with_params(JointParams::RevoluteJoint(Default::default()))
365                    .build_node()
366                    .into()
367            })
368            .with_variant("Ball Joint", |_| {
369                JointBuilder::new(BaseBuilder::new().with_name("Ball Joint"))
370                    .with_params(JointParams::BallJoint(Default::default()))
371                    .build_node()
372                    .into()
373            })
374            .with_variant("Prismatic Joint", |_| {
375                JointBuilder::new(BaseBuilder::new().with_name("Prismatic Joint"))
376                    .with_params(JointParams::PrismaticJoint(Default::default()))
377                    .build_node()
378                    .into()
379            })
380            .with_variant("Fixed Joint", |_| {
381                JointBuilder::new(BaseBuilder::new().with_name("Fixed Joint"))
382                    .with_params(JointParams::FixedJoint(Default::default()))
383                    .build_node()
384                    .into()
385            })
386            .with_group("Physics")
387    }
388}
389
390impl NodeTrait for Joint {
391    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
392        self.base.local_bounding_box()
393    }
394
395    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
396        self.base.world_bounding_box()
397    }
398
399    fn id(&self) -> Uuid {
400        Self::type_uuid()
401    }
402
403    fn on_removed_from_graph(&mut self, graph: &mut Graph) {
404        graph.physics.remove_joint(self.native.get());
405        self.native.set(ImpulseJointHandle::invalid());
406
407        Log::info(format!(
408            "Native joint was removed for node: {}",
409            self.name()
410        ));
411    }
412
413    fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
414        context
415            .physics
416            .sync_to_joint_node(context.nodes, self_handle, self);
417    }
418
419    fn on_global_transform_changed(
420        &self,
421        new_global_transform: &Matrix4<f32>,
422        _context: &mut SyncContext,
423    ) {
424        if *self.auto_rebind && !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
425            self.local_frames.borrow_mut().take();
426        }
427    }
428
429    fn validate(&self, scene: &Scene) -> Result<(), String> {
430        if let Some(body1) = scene.graph.try_get(self.body1()) {
431            if body1.component_ref::<RigidBody>().is_none() {
432                return Err("First body of 3D Joint must be an \
433                    instance of 3D Rigid Body!"
434                    .to_string());
435            }
436        } else {
437            return Err("3D Joint has invalid or unassigned handle to a \
438            first body, the joint will not operate!"
439                .to_string());
440        }
441
442        if let Some(body2) = scene.graph.try_get(self.body2()) {
443            if body2.component_ref::<RigidBody>().is_none() {
444                return Err("Second body of 3D Joint must be an instance \
445                    of 3D Rigid Body!"
446                    .to_string());
447            }
448        } else {
449            return Err("3D Joint has invalid or unassigned handle to a \
450            second body, the joint will not operate!"
451                .to_string());
452        }
453
454        Ok(())
455    }
456}
457
458/// Joint builder allows you to build Joint node in a declarative manner.
459pub struct JointBuilder {
460    base_builder: BaseBuilder,
461    params: JointParams,
462    body1: Handle<Node>,
463    body2: Handle<Node>,
464    contacts_enabled: bool,
465    auto_rebind: bool,
466}
467
468impl JointBuilder {
469    /// Creates a new joint builder instance.
470    pub fn new(base_builder: BaseBuilder) -> Self {
471        Self {
472            base_builder,
473            params: Default::default(),
474            body1: Default::default(),
475            body2: Default::default(),
476            contacts_enabled: true,
477            auto_rebind: true,
478        }
479    }
480
481    /// Sets desired joint parameters which defines exact type of the joint.
482    pub fn with_params(mut self, params: JointParams) -> Self {
483        self.params = params;
484        self
485    }
486
487    /// Sets desired first body of the joint. This handle should be a handle to rigid body node,
488    /// otherwise joint will have no effect!
489    pub fn with_body1(mut self, body1: Handle<Node>) -> Self {
490        self.body1 = body1;
491        self
492    }
493
494    /// Sets desired second body of the joint. This handle should be a handle to rigid body node,
495    /// otherwise joint will have no effect!
496    pub fn with_body2(mut self, body2: Handle<Node>) -> Self {
497        self.body2 = body2;
498        self
499    }
500
501    /// Sets whether the connected bodies should ignore collisions with each other or not.  
502    pub fn with_contacts_enabled(mut self, enabled: bool) -> Self {
503        self.contacts_enabled = enabled;
504        self
505    }
506
507    /// Sets whether the joint should automatically rebind two rigid bodies if the joint has changed its
508    /// global position.
509    pub fn with_auto_rebinding_enabled(mut self, auto_rebind: bool) -> Self {
510        self.auto_rebind = auto_rebind;
511        self
512    }
513
514    /// Creates new Joint node, but does not add it to the graph.
515    pub fn build_joint(self) -> Joint {
516        Joint {
517            base: self.base_builder.build_base(),
518            params: self.params.into(),
519            body1: self.body1.into(),
520            body2: self.body2.into(),
521            contacts_enabled: self.contacts_enabled.into(),
522            auto_rebind: self.auto_rebind.into(),
523            local_frames: Default::default(),
524            native: Cell::new(ImpulseJointHandle::invalid()),
525        }
526    }
527
528    /// Creates new Joint node, but does not add it to the graph.
529    pub fn build_node(self) -> Node {
530        Node::new(self.build_joint())
531    }
532
533    /// Creates new Joint node and adds it to the graph.
534    pub fn build(self, graph: &mut Graph) -> Handle<Node> {
535        graph.add_node(self.build_node())
536    }
537}