A body has a pose and a velocity; its shapes give it collision and mass. Bodies are static (never move), kinematic (move at the velocity they are given, pushed by nothing) or dynamic (moved by forces, contacts and joints). Positions are 64-bit (m2Pos2) and everything else is 32-bit, so a game three hundred kilometers from the origin keeps exact contacts, casts and rendering. Lengths in a body's own frame are bounded instead: every shape coordinate and radius, chain point and joint anchor lies within M2_LOCAL_LIMIT (100 km), and a larger one is refused with m2_errorLimit, because the solver's 32-bit products would overflow past it.
Impulses change the velocity at once (m2Body_ApplyLinearImpulse, m2Body_ApplyLinearImpulseAtPoint, m2Body_ApplyAngularImpulse). Forces (m2Body_ApplyForce, m2Body_ApplyForceAtPoint, m2Body_ApplyTorque) add up across calls, act during the next step and clear when it ends. Forces and impulses apply to dynamic bodies only, and velocities to kinematic and dynamic ones: a static body never moves, so a call that would move it changes nothing and counts one misuse, and a static body def with a velocity is refused.
Linear and angular damping decay velocity as v / (1 + h d) per substep. Gravity scales per body. m2Body_SetMotionLocks holds a body still along world x, world y or in rotation, whatever pushes it; the rotation lock also makes the inertia infinite.
m2Body_SetTransform teleports a body: it snaps, keeps its velocity, and wakes whatever it touched. A teleport is not a sweep, so nothing stops it from passing through a wall. To move a kinematic body smoothly, m2Body_SetTargetTransform sets the velocities that reach a pose in one step; a pose too far from the body to reach within M2_SPEED_LIMIT in that step returns m2_errorLimit and changes nothing. m2Body_SetType changes a body's type in place, ending the contacts that no longer apply.
The frame helpers (m2Body_GetWorldPoint, m2Body_GetLocalPoint, the vector and point-velocity variants) do the pose math in f64.
Bodies that come to rest sleep in islands: everything connected by contacts or joints sleeps and wakes together. A body rests below 0.05 m/s and 0.12 rad/s; sleepThreshold in its def, or m2Body_SetSleepThreshold, sets another resting speed, and the spin limit scales with it. When every dynamic body sleeps and nothing kinematic moves, a step costs almost nothing and changes nothing. Everything that should wake a body does: impulses, forces, teleports, type, filter and mass changes, a gravity change, a destroyed neighbor, a broken joint, water, a conveyor that changes speed.
m2Body_EnableSleep(world, body, false) keeps one body awake for good, and m2World_EnableSleep(world, false) wakes everyone and keeps them awake.
A body with a higher dominance (m2Body_SetDominance) cannot be pushed by a lower one: in a contact, the higher side acts as if it were static, and static bodies outrank everything. Enemies stop shoving the player, and the boss stands firm in an avalanche of crates. Dominance changes contacts only; joints stay symmetric.
m2Body_Enable(world, body, false) takes a body out of the simulation without destroying it: its contacts end, whatever rested on it wakes, queries stop seeing it, and its joints wait. m2Body_Enable(world, body, true) puts it back where it stood.
A dynamic body's mass comes from its shapes' densities. m2Body_SetMassData overrides it until a shape change, a fixed-rotation change or m2Body_ApplyMassFromShapes computes it from the shapes again. The override takes a mass from M2_MIN_MASS to M2_MASS_LIMIT (1e-9 to 1e12 kg), a center within M2_LOCAL_LIMIT, and centralInertia, the rotational inertia about that center, which is either 0, which makes a body that cannot turn, or from M2_MIN_INERTIA to M2_INERTIA_LIMIT (1e-12 to 1e24 kg m^2). The inertia is about the center, as most formulas give it (a disc's is m r^2 / 2), so a small one beside a far center is kept as given. Anything else is refused and counts one misuse, a mass of FLT_MAX and a central inertia of FLT_MAX among them. Mass summed from shapes is clamped into the same ranges, and m2Body_GetMassData reads back what the body kept (Limits).
Circles, capsules, rounded polygons (convex, up to 8 vertices) and segments attach to a body in its own frame. A segment has no area, and neither does a chain's segment, so two of them never collide, as in Box2D: two lines have nothing inside to push each other out of. A body that must land on a segment or a chain carries a circle, a capsule or a polygon; a sensor still detects a segment. A shape's friction pairs by geometric mean and its restitution by maximum. m2Shape_SetTangentSpeed makes a conveyor: the surface slides along the contact, and friction drags riders toward that speed (positive drives a rider toward +x on an upward-facing floor; a pair adds both speeds).
Shapes can change at run time: materials, filters, density, and the geometry itself (m2Shape_SetCircle and its siblings, a change of type included). The mass recomputes and touching partners wake.
Two shapes collide when each one's category bits intersect the other's mask bits. A nonzero group index shared by both overrides that: positive always collides, negative never does. Two bodies joined by a joint do not collide unless the joint's enableConnectedCollision is set; the filter joint does nothing else.
A sensor detects overlaps and never pushes back, and never affects sleep. Its overlaps arrive as sensor events, and m2Shape_GetSensorOverlaps lists who is inside now.
A chain is terrain bodies cannot snag on: a run of one-sided segments whose seams are smoothed by ghost points. Wind it so the solid side is on the right walking from one point to the next: for a floor, list the points right to left. An open chain uses its first and last points as ghosts and needs at least four. m2CreateChain returns one id for the whole run: m2DestroyChain removes every segment at once, and m2Chain_SetFriction and m2Chain_SetRestitution retune them all. m2Chain_GetFriction and m2Chain_GetRestitution read the chain's own material, as its def or those setters last set it; retuning one link with m2Shape_SetFriction leaves it as it is. A shape pool that runs out partway leaves a shorter chain, which keeps its id, and the create returns m2_errorCapacity; m2Chain_GetSegmentCount tells how many segments it has. A chain is the way to build one-way platforms.
m2ComputeHull turns a loose point cloud into a convex polygon, welding near points and refusing degenerate input. It merges a point into an edge only within 0.01 m of it, so a finely traced curve that needs more than 8 vertices to stay that close is refused with m2_errorLimit. m2DecomposeOutline goes further: a simple counter-clockwise outline of up to M2_MAX_OUTLINE_POINTS (64) points, a sprite's silhouette, becomes convex pieces of at most 8 vertices that keep its area; a longer outline is refused with m2_errorLimit. A clockwise outline, one that is not simple (it crosses itself, a vertex comes within 0.005 m of another edge, a vertex repeats, or an edge folds back), or one so thin that no piece passes validation is refused with m2_errorInvalid.
m2Body_Shatter breaks a dynamic body into one new body per piece. Each piece carries the parent's velocity at its own center of mass and the material of the parent's first shape, and the whole break is one journaled operation, so it replays and rolls back like anything else. When the pools cannot seat every piece, it returns m2_errorCapacity and nothing changes.
m2Vec2 outline[6] = {{-2.0f, 0.0f}, {2.0f, 0.0f}, {2.0f, 1.0f},
{0.5f, 1.0f}, {0.5f, 2.0f}, {-2.0f, 2.0f}};
m2Polygon pieces[8];
int32_t pieceCount = 0;
bool whole =
m2DecomposeOutline(outline, 6, pieces, 8, &pieceCount) == m2_success && pieceCount <= 8;
for (int32_t i = 0; whole && i < pieceCount; ++i)
{
m2ShapeId piece = m2_nullShapeId;
whole = m2CreatePolygonShape(world, wall, &shapeDef, &pieces[i], &piece) == m2_success;
}
// Later, when the wall breaks:
m2BodyId fragments[8];
int32_t fragmentCount = 0;
m2Result broke = m2Body_Shatter(world, wall, pieces, pieceCount, fragments, 8, &fragmentCount);
m2World_Explode is one blast: every dynamic shape within reach gets an outward impulse at its closest point, at full strength inside the radius and fading to zero across the falloff band, filtered by category. A shape with the blast inside it, its rounding included, is pushed in the direction from the blast to its body's center of mass; a blast inside it at the center of mass itself pushes nothing. It is one journal operation.