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}