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