Culverin is a Python wrapper for the Jolt Physics engine. It is designed for 3D games and simulations that require high performance and multi-threaded execution.
- Free-Threading Support: Designed for Python 3.13t and 3.14t. The engine releases the Global Interpreter Lock (GIL) during physics updates and raycast batches.
- Shadow Buffers: All body positions, rotations, and velocities are stored in contiguous C-arrays. You can access this data via
memoryviewornumpywithout the overhead of creating Python objects for every body. - Thread-Safe API: The engine uses a priority-based locking system. Simulation steps, state mutations, and queries can run on different threads without causing deadlocks or memory corruption.
- Generational Handles: Bodies are referenced by 64-bit handles rather than pointers. This ensures that using a handle for a deleted object will not crash the program.
- Double-Precision Internal: Uses double-precision floats for world positions to prevent physics jitter in large environments, while mirroring data to float32 buffers for rendering efficiency.
- Standard Primitives: Box, Sphere, Capsule, Cylinder, and Plane shapes.
- Complex Shapes: Support for Convex Hulls, Heightfields (Terrain), and static Meshes.
- Compound Bodies: Create single bodies composed of multiple child shapes.
- Character Controller: A virtual character controller with built-in support for climbing stairs, sliding down slopes, and pushing objects.
- Vehicles: Support for wheeled vehicles and tracked vehicles (tanks) with physical treads and skid-steering.
- Constraints: Fixed, Point, Hinge, Slider, Distance, and Cone constraints.
- Queries: Efficient single and batch Raycasting, Shapecasting (sweeps), and Overlap queries.
- Collision Events: Native event buffer for contact added, persisted, and removed events.
Building from source requires CMake and a C++ compiler (Visual Studio on Windows, GCC or Clang on Linux/macOS).
# Clone the repository including submodules git clone --recursive https://github.com/Evilpasture/culverin.git cd culverin # Install the package pip install .If you want Python 3.14t:
# Please install build module pip install build # Then build your wheel. Keyword: python3.14t, and have your 3.14t interpreter activated or available in PATH. usually the Python installer manages it for you. python3.14t -m build --wheel # then install via the wheel. please use your actual file name. pip install culverin-*-win_amd64.whlimport culverin import numpy as np # Initialize the world with 500 bodies capacity world = culverin.PhysicsWorld(settings={"gravity": (0, -9.81, 0), "max_bodies": 1000}) # Create a ground plane world.create_body(pos=(0, 0, 0), shape=culverin.SHAPE_PLANE, motion=culverin.MOTION_STATIC) # Create a dynamic box handle = world.create_body(pos=(0, 10, 0), size=(1, 1, 1), shape=culverin.SHAPE_BOX, motion=culverin.MOTION_DYNAMIC) # Simulation loop for _ in range(1000): world.step(1/60) # Access position directly from the shadow buffer idx = world.get_index(handle) pos = world.positions[idx * 4 : idx * 4 + 3] print(f"Box Height: {pos[1]}")| Spec | Standard |
|---|---|
| Units | Metric (1.0 = 1 meter) |
| Coordinate System | Right-Handed (Y-Up) |
| Angle Units | Radians |
| Quaternion Format | (x, y, z, w) |
| Internal Precision | Float64 (Double) |
| Buffer Precision | Float32 |
| Minimum Python | 3.11 (3.13+ recommended for multi-threading) |
For maximum performance when reading state, use the world.positions and world.rotations attributes. These return memoryview objects that point directly to the engine's internal memory. Use numpy.frombuffer(world.positions, dtype=np.float32) to wrap them in a NumPy array without copying the data.