Repository navigation
v0.1.0
0.1.0 (2026-10-25)
Changed
-
Breaking: the family API shape.
m2Worldis an opaque pointer,
andm2CreateWorld(def, &worldOut)returns anm2Result. Every call
on a world's objects takes the world first, and readers take
const m2World*. Ids are{index1, generation}, with no world field.
Creates, destroys, snapshots, restores, the journal calls and the
polygon builders return anm2Resultand write throughOut
parameters. The world table,m2WorldId,m2World_IsValid, the
*_GetWorldgetters,m2SetAllocator,m2SetAssertHandler,
m2LastResult,m2_errorConfigandm2WorldDef.workerCountare
removed. The allocator is part ofm2WorldDef, which begins with its
cookie and an ABI version thatm2CreateWorldchecks
(m2_errorVersion). Worlds share nothing, so separate worlds may be
created, stepped and destroyed on separate threads.
(#12) -
Breaking: misuse counting.
m2Counters.misusecounts the calling
code's bugs: an invalid argument, a stale id, a writer called from
host code. A stale id counts one misuse in every call, while a stale
particle id counts nothing, since particles die on their own. Corrupt
snapshot or tape bytes count nothing. Host code a replay runs (the
task hooks and the allocator) counts like any other caller.
Motor and limit calls on a joint without a motor or limit change
nothing and count one misuse; for a distance joint, use
m2DistanceJoint_SetLengthRange.
(#13,
#14,
#15) -
Breaking: ranges. Defs, setters and tapes refuse values past the
ranges inbase.h, each listed indocs/user/limits.md: speeds up to
1e5 m/s, masses from 1e-9 to 1e12 kg, time steps from 1e-6 to 10 s,
M2_JOINT_REACHof 1e9 m, and the rest. These are the other refusals:- Body-local lengths beyond
M2_HUGE(1e5 m) in shapes, chains,
outlines and joint defs. - Substeps: more than
M2_MAX_SUBSTEPS(64) inm2World_Step. - Body defs with a non-finite value, an unknown type, a negative
damping or a zero rotation, and world defs with a non-finite
gravity or particle setting. - Velocities on a static body.
- Negative dampings, which used to be stored as 0.
- Mass data whose fields do not fit together in
m2Body_SetMassData. - Outlines:
m2DecomposeOutlinerefuses an outline that touches
itself, or one whose vertex comes within 0.005 m of an edge it does
not end.
- Body-local lengths beyond
-
Breaking: what a restore or replay accepts.
m2World_Restore
refuses a snapshot in any of these cases, and leaves the world as it
was:- its arrays do not fit together (free queues, shape lists, owners,
broadphase trees); - it holds shape geometry the world would not build;
- its mass data is not what the body's shapes give it;
- a body rotation in it is not of unit length.
Replay refuses a tape record whose bool flags are not 0 or 1.
Snapshots are format 3 and journal tapes format 4, so those written
by 0.0.1 are refused.
(#23,
#27,
#29,
#31,
#39) - its arrays do not fit together (free queues, shape lists, owners,
-
Breaking: the world hash.
m2World_Hashand
m2World_GetHashPartsnow also cover:- every setting the step reads;
- the live slots, keyed by index;
- the id pools;
- the broadphase's fat boxes.
So two worlds with the same hash step on to the same bits. Every
hash value changes, andm2WorldHashPartsgains avolumespart.
Queries capped at a number of hits keep the lowest shape slots, so
they choose the same shapes in every world that hashes the same.
(#37,
#38) -
Breaking:
m2World_ShatterBodyreturns anm2Resultand hands
the count back throughcountOut, andm2IsNormalizedRotreturns
bool.
(#19,
#20)
Added
m2DistanceJoint_IsSpringEnabledreads a distance joint's spring
toggle back.
(#21)
Fixed
- The host's floating-point setting. The library computes in its
own setting (round to nearest, subnormals kept, every exception
masked) on any host thread. A thread left in flush-to-zero,
denormals-are-zero or another rounding mode therefore gets the same
bits. Task hooks, draw callbacks and the allocator run in the host's
own setting.
(#44) - Saturation in the step. Every velocity saturates at
M2_MAX_SPEED, and every joint and contact impulse at
M2_IMPULSE_LIMIT, and water and wind loads atM2_MAX_FORCE. A 2x2
joint solve whose couplings would overflow solves its rows one at a
time instead.
(#42) - Far-apart joints. A joint between two bodies far apart clamps
its separation toM2_JOINT_REACH, where it used to leave NaN in the
bodies.
(#35) - Rotations. Normalizing a rotation keeps its direction for any
finite nonzero input. Very long, very short or infinite components
used to give{0, 0}, NaN or the identity.
(#23) - Far particles. A particle far beyond the grid's reach no longer
overflows its cell coordinate.
(#28) - Polygons. A created or set polygon is rebuilt from its vertices,
so its normals are always the world's own.
(#31)