@@ -282,6 +282,16 @@ pub struct GenericJoint {
282282 /// For coupled degrees of freedoms (DoF), only the first linear (resp. angular) coupled DoF motor and `motor_axes`
283283 /// bitmask is applied to the coupled linear (resp. angular) axes.
284284 pub motors : [ JointMotor ; SPATIAL_DIM ] ,
285+ /// The natural frequency (Hz) used for the joint constraint spring-like regularization.
286+ ///
287+ /// Higher values make the joint stiffer and resolve constraint violations more quickly.
288+ /// (default: `1.0e6`).
289+ pub natural_frequency : Real ,
290+ /// The damping ratio used for the joint constraint spring-like regularization.
291+ ///
292+ /// Larger values make the joint more compliant (allowing more drift before stabilization).
293+ /// (default: `1.0`).
294+ pub damping_ratio : Real ,
285295 /// Are contacts between the attached rigid-bodies enabled?
286296 pub contacts_enabled : bool ,
287297 /// Whether the joint is enabled.
@@ -301,6 +311,8 @@ impl Default for GenericJoint {
301311 coupled_axes : JointAxesMask :: empty ( ) ,
302312 limits : [ JointLimits :: default ( ) ; SPATIAL_DIM ] ,
303313 motors : [ JointMotor :: default ( ) ; SPATIAL_DIM ] ,
314+ natural_frequency : 1.0e6 ,
315+ damping_ratio : 1.0 ,
304316 contacts_enabled : true ,
305317 enabled : JointEnabled :: Enabled ,
306318 user_data : 0 ,
@@ -440,6 +452,75 @@ impl GenericJoint {
440452 self
441453 }
442454
455+ /// The natural frequency (Hz) for this joint's constraint regularization.
456+ #[ must_use]
457+ pub fn natural_frequency ( & self ) -> Real {
458+ self . natural_frequency
459+ }
460+
461+ /// Sets the natural frequency (Hz) for this joint's constraint regularization.
462+ ///
463+ /// Higher values make the joint stiffer and resolve constraint violations more quickly.
464+ ///
465+ /// # Example
466+ /// ```
467+ /// # use rapier3d::prelude::*;
468+ /// let mut joint = RevoluteJoint::new(Vector::y_axis());
469+ /// joint.set_natural_frequency(5.0e5); // Softer than default (1.0e6)
470+ /// ```
471+ pub fn set_natural_frequency ( & mut self , frequency : Real ) -> & mut Self {
472+ self . natural_frequency = frequency;
473+ self
474+ }
475+
476+ /// The damping ratio for this joint's constraint regularization.
477+ #[ must_use]
478+ pub fn damping_ratio ( & self ) -> Real {
479+ self . damping_ratio
480+ }
481+
482+ /// Sets the damping ratio for this joint's constraint regularization.
483+ ///
484+ /// Larger values make the joint more compliant (allowing more drift before stabilization).
485+ ///
486+ /// # Example
487+ /// ```
488+ /// # use rapier3d::prelude::*;
489+ /// let mut joint = RevoluteJoint::new(Vector::y_axis());
490+ /// joint.set_damping_ratio(2.0); // More compliant than default (1.0)
491+ /// ```
492+ pub fn set_damping_ratio ( & mut self , ratio : Real ) -> & mut Self {
493+ self . damping_ratio = ratio;
494+ self
495+ }
496+
497+ /// The joint's spring angular frequency for constraint regularization.
498+ pub fn joint_angular_frequency ( & self ) -> Real {
499+ self . natural_frequency * std:: f64:: consts:: TAU as Real
500+ }
501+
502+ /// The joint ERP coefficient (multiplied by inverse timestep).
503+ pub fn joint_erp_inv_dt ( & self , dt : Real ) -> Real {
504+ let ang_freq = self . joint_angular_frequency ( ) ;
505+ ang_freq / ( dt * ang_freq + 2.0 * self . damping_ratio )
506+ }
507+
508+ /// The effective Error Reduction Parameter for calculating regularization forces.
509+ pub fn joint_erp ( & self , dt : Real ) -> Real {
510+ dt * self . joint_erp_inv_dt ( dt)
511+ }
512+
513+ /// The CFM coefficient for constraint regularization.
514+ pub fn joint_cfm_coeff ( & self , dt : Real ) -> Real {
515+ let joint_erp = self . joint_erp ( dt) ;
516+ if joint_erp == 0.0 {
517+ return 0.0 ;
518+ }
519+ let inv_erp_minus_one = 1.0 / joint_erp - 1.0 ;
520+ inv_erp_minus_one * inv_erp_minus_one
521+ / ( ( 1.0 + inv_erp_minus_one) * 4.0 * self . damping_ratio * self . damping_ratio )
522+ }
523+
443524 /// The joint limits along the specified axis.
444525 #[ must_use]
445526 pub fn limits ( & self , axis : JointAxis ) -> Option < & JointLimits < Real > > {
@@ -767,6 +848,40 @@ impl GenericJointBuilder {
767848 self
768849 }
769850
851+ /// Sets the natural frequency (Hz) for this joint's constraint regularization.
852+ ///
853+ /// Higher values make the joint stiffer and resolve constraint violations more quickly.
854+ ///
855+ /// # Example
856+ /// ```
857+ /// # use rapier3d::prelude::*;
858+ /// let joint = RevoluteJointBuilder::new(Vector::y_axis())
859+ /// .natural_frequency(5.0e5) // Softer than default
860+ /// .build();
861+ /// ```
862+ #[ must_use]
863+ pub fn natural_frequency ( mut self , frequency : Real ) -> Self {
864+ self . 0 . set_natural_frequency ( frequency) ;
865+ self
866+ }
867+
868+ /// Sets the damping ratio for this joint's constraint regularization.
869+ ///
870+ /// Larger values make the joint more compliant (allowing more drift before stabilization).
871+ ///
872+ /// # Example
873+ /// ```
874+ /// # use rapier3d::prelude::*;
875+ /// let joint = RevoluteJointBuilder::new(Vector::y_axis())
876+ /// .damping_ratio(2.0) // More compliant than default
877+ /// .build();
878+ /// ```
879+ #[ must_use]
880+ pub fn damping_ratio ( mut self , ratio : Real ) -> Self {
881+ self . 0 . set_damping_ratio ( ratio) ;
882+ self
883+ }
884+
770885 /// An arbitrary user-defined 128-bit integer associated to the joints built by this builder.
771886 pub fn user_data ( mut self , data : u128 ) -> Self {
772887 self . 0 . user_data = data;
0 commit comments