Skip to main content

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