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, SceneGraph};
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    #[visit(optional)] // Backward compatibility
64    pub x_limits_enabled: bool,
65
66    /// Allowed angle range around local X axis of the joint (in radians).
67    #[visit(optional)] // Backward compatibility
68    pub x_limits_angles: Range<f32>,
69
70    /// Whether Y angular limits are enabled or not. Default is `false`
71    #[visit(optional)] // Backward compatibility
72    pub y_limits_enabled: bool,
73
74    /// Allowed angle range around local Y axis of the joint (in radians).
75    #[visit(optional)] // Backward compatibility
76    pub y_limits_angles: Range<f32>,
77
78    /// Whether Z angular limits are enabled or not. Default is `false`
79    #[visit(optional)] // Backward compatibility
80    pub z_limits_enabled: bool,
81
82    /// Allowed angle range around local Z axis of the joint (in radians).
83    #[visit(optional)] // Backward compatibility
84    pub z_limits_angles: Range<f32>,
85}
86
87impl Default for BallJoint {
88    fn default() -> Self {
89        Self {
90            x_limits_enabled: false,
91            x_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
92            y_limits_enabled: false,
93            y_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
94            z_limits_enabled: false,
95            z_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
96        }
97    }
98}
99
100/// A fixed joint ensures that two rigid bodies does not move relative to each other. There is no
101/// straightforward real-world example, but it can be thought as two bodies were "welded" together.
102#[derive(Clone, Debug, Visit, PartialEq, Reflect, Default, Eq)]
103pub struct FixedJoint;
104
105/// Prismatic joint prevents any relative movement between two rigid-bodies, except for relative
106/// translations along one axis. The real world example is a sliders that used to support drawers.
107#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
108pub struct PrismaticJoint {
109    /// Whether linear limits along local joint X axis are enabled or not. Default is `false`
110    #[visit(optional)] // Backward compatibility
111    pub limits_enabled: bool,
112
113    /// The min an max relative position of the attached bodies along local X axis of the joint.
114    #[visit(optional)] // Backward compatibility
115    pub limits: Range<f32>,
116}
117
118impl Default for PrismaticJoint {
119    fn default() -> Self {
120        Self {
121            limits_enabled: false,
122            limits: -std::f32::consts::PI..std::f32::consts::PI,
123        }
124    }
125}
126
127/// Revolute joint prevents any relative movement between two rigid bodies, except relative rotation
128/// along one axis. The real world example is wheels, fans, etc. It can also be used to simulate door
129/// hinge.
130#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
131pub struct RevoluteJoint {
132    /// Whether angular limits around local X axis of the joint are enabled or not. Default is `false`
133    #[visit(optional)] // Backward compatibility
134    pub limits_enabled: bool,
135
136    /// Allowed angle range around local X axis of the joint (in radians).
137    #[visit(optional)] // Backward compatibility
138    pub limits: Range<f32>,
139}
140
141impl Default for RevoluteJoint {
142    fn default() -> Self {
143        Self {
144            limits_enabled: false,
145            limits: -std::f32::consts::PI..std::f32::consts::PI,
146        }
147    }
148}
149
150/// Parameters that define how the joint motor will behave.
151#[derive(Default, Clone, Debug, PartialEq, Visit, Reflect)]
152pub struct JointMotorParams {
153    /// The target velocity of the motor.
154    pub target_vel: f32,
155    /// The target position of the motor.
156    pub target_pos: f32,
157    /// The stiffness coefficient of the motor’s spring-like equation.
158    pub stiffness: f32,
159    /// The damping coefficient of the motor’s spring-like equation.
160    pub damping: f32,
161    /// The maximum force this motor can deliver.
162    pub max_force: f32,
163}
164
165/// The exact kind of the joint.
166#[derive(Clone, Debug, PartialEq, Visit, Reflect, AsRefStr, EnumString, VariantNames)]
167pub enum JointParams {
168    /// See [`BallJoint`] for more info.
169    BallJoint(BallJoint),
170    /// See [`FixedJoint`] for more info.
171    FixedJoint(FixedJoint),
172    /// See [`PrismaticJoint`] for more info.
173    PrismaticJoint(PrismaticJoint),
174    /// See [`RevoluteJoint`] for more info.
175    RevoluteJoint(RevoluteJoint),
176}
177
178uuid_provider!(JointParams = "a3e09303-9de4-4123-9492-05e27f29aaa3");
179
180impl Default for JointParams {
181    fn default() -> Self {
182        Self::BallJoint(Default::default())
183    }
184}
185
186#[derive(Visit, Reflect, Debug, Clone, Default)]
187pub(crate) struct LocalFrame {
188    pub position: Vector3<f32>,
189    pub rotation: UnitQuaternion<f32>,
190}
191
192impl LocalFrame {
193    pub fn new(isometry: &Isometry3<f32>) -> Self {
194        Self {
195            position: isometry.translation.vector,
196            rotation: isometry.rotation,
197        }
198    }
199}
200
201#[derive(Visit, Reflect, Debug, Clone, Default)]
202pub(crate) struct JointLocalFrames {
203    pub body1: LocalFrame,
204    pub body2: LocalFrame,
205}
206
207impl JointLocalFrames {
208    pub fn new(isometry1: &Isometry3<f32>, isometry2: &Isometry3<f32>) -> Self {
209        Self {
210            body1: LocalFrame::new(isometry1),
211            body2: LocalFrame::new(isometry2),
212        }
213    }
214}
215
216/// Joint is used to restrict motion of two rigid bodies. There are numerous examples of joints in
217/// real life: door hinge, ball joints in human arms, etc.
218#[derive(Visit, Reflect, Debug, ComponentProvider)]
219#[reflect(derived_type = "Node")]
220pub struct Joint {
221    base: Base,
222
223    #[reflect(setter = "set_params")]
224    pub(crate) params: InheritableVariable<JointParams>,
225
226    #[reflect(setter = "set_motor_params")]
227    #[visit(optional)] // Backward compatibility
228    pub(crate) motor_params: InheritableVariable<JointMotorParams>,
229
230    #[reflect(setter = "set_body1")]
231    pub(crate) body1: InheritableVariable<Handle<RigidBody>>,
232
233    #[reflect(setter = "set_body2")]
234    pub(crate) body2: InheritableVariable<Handle<RigidBody>>,
235
236    #[reflect(setter = "set_contacts_enabled")]
237    #[visit(optional)] // Backward compatibility
238    pub(crate) contacts_enabled: InheritableVariable<bool>,
239
240    #[reflect(setter = "set_auto_rebinding")]
241    #[visit(optional)] // Backward compatibility
242    pub(crate) auto_rebind: InheritableVariable<bool>,
243
244    #[visit(optional)]
245    #[reflect(hidden)]
246    pub(crate) local_frames: RefCell<Option<JointLocalFrames>>,
247
248    #[visit(skip)]
249    #[reflect(hidden)]
250    pub(crate) native: Cell<ImpulseJointHandle>,
251}
252
253impl Default for Joint {
254    fn default() -> Self {
255        Self {
256            base: Default::default(),
257            params: Default::default(),
258            motor_params: Default::default(),
259            body1: Default::default(),
260            body2: Default::default(),
261            contacts_enabled: InheritableVariable::new_modified(true),
262            auto_rebind: true.into(),
263            local_frames: Default::default(),
264            native: Cell::new(ImpulseJointHandle::invalid()),
265        }
266    }
267}
268
269impl Deref for Joint {
270    type Target = Base;
271
272    fn deref(&self) -> &Self::Target {
273        &self.base
274    }
275}
276
277impl DerefMut for Joint {
278    fn deref_mut(&mut self) -> &mut Self::Target {
279        &mut self.base
280    }
281}
282
283impl Clone for Joint {
284    fn clone(&self) -> Self {
285        Self {
286            base: self.base.clone(),
287            params: self.params.clone(),
288            motor_params: self.motor_params.clone(),
289            body1: self.body1.clone(),
290            body2: self.body2.clone(),
291            contacts_enabled: self.contacts_enabled.clone(),
292            local_frames: self.local_frames.clone(),
293            // Do not copy. The copy will have its own native representation.
294            auto_rebind: self.auto_rebind.clone(),
295            native: Cell::new(ImpulseJointHandle::invalid()),
296        }
297    }
298}
299
300impl TypeUuidProvider for Joint {
301    fn type_uuid() -> Uuid {
302        uuid!("439d48f5-e3a3-4255-aa08-353c1ca42e3b")
303    }
304}
305
306impl Joint {
307    /// Returns a shared reference to the current joint parameters.
308    pub fn params(&self) -> &JointParams {
309        &self.params
310    }
311
312    /// Returns a mutable reference to the current joint parameters. Obtaining the mutable reference
313    /// will force the engine to do additional calculations to reflect changes to the physics engine.
314    pub fn params_mut(&mut self) -> &mut JointParams {
315        self.params.get_value_mut_and_mark_modified()
316    }
317
318    /// Sets new joint parameters.
319    pub fn set_params(&mut self, params: JointParams) -> JointParams {
320        self.params.set_value_and_mark_modified(params)
321    }
322
323    /// Returns a shared reference to the current joint motor parameters.
324    pub fn motor_params(&self) -> &JointMotorParams {
325        &self.motor_params
326    }
327
328    /// Returns a mutable reference to the current joint motor parameters. Obtaining the mutable reference
329    ///
330    /// Recommend calling [`Self::set_motor_force_as_prismatic`] or [`Self::set_motor_torque_as_revolute`] for prismatic or revolute joints.
331    ///
332    /// Currently we do not support motor forces on more than one axis.
333    ///
334    /// If you have more complex needs, you may try to chain different joints together.
335    /// # Notice
336    /// If the joint is not RevoluteJoint or PrismaticJoint, modifying the motor parameters directly may lead to unexpected behavior.
337    pub fn motor_params_mut(&mut self) -> &mut JointMotorParams {
338        self.motor_params.get_value_mut_and_mark_modified()
339    }
340
341    /// Sets new joint motor parameters.
342    ///
343    /// Recommend calling [`Self::set_motor_force_as_prismatic`] or [`Self::set_motor_torque_as_revolute`] for prismatic or revolute joints.
344    ///
345    /// Currently we do not support motor forces on more than one axis.
346    ///
347    /// If you have more complex needs, you may try to chain different joints together.
348    /// # Notice
349    /// If the joint is not RevoluteJoint or PrismaticJoint, modifying the motor parameters directly may lead to unexpected behavior.
350    pub fn set_motor_params(&mut self, motor_params: JointMotorParams) -> JointMotorParams {
351        // to see how setting these params affect the rapier3d physics engine,
352        // go to sync_native function in this file.
353        self.motor_params.set_value_and_mark_modified(motor_params)
354    }
355
356    /// Sets the first body of the joint. The handle should point to the RigidBody node, otherwise
357    /// the joint will have no effect!
358    pub fn set_body1(&mut self, handle: Handle<RigidBody>) -> Handle<RigidBody> {
359        self.body1.set_value_and_mark_modified(handle)
360    }
361
362    /// Returns current first body of the joint.
363    pub fn body1(&self) -> Handle<RigidBody> {
364        *self.body1
365    }
366
367    /// Sets the second body of the joint. The handle should point to the RigidBody node, otherwise
368    /// the joint will have no effect!
369    pub fn set_body2(&mut self, handle: Handle<RigidBody>) -> Handle<RigidBody> {
370        self.body2.set_value_and_mark_modified(handle)
371    }
372
373    /// Returns current second body of the joint.
374    pub fn body2(&self) -> Handle<RigidBody> {
375        *self.body2
376    }
377
378    /// Sets whether the connected bodies should ignore collisions with each other or not.
379    pub fn set_contacts_enabled(&mut self, enabled: bool) -> bool {
380        self.contacts_enabled.set_value_and_mark_modified(enabled)
381    }
382
383    /// Returns true if contacts between connected bodies is enabled, false - otherwise.
384    pub fn is_contacts_enabled(&self) -> bool {
385        *self.contacts_enabled
386    }
387
388    /// Sets whether the joint should automatically rebind two rigid bodies if the joint has changed its
389    /// global position.
390    pub fn set_auto_rebinding(&mut self, enabled: bool) -> bool {
391        self.auto_rebind.set_value_and_mark_modified(enabled)
392    }
393
394    /// Returns true if automatic rebinding of the joint is enabled or not.
395    pub fn is_auto_rebinding_enabled(&self) -> bool {
396        *self.auto_rebind
397    }
398
399    /// Sets the motor force of the joint assuming it is a [`PrismaticJoint`].
400    ///
401    /// Call [`Self::disable_motor`] to properly stop the motor and set the joint free.
402    /// # Arguments
403    /// * `force` - The maximum force this motor can deliver.
404    /// * `max_vel` - The target velocity of the motor.
405    /// * `damping` - Penalizes high velocities to avoid overshooting the target velocity. A higher damping value will result in a smoother transition to the target velocity.
406    /// # Errors
407    /// If the joint is not a [`PrismaticJoint`], this function will do nothing and return an Err.
408    /// # Notice
409    /// The rigid bodies attached to the joint may fall asleep anytime regardless whether the motor is enabled or not.
410    ///
411    /// To avoid this behavior, call this function periodically or call [`RigidBody::set_can_sleep`] on the rigid bodies with "false".
412    pub fn set_motor_force_as_prismatic(
413        &mut self,
414        force: f32,
415        max_vel: f32,
416        damping: f32,
417    ) -> Result<(), String> {
418        let JointParams::PrismaticJoint(_) = self.params() else {
419            return Err("Joint is not a PrismaticJoint".to_string());
420        };
421        let motor_params = JointMotorParams {
422            target_vel: max_vel,
423            target_pos: 0.0,
424            stiffness: 0.0,
425            damping,
426            max_force: force,
427        };
428        // retrieving the mutable reference to the joint params will cause the engine to do additional calculations to reflect changes to the physics engine.
429        self.set_motor_params(motor_params);
430        Ok(())
431    }
432
433    /// Sets the motor torque of the joint assuming it is a [`RevoluteJoint`].
434    ///
435    /// Call [`Self::disable_motor`] to properly stop the motor and set the joint free.
436    /// # Arguments
437    /// * `torque` - The maximum torque this motor can deliver.
438    /// * `max_angular_vel` - The target angular velocity of the motor.
439    /// * `damping` - Penalizes high angular velocities to avoid overshooting the target angular velocity. A higher damping value will result in a smoother transition to the target angular velocity.
440    /// # Errors
441    /// If the joint is not a [`RevoluteJoint`], this function will do nothing and return an Err.
442    /// # Notice
443    /// The rigid bodies attached to the joint may fall asleep anytime regardless whether the motor is enabled or not.
444    ///
445    /// To avoid this behavior, call this function periodically or call [`RigidBody::set_can_sleep`] on the rigid bodies with "false".
446    pub fn set_motor_torque_as_revolute(
447        &mut self,
448        torque: f32,
449        max_angular_vel: f32,
450        damping: f32,
451    ) -> Result<(), String> {
452        let JointParams::RevoluteJoint(_) = self.params() else {
453            return Err("Joint is not a RevoluteJoint".to_string());
454        };
455        let motor_params = JointMotorParams {
456            target_vel: max_angular_vel,
457            target_pos: 0.0,
458            stiffness: 0.0,
459            damping,
460            max_force: torque,
461        };
462        // retrieving the mutable reference to the joint params will cause the engine to do additional calculations to reflect changes to the physics engine.
463        self.set_motor_params(motor_params);
464        Ok(())
465    }
466
467    /// Sets the motor target position of the joint assuming it is a [`PrismaticJoint`].
468    ///
469    /// After the joint reaches the target position, the joint will act as a spring with the specified stiffness and damping values.
470    ///
471    /// Call [`Self::disable_motor`] to stop the motor and remove the spring effect.
472    /// # Arguments
473    /// * `target_position` - The target position that the joint will try to reach, can be negative.
474    /// * `stiffness` - Controls how fast the joint will try to reach the target position.
475    /// * `max_force` - The maximum force this motor can deliver.
476    /// * `damping` - Penalizes high velocities to avoid overshooting the target position. A higher damping value will result in a smoother transition to the target position.
477    /// # Errors
478    /// If the joint is not a [`PrismaticJoint`], the function will do nothing and return an Err.
479    /// # Notice
480    /// The rigid bodies attached to the joint may fall asleep anytime regardless whether the motor is enabled or not.
481    ///
482    /// To avoid this behavior, call this function periodically or call [`RigidBody::set_can_sleep`] on the rigid bodies with "false".
483    pub fn set_motor_target_position_as_prismatic(
484        &mut self,
485        target_position: f32,
486        stiffness: f32,
487        max_force: f32,
488        damping: f32,
489    ) -> Result<(), String> {
490        let JointParams::PrismaticJoint(_) = self.params() else {
491            return Err("Joint is not a PrismaticJoint".to_string());
492        };
493        let motor_params = JointMotorParams {
494            target_vel: 0.0,
495            target_pos: target_position,
496            stiffness,
497            damping,
498            max_force,
499        };
500        // retrieving the mutable reference to the joint params will cause the engine to do additional calculations to reflect changes to the physics engine.
501        self.set_motor_params(motor_params);
502        Ok(())
503    }
504
505    /// Sets the motor target angle of the joint assuming it is a [`RevoluteJoint`].
506    ///
507    /// After the joint reaches the target angle, the joint will act as a spring with the specified stiffness and damping values.
508    ///
509    /// Call [`Self::disable_motor`] to stop the motor and remove the spring effect.
510    /// # Arguments
511    /// * `target_angle` - The target angle **in radians** that the joint will try to reach, can be negative. If the value is greater than 2π or less than -2π, the joint will turn multiple times to reach the target angle.
512    /// * `stiffness` - Controls how fast the joint will try to reach the target angle.
513    /// * `max_torque` - The maximum torque this motor can deliver.
514    /// * `damping` - Penalizes high angular velocities to avoid overshooting the target angle. A higher damping value will result in a smoother transition to the target angle.
515    /// # Errors
516    /// If the joint is not a [`RevoluteJoint`], the function will do nothing and return an Err.
517    /// # Notice
518    /// The rigid bodies attached to the joint may fall asleep anytime regardless whether the motor is enabled or not.
519    ///
520    /// To avoid this behavior, call this function periodically or call [`RigidBody::set_can_sleep`] on the rigid bodies with "false".
521    pub fn set_motor_target_angle_as_revolute(
522        &mut self,
523        target_angle: f32,
524        stiffness: f32,
525        max_torque: f32,
526        damping: f32,
527    ) -> Result<(), String> {
528        let JointParams::RevoluteJoint(_) = self.params() else {
529            return Err("Joint is not a RevoluteJoint".to_string());
530        };
531        let motor_params = JointMotorParams {
532            target_vel: 0.0,
533            target_pos: target_angle,
534            stiffness,
535            damping,
536            max_force: max_torque,
537        };
538        // retrieving the mutable reference to the joint params will cause the engine to do additional calculations to reflect changes to the physics engine.
539        self.set_motor_params(motor_params);
540        Ok(())
541    }
542
543    /// Makes the [`BallJoint`] to restore its original orientation with motor torque.
544    ///
545    /// Acts as a flexible fixed joint that tolerates some angular movement and tries to restore the original orientation.
546    ///
547    /// For flexible fixed joints that tolerate some translational movement, consider using a [`PrismaticJoint`] and call [`Self::set_motor_target_position_as_prismatic`].
548    ///
549    /// The motor torque is uniform across all three axes of the joint.
550    ///
551    /// /// Call [`Self::disable_motor`] to stop the motor and remove the spring effect.
552    ///
553    /// # Arguments
554    /// * `stiffness` - Controls how fast the joint will try to restore its original orientation.
555    /// * `max_torque` - The maximum torque this motor can deliver.
556    /// * `damping` - Penalizes high angular velocities to avoid overshooting the original orientation. A higher damping value will result in a smoother transition to the original orientation.
557    /// # Errors
558    /// If the joint is not a [`BallJoint`], the function will do nothing and return an Err.
559    /// # Notice
560    /// The rigid bodies attached to the joint may fall asleep anytime regardless whether the motor is enabled or not.
561    ///
562    /// To avoid this behavior, call this function periodically or call [`RigidBody::set_can_sleep`] on the rigid bodies with "false".
563    pub fn set_motor_resistive_torque_as_ball(
564        &mut self,
565        stiffness: f32,
566        max_torque: f32,
567        damping: f32,
568    ) -> Result<(), String> {
569        let JointParams::BallJoint(_) = self.params() else {
570            return Err("Joint is not a BallJoint".to_string());
571        };
572        let motor_params = JointMotorParams {
573            target_vel: 0.0,
574            target_pos: 0.0,
575            stiffness,
576            damping,
577            max_force: max_torque,
578        };
579        // retrieving the mutable reference to the joint params will cause the engine to do additional calculations to reflect changes to the physics engine.
580        self.set_motor_params(motor_params);
581        Ok(())
582    }
583
584    /// Disables the motor of the joint assuming it is a [`RevoluteJoint`], [`PrismaticJoint`] or [`BallJoint`].
585    ///
586    /// After this call, the joint will no longer apply any motor force or torque to the connected bodies.
587    /// # Errors
588    /// If the joint is not a [`RevoluteJoint`], [`PrismaticJoint`] or [`BallJoint`], the function will do nothing and return an Err.
589    pub fn disable_motor(&mut self) -> Result<(), String> {
590        if !matches!(
591            self.params(),
592            JointParams::RevoluteJoint(_)
593                | JointParams::PrismaticJoint(_)
594                | JointParams::BallJoint(_)
595        ) {
596            return Err("Joint is not a RevoluteJoint, PrismaticJoint or BallJoint".to_string());
597        }
598        let motor_params = JointMotorParams {
599            target_vel: 0.0,
600            target_pos: 0.0,
601            stiffness: 0.0,
602            damping: 0.0,
603            max_force: 0.0,
604        };
605        // retrieving the mutable reference to the joint params will cause the engine to do additional calculations to reflect changes to the physics engine.
606        self.set_motor_params(motor_params);
607        Ok(())
608    }
609}
610
611impl ConstructorProvider<Node, Graph> for Joint {
612    fn constructor() -> NodeConstructor {
613        NodeConstructor::new::<Self>()
614            .with_variant("Revolute Joint", |_| {
615                JointBuilder::new(BaseBuilder::new().with_name("Revolute Joint"))
616                    .with_params(JointParams::RevoluteJoint(Default::default()))
617                    .build_node()
618                    .into()
619            })
620            .with_variant("Ball Joint", |_| {
621                JointBuilder::new(BaseBuilder::new().with_name("Ball Joint"))
622                    .with_params(JointParams::BallJoint(Default::default()))
623                    .build_node()
624                    .into()
625            })
626            .with_variant("Prismatic Joint", |_| {
627                JointBuilder::new(BaseBuilder::new().with_name("Prismatic Joint"))
628                    .with_params(JointParams::PrismaticJoint(Default::default()))
629                    .build_node()
630                    .into()
631            })
632            .with_variant("Fixed Joint", |_| {
633                JointBuilder::new(BaseBuilder::new().with_name("Fixed Joint"))
634                    .with_params(JointParams::FixedJoint(Default::default()))
635                    .build_node()
636                    .into()
637            })
638            .with_group("Physics")
639    }
640}
641
642impl NodeTrait for Joint {
643    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
644        self.base.local_bounding_box()
645    }
646
647    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
648        self.base.world_bounding_box()
649    }
650
651    fn id(&self) -> Uuid {
652        Self::type_uuid()
653    }
654
655    fn on_removed_from_graph(&mut self, graph: &mut Graph) {
656        graph.physics.remove_joint(self.native.get());
657        self.native.set(ImpulseJointHandle::invalid());
658
659        Log::info(format!(
660            "Native joint was removed for node: {}",
661            self.name()
662        ));
663    }
664
665    fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
666        context
667            .physics
668            .sync_to_joint_node(context.nodes, self_handle, self);
669    }
670
671    fn on_global_transform_changed(
672        &self,
673        new_global_transform: &Matrix4<f32>,
674        _context: &mut SyncContext,
675    ) {
676        if *self.auto_rebind && !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
677            self.local_frames.borrow_mut().take();
678        }
679    }
680
681    fn validate(&self, scene: &Scene) -> Result<(), String> {
682        if scene.graph.try_get(self.body1()).is_none() {
683            return Err("3D Joint has invalid or unassigned handle to a \
684            first body, the joint will not operate!"
685                .to_string());
686        }
687
688        if scene.graph.try_get(self.body2()).is_none() {
689            return Err("3D Joint has invalid or unassigned handle to a \
690            second body, the joint will not operate!"
691                .to_string());
692        }
693
694        Ok(())
695    }
696}
697
698/// Joint builder allows you to build Joint node in a declarative manner.
699pub struct JointBuilder {
700    base_builder: BaseBuilder,
701    params: JointParams,
702    motor_params: JointMotorParams,
703    body1: Handle<RigidBody>,
704    body2: Handle<RigidBody>,
705    contacts_enabled: bool,
706    auto_rebind: bool,
707}
708
709impl JointBuilder {
710    /// Creates a new joint builder instance.
711    pub fn new(base_builder: BaseBuilder) -> Self {
712        Self {
713            base_builder,
714            params: Default::default(),
715            motor_params: Default::default(),
716            body1: Default::default(),
717            body2: Default::default(),
718            contacts_enabled: true,
719            auto_rebind: true,
720        }
721    }
722
723    /// Sets desired joint parameters which defines exact type of the joint.
724    pub fn with_params(mut self, params: JointParams) -> Self {
725        self.params = params;
726        self
727    }
728
729    /// Set desired motor parameters which defines how the joint motor will behave.
730    pub fn with_motor_params(mut self, motor_params: JointMotorParams) -> Self {
731        self.motor_params = motor_params;
732        self
733    }
734
735    /// Sets desired first body of the joint. This handle should be a handle to rigid body node,
736    /// otherwise joint will have no effect!
737    pub fn with_body1(mut self, body1: Handle<RigidBody>) -> Self {
738        self.body1 = body1;
739        self
740    }
741
742    /// Sets desired second body of the joint. This handle should be a handle to rigid body node,
743    /// otherwise joint will have no effect!
744    pub fn with_body2(mut self, body2: Handle<RigidBody>) -> Self {
745        self.body2 = body2;
746        self
747    }
748
749    /// Sets whether the connected bodies should ignore collisions with each other or not.
750    pub fn with_contacts_enabled(mut self, enabled: bool) -> Self {
751        self.contacts_enabled = enabled;
752        self
753    }
754
755    /// Sets whether the joint should automatically rebind two rigid bodies if the joint has changed its
756    /// global position.
757    pub fn with_auto_rebinding_enabled(mut self, auto_rebind: bool) -> Self {
758        self.auto_rebind = auto_rebind;
759        self
760    }
761
762    /// Creates new Joint node, but does not add it to the graph.
763    pub fn build_joint(self) -> Joint {
764        Joint {
765            base: self.base_builder.build_base(),
766            params: self.params.into(),
767            motor_params: self.motor_params.into(),
768            body1: self.body1.into(),
769            body2: self.body2.into(),
770            contacts_enabled: self.contacts_enabled.into(),
771            auto_rebind: self.auto_rebind.into(),
772            local_frames: Default::default(),
773            native: Cell::new(ImpulseJointHandle::invalid()),
774        }
775    }
776
777    /// Creates new Joint node, but does not add it to the graph.
778    pub fn build_node(self) -> Node {
779        Node::new(self.build_joint())
780    }
781
782    /// Creates new Joint node and adds it to the graph.
783    pub fn build(self, graph: &mut Graph) -> Handle<Node> {
784        graph.add_node(self.build_node())
785    }
786}