Getting started
Building the C bindings
The C bindings of rapier are a C11 API (with optional C++17 helpers) exposed by a native library compiled from
the c folder of the Rapier repository. They are built with
CMake (3.25 or later), which runs Cargo for you, so you
need a Rust toolchain as well as a C compiler. From the root of the repository:
cmake -S bindings/c -B build/c -DCMAKE_BUILD_TYPE=Release
cmake --build build/c --config Release --parallel
This builds the 3D single-precision shared library. Other variants of the library are selected with the following options, given to the first command. Each variant must be built in its own build directory:
-DRAPIER_DIMENSION=2: builds the library for 2D physics simulation instead of 3D.-DRAPIER_PRECISION=64: uses 64-bits floats instead of 32-bits floats, for high-precision simulation.-DRAPIER_SHARED=OFF: links the static library instead of the shared library.-DRAPIER_ENABLE_PARALLEL=ON: enables the parallelism of the physics pipeline. The number of threads is then selected withr3SetNumThreads.-DRAPIER_SIMD_LANES=8: widens the SIMD of the solver from 4 to 8 lanes (32-bits floats only).-DRAPIER_FEATURES=...: a comma-separated list of optional features among:enhanced-determinism: enables cross-platform determinism (assuming the rest of your code is also deterministic) across all 32-bit and 64-bit platforms that implements the IEEE 754-2008 standard strictly. It cannot be combined with 8 SIMD lanes.fem: adds an alternative FEM solver for the soft-bodies, selected body by body.robotics: adds the URDF and MJCF loaders (3D with 32-bits floats only).profiler: enables the internal profiler, which gives the time spent by each stage of a timestep.
Using the library with CMake
Once built, the library, its headers, and a CMake package can be installed into a folder of your choice:
cmake --install build/c --config Release --prefix /path/to/rapier-sdk
Then, your own project finds it by configuring it with -DCMAKE_PREFIX_PATH=/path/to/rapier-sdk:
find_package(Rapier CONFIG REQUIRED)
target_link_libraries(your_app PRIVATE Rapier::rapier)
The Rapier::rapier target supplies the headers, the libraries, and the compile definitions matching the variant of
the library that was installed. If you don't use CMake, you will have to define these yourself: exactly one of
RAPIER_DIM2 or RAPIER_DIM3, and one of RAPIER_F32 or RAPIER_F64, before including any header of Rapier (the
default being 3D with 32-bits floats), as well as RAPIER_FEM, RAPIER_ROBOTICS, and RAPIER_PARALLEL if the
library was built with the corresponding features. They must match the library you link to: some structures don't
have the same layout depending on these definitions. Note that when shipping an
application linked to the shared library, the library must be shipped alongside it (on Windows, the DLL goes next to
your executable).
Basic simulation example
Here is a basic example of a C program. This creates a ball bouncing on a fixed ground. Details about the elements used in this examples are given in subsequent pages of this guide.
- Example 2D
- Example 3D
#include "rapier.h"
#include "rapier_math.h"
#include <stdio.h>
#include <stdlib.h>
/* Called whenever a Rapier function fails. */
static void RAPIER_CALL on_error(R2Status status, const char *message, void *user_data) {
(void)user_data;
fprintf(stderr, "Rapier error %u: %s\n", (unsigned)status, message);
exit(EXIT_FAILURE);
}
int main(void) {
/* Abort as soon as any Rapier function reports an error. */
R2ErrorHandler error_handler = {on_error, NULL};
r2SetErrorHandler(error_handler);
/* The world owns every structure needed by the simulation. */
R2World *world = r2NewWorld();
r2SetGravity(world, r2Vector(0.0, -9.81));
/* Create the ground. */
R2ColliderDesc ground = r2CuboidColliderDesc(r2Vector(100.0, 0.1));
r2InsertColliderWithoutParent(world, &ground);
/* Create the bouncing ball. */
R2RigidBodyDesc ball_body = r2DynamicRigidBodyDesc();
ball_body.position.translation = r2Vector(0.0, 10.0);
R2RigidBodyHandle ball_body_handle = r2InsertRigidBody(world, &ball_body);
R2ColliderDesc ball = r2BallColliderDesc(0.5);
ball.restitution = 0.7;
r2InsertCollider(ball_body_handle, &ball);
/* Run the game loop, stepping the simulation once per frame. */
for (int i = 0; i < 200; i++) {
r2Step(world, NULL, NULL);
R2Vector translation = r2RigidBody_Translation(ball_body_handle);
printf("Ball altitude: %f\n", (double)translation.y);
}
/* Freeing the world frees everything it contains. */
r2FreeWorld(world);
return EXIT_SUCCESS;
}
#include "rapier.h"
#include "rapier_math.h"
#include <stdio.h>
#include <stdlib.h>
/* Called whenever a Rapier function fails. */
static void RAPIER_CALL on_error(R3Status status, const char *message, void *user_data) {
(void)user_data;
fprintf(stderr, "Rapier error %u: %s\n", (unsigned)status, message);
exit(EXIT_FAILURE);
}
int main(void) {
/* Abort as soon as any Rapier function reports an error. */
R3ErrorHandler error_handler = {on_error, NULL};
r3SetErrorHandler(error_handler);
/* The world owns every structure needed by the simulation. */
R3World *world = r3NewWorld();
r3SetGravity(world, r3Vector(0.0, -9.81, 0.0));
/* Create the ground. */
R3ColliderDesc ground = r3CuboidColliderDesc(r3Vector(100.0, 0.1, 100.0));
r3InsertColliderWithoutParent(world, &ground);
/* Create the bouncing ball. */
R3RigidBodyDesc ball_body = r3DynamicRigidBodyDesc();
ball_body.position.translation = r3Vector(0.0, 10.0, 0.0);
R3RigidBodyHandle ball_body_handle = r3InsertRigidBody(world, &ball_body);
R3ColliderDesc ball = r3BallColliderDesc(0.5);
ball.restitution = 0.7;
r3InsertCollider(ball_body_handle, &ball);
/* Run the game loop, stepping the simulation once per frame. */
for (int i = 0; i < 200; i++) {
r3Step(world, NULL, NULL);
R3Vector translation = r3RigidBody_Translation(ball_body_handle);
printf("Ball altitude: %f\n", (double)translation.y);
}
/* Freeing the world frees everything it contains. */
r3FreeWorld(world);
return EXIT_SUCCESS;
}
Conventions of the C API
The headers of the C bindings are:
rapier.h: the whole C API.rapier_math.h: small inline constructors and arithmetic for the math types, e.g.,r3Vector(x, y, z).rapier_helpers.h: the explicit invalid handles, e.g.,R3_INVALID_RIGID_BODY_HANDLE.rapier.hpp: optional C++ helpers for automatic memory management. They don't change the C API.
Every function name is prefixed by r2 (for 2D) or r3 (for 3D), and every type and constant by R2 or R3, so
that the 2D and 3D versions of a program can be linked together. The RAPIER_FN(Step), RAPIER_TYPE(World), and
RAPIER_CONST(OK) macros resolve to the names of the dimension selected by RAPIER_DIM2 or RAPIER_DIM3, which is
useful to write code that works in both dimensions. The rest of this guide uses the actual names instead. A method
of an object is named after the object it applies to, followed by an underscore, e.g.,
r3RigidBody_SetTranslation. Keep in mind that 3D rotations are unit quaternions, and that a zero-initialized
quaternion isn't a valid rotation.
The API relies on three kinds of values:
- The world (
R3World) owns every rigid-body, collider, joint, and soft-body of the simulation. It is created withr3NewWorldand must be freed withr3FreeWorld, which frees everything it contains. - The descriptions (e.g.
R3RigidBodyDesc,R3ColliderDesc,R3JointDesc) are plain structures describing an object to create. They must be initialized by one of their constructors (e.g.r3DynamicRigidBodyDesc(), orr3BallColliderDesc(0.5)) which set meaningful default values, then any of their fields can be modified before the description is inserted into the world. A description owns nothing and is copied by the insertion: it can be discarded (or reused) afterwards. - The handles (e.g.
R3RigidBodyHandle,R3ColliderHandle) identify an object inserted into the world. They are small values that don't need to be freed, and that remember the world they belong to: this is why most functions take a handle as their only way of accessing the world, e.g.,r3RigidBody_Translation(handle). A handle becomes invalid once its object is removed, or once its world is freed.
A few other objects allocated by Rapier (the event collectors, the controllers, the shared shapes, etc.) are owned by
the application and must be freed by their dedicated Free function (never with the free function of C).
Errors
Most functions return an R3Status which is R3_OK in case of success, or an error code otherwise. The functions
returning another value report their failure on the side instead: r3LastStatus() returns the status of the last
fallible call of the current thread, and r3LastError() gives a human-readable description of that error. In both
cases, it is strongly recommended to install an error handler with r3SetErrorHandler (as in the example above) so
that no error goes unnoticed. Note that failing to find something, e.g., a ray-cast that doesn't hit anything,
is reported as the R3_NOT_FOUND error by some functions: their Try variant (e.g. r3TryCastRay) can be used when
this is an expected outcome.
Because the library is loaded dynamically, it is recommended to check once, at the start of your application, that
your headers (and your feature definitions like RAPIER_FEM) match the library actually loaded:
- Example 2D
- Example 3D
/* Checks that the headers match the Rapier library actually loaded. */
void check_rapier_abi(void) {
if (r2CheckAbi(R2_ABI_VERSION, R2_DIMENSION, sizeof(R2Real), sizeof(R2Vector), sizeof(R2Pose), R2_ABI_FEATURES) != R2_OK) {
fprintf(stderr, "Incompatible Rapier library: %s\n", r2LastError());
exit(EXIT_FAILURE);
}
}
/* Checks that the headers match the Rapier library actually loaded. */
void check_rapier_abi(void) {
if (r3CheckAbi(R3_ABI_VERSION, R3_DIMENSION, sizeof(R3Real), sizeof(R3Vector), sizeof(R3Pose), R3_ABI_FEATURES) != R3_OK) {
fprintf(stderr, "Incompatible Rapier library: %s\n", r3LastError());
exit(EXIT_FAILURE);
}
}
API reference
Every function of the C API is documented in its header. The same documentation is available as the API reference of the C bindings, a searchable HTML reference including more details on the ownership rules, the threading rules, and the errors. It can also be generated locally with Doxygen (1.9.4 or later):
cmake -S bindings/c/doxygen -B build/c-docs
cmake --build build/c-docs --target rapier_docs --parallel
Then open build/c-docs/html/index.html with your web browser.