# Technical Documentation: erincatto/box3d > ℹ️ **Provenance:** Hybrid Fusion: `erincatto/box3d` (README + 10 In-Tree Chapters) · [CodeWiki Reference](https://codewiki.google/github.com/erincatto/box3d) · Recency: Active (< 180 days) ## 1. Project Overview & Quickstart (erincatto/box3d) # Box3D [](https://github.com/erincatto/box3d/actions) [](https://cla-assistant.io/erincatto/box3d) Box3D is a 3D physics engine for games. [](https://www.youtube.com/watch?v=jr_Fzl2XwKU) ## Features ### Collision - Continuous collision detection - Contact events - Convex hulls, capsules, spheres, triangle meshes, and height fields - Multiple shapes per body - Collision filtering - Ray casts, shape casts, and overlap queries - Sensor system - Character mover ### Physics - Robust _Soft Step_ rigid body solver - Continuous physics for fast translations and rotations - Island based sleep - Revolute, prismatic, distance, motor, weld, and wheel joints - Joint limits, motors, springs, and friction - Joint and contact forces - Body movement events and sleep notification ### System - Data-oriented design - Written in portable C17 - Extensive multithreading and SIMD - Optimized for large piles of bodies - Cross platform determinism - Recording and replay ### Samples - Uses sokol to run with D3D11 on Windows, Metal on macOS, and OpenGL 4.5 on Linux. - Graphical user interface with imgui. - Many samples to demonstrate features and performance. ## Building all platforms - Install [CMake](https://cmake.org/) - Install [git](https://git-scm.com/) - Ensure these run from the command line ## Building with CMake presets (recommended) This uses the presets in `CMakePresets.json`. - Windows: `cmake --preset windows` then `cmake --build --preset windows-release` - Linux: `cmake --preset linux-release` then `cmake --build --preset linux-release` - macOS: `cmake --preset macos` then `cmake --build --preset macos-release` - Windows MinGW: `cmake --preset mingw-release` then `cmake --build --preset mingw-release` Run the samples app (must be in the Box3D directory). - Windows: `.\build\bin\Release\samples.exe` - Linux: `./build/bin/samples` - macOS: `./build/bin/Release/samples` ## Building for Visual Studio - Install [Visual Studio](https://visualstudio.microsoft.com/) - Run `build_vs2026.bat` - Open and build `build/box3d.slnx` ## Building for Linux - Run `build.sh` from a bash shell - Results are in the build sub-folder ## Building for Xcode - mkdir build - cd build - cmake -G Xcode .. - Open `box3d.xcodeproj` - Select the samples scheme - Build and run the samples ## Building for Web - [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) - `emcmake cmake -B build -DBOX3D_SAMPLES=OFF` - `cmake --build build` Box3D uses SSE2 with WebAssembly. Define `BOX3D_DISABLE_SIMD` to disable SSE2. ## Building and installing - mkdir build - cd build - cmake .. - cmake --build . --config Release - cmake --install . (might need sudo) ## Using Box3D in your project The core library has no dependencies beyond the C runtime (and `libm` on Unix). Linking it gives you the `box3d::box3d` target. I recommend to use FetchContent: ```cmake include(FetchContent) FetchContent_Declare(box3d GIT_REPOSITORY https://github.com/erincatto/box3d.git GIT_TAG v0.1.0) FetchContent_MakeAvailable(box3d) target_link_libraries(my_app PRIVATE box3d::box3d) ``` For a vendored copy or git submodule, point `add_subdirectory` at it: ```cmake add_subdirectory(extern/box3d) target_link_libraries(my_app PRIVATE box3d::box3d) ``` To use a copy installed with `cmake --install`, find the package: ```cmake find_package(box3d 0.1 REQUIRED) target_link_libraries(my_app PRIVATE box3d::box3d) ``` See [`docs/hello.md`](docs/hello.md) for a minimal first program. ## Compatibility The Box3D library and samples build and run on Windows, Linux, and Mac. You will need a compiler that supports C17 to build the Box3D library. You will need a compiler that supports C++20 to build the samples. Box3D uses SSE2 and Neon SIMD math to improve performance. SIMD can be disabled by defining `BOX3D_DISABLE_SIMD`. ## Documentation The user manual lives in [`docs/`](docs/) and is built with Doxygen. Enable the `BOX3D_DOCS` CMake option and build the `doc` target. ## Community - [Discord](https://discord.gg/NKYgCBP) ## Contributing Pull requests are currently disabled. Instead, please file an issue for bugs or feature requests. For support, please visit the Discord server. ## Giving feedback Please file an issue or start a chat on discord. You can also use [GitHub Discussions](https://github.com/erincatto/box3d/discussions). ## License Box3D is developed by Erin Catto and uses the [MIT license](https://en.wikipedia.org/wiki/MIT_License). ## Sponsorship Support development of Box3D through [Github Sponsors](https://github.com/sponsors/erincatto). Please consider starring this repository and subscribing to my [YouTube channel](https://www.youtube.com/@erin_catto). ## LLM Usage LLMs are used in the following areas: - unit tests - samples app - migrating code between Box2D and Box3D - build configuration - code reviews - benchmarking Elsewhere all code is developed and written by me. I take responsibility for every line of code in Box2D/3D. ## 2. In-Tree Documentation Chapters (erincatto/box3d) ## File: README.md # Box3D [](https://github.com/erincatto/box3d/actions) [](https://cla-assistant.io/erincatto/box3d) Box3D is a 3D physics engine for games. [](https://www.youtube.com/watch?v=jr_Fzl2XwKU) ## Features ### Collision - Continuous collision detection - Contact events - Convex hulls, capsules, spheres, triangle meshes, and height fields - Multiple shapes per body - Collision filtering - Ray casts, shape casts, and overlap queries - Sensor system - Character mover ### Physics - Robust _Soft Step_ rigid body solver - Continuous physics for fast translations and rotations - Island based sleep - Revolute, prismatic, distance, motor, weld, and wheel joints - Joint limits, motors, springs, and friction - Joint and contact forces - Body movement events and sleep notification ### System - Data-oriented design - Written in portable C17 - Extensive multithreading and SIMD - Optimized for large piles of bodies - Cross platform determinism - Recording and replay ### Samples - Uses sokol to run with D3D11 on Windows, Metal on macOS, and OpenGL 4.5 on Linux. - Graphical user interface with imgui. - Many samples to demonstrate features and performance. ## Building all platforms - Install [CMake](https://cmake.org/) - Install [git](https://git-scm.com/) - Ensure these run from the command line ## Building with CMake presets (recommended) This uses the presets in `CMakePresets.json`. - Windows: `cmake --preset windows` then `cmake --build --preset windows-release` - Linux: `cmake --preset linux-release` then `cmake --build --preset linux-release` - macOS: `cmake --preset macos` then `cmake --build --preset macos-release` - Windows MinGW: `cmake --preset mingw-release` then `cmake --build --preset mingw-release` Run the samples app (must be in the Box3D directory). - Windows: `.\build\bin\Release\samples.exe` - Linux: `./build/bin/samples` - macOS: `./build/bin/Release/samples` ## Building for Visual Studio - Install [Visual Studio](https://visualstudio.microsoft.com/) - Run `build_vs2026.bat` - Open and build `build/box3d.slnx` ## Building for Linux - Run `build.sh` from a bash shell - Results are in the build sub-folder ## Building for Xcode - mkdir build - cd build - cmake -G Xcode .. - Open `box3d.xcodeproj` - Select the samples scheme - Build and run the samples ## Building for Web - [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) - `emcmake cmake -B build -DBOX3D_SAMPLES=OFF` - `cmake --build build` Box3D uses SSE2 with WebAssembly. Define `BOX3D_DISABLE_SIMD` to disable SSE2. ## Building and installing - mkdir build - cd build - cmake .. - cmake --build . --config Release - cmake --install . (might need sudo) ## Using Box3D in your project The core library has no dependencies beyond the C runtime (and `libm` on Unix). Linking it gives you the `box3d::box3d` target. I recommend to use FetchContent: ```cmake include(FetchContent) FetchContent_Declare(box3d GIT_REPOSITORY https://github.com/erincatto/box3d.git GIT_TAG v0.1.0) FetchContent_MakeAvailable(box3d) target_link_libraries(my_app PRIVATE box3d::box3d) ``` For a vendored copy or git submodule, point `add_subdirectory` at it: ```cmake add_subdirectory(extern/box3d) target_link_libraries(my_app PRIVATE box3d::box3d) ``` To use a copy installed with `cmake --install`, find the package: ```cmake find_package(box3d 0.1 REQUIRED) target_link_libraries(my_app PRIVATE box3d::box3d) ``` See [`docs/hello.md`](docs/hello.md) for a minimal first program. ## Compatibility The Box3D library and samples build and run on Windows, Linux, and Mac. You will need a compiler that supports C17 to build the Box3D library. You will need a compiler that supports C++20 to build the samples. Box3D uses SSE2 and Neon SIMD math to improve performance. SIMD can be disabled by defining `BOX3D_DISABLE_SIMD`. ## Documentation The user manual lives in [`docs/`](docs/) and is built with Doxygen. Enable the `BOX3D_DOCS` CMake option and build the `doc` target. ## Community - [Discord](https://discord.gg/NKYgCBP) ## Contributing Pull requests are currently disabled. Instead, please file an issue for bugs or feature requests. For support, please visit the Discord server. ## Giving feedback Please file an issue or start a chat on discord. You can also use [GitHub Discussions](https://github.com/erincatto/box3d/discussions). ## License Box3D is developed by Erin Catto and uses the [MIT license](https://en.wikipedia.org/wiki/MIT_License). ## Sponsorship Support development of Box3D through [Github Sponsors](https://github.com/sponsors/erincatto). Please consider starring this repository and subscribing to my [YouTube channel](https://www.youtube.com/@erin_catto). ## LLM Usage LLMs are used in the following areas: - unit tests - samples app - migrating code between Box2D and Box3D - build configuration - code reviews - benchmarking Elsewhere all code is developed and written by me. I take responsibility for every line of code in Box2D/3D. --- ## File: docs/character.md # Character Mover > **Caution**: The character mover API is experimental. Box3D provides a geometric character mover: a capsule that exists outside the rigid body simulation and is driven entirely by application code. Because it is not a simulated body, you have full control over movement without fighting the solver, at the cost of having to resolve collisions yourself. This is the style of mover common in first-person shooters and games with precise platforming. ## The Mover Capsule The mover is represented by a `b3Capsule` in world space. A capsule is a good shape for movement because its round profile slides smoothly along edges and corners without snagging. Give the capsule a meaningful radius — a very thin capsule behaves poorly with the encroachment handling described below. ```c b3Capsule mover; mover.center1 = (b3Vec3){ 0.0f, 0.35f, 0.0f }; // bottom sphere center mover.center2 = (b3Vec3){ 0.0f, 1.45f, 0.0f }; // top sphere center mover.radius = 0.35f; ``` The mover has no explicit rotation handling. Slow rotation can be made to work by updating the capsule each frame, but rapid spinning is not supported. ## Workflow Each frame: 1. Compute a desired translation from input and physics (gravity, velocity). 2. Call `b3World_CastMover` to find how far the mover can actually travel. 3. Move the capsule by the returned fraction of the desired translation. 4. Call `b3World_CollideMover` to gather all contact planes at the new position. 5. Filter and assemble the planes into a `b3CollisionPlane` array. 6. Call `b3SolvePlanes` to compute a corrected position delta. 7. Apply the delta and call `b3ClipVector` to remove velocity components that push into surfaces. ## Swept Motion: b3World_CastMover ```c float b3World_CastMover( b3WorldId worldId, b3Pos origin, // world position the mover is relative to const b3Capsule* mover, b3Vec3 translation, b3QueryFilter filter, b3MoverFilterFcn* fcn, // optional, may be NULL void* context ); ``` This casts the capsule through the world and returns the fraction `[0, 1]` of `translation` that can be traveled before hitting something. Multiply the translation by this fraction to get the safe displacement. The cast handles *encroachment*: when the mover starts out touching a surface, the inner line segment of the capsule may move slightly into that surface without the full capsule generating an overlap. This lets the mover slide along walls and floors it is already resting against without stopping dead at the first frame. The optional `b3MoverFilterFcn` callback lets you exclude specific shapes from the cast (e.g., ignore triggers, teammates, or one-way platforms): ```c typedef bool b3MoverFilterFcn(b3ShapeId shapeId, void* context); // Return true to accept the shape, false to skip it. ``` `b3World_CastMover` is intended for movement, not for gathering contact information. Use `b3World_CollideMover` for that. ## Contact Planes: b3World_CollideMover ```c void b3World_CollideMover( b3WorldId worldId, b3Pos origin, // mover and returned planes are relative to this const b3Capsule* mover, b3QueryFilter filter, b3PlaneResultFcn* fcn, void* context ); ``` This gathers all surfaces the mover is touching or overlapping and delivers them via the callback: ```c typedef bool b3PlaneResultFcn( b3ShapeId shapeId, const b3PlaneResult* plane, int planeCount, void* context ); // Return true to continue gathering planes. ``` `b3PlaneResult` carries the contact plane and a world-space contact point: ```c typedef struct b3PlaneResult { b3Plane plane; // normal + offset: separation = dot(normal, p) - offset b3Vec3 point; } b3PlaneResult; ``` The mover is treated as having fixed rotation, so only planes are needed — no full contact manifolds. ### Per-Body Query: b3Body_CollideMover When you need to test the mover against a single specific body (useful for moving platforms or elevators): ```c int b3Body_CollideMover( b3BodyId bodyId, b3BodyPlaneResult* bodyPlanes, int planeCapacity, b3Pos origin, const b3Capsule* mover, b3QueryFilter filter, b3WorldTransform bodyTransform ); ``` Returns the number of planes written into `bodyPlanes`. Each entry pairs the originating `b3ShapeId` with a `b3PlaneResult`. ## Resolving Overlap: b3SolvePlanes Convert the raw `b3PlaneResult` values from `b3World_CollideMover` into `b3CollisionPlane` entries and call: ```c typedef struct b3CollisionPlane { b3Plane plane; float pushLimit; // FLT_MAX for rigid; smaller values allow soft penetration float push; // output: how much the solver pushed along this plane bool clipVelocity; // set true to clip velocity against this plane } b3CollisionPlane; b3PlaneSolverResult b3SolvePlanes( b3Vec3 targetDelta, b3CollisionPlane* planes, int count ); ``` `b3SolvePlanes` finds the position delta closest to `targetDelta` that satisfies all planes. The result contains the corrected `delta` and an `iterationCount`. `pushLimit` controls softness. `FLT_MAX` gives a rigid surface. A smaller value allows the mover to push through — useful for other players, enemies, or doors that should yield but not fully block. ## Velocity Clipping: b3ClipVector After resolving position, clip the mover's velocity so it does not keep accelerating into blocked directions: ```c b3Vec3 b3ClipVector(b3Vec3 vector, const b3CollisionPlane* planes, int count); ``` This removes the components of `vector` that push into any plane where `clipVelocity` is true. Without this, velocity accumulates every frame the mover is pressed against a surface. ## Putting It Together ```c // The mover capsule and the planes are relative to origin. Keep origin near the // character (its world position) so the query stays precise far from the world origin. b3Pos origin = b3Pos_zero; // 1. Desired translation from input + gravity integration b3Vec3 translation = b3MulSV(timeStep, velocity); // 2. Swept cast float fraction = b3World_CastMover(worldId, origin, &mover, translation, filter, NULL, NULL); b3Vec3 safeDelta = b3MulSV(fraction, translation); // 3. Move the capsule mover.center1 = b3Add(mover.center1, safeDelta); mover.center2 = b3Add(mover.center2, safeDelta); // 4. Gather contact planes #define MAX_PLANES 16 b3CollisionPlane collisionPlanes[MAX_PLANES]; int planeCount = 0; // (user callback stores planes into collisionPlanes / planeCount) b3World_CollideMover(worldId, origin, &mover, filter, MyPlaneCallback, &planeCtx); // 5. Solve planes b3PlaneSolverResult result = b3SolvePlanes(b3Vec3_zero, collisionPlanes, planeCount); // 6. Apply correction mover.center1 = b3Add(mover.center1, result.delta); mover.center2 = b3Add(mover.center2, result.delta); // 7. Clip velocity velocity = b3ClipVector(velocity, collisionPlanes, planeCount); ``` The `Mover` sample in the samples application shows a complete implementation including acceleration, friction, jumping, and a pogo stick. --- ## File: docs/collision.md # Collision Box3D provides geometric types and functions. These include: - primitives: spheres, capsules, and convex hulls - triangle meshes and height fields for static terrain - convex hull construction from point clouds - mass and bounding box computation - local ray and shape casts - contact manifolds - shape distance (GJK) - time of impact - dynamic bounding volume tree The collision interface is designed to be usable outside of rigid body simulation. For example, you can use the dynamic tree for other aspects of your game besides physics. The main purpose of Box3D is rigid body simulation, so the collision module only contains features that are also useful in the physics engine. ## Shape Primitives Shape primitives describe collision geometry and may be used independently of physics simulation. At a minimum, you should understand how to create primitives that can later be attached to rigid bodies. Box3D shape primitives support several operations: - Test a point or proxy for overlap with the primitive - Perform a ray cast against the primitive - Compute the primitive's bounding box - Compute the mass properties of the primitive ### Spheres Spheres have a center and radius. Spheres are solid. ```c b3Sphere sphere; sphere.center = (b3Vec3){2.0f, 3.0f, 0.0f}; sphere.radius = 0.5f; ``` You can also initialize a sphere inline: ```c b3Sphere sphere = {{2.0f, 3.0f, 0.0f}, 0.5f}; ``` ### Capsules Capsules have two center points and a radius. The center points are the centers of two hemispheres connected by a cylinder. ```c b3Capsule capsule; capsule.center1 = (b3Vec3){0.0f, -1.0f, 0.0f}; capsule.center2 = (b3Vec3){0.0f, 1.0f, 0.0f}; capsule.radius = 0.25f; ``` ### Convex Hulls Box3D convex hulls are solid convex polyhedra. The geometry lives in a heavy, immutable `b3HullData` object. The world shares identical hull data through a reference counted database, so many shapes built from the same data hold one copy. A shape is convex when all line segments connecting two interior points remain inside the shape. The most common hull is a box. Use `b3MakeBoxHull` for an axis-aligned box and `b3MakeCubeHull` for a cube. The values are half-extents (half-widths): ```c b3BoxHull box = b3MakeBoxHull(0.5f, 1.0f, 0.5f); // half-widths hx, hy, hz b3BoxHull cube = b3MakeCubeHull(0.5f); // uniform half-width ``` `b3BoxHull` stores everything inline — do not call `b3DestroyHull` on one. Its `.base` member is the `b3HullData` that you pass to shape creation: ```c b3CreateHullShape(bodyId, &shapeDef, &box.base); ``` For a box that is offset or rotated from the body origin: ```c b3Transform localTransform = { offset, rotation }; b3BoxHull rotatedBox = b3MakeTransformedBoxHull(0.5f, 1.0f, 0.5f, localTransform); ``` For arbitrary convex geometry, provide a point cloud and let Box3D compute the hull: ```c b3HullData* data = b3CreateHull(points, pointCount, maxVertexCount); if (data == NULL) { // degenerate input: coincident or coplanar points, or insufficient volume } b3CreateHullShape(bodyId, &shapeDef, data); // The world keeps its own copy, so you may free yours immediately b3DestroyHull(data); ``` `maxVertexCount` limits the output complexity; pass the same value as `pointCount` to allow the full hull. `b3CreateHull` returns `NULL` on degenerate input (e.g. fewer than four non-coplanar points, or nearly-zero volume). Always check before using it. Box3D also provides helpers to create cylindrical and conical hulls: ```c b3HullData* cylinder = b3CreateCylinder(height, radius, yOffset, sides); b3HullData* cone = b3CreateCone(height, radius1, radius2, slices); b3DestroyHull(cylinder); b3DestroyHull(cone); ``` Hulls created with `b3CreateCylinder`, `b3CreateCone`, and `b3CreateHull` are heap-allocated and must be freed with `b3DestroyHull`. `b3BoxHull` values are stack/struct-allocated and must not be freed. ### Triangle Meshes Triangle meshes let you describe concave or open surfaces using a triangle soup. They are intended for static geometry: `b3CreateMeshShape` only creates contacts on static bodies. The mesh is built from a `b3MeshDef` and cooked into a `b3MeshData` that contains an internal BVH for efficient collision queries: ```c b3MeshDef def = {0}; def.vertices = myVerts; def.vertexCount = myVertCount; def.indices = myIndices; // 3 per triangle def.triangleCount = myTriCount; def.weldVertices = true; def.identifyEdges = true; // adjacency info for smooth inter-triangle normals b3MeshData* mesh = b3CreateMesh(&def, NULL, 0); ``` Pass `identifyEdges = true` when triangles share edges so Box3D can suppress internal-edge collisions between adjacent triangles (the 3D equivalent of ghost collision handling on polygon chains). The `b3Mesh` struct pairs a `b3MeshData` pointer with a scale vector: ```c b3Mesh meshShape; meshShape.data = mesh; meshShape.scale = (b3Vec3){1.0f, 1.0f, 1.0f}; ``` Scale may be non-uniform and may have negative components, but no component may be zero. Per-triangle surface materials are supported. Provide a `b3SurfaceMaterial` array in `b3ShapeDef::materials` and an index array in `b3MeshDef::materialIndices`: ```c b3SurfaceMaterial materials[2] = { ... }; def.materialIndices = perTriangleMaterialIndex; // uint8_t, 1 per triangle b3ShapeDef shapeDef = b3DefaultShapeDef(); shapeDef.materials = materials; shapeDef.materialCount = 2; b3ShapeId id = b3CreateMeshShape(bodyId, &shapeDef, mesh, scale); ``` Destroy the mesh data when no longer needed, after the shape referencing it has been destroyed: ```c b3DestroyMesh(mesh); ``` Box3D provides factory helpers for common mesh configurations: `b3CreateGridMesh`, `b3CreateWaveMesh`, `b3CreateBoxMesh`, and others. ### Height Fields Height fields describe terrain as a regular grid of sample heights. Like meshes, they are only valid on static bodies. ```c b3HeightFieldDef def = {0}; def.heights = heightSamples; // float[countX * countZ] def.countX = 256; def.countZ = 256; def.scale = (b3Vec3){1.0f, 1.0f, 1.0f}; def.globalMinimumHeight = -10.0f; def.globalMaximumHeight = 50.0f; b3HeightFieldData* hf = b3CreateHeightField(&def); b3ShapeId id = b3CreateHeightFieldShape(bodyId, &shapeDef, hf); ``` `scale` controls cell spacing (x/z) and height scale (y). Setting a material index to `B3_HEIGHT_FIELD_HOLE` (0xFF) punches a hole in that grid cell, useful for cave entrances and tunnels. When placing multiple height fields side by side, give all of them the same `globalMinimumHeight` and `globalMaximumHeight` so compressed heights quantize identically and the seams line up. Destroy the height field after the shape referencing it has been destroyed: ```c b3DestroyHeightField(hf); ``` ### Compound Shapes A compound shape aggregates spheres, capsules, hulls, and meshes into a single static collision shape. Compounds are only allowed on static bodies. They are designed for offline baking and open-world streaming — see the dedicated [compound](compound.md) page for the full API and usage pattern. ## Geometric Queries ### Shape Point Test Box3D does not expose standalone `b3PointInShape` functions. Point overlap is expressed through the `b3ShapeProxy` abstraction that GJK uses. A degenerate proxy — a single point with zero radius — serves as a point test: ```c b3Vec3 queryPoint = {5.0f, 2.0f, 1.0f}; b3ShapeProxy proxy; proxy.points = &queryPoint; proxy.count = 1; proxy.radius = 0.0f; b3Transform shapeTransform = b3Transform_identity; bool hit = b3OverlapHull(&myHull, shapeTransform, &proxy); ``` The same pattern works with `b3OverlapSphere`, `b3OverlapCapsule`, `b3OverlapMesh`, and `b3OverlapHeightField`. ### Ray Cast Cast a ray at a shape to get the point of first intersection and the surface normal. > **Caution**: No hit will register if the ray starts inside a convex shape such > as a sphere or hull. Convex shapes are treated as solid. ```c b3RayCastInput input = {0}; input.origin = (b3Vec3){0.0f, 10.0f, 0.0f}; input.translation = (b3Vec3){0.0f, -20.0f, 0.0f}; input.maxFraction = 1.0f; b3CastOutput output = b3RayCastHull(&myHull, &input); if (output.hit) { // output.point, output.normal, output.fraction } ``` Per-shape ray cast functions: `b3RayCastSphere`, `b3RayCastCapsule`, `b3RayCastHull`, `b3RayCastMesh`, `b3RayCastHeightField`, `b3RayCastCompound`. All operate in the shape's local space. Use `b3IsValidRay` to validate input before calling. To cast against the full simulation world, use `b3World_CastRay` or the convenience function `b3World_CastRayClosest`. ### Shape Cast A shape cast sweeps an abstract point cloud (a `b3ShapeProxy`) through space and finds where it first contacts another shape. A sphere is a single point with a non-zero radius; a capsule is two points with a radius; a box is eight points with zero radius. ```c b3Vec3 proxyPoints[] = {{-0.5f, -0.5f, -0.5f}, {0.5f, 0.5f, 0.5f}}; b3ShapeCastInput input = {0}; input.proxy.points = proxyPoints; input.proxy.count = 2; input.proxy.radius = 0.1f; input.translation = (b3Vec3){0.0f, -5.0f, 0.0f}; input.maxFraction = 1.0f; b3CastOutput output = b3ShapeCastHull(&myHull, &input); if (output.hit) { // output.point, output.normal, output.fraction } ``` Per-shape cast functions: `b3ShapeCastSphere`, `b3ShapeCastCapsule`, `b3ShapeCastHull`, `b3ShapeCastMesh`, `b3ShapeCastHeightField`, `b3ShapeCastCompound`. For the most general form — sweeping one proxy against another — use `b3ShapeCast` with a `b3ShapeCastPairInput`. All shape cast functions call this internally. ### Distance `b3ShapeDistance` computes the closest points and separation distance between two shapes, each expressed as a `b3ShapeProxy`. It uses the GJK algorithm. ```c b3DistanceInput input = {0}; input.proxyA = proxyA; // b3ShapeProxy for shape A input.proxyB = proxyB; // b3ShapeProxy for shape B input.transform = b3InvMulWorldTransforms(worldA, worldB); // relative pose of B in A input.useRadii = true; b3SimplexCache cache = b3_emptyDistanceCache; b3DistanceOutput output = b3ShapeDistance(&input, &cache, NULL, 0); // output.distance, output.pointA, output.pointB, output.normal are in shape A's frame ``` The query is origin independent and runs in frame A, so the witness points and normal come back in shape A's frame. Lift them into world space with shape A's transform if needed. The simplex cache warm-starts the algorithm when shapes move by small amounts between calls. On the first call zero-initialize the cache (or use `b3_emptyDistanceCache`). ### Time of Impact If two shapes move fast they may tunnel through each other in a single time step. `b3TimeOfImpact` finds the earliest time at which two swept shapes touch. Box3D uses this internally to prevent dynamic bodies from tunneling through static geometry. The algorithm identifies an initial separating axis and advances the shapes along it until they touch or pass each other. It may miss collisions that only become apparent at the final positions, but those tend to be glancing contacts that rarely matter in practice. ```c b3TOIInput input = {0}; input.proxyA = proxyA; input.proxyB = proxyB; input.sweepA = sweepA; // b3Sweep: localCenter, c1, c2, q1, q2 input.sweepB = sweepB; input.maxFraction = 1.0f; b3TOIOutput output = b3TimeOfImpact(&input); if (output.state == b3_toiStateHit) { // output.fraction is the time of impact in [0, maxFraction] // output.point and output.normal give the contact geometry } ``` `b3Sweep` describes a rigid body's motion as a translation of the center of mass plus a quaternion rotation, interpolated from `(c1, q1)` to `(c2, q2)`. Use `b3GetSweepTransform` to evaluate the transform at any fraction. ### Contact Manifolds Box3D computes contact points for overlapping shapes. In 3D, hull-hull contact can produce up to `B3_MAX_MANIFOLD_POINTS` (4) points. All points share the same contact normal so Box3D groups them into a manifold. The contact solver uses this for stable stacking. Normally you do not compute manifolds directly — you use the contact data returned by the simulation. The `b3Manifold` struct contains: - `normal`: unit normal from shape A toward shape B - `points[B3_MAX_MANIFOLD_POINTS]`: contact points with separation, impulses, and feature ids - `pointCount`: 0–4 valid points - `frictionImpulse`, `twistImpulse`, `rollingImpulse`: accumulated friction state For direct use, the low-level collide functions produce a `b3LocalManifold` in the frame of shape A: ```c b3LocalManifoldPoint points[B3_MAX_MANIFOLD_POINTS]; b3LocalManifold manifold; manifold.points = points; b3SATCache cache = {0}; b3CollideHulls(&manifold, B3_MAX_MANIFOLD_POINTS, &hullA, &hullB, transformBtoA, &cache); ``` Available collide functions: - `b3CollideSpheres` - `b3CollideCapsuleAndSphere` - `b3CollideCapsules` - `b3CollideHullAndSphere` - `b3CollideHullAndCapsule` - `b3CollideHulls` - `b3CollideTriangleAndCapsule` - `b3CollideTriangleAndHull` - `b3CollideTriangleAndSphere` The SAT cache in `b3CollideHulls` warm-starts the separating axis search between frames, the same idea as the simplex cache for GJK distance. Box3D uses speculative collision: some contact points in a `b3Manifold` may report a positive (separated) `separation`. Check `totalNormalImpulse` to determine whether a speculative point actually had an interaction during the step. ## Dynamic Tree `b3DynamicTree` organizes large numbers of AABBs into a hierarchical binary tree for fast ray casts and region queries. Box3D uses it internally to manage the broad phase, but it is also available for organizing spatial game data unrelated to physics. Each tree node stores a `b3AABB` and a 64-bit `userData` value. Internal nodes have two children; leaf nodes are proxies you manage directly. ```c // Create a tree with an initial proxy capacity b3DynamicTree tree = b3DynamicTree_Create(256); // Insert a proxy b3AABB aabb = { lowerBound, upperBound }; int proxyId = b3DynamicTree_CreateProxy(&tree, aabb, categoryBits, userData); // Move it b3DynamicTree_MoveProxy(&tree, proxyId, newAabb); // Remove it b3DynamicTree_DestroyProxy(&tree, proxyId); // Done b3DynamicTree_Destroy(&tree); ``` **Category bits** allow broad-phase filtering without invoking the callback. A proxy is only visited if `(maskBits & node->categoryBits) != 0` (or if `requireAllBits` is set, the AND must equal `maskBits`). ### AABB Query Find all proxies whose AABBs overlap a query box: ```c b3TreeStats stats = b3DynamicTree_Query( &tree, queryAabb, maskBits, requireAllBits, myQueryCallback, context); ``` The callback receives `proxyId` and `userData` and returns `true` to continue. ### Closest Query Find the proxy closest to a point: ```c float minDistSqr = FLT_MAX; b3TreeStats stats = b3DynamicTree_QueryClosest( &tree, point, maskBits, requireAllBits, myClosestCallback, context, &minDistSqr); ``` The callback receives the current minimum squared distance and returns the squared distance to the user object inside the proxy, allowing the tree to prune distant branches. ### Ray Cast ```c b3TreeStats stats = b3DynamicTree_RayCast( &tree, &rayInput, maskBits, requireAllBits, myRayCastCallback, context); ``` The callback returns a new `maxFraction`. Return 0 to stop, a fraction less than `input->maxFraction` to clip the ray (e.g. for closest-hit semantics), or `input->maxFraction` to continue unclipped. ### Box Cast ```c b3TreeStats stats = b3DynamicTree_BoxCast( &tree, &boxCastInput, maskBits, requireAllBits, myBoxCastCallback, context); ``` The tree sweeps the AABB in `boxCastInput`; the caller folds the cast shape's radius (and any world origin) into that box. The callback then does the precise narrow-phase cast against each leaf, taking only the advancing fraction from the tree. `b3TreeStats` reports `nodeVisits` and `leafVisits` for performance profiling. The tree can be rebuilt with `b3DynamicTree_Rebuild` to reclaim quality after many insertions, and saved/loaded with `b3DynamicTree_Save` / `b3DynamicTree_Load` for debugging. Normally you will not use `b3DynamicTree` directly. For world-level ray casts and overlap queries use `b3World_CastRay`, `b3World_OverlapAABB`, and `b3World_OverlapShape`. See the `DynamicTree` sample for direct usage examples. --- ## File: docs/compound.md # Compound Shapes A compound is a single immutable shape that aggregates many child primitives — spheres, capsules, convex hulls, and triangle meshes — under one internal AABB tree. To the simulation it appears as one shape, but collision queries descend the internal tree and test individual children, so the cost scales with what is actually touched rather than the total child count. ## Design Intent Compounds are **static-body only** and **immutable after creation**. They are not a runtime mutation primitive. The intended workflow is: 1. Author geometry offline (level mesh, terrain tile, building shell). 2. Bake the compound once with `b3CreateCompound`. 3. Serialize it to a byte buffer with `b3ConvertCompoundToBytes` and store it on disk or in a streaming cache. 4. At runtime, load the bytes and reconstruct the compound with `b3ConvertBytesToCompound`, then attach it to a static body. This makes compounds well-suited to open-world streaming: tiles are baked offline, kept in memory as flat byte buffers (which the engine uses directly without copying), and attached to static bodies when a region loads, then detached when it unloads. Do not use a compound on dynamic or kinematic bodies — `b3CreateCompoundShape` enforces static-only attachment. ## Building a Compound Fill a `b3CompoundDef` with arrays of child-shape descriptors: ```c // Child type descriptors b3CompoundCapsuleDef capsuleDefs[N_CAPSULES]; b3CompoundHullDef hullDefs[N_HULLS]; b3CompoundMeshDef meshDefs[N_MESHES]; b3CompoundSphereDef sphereDefs[N_SPHERES]; // Fill capsuleDefs[i].capsule, .material ... // Fill hullDefs[i].hull, .transform, .material ... // Fill meshDefs[i].meshData, .transform, .scale, .materials, .materialCount ... // Fill sphereDefs[i].sphere, .material ... b3CompoundDef def; def.capsules = capsuleDefs; def.capsuleCount = N_CAPSULES; def.hulls = hullDefs; def.hullCount = N_HULLS; def.meshes = meshDefs; def.meshCount = N_MESHES; def.spheres = sphereDefs; def.sphereCount = N_SPHERES; b3Compound* compound = b3CreateCompound(&def); ``` `b3CreateCompound` clones all input data into the compound. The source arrays can be freed immediately after the call. Mesh children share the mesh pointer rather than cloning triangle data — the `b3MeshData` must remain valid for the lifetime of the compound. Triangle materials are limited to `B3_MAX_COMPOUND_MESH_MATERIALS` (4) slots per mesh child; if your mesh needs more materials, attach it as a standalone mesh shape on the static body instead. ## The b3Compound Structure ```c typedef struct b3Compound { uint64_t version; // versioned against the tree/mesh/hull formats int byteCount; // total size when serialized // internal tree, child arrays, material table // ... (treat as opaque) } b3Compound; ``` `version` is a compile-time constant (`B3_COMPOUND_VERSION`) that incorporates the dynamic tree, mesh, and hull format versions. A version mismatch at load time means the bytes were baked with a different engine version and cannot be used. ## Serialization Convert a live compound to a self-contained byte buffer: ```c uint8_t* bytes = b3ConvertCompoundToBytes(compound); // compound is now in an unusable state; its pointers have been nullified. // Write bytes[0 .. compound->byteCount - 1] to disk. ``` Reconstruct from bytes at runtime (zero-copy; bytes must remain in scope): ```c b3Compound* compound = b3ConvertBytesToCompound(bytes, byteCount); ``` The bytes are mutated in place to fixup internal pointers. Multiple static bodies can use the same byte buffer simultaneously (instancing), since the compound itself holds no per-body state. ## Attaching to a Static Body ```c b3BodyDef bodyDef = b3DefaultBodyDef(); bodyDef.type = b3_staticBody; bodyDef.position = tileOrigin; b3BodyId bodyId = b3CreateBody(worldId, &bodyDef); b3ShapeDef shapeDef = b3DefaultShapeDef(); b3ShapeId shapeId = b3CreateCompoundShape(bodyId, &shapeDef, compound); ``` `b3CreateCompoundShape` asserts that the body is static. ## Querying a Compound Compute the world AABB: ```c b3AABB aabb = b3ComputeCompoundAABB(compound, bodyTransform); ``` Query child shapes by AABB: ```c typedef bool b3CompoundQueryFcn(const b3Compound* compound, int childIndex, void* context); b3QueryCompound(compound, queryAABB, MyChildCallback, ctx); ``` Access a specific child by index: ```c b3ChildShape child = b3GetCompoundChild(compound, childIndex); // child.type tells you capsule / hull / mesh / sphere // child.transform is the child's local transform within the compound ``` Individual typed accessors are also available: ```c b3CompoundCapsule cc = b3GetCompoundCapsule(compound, i); b3CompoundHull ch = b3GetCompoundHull(compound, i); b3CompoundMesh cm = b3GetCompoundMesh(compound, i); b3CompoundSphere cs = b3GetCompoundSphere(compound, i); const b3SurfaceMaterial* mats = b3GetCompoundMaterials(compound); ``` ## Teardown Destroy a live compound (not needed if you are using the byte-buffer path, where you manage the buffer lifetime yourself): ```c b3DestroyCompound(compound); ``` Do not call `b3DestroyCompound` on a compound reconstructed from bytes with `b3ConvertBytesToCompound` — free the byte buffer instead. --- ## File: docs/faq.md # FAQ ## What is Box3D? Box3D is a feature rich 3D rigid body physics engine, written in C17 by Erin Catto. It has been used in games and game engines as a 3D counterpart to the well-established Box2D engine. Box3D uses the [MIT license](https://en.wikipedia.org/wiki/MIT_License) and can be used free of charge. Credit should be included if possible. Support is [appreciated](https://github.com/sponsors/erincatto). ## What platforms does Box3D support? Box3D is developed using C17. It is portable and can be compiled for any platform with a conforming C17 compiler. Erin Catto maintains the C version. Community ports and bindings for other languages are not officially supported. ## Who makes it? Erin Catto is the creator and primary author of Box3D. It is an open source project, and accepts community feedback via [GitHub Issues](https://github.com/erincatto/box3d/issues) and [GitHub Discussions](https://github.com/erincatto/box3d/discussions). ## How do I get help? You should read the documentation and the rest of this FAQ first. Also, you should study the examples included in the source distribution. Then you can visit the [Discord](https://discord.gg/NKYgCBP) to ask any remaining questions. Please do not message or email Erin Catto directly for support. It is best to ask questions on the Discord server so that everyone can benefit from the discussion. ## Documentation ### Why isn't a feature documented? If you grab the latest code from the git main branch you will likely find features that are not documented in the manual. New features are added to the manual after they are mature and a new point release is imminent. However, all major features added to Box3D are accompanied by example code in the samples application to test the feature and show the intended usage. ## Prerequisites ### Programming You should have a working knowledge of C before you use Box3D. You should understand functions, structures, and pointers. There are plenty of resources on the web for learning C. You should also understand your development environment: compilation, linking, and debugging. ### Math and Physics You should have a basic knowledge of rigid bodies, force, torque, and impulses. In 3D you will also encounter quaternions for orientation and 3x3 inertia tensors for rotational dynamics. If you come across a math or physics concept you don't understand, please read about it on Wikipedia. Visit this [page](https://box2d.org/publications/) if you want a deeper knowledge of the algorithms used in Box3D. ### Working with Unreal You can use Box3D in Unreal as a plug-in. Here is the module file I use: [Box3D module](https://gist.github.com/erincatto/6f08df5f1e6e9a79fb4be6298a3e1125) Unreal uses centimeters as units while Box3D has tolerances tuned for meters. You can either scale dimensions you send and recieve from Box3D or you can use this to set the length units: ```c b3SetLengthUnitsPerMeter(100.0f); ``` This doesn't scale the units. Instead it scales the tolerances used internally in Box3D to be more appropriate for centimeters. Another issue is that Box3D is right-handed while Unreal is left-handed. Box3D has way too much math for it to be reasonable to support a left-handed option. However, it doesn't matter. In practice you don't need to do any conversions. Box3D will run in a mirrored world and the outputs from Box3D are fully compatible with the Unreal coordinates. I would just be careful about using Box3D math functions like `b3Cross` and expecting it to give the same result as the Unreal cross product. ## API ### What units does Box3D use? Box3D is tuned for meters-kilograms-seconds (MKS). This is recommended as the unit system for your game. However, you may use different units if you are careful. Call `b3SetLengthUnitsPerMeter()` at startup to change the length unit. ### What coordinate system does Box3D use? Box3D has no built-in notion of up. The gravity vector in `b3WorldDef` can point in any direction. The default is `(0, -10, 0)` (negative Y is down) but you can set it to whatever suits your application. ### Why don't you use this awesome language? Box3D is designed to be portable and easy to wrap with other languages, so I decided to use C17. I used C17 to get support for atomics. ### Can I use Box3D in a DLL? Yes. See the CMake option `BUILD_SHARED_LIBS`. ### Is Box3D thread-safe? No. Box3D will likely never be fully thread-safe from the outside. Box3D has a large API and trying to make such an API thread-safe would have a large performance and complexity impact. However, you can call read-only functions from multiple threads. For example, all the spatial query functions are read-only. Box3D does use multithreading internally during `b3World_Step`. You supply your own task system via the `enqueueTask` and `finishTask` callbacks in `b3WorldDef`. ## Build Issues ### Why doesn't my code compile and/or link? There are many reasons why a build can go bad. Here are a few that have come up: * Using old Box3D headers with new code * Not linking the Box3D library with your application * Using old project files that don't include some new source files ## Rendering ### What are Box3D's rendering capabilities? Box3D is only a physics engine. How you draw stuff is up to you. ### But the samples application draws stuff Visualization is very important for debugging collision and physics. The samples application helps test Box3D and gives you examples of how to use Box3D. The samples are not part of the Box3D library. ### How do I draw shapes? Fill out a `b3DebugDraw` struct with your drawing callbacks and call `b3World_Draw(worldId, &draw, maskBits)`. The mask bits let you filter which shape categories are drawn. ## Accuracy Box3D uses approximate methods for a few reasons. * Performance * Some differential equations don't have known solutions * Some constraints cannot be determined uniquely What this means is that constraints are not perfectly rigid and sometimes you will see some bounce even when the restitution is zero. Box3D uses [Gauss-Seidel](https://en.wikipedia.org/wiki/Gauss%E2%80%93Seidel_method) to approximately solve constraints. Box3D also uses [Semi-implicit Euler](https://en.wikipedia.org/wiki/Semi-implicit_Euler_method) to approximately solve the differential equations. Box3D also does not have exact collision between dynamic shapes. Slow moving shapes may have small overlap for a few time steps. In extreme stacking scenarios, shapes may have sustained overlap. ## Making Games ### Tile / Voxel Based Environments Using many boxes for terrain may not work well because box-like characters can get snagged on internal corners. Box3D provides capsules and convex hulls that may work better for characters. Consider the character mover API (`b3World_CastMover`, `b3World_CollideMover`) for smooth first/third person movement. ### Asteroid Type Coordinate Systems Box3D does not have any support for coordinate frame wrapping. You would likely need to customize Box3D for this purpose. ## Determinism ### Is Box3D deterministic? For the same input Box3D will reproduce any simulation. Box3D does not use any random numbers nor base any computation on random events (such as timers, etc). Box3D is also deterministic under multithreading. A simulation using two threads will give the same result as eight threads. Box3D inherits cross-platform determinism from its design: floating-point contraction is disabled (`-ffp-contract=off`) and IEEE 754 arithmetic is relied upon consistently. However, Box3D does not have rollback determinism. There is no mechanism to set a world back to a prior state and then resume simulation expecting identical results. Box3D caches a lot of internal state to improve simulation stability and performance. ### But I really want determinism This naturally leads to the question of fixed-point math. Box3D does not support fixed-point math. Fixed-point math is slower and more tedious to develop, and I have chosen not to use it. ## What are the common mistakes made by new users? * Using non-metric units instead of meters * Expecting Box3D to give pixel-perfect results * Testing their code in release mode (always use Debug for testing — it enables assertions and validation) * Not learning C before using Box3D * Confusing b2 (Box2D) and b3 (Box3D) symbols when reading documentation --- ## File: docs/foundation.md # Foundations Box3D provides minimal base functionality for allocation hooks and vector math. The C interface allows most runtime data and types to be defined internally in the `src` folder. ## Assertions Box3D will assert on bad input. This includes things like sending in NaN or infinity for values. It will assert if you use negative values for things that should only be positive, such as density. Box3D will also assert if an internal bug is detected. For this reason, it is advisable to build Box3D from source. The library compiles in about a second. You may wish to capture assertions in your application. In that case use `b3SetAssertFcn()`. This lets you override the debugger break and/or perform your own error handling. ## Allocation Box3D uses memory efficiently and minimizes per-frame allocations by pooling memory. The engine quickly adapts to the simulation size. After the first step or two of simulation there should be no further per-frame allocations. As bodies, shapes, and joints are created and destroyed, their memory is recycled. Internally all this data is stored in contiguous arrays. When an object is destroyed, the array element is marked empty. When an object is created it fills an empty slot via an efficient free list. Once the internal memory pools are initially filled, the only allocations should be for sleeping islands, since their data is copied out of the main simulation. Those allocations are generally infrequent. You can provide a custom allocator using `b3SetAllocator()` and query the total bytes currently allocated using `b3GetByteCount()`. ## Version The `b3Version` structure holds the current version so you can query it at run-time using `b3GetVersion()`. ```c b3Version version = b3GetVersion(); printf("Box3D version %d.%d.%d\n", version.major, version.minor, version.revision); ``` ## Vector Math Box3D includes a vector math library covering types `b3Vec3`, `b3Quat`, `b3Transform`, `b3Matrix3`, and `b3AABB`. The library is designed to suit the internal needs of Box3D and its interface. All members are exposed, so you can use them freely in your application. ### b3Vec3 Three-component float vector with fields `x`, `y`, `z`. Useful constants and operations: ```c b3Vec3 a = {1.0f, 0.0f, 0.0f}; // inline init b3Vec3 z = b3Vec3_zero; // {0,0,0} b3Vec3 c = b3Add(a, b); // component-wise add b3Vec3 d = b3Sub(a, b); // subtract b3Vec3 e = b3MulSV(2.0f, a); // scalar * vector float f = b3Dot(a, b); // dot product b3Vec3 g = b3Cross(a, b); // cross product float h = b3Length(a); // Euclidean length b3Vec3 n = b3Normalize(a); // unit vector b3Vec3 p = b3Perp(a); // any perpendicular unit vector b3Vec3 q = b3Lerp(a, b, 0.5f); // linear interpolation ``` ### b3Quat Unit quaternion representing orientation. Stored as a vector part `q.v` (x, y, z) and a scalar part `q.s`. The identity quaternion is `b3Quat_identity`. Useful operations: ```c // Construct from axis (must be unit) and angle in radians b3Quat q = b3MakeQuatFromAxisAngle(axis, radians); // Rotate a vector b3Vec3 r = b3RotateVector(q, v); // Inverse-rotate a vector (equivalent to rotating by the conjugate) b3Vec3 s = b3InvRotateVector(q, v); // Compose two rotations: apply q2 first, then q1 b3Quat qc = b3MulQuat(q1, q2); // Conjugate (same as inverse for a unit quaternion) b3Quat qi = b3Conjugate(q); // Extract axis-angle float angle; b3Vec3 axis = b3GetAxisAngle(&angle, q); // Total rotation angle (ignoring axis) float totalAngle = b3GetQuatAngle(q); // Convert to rotation matrix b3Matrix3 m = b3MakeMatrixFromQuat(q); // Normalized linear interpolation b3Quat qi = b3NLerp(q1, q2, alpha); ``` Because orientation in 3D is three-dimensional, there is no single scalar angle as there was in 2D. Always work with the full quaternion or the derived matrix. ### b3Transform A rigid transform: a position vector `t.p` (`b3Vec3`) combined with an orientation `t.q` (`b3Quat`). The identity transform is `b3Transform_identity`. ```c // Apply transform to a point in the transform's local frame -> world frame b3Vec3 world = b3TransformPoint(t, localPoint); // Inverse: world frame -> local frame b3Vec3 local = b3InvTransformPoint(t, worldPoint); // Compose: t_child expressed in t_parent's frame b3Transform combined = b3MulTransforms(t_parent, t_child); // Relative transform: t_b expressed in t_a's frame b3Transform rel = b3InvMulTransforms(t_a, t_b); // Invert a transform b3Transform inv = b3InvertTransform(t); ``` ### b3Matrix3 3×3 matrix stored as three column vectors `cx`, `cy`, `cz`. Primarily used for inertia tensors and rotation matrices. Useful operations include `b3MulMV` (matrix-vector multiply), `b3MulMM` (matrix-matrix multiply), `b3Transpose`, `b3InvertMatrix`, and `b3MakeMatrixFromQuat`. ### b3AABB Axis-aligned bounding box with `lowerBound` and `upperBound` as `b3Vec3`. Helpers include `b3AABB_Overlaps`, `b3AABB_Contains`, `b3AABB_ContainsPoint`, `b3AABB_Union`, `b3AABB_Center`, `b3AABB_Extents`, `b3AABB_Inflate`, and `b3AABB_Transform`. ## Multithreading {#multi} Box3D has been optimized for multithreading. Multithreading is not required and by default Box3D will run single-threaded. If performance is important for your application, you should consider using the multithreading features. ### Internal scheduler Box3D has a built-in task scheduler that creates threads. You can use the built-in scheduler by setting the worker count in the world definition. This example shows how to use 4 workers. In this case Box3D will create 3 threads, and count the thread that calls `b3World_Step` as the fourth worker. I recommend to use the core count of the CPU as the worker count, not counting hyper-threads or efficiency cores. ```c b3WorldDef worldDef = b3DefaultWorldDef(); worldDef.workerCount = 4; ``` ### External scheduler You can optionally connect your own task scheduler if you want more control. Multithreading is established for each Box3D world you create and must be hooked up to the world definition. See `b3TaskCallback()`, `b3EnqueueTaskCallback()`, and `b3FinishTaskCallback()` for more details. Also see `b3WorldDef::workerCount`, `b3WorldDef::enqueueTask`, and `b3WorldDef::finishTask`. ```c // Implement b3EnqueueTaskCallback void* MyEnqueueTask(b3TaskCallback* task, void* taskContext, void* userContext, const char* taskName) { MyTask* t = AllocTask(); t->task = task; t->taskContext = taskContext; Scheduler* myScheduler = (Scheduler*)userContact; SubmitToThreadPool(myScheduler, t); return t; } // Implement b3FinishTaskCallback void MyFinishTask(void* userTask, void* userContext) { MyTask* t = (MyTask*)userTask; WaitForCompletion(t); FreeTask(t); } b3WorldDef worldDef = b3DefaultWorldDef(); worldDef.enqueueTask = MyEnqueueTask; worldDef.finishTask = MyFinishTask; worldDef.userTaskContext = myScheduler; worldDef.workerCount = GetMyWorkerCount(); ``` ### Threading model The multithreading design for Box3D is focused on [data parallelism](https://en.wikipedia.org/wiki/Data_parallelism). The goal is to use multiple cores to finish the world simulation as fast as possible. Box3D multithreading is not designed for [task parallelism](https://en.wikipedia.org/wiki/Task_parallelism). Often in games you have a render thread or an audio thread doing work in isolation from the main thread. Those are examples of task parallelism. So when you design your game loop, you should let Box3D *go wide* and use multiple cores to finish its work quickly, without other threads interacting with the Box3D world at the same time. It is expected that the thread that calls `b3World_Step` participates in making progress. Do not call `b3World_Step` and park that fiber. Tasks will only be enqueued on the thread that calls `b3World_Step`. `MyFinishTask` must block until the task has completed. Ideally your scheduler is helping make progress when `MyFinishTask` is called. ### Avoiding race conditions In a multithreaded environment you must be careful to avoid [race conditions](https://en.wikipedia.org/wiki/Race_condition). Modifying the world while it is simulating will lead to unpredictable behavior and is never safe. It is also not safe to read data from a Box3D world while it is simulating. Box3D may move data structures to improve cache performance, so you could easily read garbage. > **Caution**: > Do not perform read or write operations on a Box3D world during `b3World_Step()`. > Do not write to the Box3D world from multiple threads. Any operation that wakes > a body is not thread-safe. It *is safe* to do ray-casts, shape-casts, and overlap tests from multiple threads outside of `b3World_Step()`. Generally any read-only operation is safe to do multithreaded outside of `b3World_Step()`. This can be very useful if you have multithreaded game logic. ## Multithreading Multiple Worlds Some applications may wish to create multiple Box3D worlds and simulate them on different threads. This works fine because Box3D has very limited use of globals. There are a few caveats: - You will get a race condition if you create or destroy Box3D worlds from multiple threads. Use a mutex to guard those operations. - If you simulate multiple Box3D worlds simultaneously, they should probably not share a task system. Otherwise you risk preemption between worlds competing for the same workers. - Any callbacks you hook up to Box3D must be thread-safe, including memory allocators. - All the limitations for single-world simulation still apply. --- ## File: docs/hello.md # Hello Box3D {#hello} The Box3D distribution includes a Hello World unit test written in C. The test creates a large static ground box and a small dynamic box. This code does not contain any graphics. All you will see is text output in the console showing the box's position over time. This is a good example of how to get up and running with Box3D. ## Creating a World Every Box3D program begins with the creation of a world object. The world is the physics hub that manages memory, objects, and simulation. The world is represented by an opaque handle called `b3WorldId`. It is easy to create a Box3D world. First, create the world definition: ```c b3WorldDef worldDef = b3DefaultWorldDef(); ``` The world definition is a temporary object you can create on the stack. The function `b3DefaultWorldDef()` populates the world definition with default values. This is necessary because C does not have constructors and zero-initializing `b3WorldDef` is not appropriate. Box3D has no built-in concept of *up*. The gravity vector is a `b3Vec3` and can point in any direction. Convention in Box3D examples uses +Y as the up axis. The default gravity is already `{0, -10, 0}`, but it can be set explicitly: ```c worldDef.gravity = (b3Vec3){ 0.0f, -10.0f, 0.0f }; ``` Now create the world: ```c b3WorldId worldId = b3CreateWorld(&worldDef); ``` World creation copies all the data it needs from the definition, so the definition can go out of scope immediately afterward. ## Creating a Ground Box Bodies are built using the following steps: 1. Define a body with position, type, etc. 2. Use the world id to create the body. 3. Build a hull shape with the desired extents. 4. Create the shape on the body. For step 1, create the ground body definition and set its initial position: ```c b3BodyDef groundBodyDef = b3DefaultBodyDef(); groundBodyDef.position = (b3Vec3){ 0.0f, -10.0f, 0.0f }; ``` For step 2, use the world id to create the ground body. Bodies are static by default, meaning they have zero mass, never move, and do not collide with other static bodies. ```c b3BodyId groundId = b3CreateBody(worldId, &groundBodyDef); ``` Notice that `worldId` is passed by value. Ids are small structures and are always passed by value. For steps 3 and 4, build a box hull and attach it. Box3D uses convex hulls for box shapes. The `b3MakeBoxHull` helper takes three **half-extents** (hx, hy, hz), so the ground slab below is 100 units wide in X, 20 units tall in Y, and 100 units deep in Z: ```c b3BoxHull groundBox = b3MakeBoxHull(50.0f, 10.0f, 50.0f); b3ShapeDef groundShapeDef = b3DefaultShapeDef(); b3CreateHullShape(groundId, &groundShapeDef, &groundBox.base); ``` The `.base` field holds the `b3HullData` that `b3CreateHullShape` expects. Box3D copies the hull data into a shared internal database, so `groundBox` does not need to outlive the call. Do not call `b3DestroyHull` on a `b3BoxHull`; it is stack-allocated. Box3D is tuned for meters, kilograms, and seconds, so the extents above are in meters. The engine works best when objects are sized like real-world objects (a barrel is roughly 1 m tall). Simulating glaciers or dust particles would push the limits of single-precision floating point. Every shape must have a parent body, even static shapes. You can attach multiple shapes to one body. A shape's world transform is inherited from its parent body; there is no independent shape transform. ## Creating a Dynamic Body Creating a dynamic body follows the same steps. The key difference is setting the body type to `b3_dynamicBody` and giving the shape a non-zero density. ```c b3BodyDef bodyDef = b3DefaultBodyDef(); bodyDef.type = b3_dynamicBody; bodyDef.position = (b3Vec3){ 0.0f, 4.0f, 0.0f }; b3BodyId bodyId = b3CreateBody(worldId, &bodyDef); ``` > **Caution**: > You must set the body type to `b3_dynamicBody` if you want the body to > move in response to forces such as gravity. Create a unit cube hull and a shape definition with density and friction: ```c b3BoxHull dynamicBox = b3MakeCubeHull(1.0f); b3ShapeDef shapeDef = b3DefaultShapeDef(); shapeDef.density = 1.0f; shapeDef.baseMaterial.friction = 0.3f; b3CreateHullShape(bodyId, &shapeDef, &dynamicBox.base); ``` `b3MakeCubeHull(r)` is a convenience that produces a cube with half-extent `r` on all three axes, equivalent to `b3MakeBoxHull(r, r, r)`. > **Caution**: > A dynamic body should have at least one shape with a non-zero density. > Otherwise you will get unexpected behavior. That completes initialization. We are now ready to simulate. ## Simulating the World Box3D uses a numerical integrator that advances the simulation by discrete time steps. A fixed time step of 1/60 seconds (60 Hz) is recommended for most games. Avoid tying the time step to your frame rate; a variable time step produces variable results that are hard to debug. ```c float timeStep = 1.0f / 60.0f; ``` In addition to integration, Box3D uses a constraint solver. Box3D advances through the time step in several *sub-steps*, giving each constraint multiple chances to react. Four sub-steps is the suggested value: ```c int subStepCount = 4; ``` At 60 Hz with 4 sub-steps the constraints run at 240 Hz internally. More sub-steps improve accuracy at the cost of performance. The simulation loop calls `b3World_Step` once per game tick: ```c for (int i = 0; i < 90; ++i) { b3World_Step(worldId, timeStep, subStepCount); b3Vec3 position = b3Body_GetPosition(bodyId); b3Quat rotation = b3Body_GetRotation(bodyId); printf("%4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f\n", position.x, position.y, position.z, rotation.v.x, rotation.v.y, rotation.v.z, rotation.s); } ``` `b3Body_GetPosition` returns a `b3Vec3` with the body origin in world space. `b3Body_GetRotation` returns a `b3Quat` — a unit quaternion stored as a vector part `q.v` (x, y, z) and a scalar part `q.s`. There is no single angle to extract as there was in 2D; orientation in 3D requires the full quaternion. To convert to an axis-angle representation use `b3GetAxisAngle`: ```c float angle; b3Vec3 axis = b3GetAxisAngle(&angle, rotation); ``` `angle` is in radians and `axis` is the unit rotation axis. Use `b3MakeMatrixFromQuat` when you need a 3×3 rotation matrix, for example to feed a renderer. The output should show the box falling from y = 4 and coming to rest on the ground at approximately y = 1 (the box half-height sits at y = 0 + 1 after the ground surface at y = 0): ``` 0.00 4.00 0.00 ... 0.00 3.99 0.00 ... 0.00 3.98 0.00 ... ... 0.00 1.25 0.00 ... 0.00 1.13 0.00 ... 0.00 1.01 0.00 ... ``` For advice on managing a fixed simulation rate alongside a variable render rate, see [Fix Your Timestep!](https://gafferongames.com/post/fix_your_timestep/). ## Multithreading (optional) By default Box3D runs single-threaded. The `b3DefaultWorldDef` leaves `workerCount` at 1 and the task callbacks null, which is fine for getting started. When performance matters, Box3D can drive a task system. Supply `workerCount` plus `enqueueTask`, `finishTask`, and `userTaskContext` on the world definition before calling `b3CreateWorld`. See the Foundations page for details. ## Cleanup When you are done with the simulation, destroy the world: ```c b3DestroyWorld(worldId); ``` This efficiently destroys all bodies, shapes, and joints in the simulation. --- ## File: docs/large_worlds.md # Large Worlds (Double Precision) {#large-worlds} Box3D can be built with double precision world positions for large worlds: simulations that range far from the origin, where a single precision float can no longer resolve a position. At a coordinate of 1e7 meters a float has a step of about one meter, so bodies snap to a coarse grid and contacts jitter. Double precision keeps full sub-millimeter resolution out to planetary distances. Only the world position boundary is double. Velocities, forces, shapes, contact manifolds, the contact solver, and the broad-phase tree all stay float. This is the same boundary design Jolt Physics uses for its big-world mode: doubles carry the absolute position, everything inside one body's frame stays float, and the per-step motion the solver integrates is small and meters-scale where float precision is ample. The cost is a few percent, not the 2x of an all-double build. Double precision is **off by default**. With it off every double-precision type collapses to its float counterpart through a typedef and the boundary helpers reduce to plain float operations, so a float build behaves exactly as Box3D always has, with no measurable cost. This implementation is inspired by [Jolt](https://jrouwe.github.io/JoltPhysics/index.html#big-worlds). ## Enabling it Set the CMake option: ```cmake set(BOX3D_DOUBLE_PRECISION ON) ``` The define is propagated to consumers as a `PUBLIC` compile definition, so anything that links Box3D through CMake sees the same precision mode in the headers and cannot mismatch. For non-CMake consumers there is a link-time guard: a float application linked against a double-precision library (or the reverse) fails to link on the first Box3D call rather than miscompiling silently. At runtime `b3IsDoublePrecision()` reports which mode the library was built in, for bindings and diagnostics. ## The two world-position types Double precision adds two types and leaves the existing math types alone: - `b3Pos` — a world position. Three doubles in large world mode, an alias for `b3Vec3` otherwise. - `b3WorldTransform` — a world transform: a `b3Pos` translation and a float `b3Quat` rotation. An alias for `b3Transform` otherwise. `b3Vec3`, `b3Quat`, `b3Transform`, and `b3AABB` stay float in both modes. `b3Transform` remains the type for local and relative frames; `b3WorldTransform` is used for world space. Rotations are float quaternions in both modes, so the trig and the cross-platform determinism properties are unchanged. The public API uses these types wherever it accepts or returns a world position: `b3BodyDef.position`, `b3Body_GetPosition` / `b3Body_GetTransform`, `b3Body_SetTransform`, `b3Body_GetWorldPoint` / `b3Body_GetLocalPoint`, `b3Body_GetWorldCenter`, the explosion and ray-cast origins, contact and ray-cast result points, and the body move event. With double precision off these are all the float types they have always been, so existing code compiles unchanged. With double precision **on**, `b3Pos` and `b3Vec3` are distinct structs by design. Code that passed a `b3Vec3` where a world position is now required no longer compiles, which is the intended cost: enabling large world mode is a deliberate source migration, and the compiler points at every site that needs a conversion. Helpers cover the boundary: ```c b3Pos p = b3ToPos( v ); // float vector -> world position b3Vec3 v = b3ToVec3( p ); // world position -> float vector (lossy far from origin) b3Vec3 d = b3SubPos( a, b ); // a - b, demoted to float (the precision boundary) b3Pos q = b3OffsetPos( p, d ); // p + d float x = b3RoundDownFloat( p.x ); // conservative narrowing, pair with b3RoundUpFloat to // build a float box that always contains double bounds ``` ## Operating range Full simulation correctness holds everywhere `b3Pos` can represent. Body integration, the contact solver, joints, and continuous collision all run in float relative to each body's own moving frame, so a stack settles and a bullet is caught the same way at 1e7 as at the origin. The practical limit comes from the broad phase, which stays float and stores conservative (outward-rounded) float bounds. Far from the origin the float bound quantization grows: about one meter at 1e7, sixteen meters at 1e8. Overlapping shapes always still produce a pair, so correctness is preserved, but beyond roughly 1e7 to 1e8 meters the broad phase reports extra false pairs and loses some margin hysteresis, which costs performance. Stay within about ±1e7 to ±1e8 meters. ## Queries far from the origin Every spatial query takes a caller supplied `b3Pos` origin and re-differences each shape against a nearby base at full precision, so hit points and fractions stay accurate far from the world origin. The one shared limit is the broad phase: the tree is traversed in conservative outward rounded float, so it never misses a pair, but a cast that grazes a shape by less than a coordinate float ULP far from the origin can still miss at the tree level. Only the explosion is a pure float carve-out. - `b3World_OverlapShape`, `b3World_CastShape`, `b3World_CastMover`, and `b3World_CollideMover` take a `b3Pos` origin. Their proxy, mover, and returned planes are relative to that origin and each shape is re-differenced against it in float, so a query around a mover at 1e7 is as precise as one at the origin. Shape cast hit points come back as `b3Pos`. - `b3World_CastRay` / `b3World_CastRayClosest` take a `b3Pos` origin and re-difference each shape against its body in full precision, so hit points and fractions stay accurate far from the origin. The tree traversal itself is float (see the broad phase limit above). Hit points come back as `b3Pos`. - `b3Shape_RayCast` takes a `b3Pos` origin and a translation and returns a `b3WorldCastOutput` whose hit point is a world `b3Pos`, re-centered on the origin for full precision. - `b3World_Explode` resolves the per-shape impulse in float around the explosion position. The character controller (`b3World_CastMover` / `b3World_CollideMover`) drives this: pass the character's world position as the origin and the mover stays precise even at 1e7. The only remaining float carve-out is the broad phase tree traversal, which Box2D shares. ## Debug drawing `b3World_Draw` hands every callback world coordinates in the double-capable `b3Pos` and `b3WorldTransform` types, so the engine stays camera agnostic. The host shifts into its own camera frame inside the callbacks: keep a draw origin near the camera and difference against it in double before the coordinates demote to float, and a distant scene draws crisply instead of snapping to the coarse float grid around the absolute origin. A zero origin reproduces absolute coordinates and leaves a near-origin scene unchanged. The sample app sets the draw origin from the camera eye each frame, and the Large World sample uses it to render a stack at 1e7 with no jitter. ## Determinism Both precision modes are internally deterministic and reproduce across worker counts. The numerics differ between modes — double precision accumulates body positions in double, so a body settles and sleeps on a slightly different step and the state hash differs — so a double-precision build is not bit-identical to a float build. The `DeterminismTest` carries a separate set of expected values for each mode. ## SIMD Double precision is orthogonal to SIMD. The wide types used in the broad phase and mesh collision stay 4-wide float, and the contact solver is untouched in both modes. There is no double-precision SIMD path and none is needed: the hot interior never sees an absolute world coordinate. --- ## File: docs/loose_ends.md # Loose Ends ## User Data Bodies, shapes, and joints allow you to attach user data as a `void*`. This is useful when you receive a body, shape, or joint id from an event or query and need to map it back to a game object. A common pattern is storing a game-entity pointer on the body: ```c GameEntity* entity = GameCreateEntity(); b3BodyDef bodyDef = b3DefaultBodyDef(); bodyDef.userData = entity; entity->bodyId = b3CreateBody(myWorldId, &bodyDef); ``` The circular reference lets you go from entity to body and back. Some typical uses: - Applying damage to an entity from a contact event. - Triggering a scripted event when a body enters a region. - Cleaning up game state when a joint is destroyed. Keep the type consistent. If one body stores a `GameEntity*`, all bodies should. Mixing pointer types and casting without a discriminant leads to crashes. Setters and getters: ```c // Body b3Body_SetUserData(bodyId, ptr); void* ptr = b3Body_GetUserData(bodyId); // Shape b3Shape_SetUserData(shapeId, ptr); void* ptr = b3Shape_GetUserData(shapeId); // Joint b3Joint_SetUserData(jointId, ptr); void* ptr = b3Joint_GetUserData(jointId); // World b3World_SetUserData(worldId, ptr); void* ptr = b3World_GetUserData(worldId); ``` ## Coordinate Systems Box3D uses a right-handed coordinate system. Positive Y is up by default, meaning the default gravity vector points in the negative Y direction (`{0, -9.8f, 0}`). Nothing in the engine hard-codes this — gravity is just a `b3Vec3` set on the world and you can orient it however your application requires. Use MKS units: meters, kilograms, seconds, and radians. The solver is tuned for objects in the range of roughly 0.1 to 10 meters. Very small or very large objects relative to this range can degrade numerical stability. If your content is authored at a different unit scale, apply a single conversion factor at the boundary between your asset pipeline and Box3D, not scattered throughout the simulation code. Bodies in Box3D have 6 degrees of freedom. Position is a `b3Vec3`, orientation is a `b3Quat`, angular velocity and torque are `b3Vec3` values. The rotational inertia tensor is a `b3Matrix3`. ## Debug Drawing Implement the function pointers in `b3DebugDraw` to get detailed drawing of the Box3D world. The struct lives in `types.h` and has slots for: - `DrawShapeFcn` — draws a shape; receives a user-shape object created by `b3WorldDef::createDebugShape` and the current transform and color. - `DrawSegmentFcn` — draws a line segment. - `DrawTransformFcn` — draws a coordinate frame. - `DrawPointFcn` — draws a point. - `DrawBoundsFcn` — draws an AABB as a wireframe box. - `DrawBoxFcn` — draws an oriented box by extents and transform. - `DrawStringFcn` — draws a world-space label. Category flags on the struct control what gets drawn: | Flag | What it shows | |---|---| | `drawShapes` | Shape geometry | | `drawJoints` | Joint frames and constraints | | `drawJointExtras` | Extra joint information (limits, motors) | | `drawBounds` | Shape AABBs | | `drawMass` | Center-of-mass marker and mass value for dynamic bodies | | `drawBodyNames` | Body name strings | | `drawContacts` | Contact points and anchors | | `drawContactNormals` | Contact normal directions | | `drawContactForces` | Normal impulse magnitudes | | `drawGraphColors` | Constraint-graph color assignment | | `drawContactFeatures` | Raw contact feature indices | | `drawIslands` | Island bounding boxes | Call the draw function after stepping the world: ```c b3DebugDraw draw = b3DefaultDebugDraw(); draw.DrawShapeFcn = MyDrawShape; draw.DrawSegmentFcn = MyDrawSegment; // ... fill remaining callbacks ... draw.drawShapes = true; draw.drawingBounds = myViewAABB; b3World_Draw(worldId, &draw, UINT64_MAX); ``` The `maskBits` argument is tested against each shape's `categoryBits` in the broad-phase tree. Only shapes where `(maskBits & shape->categoryBits) != 0` are visited. Pass `UINT64_MAX` to draw everything, or use a subset of your category bits to limit drawing to specific layers (e.g. skip debris for a clean overhead view). The shape draw path uses a two-callback model set on `b3WorldDef`: `createDebugShape` is called once when a shape is first drawn, giving you a chance to upload GPU geometry; `destroyDebugShape` is called when the shape is modified or destroyed so you can release it. The `DrawShapeFcn` callback receives the opaque handle returned by `createDebugShape`, which enables efficient multi-pass rendering without re-uploading geometry every frame. The samples application demonstrates a complete `b3DebugDraw` implementation. ## Limitations Box3D uses several approximations to simulate rigid body physics efficiently. As a v0.1 engine it is still maturing, so expect rougher edges than Box2D. Current limitations: 1. Extreme mass ratios between connected bodies can cause joint stretching and contact overlap. 2. Soft constraints improve robustness but allow a small amount of flexing in joints and contacts. 3. Continuous collision handles fast-moving dynamic bodies against static geometry (and bullets against dynamic bodies), but not general dynamic-versus-dynamic continuous collision. 4. Continuous collision does not propagate through joints, so fast-moving articulated chains may momentarily stretch. 5. The integrator is semi-implicit Euler, which gives first-order accuracy. Projectile arcs are approximate. Increasing `subStepCount` in `b3World_Step` improves accuracy at a proportional cost. 6. Constraint resolution uses an iterative Gauss-Seidel solver for real-time performance. Collisions are not perfectly rigid and contacts are not pixel-accurate. More sub-steps tighten the result. 7. Mesh and height-field shapes are static-only. They do not participate in dynamic-versus-dynamic contact. 8. The compound shape type is static-body only and immutable after creation. 9. The character mover API is experimental. --- ## File: docs/overview.md # Overview {#mainpage} > **Caution**: > The written part of this manual is a work in progress. > It will be updated as v1.0 approaches. > The reference section should be complete and accurate. Box3D is a 3D rigid body simulation library for games. Programmers can use it in their games to make objects move in realistic ways and make the game world more interactive. From the game engine's point of view, a physics engine is a system for procedural animation. Box3D also provides many collision routines that can be used even when rigid body simulation is not used. There are functions for overlap and cast queries. There is also a bounding volume hierarchy (dynamic tree) that can be used for game specific spatial sorting needs. Box3D is written in portable C17. Most of the types defined in the engine begin with the `b3` prefix. Hopefully this is sufficient to avoid name clashing with your application. ## Prerequisites In this manual I'll assume you are familiar with basic physics concepts, such as mass, force, torque, and impulses. If not, please first consult Google search and Wikipedia. Box2D (the 2D sibling of Box3D) was created as part of a physics tutorial at the Game Developer Conference. You can get those tutorials from the publications section of [box2d.org](https://box2d.org/publications/), which cover the underlying algorithms shared by both engines. Since Box3D is written in C, you are expected to be experienced in C programming. Box3D should not be your first C programming project. You should be comfortable with compiling, linking, and debugging. > **Caution**: > Box3D should not be your first C project. Please learn C > programming, compiling, linking, and debugging before working with > Box3D. There are many resources for this online. ## Scope This manual covers the majority of the Box3D API. However, not every aspect is covered. Please look at the Reference section and samples application included with Box3D to learn more. This manual is only updated with new releases. The latest version of Box3D may be out of sync with this manual. > **Caution**: > This manual applies to the associated release and not necessarily the > latest version on the main branch. ## Feedback and Bugs Please file bugs and feature requests here: [Box3D Issues](https://github.com/erincatto/box3d/issues) You can help to ensure your issue gets fixed if you provide sufficient detail. A testbed example that reproduces the problem is ideal. You can read about the testbed later in this document. There is also a [Discord server](https://discord.gg/NKYgCBP) and [GitHub Discussions](https://github.com/erincatto/box3d/discussions). ## Core Concepts Box3D works with several fundamental concepts and objects. I briefly define these objects here and more details are given later in this document. ### rigid body A chunk of matter that is so strong that the distance between any two bits of matter on the chunk is constant. They are hard like a diamond. In the following discussion I use *body* interchangeably with rigid body. ### shape A shape binds collision geometry to a body and adds material properties such as density, friction, and restitution. A shape puts collision geometry into the collision system (broad-phase) so that it can collide with other shapes. ### constraint A constraint is a physical connection that removes degrees of freedom from bodies. A 3D body has 6 degrees of freedom (three translation coordinates and three rotation coordinates). If I take a body and pin it to the wall (like a pendulum) I have constrained the body to the wall. At this point the body can only rotate about the pin, so the constraint has removed 3 translational degrees of freedom, leaving the body free to swing in 3D about the fixed anchor. ### contact constraint A special constraint designed to prevent penetration of rigid bodies and to simulate friction and restitution. You do not create contact constraints; they are created automatically by Box3D. ### joint constraint This is a constraint used to hold two or more bodies together. Box3D supports several joint types: revolute, prismatic, distance, spherical, weld, wheel, motor, parallel, and filter. Joints may have limits, motors, and/or springs. ### joint limit A joint limit restricts the range of motion of a joint. For example, the human elbow only allows a certain range of angles. ### joint motor A joint motor drives the motion of the connected bodies according to the joint's degrees of freedom. For example, you can use a motor to drive the rotation of an elbow. Motors have a target speed and a maximum force or torque. The simulation will apply the force or torque required to achieve the desired speed. ### joint spring A joint spring has a stiffness and damping. In Box3D spring stiffness is expressed in terms of Hertz or cycles per second. This lets you configure how quickly a spring reacts regardless of the body masses. Joint springs also have a damping ratio to let you specify how quickly the spring will come to rest. ### world A physics world is a collection of bodies, shapes, joints, and contacts that interact together. Box3D supports the creation of multiple worlds which are completely independent. ### solver The physics world has a solver that is used to advance time and to resolve contact and joint constraints. The Box3D solver is a high performance sequential solver that operates in order N time, where N is the number of constraints. ### continuous collision The solver advances bodies in time using discrete time steps. Without intervention this can lead to tunneling. Box3D contains specialized algorithms to deal with tunneling. First, the collision algorithms can interpolate the motion of two bodies to find the first time of impact (TOI). Second, speculative collision is used to create contact constraints between bodies before they touch. ### events World simulation leads to the creation of events that are available at the end of the time step: - body movement events - contact begin and end events - sensor begin and end events - contact hit events These events allow your application to react to changes in the simulation. ## Modules Box3D's primary purpose is to provide rigid body simulation. However, there are math and collision features that may be useful apart from the rigid body simulation. These are provided in the `include` directory. Anything in the `include` directory is considered public, while everything in the `src` directory is considered internal. Public features are supported and you can get help with these on the Discord server. Using internal code directly is not supported. However, feel free to study the code and ask questions. I'm happy to share all the details of how Box3D works internally. ## Units Box3D works with floating point numbers and tolerances have to be used to make Box3D perform well. These tolerances have been tuned to work well with meters-kilogram-second (MKS) units. In particular, Box3D has been tuned to work well with moving shapes between 0.1 and 10 meters. So this means objects between soup cans and buses in size should work well. Static shapes may be up to 50 meters long without trouble. If you have a large world, you should split it up into multiple static bodies. This will improve precision and simulation behavior. > **Caution**: > Box3D is tuned for MKS units. Keep the size of moving objects larger than 1cm. > You'll need to use some scaling system when you render your environment and > actors. Do not use non-metric units unless you understand the implications. Another limitation to consider is overall world size. If your world units become larger than 12 kilometers or so, then the lost precision can affect stability. > **Caution**: > Box3D works best with world sizes less than 12 kilometers. If you are > careful with your simulation tuning, this can be pushed up to around 24 > kilometers, which is much larger than most game worlds. Box3D uses radians for angles. Orientation is stored as a quaternion (`b3Quat`), so there is no concept of a body angle clamped to a range; the full 3D rotation is tracked continuously. > **Caution**: > Box3D uses radians, not degrees. ## Changing the length units Advanced users may change the length unit by calling `b3SetLengthUnitsPerMeter()` at application startup. If you keep Box3D in a shared library, you will need to call this if the shared library is reloaded. It is harder to get support for using Box3D if you change the unit system, because values are harder to communicate and may become non-intuitive. One of the benefits of using MKS units for physics simulation is that you can use real world values to get reasonable results. ## Ids and Definitions Fast memory management plays a central role in the design of the Box3D interface. When you create a world, body, shape or joint, you will receive a handle called an *id*. These ids are opaque and are passed to various functions to access the underlying data. These ids provide some safety. If you use an id after it has been freed you will usually get an assertion. All ids support 64k generations of safety. All ids also have a corresponding function you can call to check if it is valid. When you create a world, body, shape, or joint, you need to provide a definition structure. These definitions contain all the information needed to build the Box3D object. By using this approach I can prevent construction errors, keep the number of function parameters small, provide sensible defaults, and reduce the number of accessors. Here is an example of body creation: ```c b3BodyDef bodyDef = b3DefaultBodyDef(); bodyDef.position = (b3Vec3){10.0f, 0.0f, 5.0f}; b3BodyId myBodyId = b3CreateBody(myWorldId, &bodyDef); ``` Notice the body definition is initialized by calling `b3DefaultBodyDef()`. This is needed because C does not have constructors and zero initialization is generally not suitable for the definitions used in Box3D. Also notice that the body definition is a temporary object that is fully copied into the internal body data structures. Definitions should usually be created on the stack as temporaries. This is how a body is destroyed: ```c b3DestroyBody(myBodyId); myBodyId = b3_nullBodyId; ``` Notice that the body id is set to null using the constant `b3_nullBodyId`. You should treat ids as opaque data, however you may zero initialize all Box3D ids and they will be considered *null*. Shapes are created in a similar way. For example, here is how a box shape is created: ```c b3ShapeDef shapeDef = b3DefaultShapeDef(); shapeDef.baseMaterial.friction = 0.42f; b3BoxHull boxHull = b3MakeBoxHull(0.5f, 0.25f, 0.5f); b3ShapeId myShapeId = b3CreateHullShape(myBodyId, &shapeDef, &boxHull.base); ``` And the shape may be destroyed as follows: ```c b3DestroyShape(myShapeId, true); myShapeId = b3_nullShapeId; ``` For convenience, Box3D will destroy all shapes on a body when the body is destroyed. You don't need to store the shape id. There are some macros to assist using ids in logical operations. ```c bool isNull = B3_IS_NULL(myBodyId); bool isNonNull = B3_IS_NON_NULL(myJointId); bool areEqual = B3_ID_EQUALS(myShapeIdA, myShapeIdB); ``` --- METRICS --- - Files Extracted: 11 - Estimated Token Budget: ~22451 tokens - Recency Window: Active (< 180 days) - Canonical Reference: https://codewiki.google/github.com/erincatto/box3d