PhysicsRevoluteJoint#

Hinge joint: one rotational degree of freedom about a local axis, optionally limited in degrees. Inherits every PhysicsJoint column and adds the axis and the two limit bounds. Standalone it is a PxD6Joint with all axes locked except eTWIST; inside an articulation it is an eREVOLUTE (free) or eREVOLUTE_UNWRAPPED (limited) reduced-coordinate joint.

What USD population writes#

As PhysicsJoint; additionally publishes physics:axis, physics:lowerLimit and physics:upperLimit at their resolved values (X, -inf, inf when unauthored).

Columns#

Raw fallback is the USD schema value. Producer writes is what ovstage population puts in the column for a USD stage. Parser default applies on the consumer when the column is absent, in stage units. diverges means writing the raw fallback literally parses differently from omitting the column (the parser default): USD population may well publish that very value for an unauthored attribute, so the marker is about the column being present, not about its value.

Column

Type

Units

Raw fallback

Producer writes

Parser default

If absent

physics:body0 (inherited from PhysicsJoint)

uint64 array, RELATIONSHIP_PATH_ID

not written

Side 0 anchors to the world. The consumer keeps localPos0/localRot0 verbatim as a world-space frame (only the quaternion is normalised, no body scale is baked) and, for articulations, treats the joint as a fixed-base anchor candidate. Only the first target is read; it is resolved to the nearest enclosing enabled rigid body, which may differ from the target itself.

physics:body1 (inherited from PhysicsJoint)

uint64 array, RELATIONSHIP_PATH_ID

not written

Side 1 anchors to the world; see physics:body0. A joint with both relationships absent is still enumerated and parsed but no PhysX joint is created (no actor on either side).

physics:localPos0 (inherited from PhysicsJoint)

float32[3], POINT

length

(0, 0, 0)

resolved USD value (authored, else raw fallback)

(0, 0, 0)

Frame origin at the body-0 origin. Stored in stage length units; there is no metersPerUnit scaling, but the resolved body’s world scale is multiplied component-wise into the authored value because PhysX actors carry no scale (localPos0 (1,1,1) under scale (2,3,4) reaches PhysX as (2,3,4)). When the relationship target is not the resolved body the frame is lifted to world through the target and pulled back into the body frame with scale and shear removed.

physics:localRot0 (inherited from PhysicsJoint)

float32[4], QUATERNION

none

(0, 0, 0, 1)

resolved USD value (authored, else raw fallback)

(0, 0, 0, 1)

Identity rotation. The consumer normalises the quaternion unconditionally, even when the body relationship is absent.

physics:localPos1 (inherited from PhysicsJoint)

float32[3], POINT

length

(0, 0, 0)

resolved USD value (authored, else raw fallback)

(0, 0, 0)

Frame origin at the body-1 origin. Same scale and re-anchoring rules as physics:localPos0.

physics:localRot1 (inherited from PhysicsJoint)

float32[4], QUATERNION

none

(0, 0, 0, 1)

resolved USD value (authored, else raw fallback)

(0, 0, 0, 1)

Identity rotation; see physics:localRot0.

physics:jointEnabled (inherited from PhysicsJoint)

bool8

none

true

resolved USD value (authored, else raw fallback)

true

Joint is created and simulated. false creates no PhysX joint and removes the joint from the articulation graph entirely.

physics:collisionEnabled (inherited from PhysicsJoint)

bool8

none

false

resolved USD value (authored, else raw fallback)

false

Collision between the two jointed bodies is disabled.

physics:breakForce (inherited from PhysicsJoint)

float32

mass*length/s^2

inf

resolved USD value (authored, else raw fallback)

FLT_MAX

Unbreakable. The consumer seeds FLT_MAX before reading; the USD schema fallback is inf. Either value reaches PhysX as FLT_MAX because the runtime maps every non-finite break force to FLT_MAX.

physics:breakTorque (inherited from PhysicsJoint)

float32

mass*length^2/s^2

inf

resolved USD value (authored, else raw fallback)

FLT_MAX

Unbreakable; see physics:breakForce.

physics:excludeFromArticulation (inherited from PhysicsJoint)

bool8

none

false

resolved USD value (authored, else raw fallback)

false

The joint is a candidate articulation joint: inside an articulation subtree it becomes a PxArticulationJointReducedCoordinate and its body-to-world side (if any) elects a fixed base. true keeps it a standalone PxJoint (graph weight +1000 instead of +100000/+100, no recursion through it).

physics:axis

uint64, TOKEN_ID

none

X

resolved USD value (authored, else raw fallback)

X

Rotation about local X. Only the tokens Y and Z are matched; any other token, including a malformed one, falls back to X. For Y or Z the runtime post-multiplies a fixup rotation into both local poses so the hinge axis becomes PhysX X (eTWIST).

physics:lowerLimit

float32

deg (deg_to_rad)

-inf

resolved USD value (authored, else raw fallback)

0.0

CAUTION: with the column absent the bound stays at the JointInfo default 0 and the enable rule (a bound is finite and inside +-0.5e38) makes the limit ACTIVE at [0, 0], a locked hinge. USD population publishes the resolved fallback -inf, which disables the limit. Always write the resolved value; -inf is safe to write literally.

