API changes
- Joint definitions share a common b2JointDef Base and use local frames instead of anchors, axes and reference angles.
- Mouse joint removed, the motor joint now has spring and velocity targets.
- Motion locks replace fixed rotation.
- Chain shapes are segment based.
- Contact ids (b2ContactId) in contact events, with b2Contact_GetData.
- Pre-solve callback redesigned, plus a separate continuous pre-solve callback.
- Native task system. Box2D runs its own threads when no task callbacks are hooked up.
New features
- Large worlds with optional double precision (BOX2D_DOUBLE_PRECISION).
- Recording and replay, plus world snapshots (experimental).
- Contact recycling for better stability and performance.
- Dynamic character mover with new mover and pogo joints (experimental).
- Joint events for breakable joints (force and torque thresholds).
- Tunable continuous collision per body (safety factor).
- Restitution iterations and propagation.
- Wind and drag on shapes.
- Distance joint spring force limits.
- Loose chain segments.
- Continuous sensor events. Fast shapes are swept against static sensors, and bullets against all non-bullet sensors.
- Runtime AVX2 dispatch with SSE2 fallback.
Improvements
- Many optimizations.
- Faster broad-phase and ray casts. Much less single threaded work.
- Faster island merging and splitting.
- Kinematic bodies are cheaper. Graph coloring treats them like static bodies.
- More stable prismatic joint under stress.
- More accurate rotational inertia for shapes far from the body origin.
- Invalid input is now rejected and logged in release builds.
- Various SIMD optimizations.
Infrastructure
- pkg-config support.
- Swift and Zig builds in CI.
- MinGW, clang-cl and Windows ARM64 builds.
- JSON descriptions of the public API in api/ for binding generators, one per precision.
- CMake presets.
- Revised samples UI and many new samples.
Breaking changes
These compile but behave differently:
- b2RelativeAngle(a, b) swapped its arguments and still returns b minus a, so existing calls silently change sign.
- The wheel joint axis is the x-axis of localFrameA. The old default axis was vertical, so vertical suspension now needs base.localFrameA.q = b2MakeRot(0.5f * B2_PI).
- Motor joint defaults are now all zero, so a default motor joint does nothing.
- b2JointType values changed, so stored joint type integers are invalid.
- Custom filtering is opt-in per shape. The custom filter callback only runs for pairs where one of the shapes sets b2ShapeDef::enableCustomFiltering.
- Contact recycling is on by default. A touching contact reuses its manifold and skips material updates and the pre-solve callback until the shapes move about 5cm or rotate about 11 degrees relative to each other. Disable it on the world with b2World_SetContactRecycleDistance(worldId, 0.0f) or per body with enableContactRecycling.
- b2Normalize and b2GetLengthAndNormalize used to return zero for a length below FLT_EPSILON. Now only a squared length at or below 1000 * FLT_MIN returns zero, so tiny vectors normalize.
- Debug draw always applies drawingBounds, so a zeroed b2DebugDraw draws nothing. Start from b2DefaultDebugDraw().
- Body names are truncated to B2_NAME_LENGTH characters, which is 10. It was 31.
- Contact point normal velocity is only computed if needed for hit events or restitution.
- b2CreateSegmentShape and b2CreateCapsuleShape return b2_nullShapeId for a degenerate segment or capsule.
- Fields were reordered in b2BodyMoveEvent, b2PrismaticJointDef and b2DebugDraw. A positional initializer can still compile and fill the wrong fields.
- A joint between two bodies that are not dynamic is not simulated.
- World shape casts ignore chain segments approached from the back side.
- Contact push speed no longer depends on the contact hertz and damping ratio.
- b2ManifoldPoint::totalNormalImpulse now includes the warm starting impulse.
Renamed and changed functions:
- World queries take a b2Pos origin as the second argument. Pass b2Pos_zero for the old behavior.
- Positions are b2Pos and body transforms are b2WorldTransform across the body, shape, event, query result and debug draw API. In single precision these are aliases of b2Vec2 and b2Transform, so existing code compiles unchanged.
- Collision functions are verb first: b2RayCast* to b2CastRay*, b2ShapeCast* to b2CastShape*, b2ShapeCast to b2CastShape, b2ShapeDistance to b2ComputeShapeDistance, b2SegmentDistance to b2ComputeSegmentDistance, b2TimeOfImpact to b2ComputeTimeOfImpact.
- b2Shape_RayCast is now b2Shape_CastRay(shapeId, origin, translation) and returns b2WorldCastOutput.
- Task callbacks: b2TaskCallbackFcn(void* taskContext) and b2EnqueueTaskCallbackFcn(task, taskContext, userContext). There is no item range and no worker index. B2_MAX_WORKERS is 32, it was 64.
- Allocator hooks: b2AllocCallbackFcn(size_t size, int alignment) and b2FreeCallbackFcn(void* mem, size_t size). b2GetByteCount and b2DynamicTree_GetByteCount return size_t.
- Callback types end in CallbackFcn, such as b2PreSolveCallbackFcn, b2CastResultCallbackFcn and b2FrictionCallbackFcn. b2SetAssertFcn is now b2SetAssertCallback.
- Body: b2Body_ApplyMassFromShapes to b2Body_UpdateMassFromShapes, b2Body_GetLocalCenterOfMass to b2Body_GetLocalCenter, b2Body_GetWorldCenterOfMass to b2Body_GetWorldCenter. b2Body_SetTargetTransform takes a wake flag. b2Body_EnableSleep and b2Body_IsSleepEnabled are now b2Body_EnableSleeping and b2Body_IsSleepingEnabled. b2Body_ComputeAABB is now b2Body_ComputeBounds.
- Shape: b2Shape_GetMassData to b2Shape_ComputeMassData, b2Shape_SetMaterial/GetMaterial to b2Shape_SetUserMaterial/GetUserMaterial with a uint64_t id, b2Shape_GetSensorOverlaps to b2Shape_GetSensorData. b2Shape_SetSurfaceMaterial takes a pointer. b2Shape_GetAABB is now b2Shape_GetBounds. b2SurfaceMaterial::userMaterialId is a uint64_t.
- Chain: open chains require ghost1 and ghost2, and every segment of an open chain collides. b2ChainDef::count is now pointCount, and materialCount must be 1 or the segment count. The chain friction, restitution and material functions are replaced by b2Chain_SetSurfaceMaterial and b2Chain_SetAllSurfaceMaterials.
- World: b2WorldDef::maxContactPushSpeed is now contactSpeed. b2World_EnableSpeculative is removed. b2World_SetMaximumLinearSpeed and b2World_GetMaximumLinearSpeed are now b2World_SetMaxLinearSpeed and b2World_GetMaxLinearSpeed. b2World_GetContactTuning is new.
- Contacts: b2ManifoldPoint::point and b2ContactBeginTouchEvent::manifold are removed. Use the contact id with b2Contact_GetData.
- Collision: the shape comes first in b2PointIn*, b2CastRay* and b2CastShape*. b2Collide* take one relative transform and return a b2LocalManifold. b2DistanceInput and b2ShapeCastPairInput take one relative transform. b2ComputeCapsuleAABB, b2ComputeCircleAABB, b2ComputePolygonAABB and b2ComputeSegmentAABB are now b2Compute*Bounds, matching b2Shape_GetBounds and b2Body_ComputeBounds.
- Dynamic tree: b2CreateDynamicTree, b2DestroyDynamicTree, b2CreateTreeProxy and b2DestroyTreeProxy replace the b2DynamicTree_ versions. b2DynamicTree is opaque, b2CreateDynamicTree returns a pointer, and b2TreeNode and b2TreeProxy are no longer public. b2DynamicTree_RayCast is now b2DynamicTree_CastRay, and b2DynamicTree_ShapeCast is now b2DynamicTree_CastBox, which sweeps an AABB. EnlargeProxy, Rebuild and ValidateNoEnlarged are removed. b2DynamicTree_Query is now b2DynamicTree_OverlapAABB and b2DynamicTree_GetAABB is now b2DynamicTree_GetProxyBounds.
- Distance joint: b2DistanceJoint_SetLength and GetLength are now SetRestLength and GetRestLength. SetLengthRange, GetMinLength and GetMaxLength are now SetLimits, GetLowerLimit and GetUpperLimit, matching the other joints.
- Current joint values say so: b2RevoluteJoint_GetAngle is now b2RevoluteJoint_GetCurrentAngle, and b2PrismaticJoint_GetTranslation and b2PrismaticJoint_GetSpeed are now b2PrismaticJoint_GetCurrentTranslation and b2PrismaticJoint_GetCurrentSpeed. b2RevoluteJoint_GetMotorTorque and b2PrismaticJoint_GetMotorForce are now b2RevoluteJoint_GetCurrentMotorTorque and b2PrismaticJoint_GetCurrentMotorForce.
- Distance, revolute, prismatic and wheel joints: SetMotorSpeed/GetMotorSpeed to SetTargetMotorSpeed/GetTargetMotorSpeed. b2WheelJoint_GetMotorTorque is now b2WheelJoint_GetCurrentMotorTorque and b2DistanceJoint_GetMotorForce is now b2DistanceJoint_GetCurrentMotorForce.
- Joint: b2Joint_SetLocalAnchorA/B, b2Joint_SetLocalAxisA, b2Joint_SetReferenceAngle and their getters are replaced by b2Joint_SetLocalFrameA/B and b2Joint_GetLocalFrameA/B.
- Motor joint: b2MotorJoint_SetMaxForce/GetMaxForce to SetMaxVelocityForce/GetMaxVelocityForce and SetMaxTorque/GetMaxTorque to SetMaxVelocityTorque/GetMaxVelocityTorque. The linear and angular offset and correction factor functions are removed.
- Character mover: b2PlaneSolverResult::translation is now delta.
- Math: b2IsValidRotation is now b2IsValidRot.
- Debug draw: callbacks take b2Pos and b2WorldTransform, DrawPolygonFcn takes a transform, and DrawSolidCircleFcn takes a local center. DrawSegmentFcn is now DrawLineFcn, drawContactImpulses is now drawContactForces and drawFrictionImpulses is now drawFrictionForces.
- Profiling: the b2Profile stage timers are renamed to match the solver stages. b2Counters::byteCount is a size_t and colorCounts has B2_GRAPH_COLOR_COUNT entries, which is 24.
- Headers: collision.h and types.h no longer include math_functions.h, so include it directly to use the math functions without box2d.h. The math structs are in the new math_types.h, constants such as B2_HUGE are public in constants.h, and compile options are in the new config.h. Define BOX2D_USER_CONFIG to include your own options header first.
- CMake: BOX2D_AVX2 is removed, AVX2 is selected at runtime.