Skip to main content

Serialization

The complete state of the simulation can be serialized by taking a snapshot of the physics world with r3SerializeWorld. This results in an R3Bytes object owned by the application, which bytes are borrowed with r3Bytes_Data (e.g. to save them on the disk, or to send them through the network), and which must be freed with r3FreeBytes. The snapshot can then be restored with r3DeserializeWorld, which creates a new world (freed with r3FreeWorld like any other world) in the exact same state as the serialized one:

/* Serialize the whole physics world. */
R2Bytes *snapshot = r2SerializeWorld(world);
/* The serialized bytes, borrowed from the snapshot, e.g., to be written to a file. */
R2ByteView bytes = r2Bytes_Data(snapshot);
printf("The snapshot takes %zu bytes.\n", bytes.count);

/* Deserialize it: this creates a new world, independent from the original one. */
R2World *deserialized = r2DeserializeWorld(bytes.data, bytes.count);
r2FreeBytes(snapshot);

/* The simulation can continue using the deserialized world. */
r2Step(deserialized, NULL, NULL);

The snapshot contains everything the world is made of: the rigid-bodies, colliders, joints, and soft-bodies (including their user data), the gravity, the integration parameters, the island manager, the broad-phase, and the narrow-phase (with its contacts). However, the thread pool configured by r3SetNumThreads and the profiling counters are not part of the snapshot, nor is anything owned by your application (the event collectors, the physics hooks, the controllers, etc.): they must be configured again for the deserialized world. Keep in mind that the handles of the serialized world keep referring to the serialized world: the handles of the deserialized world must be retrieved from it (each object keeps the index and the generation it had in the serialized world):

/* The handles of the original world don't refer to the deserialized world: get new ones. */
size_t num_bodies = r2RigidBodyHandles(deserialized, NULL, 0);
R2RigidBodyHandle *bodies = malloc(num_bodies * sizeof(R2RigidBodyHandle));
num_bodies = r2RigidBodyHandles(deserialized, bodies, num_bodies);

for (size_t i = 0; i < num_bodies; i++) {
printf("Restored rigid-body {%u, %u} at altitude %f\n", bodies[i].index, bodies[i].generation,
(double)r2RigidBody_Translation(bodies[i]).y);
}
free(bodies);
warning

A snapshot is not a stable file format: it can only be deserialized by the exact same build of the Rapier library (same version, dimension, precision, and features) as the one that serialized it, and r3DeserializeWorld rejects the snapshots it recognizes as incompatible with the R3_INVALID_ARGUMENT error. Only deserialize snapshots coming from a trusted source.

info

If the library is built with the enhanced-determinism feature, and if your platform fulfills the required determinism requirements, then you have the guarantee that running the exact same simulation on two different machines will result in the exact same snapshot bytes if the world is serialized on both machines after the same number of timesteps.