-
-
Notifications
You must be signed in to change notification settings - Fork 189
Joint entities rotations #511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| } | ||
|
|
||
| #[cfg(feature = "3d")] | ||
| impl std::ops::MulAssign for Rotation { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this impl is not needed for this PR, but it probably doesn't hurt to have it
7320a8e to
5386327
Compare
# 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: - Full local frames are not supported (anchor + basis, see #254) - Accessing joint forces in a generic way is non-trivial, making joint breakage (#199) trickier to implement - Joint components store implementation details like Lagrange multipliers and pre-step data - Joints are strictly tied to XPBD, which has patenting concerns (#440), and makes it difficult to implement custom joint solvers - Debug rendering is the same for each joint type, with no support for rendering limits or other relevant data - Documentation is somewhat poor, with no images to visualize the different joint types - A lot of the APIs are not very polished 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`, and `SphericalJoint` now support a full `JointFrame` that contains a `JointAnchor` and `JointBasis`. They are enums with `Local` and `FromGlobal` variants, allowing the initialization of local frames using world-space values. ```rust // Use a world-space anchor of (5, 2), and rotate the second body's local frame by 45 degrees. commands.spawn(( RevoluteJoint::new(body1, body2) .with_anchor(Vec2::new(5.0, 2.0)) .with_local_basis2(Rot2::degrees(45.0)) )); ``` While the basis is now configurable, it is still also possible to configure axes like the "hinge axis" for a 3D `RevoluteJoint` or the "twist axis" for a `SphericalJoint`. 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 `JointDamping` component with `linear` and `angular` properties. ```rust commands.spawn(( DistanceJoint::new(body1, body2), JointDamping { linear: 0.1, // Linear damping angular: 0.1, // Angular damping }, )); ``` ### 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 `JointForces` component 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. ```rust commands.spawn(( RevoluteJoint::new(body1, body2), JointForces::new(), )); ``` An example of where this may be useful is breaking joints when their forces or torques exceed some threshold: ```rust fn break_joints( mut commands: Commands, query: Query<(Entity, &JointForces), Without<JointDisabled>>, ) { for (entity, joint_forces) in &query { if joint_forces.force().length() > BREAK_THRESHOLD { // Break the joint by adding the `JointDisabled` component. // Alternatively, you could simply remove the joint component or despawn the entity. commands.entity(entity).insert(JointDisabled); } } } ``` ### Joint Debug Rendering Joints now use a `ConstraintDebugRendering` trait 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 the `xpbd_joints` feature. The actual joint API has been extracted out into `dynamics::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: - There is a new `SolverPlugins` plugin group that adds the default solver's plugins. - XPBD system sets from `SubstepSolverSet` have been extracted to a separate `XpbdSolverSet` enum. - XPBD systems are now initialized by an `XpbdSolverPlugin`. The joint traits and XPBD helpers were also changed a bit, the `Joint` trait was removed, and `Dominance` is now stored for `SolverBodyInertia` and 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: - Renamed `entity1` and `entity2` to `body1` and `body2` - Renamed `free_axis` to `slider_axis` for `PrismaticJoint` (more accurate and matches some other engines) - Renamed `aligned_axis` to `hinge_axis` for `RevoluteJoint` (maybe clearer and matches some other engines) - Made more methods `const` where possible - Vastly improved joint documentation, added more code examples, and added SVGs for illustration - Miscellaneous other improvements --- ## Migration Guide ### Joints - Joint APIs are now in `dynamics::joints` instead of `dynamics::solver::joints` - The `Joint` trait has been removed in favor of the `EntityConstraint` trait and helper methods on the joint types themselves - Renamed `entity1` and `entity2` to `body1` and `body2` - Renamed `free_axis` to `slider_axis` for `PrismaticJoint` - Renamed `aligned_axis` to `hinge_axis` for `RevoluteJoint` - Renamed `with_local_anchor_1`, `with_local_anchor_2`, `local_anchor_1`, and `local_anchor_2` to `with_local_anchor1`, `with_local_anchor2`, `local_anchor1`, and `local_anchor2` - The `local_anchor1` and `local_anchor2` methods now return an `Option` - The `FixedJoint`, `PrismaticJoint`, `RevoluteJoint`, and `SphericalJoint` now store a full `JointFrame` (anchor + basis) for each body instead of just local anchors - Removed `swing_axis` from `SphericalJoint`; just set the `twist_axis`, and the swing limit cone will be oriented accordingly - Removed damping properties and methods from joint types in favor of the `JointDamping` component - Removed force properties and methods from joint types in favor of the `JointForces` component ### Solver Reorganization and XPBD - XPBD logic is now contained within `dynamics::solver::xpbd`, gated behind the `xpbd_joints` feature - XPBD system sets from `SubstepSolverSet` have been extracted to a separate `XpbdSolverSet` enum - XPBD systems are now initialized by an `XpbdSolverPlugin` - `SupstepSolverSet` has a new `Damping` system set for constraint velocity damping ### Custom XPBD Constraints - `XpbdConstraint` now has a `SolverData` associated type for a solver data component implementing the `XpbdConstraintSolverData` trait. This is taken by `prepare` and `solve`. - `apply_positional_lagrange_update` has been removed. Use `apply_positional_impulse` instead. - Most methods that previously returned forces or torques now return Lagrange multiplier updates. - See the `custom_constraint` example for a functional demonstration of implementing a custom constraint.
|
Hi, sorry for leaving this sitting for so long 😅 I wanted to implement this more consistently for all joints using local frames (anchor + basis), which are commonly used for this sort of thing in most other engines such as Rapier, Box2D (main branch), Bullet, PhysX... I've implemented that as part of #803 for all joints (except the |
Objective
RevoluteJoint(in 3D only) and inSphericalJointSolution
local_rotation1andlocal_rotation2(analogous tolocal_anchors) that are being added to the rotations synced from the entities transforms