Avian v0.4.0
Avian Physics 0.4 has been released! 🪶
Highlights
Avian 0.4 is the biggest release yet, with several new features, quality-of-life improvements, and important bug fixes. Highlights include:
- Massive performance improvements: Avian is 3x as fast as before, with much better scaling for multi-core hardware.
- Solver bodies: The solver stores bodies in a much more efficient format optimized for cache locality and future wide SIMD support.
- Graph coloring: The constraint solver is now multi-threaded with graph coloring.
- Simulation islands: Islands are used for a much improved sleeping and waking system, reducing overhead for large game worlds with many resting bodies.
- Force overhaul: The force and impulse APIs have been redesigned from the ground up, providing a much more capable and intuitive interface.
- Joint Improvements: Joints now support full reference frames (anchor + basis), and have new
JointDampingandJointForcescomponents. - Voxel colliders: Avian now supports voxel colliders for efficient representation of Minecraft-like worlds and other volumetric data.
- Bevy 0.17 support: Avian has been updated to the latest version of Bevy, and has changed its collision event types and system set naming to match.
- Contact API improvements: Contact data now provides access to world-space points, normal speeds, and more accurate contact impulses.
- Benchmarking CLI: Avian has a new CLI tool for benchmarking various scenes and profiling multi-threaded scaling.
Check out the announcement blog post for a more in-depth overview of what has changed and why. A more complete changelog can also be found after the migration guide below.
Migration Guide
Bevy 0.17 #815
Avian 0.4 migrates Avian to Bevy 0.17. This involves some breaking changes.
Collision Events
The CollisionStarted, CollisionEnded, OnCollisionStart, and OnCollisionEnd events have been replaced by the CollisionStart and CollisionEnd events. They are both written as a Message and triggered as an Event, and store the collider and body entity of both colliders involved in the event.
A collision event observer before:
fn on_player_stepped_on_plate(event: Trigger<OnCollisionStart>, player_query: Query<&Player>) {
let pressure_plate = event.target();
let other_entity = event.collider;
if player_query.contains(other_entity) {
println!("Player {other_entity} stepped on pressure plate {pressure_plate}");
}
}and after:
fn on_player_stepped_on_plate(event: On<CollisionStart>, player_query: Query<&Player>) {
let pressure_plate = event.collider1;
let other_entity = event.collider2;
if player_query.contains(other_entity) {
println!("Player {other_entity} stepped on pressure plate {pressure_plate}");
}
}A collision event reader system before:
fn print_started_collisions(mut collision_reader: EventReader<CollisionStarted>) {
for CollisionStarted(collider1, collider2) in collision_reader.read() {
println!("{collider1} and {collider2} started colliding");
}
}and after:
fn print_started_collisions(mut collision_reader: MessageReader<CollisionStart>) {
// Note: The event now also stores `body1` and `body2`
for event in collision_reader.read() {
println!("{} and {} started colliding", event.collider1, event.collider2);
}
}System Sets
System sets have been renamed to follow the new FooSystems naming convention.
PhysicsSet→PhysicsSystemsPhysicsStepSet→PhysicsStepSystemsSubstepSet→SubstepSystemsSubstepSolverSet→SubstepSolverSystemsSolverSet→SolverSystemsIntegrationSet→IntegrationSystemsBroadPhaseSet→BroadPhaseSystemsNarrowPhaseSet→NarrowPhaseSystemsSweptCcdSet→SweptCcdSystemsPhysicsTransformSet→PhysicsTransformSystems
ColliderConstructorHierarchy
Bevy 0.17 changed the way Name works for glTF mesh primitives (migration guide). Instead of MeshName.PrimitiveIndex, it is now in the form of MeshName.MaterialName. This also means that APIs such as ColliderConstructorHierarchy::with_constructor_for_name now use this format.
ColliderConstructorHierarchy::new(ColliderConstructor::ConvexDecompositionFromMesh)
.with_density_for_name("armL_mesh.ferris_material", 3.0)
.with_density_for_name("armR_mesh.ferris_material", 3.0)Force Overhaul #770
Avian 0.4 overhauls the force APIs, and removes the ExternalForce, ExternalTorque, ExternalImpulse, and ExternalAngularImpulse components.
- For persistent forces and torques, use the new
ConstantForceandConstantTorquecomponents. - For non-persistent forces that get cleared automatically, use the new
ForceshelperQueryData. - Impulses can no longer be persistent. Use persistent forces instead.
The new ForcePlugin must be enabled for forces to function properly. It is included in PhysicsPlugins by default.
Joints #517, #735, #799, #803
Avian 0.4 contains a lot of changes and improvements for joints.
Core APIs
- Joint APIs are now in
dynamics::jointsinstead ofdynamics::solver::joints - The
Jointtrait has been removed in favor of theEntityConstrainttrait and helper methods on the joint types themselves - Renamed
entity1andentity2tobody1andbody2 - Renamed
free_axistoslider_axisforPrismaticJoint - Renamed
aligned_axistohinge_axisforRevoluteJoint - Renamed
with_local_anchor_1,with_local_anchor_2,local_anchor_1, andlocal_anchor_2towith_local_anchor1,with_local_anchor2,local_anchor1, andlocal_anchor2 - The
local_anchor1andlocal_anchor2methods now return anOption - The
FixedJoint,PrismaticJoint,RevoluteJoint, andSphericalJointnow store a fullJointFrame(anchor + basis) for each body instead of just local anchors - Removed
swing_axisfromSphericalJoint; just set thetwist_axis, and the swing limit cone will be oriented accordingly - Removed damping properties and methods from joint types in favor of the
JointDampingcomponent - Removed force properties and methods from joint types in favor of the
JointForcescomponent - Each part of a joint has its own compliance and helpers for it, such as
with_point_complianceandwith_swing_compliance.
Only One Joint Per Entity
Entities can now only hold up to one type of joint component. If a joint is added on an entity that already has a joint, the old joint is automatically removed, and a warning is emitted if debug_assertions are enabled. A rigid body can still be attached to other bodies by multiple joints, but each joint must be on its own entity.
ContactEdge now also stores the rigid body entities.
Remove DistanceJoint::rest_length
The rest_length property of DistanceJoint has been removed in favor of just using length_limits.
- Replace
DistanceJoint::with_rest_length(1.0)withDistanceJoint::with_limits(1.0, 1.0). - Replace
joint.rest_length = 1.0withjoint.length_limits = 1.0.into().
Solver Reorganization and XPBD
- XPBD logic is now contained within
dynamics::solver::xpbd, gated behind thexpbd_jointsfeature - XPBD system sets from
SubstepSolverSethave been extracted to a separateXpbdSolverSystemsenum - XPBD systems are now initialized by an
XpbdSolverPlugin
Custom XPBD Constraints
- The
XpbdConstrainttrait now has apreparestep to prepare base translational and rotational offsets and any other pre-step data. XpbdConstraintnow has aSolverDataassociated type for a solver data component implementing theXpbdConstraintSolverDatatrait. This is taken byprepareandsolve.XpbdConstraint::solveand many related methods now useSolverBodyandSolverBodyInertiastructs. Positional corrections are applied to thedelta_positionanddelta_rotationproperties. Updated positional information can be computed based on the pre-step data and these deltas.apply_positional_lagrange_updatehas been removed. Useapply_positional_impulseinstead.xpbd::solve_constraintis nowxpbd::solve_xpbd_joint, and only supports two entities (make your own system if you need more).- Most methods that previously returned forces or torques now return Lagrange multiplier updates.
- See the
custom_constraintexample for a functional demonstration of implementing a custom constraint.
Contact APIs #771, #788
Contact Impulses
ContactPoint::normal_impulse previously corresponded to the clamped accumulated normal impulse from the last substep, used for warm starting the contact solver. It did not represent the total impulse applied across substeps and restitution, despite the ambiguous name.
Now, ContactPoint::normal_impulse works like you would expect, and represents the total normal impulse applied at a contact point. Divide by the time step to get the corresponding force.
The old warm starting impulses are now stored as warm_start_normal_impulse and warm_start_tangent_impulse (previously tangent_impulse).
The method APIs such as total_normal_impulse, total_normal_impulse_magnitude, and max_normal_impulse have been updated accordingly to use the new normal_impulse. Additionally, ContactPair::max_normal_impulse has been split into ContactPair::max_normal_impulse (returns a vector) and ContactPair::max_normal_impulse_magnitude (returns a scalar).
The normal_force and tangent_force methods of ContactPoint have been removed.
ContactConstraintPoint::max_normal_impulse is now stored in the ContactNormalPart as total_impulse.
Contact Points
The local_point1 and local_point2 properties of ContactPoint have been removed in favor of a world-space anchor1 and anchor2 relative to the center of mass.
The global_point1 and global_point2 methods have also been removed, but a new world-space point is available for the midpoint between the closest points.
Contact Graph
- The
ContactGraphnow storesContactEdges in the graph, containing only edge connectivity information with stableContactIds. - The
ContactGraphnow storesContactPairs in separateactive_pairsandsleeping_pairslists. iteranditer_muthave been renamed toiter_activeanditer_active_mut/iter_sleepinganditer_sleeping_mut.iter_touchinganditer_touching_muthave been renamed toiter_touching_activeanditer_touching_active_mut/iter_touching_sleepinganditer_touching_sleeping_mut.collisions_withhas been renamed tocontact_pairs_with.collisions_with_muthas been removed for now.add_pairhas been replaced byadd_edge, andadd_pair_with_keyhas been replaced byadd_edge_and_key_with.insert_pairandinsert_pair_withhave been removed. Add or update pairs manually instead.remove_pairhas been renamed toremove_edge.
Contact Pairs
ContactPairnow stores theContactIdassociated with theContactEdgein theContactGraph. It must be provided toContactPair::new. It is initialized automatically by calls to methods likeContactGraph::add_edge.ContactPairFlagsno longer stores whetherCONTACT_EVENTSare enabled. It is instead stored onContactEdgeFlags.ContactManifoldno longer stores theindexof the manifold.
Sleeping and Simulation Islands #809
Stacks of bodies as well as bodies connected by joints now form "simulation islands" that are allowed to enter a low-cost sleeping state when all bodies in a given island are resting. This can significantly reduce CPU overhead for large game worlds with lots of dynamic bodies. Previously, bodies were only allowed to sleep when they were not interacting with other dynamic bodies.
The following components have been renamed:
TimeSleeping->SleepTimerSleepingThreshold->SleepThresholdDeactivationTime->TimeToSleep(also a component and not a resource now)
Additionally, the SleepingPlugin has been replaced by the IslandSleepingPlugin.
Solver Bodies #735
Avian's solver now uses special SolverBody and SolverBodyInertia components instead of separate components like Position, Rotation, AccumulatedTranslation, LinearVelocity, and AngularVelocity. They are initialized for awake dynamic and kinematic bodies in the substepping loop, and the results are written back to the user-facing components at the end. This also means that if you are running custom logic inside the substepping loop, you should generally use SolverBody instead of the separate components.
Solver bodies were added to drastically improve the performance of the solver, but it also means significant changes to internals.
Removed Components and Properties
The following components have been removed and are no longer stored for rigid bodies:
AccumulatedTranslationPreSolveAccumulatedTranslation(now calledPreSolveDeltaPosition)PreSolveLinearVelocityPreSolveAngularVelocityPreSolveRotationPreviousRotation
A new PreSolveDeltaRotation component has also been added.
The current_position helper on RigidBodyQueryItem and ColliderQueryItem has been removed.
The local_anchor1 and local_anchor2 properties on ContactConstraintPoint have been removed.
Other
SolverSet::ApplyTranslationhas been renamed toSolverSystems::Finalize.
Physics Transforms and PreparePlugin #760, #785
Position,Rotation, andColliderAabbare now initialized with correct world-space values right after spawn.- The
SyncPluginhas been renamed toPhysicsTransformPlugin, andSyncSethas been renamed toPhysicsTransformSystems. - The
PreparePluginandPrepareSethave been removed. UsePhysicsSet::Preparefor preparation logic, and run it afterPhysicsTransformSystems::TransformToPositionif it must run after transforms have been applied to physics positions. - Logic inside the
PhysicsSchedulecan no longer useTransform. UsePositionandRotationinstead. PhysicsSet::Synchas been renamed toPhysicsSet::Writeback.SyncConfighas been renamed toPhysicsTransformConfig.
Symmetric 3x3 Matrix for Angular Inertia #777
In 3D, AngularInertia and ComputedAngularInertia APIs now use a SymmetricMat3 from glam_natrix_extras instead of the old Mat3 for the angular inertia tensor. AngularInertia has new constructors like try_from_mat3 and from_mat3_unchecked to use Mat3 directly.
The GlobalAngularInertia component has been removed. You can compute the rotated angular inertia tensor using ComputedAngularInertia::rotated.
Debug Rendering #829
The PhysicsDebugPlugin now always runs in PostUpdate. Replace calls to PhysicsDebugPlugin::new(SomeSchedule) with PhysicsDebugPlugin or PhysicsDebugPlugin::default().
Additionally, debug rendering now uses GlobalTransform instead of Position and Rotation. This allows debug rendering to use interpolated transforms if TransformInterpolation is used.
Fallible Trimesh Colliders #761
Collider::trimesh and Collider::trimesh_with_config can now panic for degenerate input. Use Collider::try_trimesh and Collider::try_trimesh_with_config to instead get a Result and handle error cases manually.
Remove ColliderQuery #775
The ColliderQuery QueryData type no longer exists. Query for the components you need manually, or define your own QueryData type.
What's Changed
- Add convex_polyline for creating Collider by @kirilllysenko in #727
- Export TransformEasingSet by @janhohenheim in #728
- Improve CI performance, cache size and reactivate Linux tests by @janhohenheim in #729
- Fix CI, speed it up some more, and add Lockfile by @janhohenheim in #730
- Improve CI caching by @janhohenheim in #732
- Make CI faster by @janhohenheim in #736
- Solver Bodies by @Jondolf in #735
- Optimize gyroscopic motion by @Jondolf in #738
- Cache colliders created from meshes via constructors by @janhohenheim in #743
- Fix gyroscopic torque by @Jondolf in #745
- Fix order of quaternion multiplication by @Jondolf in #747
- Fix
bodyproperty of collision events and reflectCollisionEventsEnabledproperly by @Jondolf in #752 - Benchmarking CLI by @Jondolf in #753
- Fix new
mismatched_lifetime_syntaxeslints by @Jondolf in #759 - ColliderConstructor::Compound by @Piefayth in #758
- Add support for
Voxelsshape by @Jondolf in #761 - Rework
PositionandRotationinitialization by @Jondolf in #760 - Fix
PreviousGlobalTransforminitialization by @Jondolf in #764 - Account for COM in constrants center difference by @MiniaczQ in #765
- Fix
DistanceLimit::compute_correction()signs by @MiniaczQ in #766 - docs: fix typo by @Jarne-Vercruysse in #767
- Fix AABB interval initialization in broad phase by @Jondolf in #772
- Fix transform bugs by @Jondolf in #773
- Upgrade to Rust 2024 edition by @Jondolf in #774
- Parallel Solver With Graph Coloring by @Jondolf in #771
- Only query for what is needed in the
NarrowPhaseby @Jondolf in #775 - Use
SymmetricMat3fromglam_matrix_extensionsfor angular inertia by @Jondolf in #777 - Force Rework by @Jondolf in #770
- Add tests for
Forcesby @Jondolf in #779 - Fix double bracket in docs by @Jondolf in #780
- Fix skipping kinematic bodies for velocity integration by @Jondolf in #781
- Fix
Disabledfor colliders by @Jondolf in #782 - Allow
Forcesto wake or not wake up bodies by @Jondolf in #783 - Implement
DerefandIntoIteratorforRigidBodyCollidersby @Jondolf in #784 - Rework
SyncPlugin, removePreparePlugin, and improve system scheduling by @Jondolf in #785 - Fix global center of mass for
Forcesand addPhysicsTransformHelperby @Jondolf in #787 - Fix UB and improve parallel iteration in solver by @Jondolf in #790
- Expose total normal impulses, improve contact impulse APIs by @Jondolf in #788
- Fix colliders not working after removing
Disabled/ColliderDisabledby @Jondolf in #791 - Expose anchors, world-space
point, andnormal_speedfor contacts by @Jondolf in #792 - Improve inverse angular inertia tensor computation by @Jondolf in #793
- Fix 3D friction tangents and refactor constraint preparation by @Jondolf in #794
- Fix
Plane3dcolliders ignoring half size by @janhohenheim in #795 - Add
tumblerexample by @Jondolf in #796 - Contact pruning for 3D by @Jondolf in #798
- Add
JointGraphandJointCollisionDisabledby @Jondolf in #799 - Fix errors and warnings with minimal feature configurations by @Jondolf in #802
- Enable
smallvec/serdewhenserializeis enabled by @Jondolf in #804 - Fix CI nightly error by @Jondolf in #806
- Remove
rest_lengthfield fromDistanceJoint. by @shanecelis in #517 - Remove
countfromRayHitsandShapeHitsby @Jondolf in #808 - Allocate free IDs lowest-first using an
IdPoolforStableUnGraphby @Jondolf in #807 - Joint Rework - API and Reorganization by @Jondolf in #803
- Fix minimal feature configurations by @Jondolf in #811
- Check minimal features in CI by @Jondolf in #812
- fix doc example for
shape_hitsby @ericmccarthy7 in #814 - make damping framerate independent in kinematic character example by @hymm in #818
- Persistent Simulation Islands by @Jondolf in #809
- Improve tunneling prevention by solving static contacts last by @Jondolf in #824
- Make
SleepTimeroptional forForcesby @Jondolf in #825 - Gate island validation behind
validatefeature by @Jondolf in #826 - Bump crate versions to 0.4.0-dev by @Jondolf in #827
- Fix
glam_matrix_extensionsandbevy_heavycommit refs by @Jondolf in #832 - Allow adding/removing
Sleepingto sleep/wake bodies by @Jondolf in #834 - Update to Bevy 0.17 by @Jondolf in #815
- add ApplyPosToTransform component to include an entity in sync by @cBournhonesque in #837
- Show debug gizmos for interpolated transforms by @janhohenheim in #829
- Fix infinite loop in island debug rendering by @Jondolf in #836
- Implement collider constructor ready events by @janhohenheim in #830
- Update to Bevy 0.17 from RC by @Jondolf in #838
- Fix feature gate by @janhohenheim in #840
- Fix damping for joints with a static body by @Jondolf in #842
- Add PartialEq bound on Sleeping by @cBournhonesque in #843
- Expose Position/Rotation from Forces by @cBournhonesque in #845
- Add
apply_linear_acceleration_at_pointby @Jondolf in #846 - Add
RigidBodyForces::velocity_at_pointby @Jondolf in #847 - Disable mass property warning for collider constructors by @Jondolf in #849
- Bump bevy_mod_debugdump version by @kristoff3r in #848
- Update scale when the collider changes, not just when the transform changes. by @pcwalton in #851
- Add Default to ApplyPosToTransform by @cBournhonesque in #854
- Don't update the diagnostics UI if it isn't visible. by @pcwalton in #856
- Change
inserttotry_insertfor removal of disabled entities by @Jondolf in #858 - Add public
IslandNode::island_idgetter by @Jondolf in #859 - Add
voxels_3dexample by @Jondolf in #857 - fix slab version by @hocop in #861
- New pub methods to facilitate external query implementations by @unpairedbracket in #862
New Contributors
- @kirilllysenko made their first contribution in #727
- @Piefayth made their first contribution in #758
- @MiniaczQ made their first contribution in #765
- @Jarne-Vercruysse made their first contribution in #767
- @ericmccarthy7 made their first contribution in #814
- @hymm made their first contribution in #818
- @pcwalton made their first contribution in #851
Full Changelog: v0.3.1...v0.4.0