Animated vs Static Model Components in Pyrite64: Data Structures and Build Pipeline Integration

Animated model components store skeleton hierarchies and animation clips requiring GPU skinning buffers, while static model components store only pre-baked vertex data, resulting in fundamentally different build pipeline stages and runtime costs.

The pyrite64 engine distinguishes between animated and static geometry through its component registration system. While both implement the same CompInfo interface defined in src/project/component/components.h, their data structures, build pipeline integration, and runtime execution paths diverge significantly. Understanding these differences is critical for optimizing asset workflows and rendering performance in Nintendo 64-style development.

Component Registration and Metadata

Both component types register through the master component table in src/project/component/components.h, but they occupy different IDs and invoke distinct function implementations.

The animated model component occupies entry #10 with the display name "Model (Animated)":

CompInfo{
    .id   = 10,
    .icon = ICON_MDI_HUMAN " ",
    .name = "Model (Animated)",
    .funcInit        = AnimModel::init,
    .funcDraw        = AnimModel::draw,
    .funcDraw3D      = AnimModel::draw3D,
    .funcSerialize   = AnimModel::serialize,
    .funcDeserialize = AnimModel::deserialize,
    .funcBuild       = AnimModel::build,
};

Static mesh components typically register as "Mesh Filter" (ID 3) and bind to MeshFilter methods instead. Both entries share the same structure but point to different implementation files: src/project/component/shared/animModel.h for animated models and src/project/component/shared/meshFilter.h for static geometry.

Data Structure Differences

The underlying C++ classes reflect the distinct requirements of skeletal animation versus rigid geometry.

Animated Model Data Structures

The AnimModel class maintains a complex hierarchy required for skinning:

  • Skeleton data: Bone hierarchy and bind poses
  • Animation clips: Keyframe data for skeletal animation
  • Skinned vertex buffers: Vertex data containing bone indices and weights
  • Per-frame bone matrices: Uploaded each frame to uniform buffers or texture memory for GPU skinning

These structures require significantly more memory than static equivalents, as they must store both the mesh deformation data and the animation state.

Static Model Data Structures

The MeshFilter class implements a lightweight storage model:

  • Static mesh data: Vertex positions, normals, UV coordinates, and indices
  • Material reference: Optional pointer to rendering material
  • Static vertex buffer: GPU buffer created once at build time

No bone data or per-frame transform buffers exist in this implementation, resulting in lower memory overhead and simpler cache patterns.

Build Pipeline Integration

The asset build pipeline, orchestrated through src/utils/toolchain.cpp, treats these components differently when executing CompInfo::funcBuild callbacks.

Animated Model Build Process

When the toolchain encounters an animated model, AnimModel::build performs three critical steps:

  1. Skeleton parsing: Extracts bone hierarchy from source assets (e.g., glTF, FBX)
  2. Skinning buffer generation: Creates bone-matrix textures or uniform buffer objects (UBOs) for runtime skinning
  3. Binary export: Generates .skel and .anim files alongside the mesh data

The src/utils/meshGen.cpp utility handles the vertex buffer generation for animated meshes, specifically adding bone-weight attributes to the vertex format that the static path omits.

Static Model Build Process

Static models follow a simplified pipeline via MeshFilter::build:

  1. Mesh import: Loads geometry files (OBJ, static glTF)
  2. Buffer generation: Creates static vertex and index buffers
  3. Binary export: Outputs only a .mesh file

No additional animation processing occurs, and the build step completes without generating skeleton or animation metadata.

Runtime Execution and Performance

The divergence continues into the rendering loop through the funcDraw and funcDraw3D callbacks.

Animated models incur higher CPU and GPU costs. Each frame, AnimModel::draw updates bone matrices based on animation state, uploads these to GPU memory, and issues draw calls using skinned vertex buffers. The fragment shader performs matrix palette skinning, consuming additional ALU cycles.

Static models execute MeshFilter::draw with minimal overhead. The function binds the pre-baked vertex buffer and issues a single draw call. No per-frame matrix updates or vertex transformations occur on the GPU, resulting in optimal cache utilization and lower bandwidth requirements.

Summary

  • Both component types register through the CompInfo table in src/project/component/components.h, but bind to different function pointer implementations.
  • Animated models use the AnimModel class to store skeletons, animation clips, and skinning buffers, while static models use MeshFilter to store only rigid vertex data.
  • The build pipeline generates .skel and .anim files for animated components via AnimModel::build, whereas static components produce only .mesh files through MeshFilter::build.
  • Runtime costs differ significantly: animated models require per-frame bone matrix updates and GPU skinning, while static models execute single draw calls with pre-baked geometry.

Frequently Asked Questions

How does the build pipeline know which processing steps to run for each component type?

The build pipeline iterates over the CompInfo::funcBuild function pointers defined in src/project/component/components.h. For component ID 10 (animated model), this pointer references AnimModel::build, which internally triggers skeleton parsing and skinning buffer generation. For static mesh components, the pointer references MeshFilter::build, which skips all animation-related processing and generates only static vertex buffers.

Can a single entity contain both animated and static model components?

While technically possible through the component system architecture, this configuration is generally redundant. The AnimModel class already contains mesh data within its skinned vertex buffers. Static geometry that needs to attach to bones should be implemented as separate mesh sections within the same animated model asset rather than distinct components.

What file formats does the animated model build pipeline output?

According to the implementation in src/utils/toolchain.cpp and AnimModel::build, the pipeline exports three distinct binary formats: .mesh files containing the skinned vertex data, .skel files storing the bone hierarchy and bind poses, and .anim files containing the animation keyframe data. Static models export only the .mesh format.

Why do animated models require specific vertex attributes that static models lack?

GPU skinning requires per-vertex bone influence data. During the build phase in src/utils/meshGen.cpp, animated model vertices include bone index and bone weight attributes (typically 4 bones per vertex) that static meshes omit. These attributes allow the vertex shader to blend transformation matrices from the bone matrix texture/UBO, enabling mesh deformation at runtime.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →