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}