Box3D 0.1.0
A 3D physics engine for games
Loading...
Searching...
No Matches
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:

// 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 ...
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);
b3CompoundSphereDef * spheres
Sphere instances.
Definition types.h:2395
int meshCount
Number of mesh instances.
Definition types.h:2392
int capsuleCount
Number of capsules.
Definition types.h:2380
int sphereCount
Number of spheres.
Definition types.h:2398
b3CompoundCapsuleDef * capsules
Capsule instances.
Definition types.h:2377
b3CompoundHullDef * hulls
Hulls instances.
Definition types.h:2383
b3CompoundMeshDef * meshes
Mesh instances.
Definition types.h:2389
int hullCount
Number of hull instances.
Definition types.h:2386
b3CompoundData * b3CreateCompound(const b3CompoundDef *def)
Create a compound shape. All input data in the definition is cloned into the resulting compound.
Definition for a capsule in a compound shape.
Definition types.h:2321
Definition for creating a compound shape.
Definition types.h:2375
Definition for a convex hull in a compound shape.
Definition types.h:2331
Definition for a triangle mesh in a compound shape.
Definition types.h:2344
Definition for a sphere in a compound shape.
Definition types.h:2364

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

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:

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.
uint8_t * b3ConvertCompoundToBytes(b3CompoundData *compound)
If bytes is null then this returns the number of required bytes.

Reconstruct from bytes at runtime (zero-copy; bytes must remain in scope):

b3Compound* compound = b3ConvertBytesToCompound(bytes, byteCount);
b3CompoundData * b3ConvertBytesToCompound(uint8_t *bytes, int byteCount)
Convert bytes to compound.

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

bodyDef.type = b3_staticBody;
bodyDef.position = tileOrigin;
b3BodyId bodyId = b3CreateBody(worldId, &bodyDef);
b3ShapeId shapeId = b3CreateCompoundShape(bodyId, &shapeDef, compound);
b3BodyType type
The body type: static, kinematic, or dynamic.
Definition types.h:270
b3Pos position
The initial world position of the body.
Definition types.h:275
b3BodyDef b3DefaultBodyDef(void)
Use this to initialize your body definition.
b3BodyId b3CreateBody(b3WorldId worldId, const b3BodyDef *def)
Create a rigid body given a definition.
@ b3_staticBody
zero mass, zero velocity, may be manually moved
Definition types.h:227
A body definition holds all the data needed to construct a rigid body.
Definition types.h:268
Body id references a body instance. This should be treated as an opaque handle.
Definition id.h:45
Shape id references a shape instance. This should be treated as an opaque handle.
Definition id.h:53
b3ShapeId b3CreateCompoundShape(b3BodyId bodyId, b3ShapeDef *def, const b3CompoundData *compound)
Compound shapes are only allowed on static bodies.
b3ShapeDef b3DefaultShapeDef(void)
Use this to initialize your shape definition.
Used to create a shape.
Definition types.h:457

b3CreateCompoundShape asserts that the body is static.

Querying a Compound

Compute the world AABB:

b3AABB aabb = b3ComputeCompoundAABB(compound, bodyTransform);
b3AABB b3ComputeCompoundAABB(const b3CompoundData *shape, b3Transform transform)
Compute the bounding box of a compound.
Axis aligned bounding box.
Definition math_functions.h:105

Query child shapes by AABB:

typedef bool b3CompoundQueryFcn(const b3Compound* compound, int childIndex, void* context);
b3QueryCompound(compound, queryAABB, MyChildCallback, ctx);
bool b3CompoundQueryFcn(const b3CompoundData *compound, int childIndex, void *context)
Callback for compound overlap queries.
Definition types.h:2540
void b3QueryCompound(const b3CompoundData *compound, b3AABB aabb, b3CompoundQueryFcn *fcn, void *context)
Query a compound shape for children that overlap an AABB.

Access a specific child by index:

b3ChildShape child = b3GetCompoundChild(compound, childIndex);
// child.type tells you capsule / hull / mesh / sphere
// child.transform is the child's local transform within the compound
b3ChildShape b3GetCompoundChild(const b3CompoundData *compound, int childIndex)
Get a child shape of a compound.
Child shape of a compound.
Definition types.h:2518

Individual typed accessors are also available:

const b3SurfaceMaterial* mats = b3GetCompoundMaterials(compound);
b3CompoundCapsule b3GetCompoundCapsule(const b3CompoundData *compound, int index)
Access a child capsule by index.
b3CompoundSphere b3GetCompoundSphere(const b3CompoundData *compound, int index)
Access a child sphere by index.
const b3SurfaceMaterial * b3GetCompoundMaterials(const b3CompoundData *compound)
Access the compound material array.
b3CompoundHull b3GetCompoundHull(const b3CompoundData *compound, int index)
Access a child hull by index.
b3CompoundMesh b3GetCompoundMesh(const b3CompoundData *compound, int index)
Access a child mesh by index.
A capsule that lives in a compound.
Definition types.h:2466
A hull that lives in a compound.
Definition types.h:2476
A mesh with non-uniform scale that lives in a compound.
Definition types.h:2489
A sphere that lives in a compound.
Definition types.h:2508
Material properties supported per triangle on meshes and height fields.
Definition types.h:399

Teardown

Destroy a live compound (not needed if you are using the byte-buffer path, where you manage the buffer lifetime yourself):

void b3DestroyCompound(b3CompoundData *compound)
Destroy a compound shape.

Do not call b3DestroyCompound on a compound reconstructed from bytes with b3ConvertBytesToCompound — free the byte buffer instead.