GameSkelIO is a high-performance C-compatible library designed for 3D skeletal model and animation transcoding. It serves as a middleware layer between modern 3D formats (like glTF and FBX) and legacy or specialized game engine formats (like IQM and SKM).
The library is designed with a memory-first architecture, allowing it to be easily integrated into game engine Virtual File Systems (VFS) without disk I/O overhead.
- Memory-First API: Load and export models directly from/to memory buffers.
- Frame-Agnostic Core: Internally operates on sparse, timestamp-based animation tracks (seconds), making it strictly format-neutral.
- Format Support:
- IQM: Full Read/Write (Z-up to Y-up conversion, CW to CCW winding).
- GLB/glTF: Full Read/Write (Native Y-up reference).
- FBX (Binary): Full Read/Write (Uses high-performance
ufbxbackend). - SKM/SKP: Read-only support for legacy Warsow/Warfork formats.
- Legacy Engine Support: Specific toggles for forcing single-animation stacks and automated metadata generation for
.cfgfiles. - Precision Baking: Linear duration snapping ensures that sparse tracks are baked into dense frames (e.g., 30 FPS) with perfect alignment.
The project uses a standard Makefile. Running make in the root directory will produce:
libgameskelio.a: The static library for integration.gskelconv.exe: A reference command-line tool.
make clean
makeNote: Linking requires a C++ linker (e.g., g++) to resolve internal dependencies.
If you are building a 3D tool like a viewer or an editor, you can load models directly from disk into the gs_model representation, manipulate the data, and save it back to any supported format.
#include "gameskelio.h"
#include <stdio.h>
// 1. Load a model from disk
gs_model* model = gsk_load_glb("player_input.glb");
if (model) {
// 2. Access and manipulate data directly
printf("Model loaded: %u vertices, %u joints\n", model->num_vertices, model->num_joints);
// Derived data calculation
gsk_compute_bind_pose(model);
gsk_compute_bounds(model);
// 3. Save the model back to disk in a different format
// Parameters: path, model, force_single_anim
if (gsk_write_iqm("player_output.iqm", model, false)) {
printf("IQM saved successfully.\n");
}
// 4. Cleanup
gsk_free_model(model);
}For high-performance engine integration, GameSkelIO can act as a buffer-to-buffer transcoder, allowing you to load assets from your engine's VFS and convert them without touching the disk.
#include "gameskelio.h"
// 1. Load a model from a memory buffer (e.g., from a .pak or .pk3 file)
size_t glb_size = /* size from VFS */;
void* glb_data = /* pointer from VFS */;
gs_model* model = gsk_load_glb_buffer(glb_data, glb_size);
if (model) {
// 2. Initialize derived data (Bounds, Bind Pose)
gsk_compute_bind_pose(model);
gsk_compute_bounds(model);
// 3. Export to a memory-baked IQM buffer for the engine
size_t iqm_size = 0;
bool force_single = true; // Merge all frames into a single animation?
void* iqm_buffer = gsk_export_iqm_buffer(model, &iqm_size, force_single, NULL, NULL);
if (iqm_buffer) {
// Use 'iqm_buffer' with your engine's native IQM loader...
// 4. Cleanup
gsk_free_buffer(iqm_buffer);
}
gsk_free_model(model);
}gskelconv is a command-line utility included with the library. It demonstrates the library's capabilities and is useful for batch processing.
gskelconv <input.iqm/glb/skm/fbx> <output.iqm/glb/fbx> [flags]--qfusion: Repurposes the IQM writer to force a single animation stack (essential for QFusion/Warfork compatibility) and automatically generates a matching<model>.cfgfile.--base: (FBX only) Export the base pose only.--anim: (FBX only) Export animations only.
./gskelconv player.glb player.iqm --qfusiongsrebind is a specialized utility used to change a character's Bind Pose (Rest Pose) while ensuring all existing animations remain visually invariant.
It is commonly used to:
- Convert a "T-Pose" model to an "A-Pose" standard.
- "Freeze" a specific frame of an animation into the model's default resting shape.
gsrebind <input.glb/iqm/fbx> <anim_idx_or_name> <output.glb/iqm>- Static Target: The target animation (specified by name or index) must be a static pose (a single frame or a constant pose).
- Preservation: The original bind pose is captured and added to the end of the animation list as
"original_bind"for reference.
./gsrebind player.glb "A-Pose" player_rebound.glbTo ensure consistency across all formats, GameSkelIO standardizes data into the following Intermediate Representation (IR):
- Coordinate System: Y-Up, Right-Handed.
- Winding Order: Counter-Clockwise (CCW).
- Rotations: Quaternions (xyzw).
- Animations: Sparse tracks with
doubleprecision timestamps. - Joints: Hierarchical parent-index system.
- Weights: Normalized 4-bone per vertex influence.
MIT License. See LICENSE for details.