Joint Rework - API and Reorganization#803
Merged
Merged
Conversation
This was referenced Aug 20, 2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
Closes #198.
Closes #199.
Closes #254.
Supersedes #507.
Supersedes #511.
Partially addresses #440.
Avian's joints are in need of a rework. Some problems include:
A future goal is to also replace the XPBD joint solver, but in the meanwhile, we can improve the API and code organization by fixing the above problems.
This PR is massive (sorry!) and could be split into smaller chunks, but I wanted to do a fairly comprehensive pass to fix all the low-hanging fruit, and a lot of the changes are somewhat entangled, so doing (most of) it at once felt the easiest / most productive here.
Solution
Joint Frames
FixedJoint,PrismaticJoint,RevoluteJoint, andSphericalJointnow support a fullJointFramethat contains aJointAnchorandJointBasis. They are enums withLocalandFromGlobalvariants, allowing the initialization of local frames using world-space values.While the basis is now configurable, it is still also possible to configure axes like the "hinge axis" for a 3D
RevoluteJointor the "twist axis" for aSphericalJoint. This is unlike some engines where e.g. the x-axis is the de-facto slider axis for prismatic joints, and the local frames must be rotated to get other behavior. The motivation behind my approach is (1) user-friendliness, (2) minimizing implicit defaults, and (3) being more agnostic to different joint setups and coordinate systems.Joint Damping
Previously, each joint type stored its own damping coefficients. However, the actual damping logic is not joint-specific. and not all joints need damping. Thus, it is now handled by a separate
JointDampingcomponent withlinearandangularproperties.Joint Forces
The details of joint forces are solver-specific. However, ultimately users will tend to want to read a force vector and torque. This has now been generalized as a
JointForcescomponent that the constraint solver writes to and users can read. It is not added automatically and must be added manually for the desired joint entities.An example of where this may be useful is breaking joints when their forces or torques exceed some threshold:
Joint Debug Rendering
Joints now use a
ConstraintDebugRenderingtrait for their debug rendering. This makes custom rendering logic for each joint type more doable.For now, the old debug rendering is still used, but the infrastructure is there to e.g. visualize limits for each joint type.
Solver Reorganization and XPBD
All XPBD logic is now contained within
dynamics::solver::xpbd, gated behind thexpbd_jointsfeature. The actual joint API has been extracted out intodynamics::joints, and the solver internal data has been moved out into separate solver data components. This makes Avian's joints much more solver agnostic, and allows usage without XPBD!This involved some broader restructuring to do cleanly. Some of the big changes include:
SolverPluginsplugin group that adds the default solver's plugins.SubstepSolverSethave been extracted to a separateXpbdSolverSetenum.XpbdSolverPlugin.The joint traits and XPBD helpers were also changed a bit, the
Jointtrait was removed, andDominanceis now stored forSolverBodyInertiaand used for computing relative dominance for constraints.Polish and Documentation
I did a lot of work on polishing up the joint APIs and documentation some more. Notably:
entity1andentity2tobody1andbody2free_axistoslider_axisforPrismaticJoint(more accurate and matches some other engines)aligned_axistohinge_axisforRevoluteJoint(maybe clearer and matches some other engines)constwhere possibleMigration Guide
Joints
dynamics::jointsinstead ofdynamics::solver::jointsJointtrait has been removed in favor of theEntityConstrainttrait and helper methods on the joint types themselvesentity1andentity2tobody1andbody2free_axistoslider_axisforPrismaticJointaligned_axistohinge_axisforRevoluteJointwith_local_anchor_1,with_local_anchor_2,local_anchor_1, andlocal_anchor_2towith_local_anchor1,with_local_anchor2,local_anchor1, andlocal_anchor2local_anchor1andlocal_anchor2methods now return anOptionFixedJoint,PrismaticJoint,RevoluteJoint, andSphericalJointnow store a fullJointFrame(anchor + basis) for each body instead of just local anchorsswing_axisfromSphericalJoint; just set thetwist_axis, and the swing limit cone will be oriented accordinglyJointDampingcomponentJointForcescomponentSolver Reorganization and XPBD
dynamics::solver::xpbd, gated behind thexpbd_jointsfeatureSubstepSolverSethave been extracted to a separateXpbdSolverSetenumXpbdSolverPluginSupstepSolverSethas a newDampingsystem set for constraint velocity dampingCustom XPBD Constraints
XpbdConstraintnow has aSolverDataassociated type for a solver data component implementing theXpbdConstraintSolverDatatrait. This is taken byprepareandsolve.apply_positional_lagrange_updatehas been removed. Useapply_positional_impulseinstead.custom_constraintexample for a functional demonstration of implementing a custom constraint.