From 86c775c45071e216cd2b8b34c445cbe6e99d6884 Mon Sep 17 00:00:00 2001 From: Zach Tang Date: Sat, 23 May 2026 22:10:07 -0700 Subject: [PATCH 1/2] Expose revolute joints with separate local axes --- src.ts/dynamics/impulse_joint.ts | 53 ++++++++++++++++++++++++++++++-- src/dynamics/joint.rs | 29 +++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src.ts/dynamics/impulse_joint.ts b/src.ts/dynamics/impulse_joint.ts index a69c38ef..12e2cb6c 100644 --- a/src.ts/dynamics/impulse_joint.ts +++ b/src.ts/dynamics/impulse_joint.ts @@ -369,6 +369,10 @@ export class JointData { anchor1: Vector; anchor2: Vector; axis: Vector; + // #if DIM3 + axis1: Vector; + axis2: Vector; + // #endif frame1: Rotation; frame2: Rotation; jointType: JointType; @@ -588,6 +592,36 @@ export class JointData { res.jointType = JointType.Revolute; return res; } + + /** + * Create a new joint descriptor that builds Revolute joints with independent + * local axes for each attached rigid-body. + * + * This is useful when the same world-space hinge axis is represented by + * different local axes on the two bodies. + * + * @param anchor1 - Point where the joint is attached on the first rigid-body affected by this joint. Expressed in the + * local-space of the rigid-body. + * @param anchor2 - Point where the joint is attached on the second rigid-body affected by this joint. Expressed in the + * local-space of the rigid-body. + * @param axis1 - Axis of the joint, expressed in the local-space of the first rigid-body. + * @param axis2 - Axis of the joint, expressed in the local-space of the second rigid-body. + */ + public static revoluteWithAxes( + anchor1: Vector, + anchor2: Vector, + axis1: Vector, + axis2: Vector, + ): JointData { + let res = new JointData(); + res.anchor1 = anchor1; + res.anchor2 = anchor2; + res.axis = axis1; + res.axis1 = axis1; + res.axis2 = axis2; + res.jointType = JointType.Revolute; + return res; + } // #endif public intoRaw(): RawGenericJoint { @@ -674,9 +708,22 @@ export class JointData { result = RawGenericJoint.spherical(rawA1, rawA2); break; case JointType.Revolute: - rawAx = VectorOps.intoRaw(this.axis); - result = RawGenericJoint.revolute(rawA1, rawA2, rawAx); - rawAx.free(); + if (!!this.axis1 && !!this.axis2) { + let rawAx1 = VectorOps.intoRaw(this.axis1); + let rawAx2 = VectorOps.intoRaw(this.axis2); + result = RawGenericJoint.revoluteWithAxes( + rawA1, + rawA2, + rawAx1, + rawAx2, + ); + rawAx1.free(); + rawAx2.free(); + } else { + rawAx = VectorOps.intoRaw(this.axis); + result = RawGenericJoint.revolute(rawA1, rawA2, rawAx); + rawAx.free(); + } break; // #endif } diff --git a/src/dynamics/joint.rs b/src/dynamics/joint.rs index 74ab2b3d..a37c9c5c 100644 --- a/src/dynamics/joint.rs +++ b/src/dynamics/joint.rs @@ -311,4 +311,33 @@ impl RawGenericJoint { .into(), )) } + + /// Create a new joint descriptor that builds Revolute joints with + /// independent local axes for each attached rigid-body. + /// + /// This is equivalent to a revolute generic joint with all linear axes + /// locked and only angular X free, but it preserves the local hinge axis + /// on each body instead of assuming they are identical. + #[cfg(feature = "dim3")] + pub fn revoluteWithAxes( + anchor1: &RawVector, + anchor2: &RawVector, + axis1: &RawVector, + axis2: &RawVector, + ) -> Option { + let axis1 = Unit::try_new(axis1.0, 0.0)?; + let axis2 = Unit::try_new(axis2.0, 0.0)?; + let axes_mask = JointAxesMask::LIN_X + | JointAxesMask::LIN_Y + | JointAxesMask::LIN_Z + | JointAxesMask::ANG_Y + | JointAxesMask::ANG_Z; + let joint: GenericJoint = GenericJointBuilder::new(axes_mask) + .local_anchor1(anchor1.0.into()) + .local_anchor2(anchor2.0.into()) + .local_axis1(axis1) + .local_axis2(axis2) + .into(); + Some(Self(joint)) + } } From 3c06018c9d9172656ceac55339d1deec32bc4993 Mon Sep 17 00:00:00 2001 From: zdtango <38231181+zdtango@users.noreply.github.com> Date: Sun, 24 May 2026 11:06:24 -0700 Subject: [PATCH 2/2] Update src/dynamics/joint.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sébastien Crozet --- src/dynamics/joint.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/dynamics/joint.rs b/src/dynamics/joint.rs index a37c9c5c..e66bb7be 100644 --- a/src/dynamics/joint.rs +++ b/src/dynamics/joint.rs @@ -327,12 +327,7 @@ impl RawGenericJoint { ) -> Option { let axis1 = Unit::try_new(axis1.0, 0.0)?; let axis2 = Unit::try_new(axis2.0, 0.0)?; - let axes_mask = JointAxesMask::LIN_X - | JointAxesMask::LIN_Y - | JointAxesMask::LIN_Z - | JointAxesMask::ANG_Y - | JointAxesMask::ANG_Z; - let joint: GenericJoint = GenericJointBuilder::new(axes_mask) + let joint: GenericJoint = GenericJointBuilder::new(JointAxesMask::LOCKED_REVOLUTE_AXES) .local_anchor1(anchor1.0.into()) .local_anchor2(anchor2.0.into()) .local_axis1(axis1)