# Performance Implications of Collision-Mesh and Collision-Body Components in Pyrite64

> Understand Pyrite64's Collision-Mesh vs Collision-Body components. Discover performance implications and choose the right collision type for static or dynamic objects to optimize your game.

- Repository: [Max Bebök/pyrite64](https://github.com/hailtododongo/pyrite64)
- Tags: performance
- Published: 2026-02-19

---

**Collision-Mesh components provide high-precision static geometry with BVH acceleration but incur significant memory and traversal costs, while Collision-Body components offer lightweight primitive shapes ideal for dynamic objects and triggers with minimal CPU overhead.**

Pyrite64 is an open-source Nintendo 64 engine that treats collision geometry as two distinct component types to optimize for the console's limited resources. Understanding the performance implications of **Collision-Mesh** and **Collision-Body** components is essential for maintaining 60 FPS while handling complex level geometry and interactive gameplay objects.

## What Are Collision-Mesh and Collision-Body Components?

### Collision-Mesh: High-Fidelity Static Geometry

A **Collision-Mesh** stores a full triangle mesh extracted from a GLTF model, converted at build time into a binary chunk containing vertex lists, normal lists, and a **BVH** (bounding-volume hierarchy). In [`src/project/assets/collision.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/assets/collision.h), the `createBVH` function constructs this acceleration structure【/tmp/instagit_0o_ymwp6/src/project/assets/collision.h#L12-L16】. The `CollisionMesh::build` method in [`src/build/collisionBuilder.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/build/collisionBuilder.cpp) writes this data to the level binary【/tmp/instagit_0o_ymwp6/src/build/collisionBuilder.cpp#L77-L87】.

### Collision-Body: Lightweight Primitives

A **Collision-Body** represents simple primitives—box, sphere, or cylinder—along with trigger flags, collision masks, and positional offsets. Unlike meshes, bodies require no BVH. The `CompCollBody::build` method in [`src/project/component/types/compCollBody.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/component/types/compCollBody.cpp) serializes these as fixed-size records containing half-extents, offsets, type enums, and flag bytes【/tmp/instagit_0o_ymwp6/src/project/component/types/compCollBody.cpp#L70-L85】.

## Performance Implications and Memory Costs

### CPU Traversal Costs

**Collision-Mesh** components incur **high CPU costs** during runtime. Every ray-cast or collision test requires traversing the BVH structure built by `createBVH`, then performing triangle-level intersection tests. This makes mesh collision suitable for static geometry that does not move, as the BVH remains valid throughout the frame.

**Collision-Body** components offer **low CPU overhead**. Collision tests resolve to simple AABB, sphere, or cylinder checks—constant-time operations requiring no tree traversal. The runtime collision resolver in [`n64/engine/src/collision/resolver.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/n64/engine/src/collision/resolver.cpp) performs bitwise flag checks using `BCSFlags` defined in [`n64/engine/include/collision/flags.h`](https://github.com/HailToDodongo/pyrite64/blob/main/n64/engine/include/collision/flags.h)【/tmp/instagit_0o_ymwp6/n64/engine/include/collision/flags.h#L17-L24】:

```cpp
bool isFixedA = bcsA.flags & Coll::BCSFlags::FIXED_XYZ;
bool isTrigger = bcs.flags & Coll::BCSFlags::TRIGGER;

```

### Memory Footprint

On memory-constrained platforms like the N64, the distinction is critical. A **Collision-Mesh** can consume several kilobytes to megabytes per static mesh due to vertex data, indices, and BVH nodes. In contrast, a **Collision-Body** requires fewer than 32 bytes per instance, making it feasible to have hundreds of dynamic bodies active simultaneously.

## Use Cases: When to Use Each Component

### Static World Geometry: Use Collision-Mesh

For level terrain, static walls, floors, and decorative architecture that never moves, **Collision-Mesh** provides the necessary precision. The mesh is built once during the asset pipeline in [`collisionBuilder.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/collisionBuilder.cpp), and the BVH is generated by `Project::Assets::Collision::createBVH`. This approach ensures that players and objects collide accurately with visual geometry without runtime construction costs.

### Dynamic Objects and Physics: Use Collision-Body

For moving platforms, doors, physics props, or any entity that changes position or rotation each frame, **Collision-Body** is the correct choice. The primitive shape eliminates the need to rebuild or transform a BVH hierarchy every frame. You can also mark bodies as `FIXED_XYZ` to prevent displacement by other objects, handled in the resolver via bitwise operations on the flags field.

### Trigger Zones: Use Collision-Body with Trigger Flag

Invisible gameplay zones—such as collectible pickups, cutscene triggers, or damage areas—should use **Collision-Body** components with the `TRIGGER` flag set. This configuration allows the engine to detect overlap without generating physical response impulses. The flag is serialized in [`compCollBody.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/compCollBody.cpp) and checked in the runtime resolver using `BCSFlags::TRIGGER`.

### Hybrid Approaches

Some entities benefit from both components simultaneously. For example, an enemy character might use a **Collision-Mesh** for precise hit-detection against weapons (visual fidelity), while also possessing a **Collision-Body** sphere for cheap AI proximity checks (aggro range). This dual-component approach balances accuracy and performance.

## Implementation Details and Source Code

### Component Registration

Both types are registered in [`src/project/component/components.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/component/components.h) with distinct IDs and editor icons【/tmp/instagit_0o_ymwp6/src/project/component/components.h#L128-L142】. The editor distinguishes them in the viewport: **Collision-Mesh** debug drawing can be toggled via `showCollMesh` in [`src/editor/pages/parts/viewport3D.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/editor/pages/parts/viewport3D.cpp)【/tmp/instagit_0o_ymwp6/src/editor/pages/parts/viewport3D.cpp#L216-L230】, while bodies are always rendered due to their low cost.

### Building Collision-Mesh Assets

When building a level, `CompCollMesh::build` prepares the mesh data and invokes `Build::buildT3DCollision` to generate the BVH if not already cached【/tmp/instagit_0o_ymwp6/src/project/component/types/compCollMesh.cpp#L66-L86】:

```cpp
// From collisionBuilder.cpp - writing the binary chunk
void CollisionMesh::build(BuildContext& ctx) {
    // ... vertex and index extraction from GLTF ...
    ctx.writeChunk(C4B_CHUNK_COLMESH, 
        vertices.data(), vertices.size() * sizeof(Vec3),
        indices.data(), indices.size() * sizeof(uint16_t),
        bvhNodes.data(), bvhNodes.size() * sizeof(BVHNode)
    );
}

```

### Serializing Collision-Body Components

For dynamic bodies, `CompCollBody::build` writes a fixed-size record containing the primitive type, half-extents, offset, and packed flags【/tmp/instagit_0o_ymwp6/src/project/component/types/compCollBody.cpp#L70-L85】:

```cpp
// Serialization in compCollBody.cpp
void CompCollBody::build(BuildContext& ctx) {
    uint8_t flags = 0;
    if (isTrigger.value) flags |= BCSFlags::TRIGGER;
    if (isFixed.value)   flags |= BCSFlags::FIXED_XYZ;
    
    ctx.writeRecord(
        type.value,        // SHAPE_BOX, SHAPE_SPHERE, etc.
        halfExtend.value,  // Vec3 half-extents
        offset.value,      // Vec3 position offset
        flags              // Packed bit-flags
    );
}

```

### Runtime Flag Evaluation

The engine's collision resolver uses these flags to determine behavior without accessing geometry data. In [`n64/engine/src/collision/resolver.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/n64/engine/src/collision/resolver.cpp), the system checks flags from [`n64/engine/include/collision/flags.h`](https://github.com/HailToDodongo/pyrite64/blob/main/n64/engine/include/collision/flags.h):

```cpp
// Runtime collision logic
bool isTrigger = (body.flags & Coll::BCSFlags::TRIGGER) != 0;
bool isFixed   = (body.flags & Coll::BCSFlags::FIXED_XYZ) != 0;

if (isTrigger) {
    // Notify gameplay systems, apply no physical impulse
    triggerCallback(body.id);
} else if (!isFixed) {
    // Resolve penetration, apply forces
    resolvePhysics(bodyA, bodyB);
}

```

## Summary

- **Collision-Mesh** components provide triangle-level precision for static world geometry using pre-built BVHs, but consume significant memory and require tree traversal for every query.
- **Collision-Body** components use simple primitives (box, sphere, cylinder) with minimal memory footprint (<32 bytes), enabling cheap per-frame updates and bitwise flag checks for triggers and fixed objects.
- **Static geometry** (floors, walls) should use **Collision-Mesh** for accuracy, while **dynamic objects, triggers, and physics props** should use **Collision-Body** for performance.
- The distinction is enforced at build time in [`collisionBuilder.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/collisionBuilder.cpp) and [`compCollBody.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/compCollBody.cpp), with runtime behavior controlled by flags defined in [`n64/engine/include/collision/flags.h`](https://github.com/HailToDodongo/pyrite64/blob/main/n64/engine/include/collision/flags.h).

## Frequently Asked Questions

### Can a single entity have both a Collision-Mesh and a Collision-Body?

Yes. An entity can possess both components simultaneously to serve different purposes. For example, a boss enemy might use a **Collision-Mesh** for precise weapon hit detection against its visual geometry, while also having a **Collision-Body** sphere for cheap AI proximity checks like aggro range detection. This hybrid approach balances visual fidelity with computational efficiency.

### Why does Collision-Mesh require a BVH while Collision-Body does not?

**Collision-Mesh** represents arbitrary triangle soup from GLTF models, which requires a **Bounding Volume Hierarchy** (BVH) to accelerate ray-casting and collision queries from O(n) to O(log n). The BVH is built once at compile time by `Project::Assets::Collision::createBVH` in [`src/project/assets/collision.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/assets/collision.h). **Collision-Body** uses simple convex primitives (boxes, spheres) that require only a single AABB or distance test, eliminating the need for complex spatial acceleration structures.

### How do trigger zones work with Collision-Body components?

Trigger zones are implemented by setting the `TRIGGER` flag bit in the **Collision-Body** component's flag byte. During serialization in `CompCollBody::build` ([`src/project/component/types/compCollBody.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/component/types/compCollBody.cpp)), the flag is packed into the binary record. At runtime, the collision resolver in [`n64/engine/src/collision/resolver.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/n64/engine/src/collision/resolver.cpp) checks this flag using `Coll::BCSFlags::TRIGGER` and routes the collision to a callback system rather than applying physical response impulses, allowing for gameplay logic like collectible pickups or cutscene triggers.

### What happens if I use Collision-Mesh for moving objects?

Using **Collision-Mesh** for dynamic objects is strongly discouraged because the BVH is built for a specific static spatial configuration. Moving the object would require either rebuilding the BVH every frame (prohibitively expensive on N64 hardware) or accepting incorrect collision results as the hierarchy no longer matches the transformed geometry. For any object that changes position, rotation, or scale at runtime, **Collision-Body** with a primitive shape is the correct choice.