Skip to main content

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