physics:upperLimit

float32

deg (deg_to_rad)

inf

resolved USD value (authored, else raw fallback)

0.0

Same trap as physics:lowerLimit: an absent column reads as 0 and the limit is active. Write the resolved value; inf is safe to write literally.

Interactions#

  • prim: When a body relationship is absent (or its target resolves to no enabled dynamic body) that side of the joint is anchored to the world: the authored local frame is used verbatim in world space and no body scale is baked into it. When a body resolves, its world scale, decomposed from the composed transform, is multiplied component-wise into localPos on that side.

  • PhysicsRigidBodyAPI: body0/body1 are resolved to the nearest enclosing prim with an enabling rigid body. Without an actor on at least one side no PhysX joint is created.

  • PhysicsArticulationRootAPI: A joint with one side anchored to the world (absent relationship, or a static, disabled or kinematic body) and excludeFromArticulation false becomes the fixed-base anchor of an articulation that contains its other body; the joint prim itself is elected root and fixBase is set.

  • PhysxJointAPI: The producer creates physxJoint:maxJointVelocity = FLT_MAX on every joint-typed prim when unauthored, even without PhysxJointAPI in usd-schemas; the consumer ignores it unless the API is listed.

  • PhysicsLimitAPI: Only a prim typed exactly PhysicsJoint (the D6) reads PhysicsLimitAPI:; typed joints read their own limit columns.

  • PhysicsDriveAPI: The D6 reads PhysicsDriveAPI:transX..rotZ and :distance; revolute reads :angular, prismatic reads :linear; the other subtypes read no drive.

  • PhysicsDriveAPI: Reads PhysicsDriveAPI:angular only. The drive lands on PxD6Drive::eTWIST (standalone) or PxArticulationAxis::eTWIST; stiffness and damping are multiplied by 57.29577951308232, targets are converted degrees to radians, and the standalone angular target velocity is negated by the runtime (OM-42441).

  • PhysxLimitAPI: Reads PhysxLimitAPI:angular for restitution, bounceThreshold, stiffness and damping of the twist limit.

  • PhysxJointAxisAPI: Reads PhysxJointAxisAPI:angular with rotational conversions; effective on articulation joints only.

  • PhysicsJointStateAPI: Reads PhysicsJointStateAPI:angular with degrees-to-radians conversion; effective on articulation joints only.

Known divergences#

  • note (consumer) physics:breakForce: The ovstage walker seeds breakForce/breakTorque with FLT_MAX while the USD walker leaves the schema fallback inf, so the descriptor differs bit-for-bit between backends when the columns are unauthored. No simulation difference: the runtime maps any non-finite value to FLT_MAX before calling PxJoint::setBreakForce.

  • fragile (consumer) physics:localRot0: The PhysxJointDesc constructor default for localPose0Orientation/localPose1Orientation is {1, 0, 0, 0} in the (x, y, z, w) layout, a 180 degree rotation about X rather than identity. Masked today because copyBaseFields always overwrites it from JointInfo, whose default {0, 0, 0, 1} is identity. Any future path that builds a joint descriptor without copyBaseFields inherits a flipped frame.

  • note (producer) physics:localRot0: Quaternion lanes are published (x, y, z, w) although USD authors (w, x, y, z); the consumer reads the lanes verbatim. A hand populator that copies the USD tuple order produces a wrong frame with no diagnostic. Also applies to physics:localRot1.

  • defect (consumer) physics:lowerLimit: An absent lowerLimit/upperLimit column leaves the JointInfo bound at 0, and the OR-form enable rule then activates the limit at [0, 0], locking the hinge. USD population always publishes the resolved -inf/inf so the defect is masked there; only a hand-populated stage that omits the columns is affected. The active limit also overwrites the PhysX default bounceThreshold of the untouched limit (0.5 for angular and cone limits, 2 * tolerances length for linear limits) with the PhysxLimitAPI value or 0, so physxLimit::bounceThreshold diverges too (observed: 0 vs 0.5 / 200).

  • fragile (consumer) physics:lowerLimit: Limit enable differs between backends for revolute joints: the ovstage walker enables the limit when EITHER bound is finite and inside +-0.5e38 (OR), the USD walker requires BOTH (AND). A revolute with lowerLimit -30 and upperLimit inf is limited on ovstage and free on USD. Prismatic and D6 use OR on both backends.

Notes#

  • Instance matrix for this type: PhysicsDriveAPI:angular, PhysxLimitAPI:angular, PhysxJointAxisAPI:angular, PhysxDrivePerformanceEnvelopeAPI:angular, PhysicsJointStateAPI:angular (degrees to radians). PhysicsLimitAPI is not read; the limit comes from physics:lowerLimit/upperLimit.

  • Articulation joint: limit enabled selects eREVOLUTE_UNWRAPPED with eLIMITED on eTWIST, otherwise eREVOLUTE with eFREE. Limits are flipped to (-upper, -lower) when body0 is not the parent link.

  • Limits beyond +-360 degrees are clamped to (-2pi, 2pi) by the runtime with a warning.