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