A Wolfram Language paclet providing high-performance 3D physics simulation bindings to the Rust rapier3d physics engine via wolfram-library-link.
This paclet acts as a wrapper around the Rapier physics engine. To bridge the gap between Mathematica's functional nature and Rapier's stateful dependencies, it implements an internal Handle-Based Global memory map inside the dynamically linked Rust library.
The simulation world is persistent in memory, isolated securely per session. You iteratively create a world, populate it dynamically with bodies and colliders, step the physics pipeline, and retrieve flat matrix transformation arrays back instantly over WSTP for zero-latency 3D visual plotting.
Ensure you have Rust/Cargo installed, as well as the Wolfram Engine or Mathematica.
- Navigate to the project root.
- Run the build script to compile the shared library and automatically route it to your OS-specific
$SystemIDpaclet directory:./scripts/build.wls
Load the paclet from a local checkout:
PacletDirectoryLoad[FileNameJoin[{NotebookDirectory[], "PhysicsModelLink"}]];
Needs["PhysicsModelLink`"];Create a world with gravity:
worldId = RapierWorldCreate[{0.0, 0.0, -9.81}];Add fixed objects (a floor cuboid and a fixed sphere obstacle):
floorId = RapierAddRigidBody[worldId, {0.0, 0.0, -1.0}, "Fixed"];
RapierAddColliderCuboid[worldId, floorId, {8.0, 8.0, 1.0}, 1.0];
fixedSphereId = RapierAddRigidBody[worldId, {2.0, 0.0, 0.8}, "Fixed"];
RapierAddColliderSphere[worldId, fixedSphereId, 0.8, 1.0];Add dynamic objects (a cuboid, a sphere, a cylinder, a cone, and a capsule):
dynCuboidId = RapierAddRigidBody[worldId, {-1.5, 0.0, 4.5}, "Dynamic"];
RapierAddColliderCuboid[worldId, dynCuboidId, {0.5, 0.5, 0.5}, 1.0];
dynSphereId = RapierAddRigidBody[worldId, {0.0, 0.0, 6.0}, "Dynamic"];
RapierAddColliderSphere[worldId, dynSphereId, 0.5, 1.0];
dynCylinderId = RapierAddRigidBody[worldId, {1.5, 0.0, 5.2}, "Dynamic"];
RapierAddColliderCylinder[worldId, dynCylinderId, {0.6, 0.35}, 1.0];
dynConeId = RapierAddRigidBody[worldId, {-0.8, 1.2, 5.8}, "Dynamic"];
RapierAddColliderCone[worldId, dynConeId, {0.5, 0.3}, 1.0];
dynCapsuleId = RapierAddRigidBody[worldId, {0.8, -1.2, 6.3}, "Dynamic"];
RapierAddColliderCapsule[worldId, dynCapsuleId, {0.6, 0.25}, 1.0];Step the simulation and capture body transforms:
dt = 1.0/60.0;
steps = 240;
frames = Table[
RapierWorldStep[worldId, 1, dt];
RapierGetBodyPositions[worldId],
{steps}
];Visualize using full rigid transforms (translation + rotation) with GeometricTransformation:
rowToTransform[row_] := {QuaternionToTransformation[row[[5 ;; 8]]], row[[2 ;; 4]]};
handleToTransform[mat_] := Association @ Map[(#[[1]] -> rowToTransform[#]) &, mat];
ListAnimate[
Table[
Module[{t = handleToTransform[frame]},
Graphics3D[
{
Gray, GeometricTransformation[Cuboid[{-8, -8, -1}, {8, 8, 1}], t[floorId]],
Darker@Blue, GeometricTransformation[Sphere[{0, 0, 0}, 0.8], t[fixedSphereId]],
Orange, GeometricTransformation[Cuboid[{-0.5, -0.5, -0.5}, {0.5, 0.5, 0.5}], t[dynCuboidId]],
Red, GeometricTransformation[Sphere[{0, 0, 0}, 0.5], t[dynSphereId]],
Purple, GeometricTransformation[Cylinder[{{0, 0, -0.6}, {0, 0, 0.6}}, 0.35], t[dynCylinderId]],
Brown, GeometricTransformation[Cone[{{0, 0, -0.5}, {0, 0, 0.5}}, 0.3], t[dynConeId]],
Darker@Green, GeometricTransformation[CapsuleShape[{{0, 0, -0.6}, {0, 0, 0.6}}, 0.25], t[dynCapsuleId]]
},
PlotRange -> {{-6, 6}, {-6, 6}, {-2, 8}},
Axes -> True,
Boxed -> True
]
],
{frame, frames}
],
AnimationRate -> 60
]Cleanup:
RapierWorldDestroy[worldId]