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).
GameSkelIO has evolved from a single-standard library to a flexible, automated transcoding engine.
- Format Loaders: Loaders (IQM, SKM, GLB, FBX) read data in its native format and convert it into the common
gs_modelformat. - Format Writers: Writers convert the
gs_modeldata into the target format, automatically conforming the orientation to the format's native standard (e.g., Y-Up for GLB, Z-Up for IQM). - Generic Skeletal Model (
gs_model): A unified skeletal model definition featuring time-based animations and self-identifying orientation and winding fields. - Transformation & Processing Helpers: Internal tools to perform manual operations on
gs_model, such as orientation and winding order swaps or baking time-based animations into frame-based buffers. - Embedded Texture Support: The root
gs_modelstruct includes atexturesarray containing raw binary image data (PNG, JPG) extracted from GLB or FBX containers. - Transparent C API: The public C functions work on temporary C++ copies, ensuring that the user's original
gs_model*is never mutated by a write operation.
graph LR
A[<b>Input Formats</b><br/>GLB, FBX, IQM, SKM] --> B[<b>Format Loader</b>]
B --> C{<b>gs_model</b><br/>Native Data +<br/>Self-Describing}
C --> D[<b>Processing Helpers</b><br/>Baking, Rebinding,<br/>Orientation Swap]
D --> E[<b>Format Writer</b>]
E --> F[<b>Output Formats</b><br/>GLB, FBX, IQM]
E -.-> G[<i>Auto-Conforming to<br/>Standard Orientation</i>]
GameSkelIO uses a unified, self-describing container (gs_model) that holds all the information necessary for a high-fidelity skeletal model.
graph TD
Root["<b>gs_model</b><br/>Root Container"] --> Meta["<b>Metadata</b><br/>Orientation, Winding"]
Root --> Geom["<b>Geometry Blocks</b>"]
Geom --> VAttribs["Vertex Attributes<br/><i>Positions, Normals, Tangents, UVs, Colors</i>"]
Geom --> VSkin["Skinning Data<br/><i>Joint Indices & Weights (4-bone influence)</i>"]
Geom --> VIdx["Index Buffer<br/><i>Triangle Indices</i>"]
Root --> Scene["<b>Scene Structure</b>"]
Scene --> Joints["Skeleton<br/><i>Joint Hierarchy & Bind Poses</i>"]
Scene --> Meshes["Meshes<br/><i>Surface ranges & Material mapping</i>"]
Scene --> Materials["Materials<br/><i>PBR Factors & Texture Paths</i>"]
Root --> Animation["<b>Motion Data</b>"]
Animation --> Tracks["Animation Clips<br/><i>Time-based TRS Channels</i>"]
Animation --> Morphs["Morph Targets<br/><i>Vertex attribute deltas</i>"]
Root --> Assets["<b>Resource Blocks</b>"]
Assets --> Tex["Embedded Textures<br/><i>Raw PNG/JPG binary data</i>"]
- Memory-First API: All loaders and writers operate primarily on memory buffers, making it ideal for custom engine asset pipelines.
- PBR & Legacy Materials: Full support for Metallic-Roughness workflows with automated fallbacks and conversion from Legacy (Phong/Lambert) shaders.
- Embedded Texture Retrieval: Access raw image bytes (PNG, JPG) stored within GLB/FBX containers via the
gsk_get_embedded_textureAPI. - Advanced Orientation API: Perform zero-copy, in-place orientation and winding order swaps on loaded models.
- On-Demand Animation Baking: Convert sparse, time-based animation tracks into dense, frame-based buffers for legacy engines.
| Format | Read | Write | Standard Orientation | Notes |
|---|---|---|---|---|
| GLB / glTF | ✅ | ✅ | Y-Up, CCW | Strictly writes PBR Metallic-Roughness. |
| FBX (Binary) | ✅ | ✅ | Y-Up, CCW | Supports PBR shaders and custom orientation detection. |
| IQM | ✅ | ✅ | Z-Up, CW | Automated conversion from any source space. |
| SKM / SKP | ✅ | ❌ | Z-Up, CW | Legacy format; upgraded to PBR on GLB export. |
The project uses a standard Makefile. Running make in the root directory will produce:
libgameskelio.a: The static library for integration.libgameskelio_x64.dll: The dynamic library for Windows integration.gskelconv.exe: A reference command-line conversion tool.gsrebind.exe: A specialized tool for rebinding rest poses.
make clean
make -j8Note: Linking requires a C++ linker (e.g., g++) to resolve internal dependencies.
This example shows a simple transcoding operation from a GLB file to an IQM file. The IQM writer will automatically handle the conversion from GLB's Y-Up space to IQM's Z-Up space.
#include "gameskelio.h"
#include <stdio.h>
// 1. Load a model from disk
gs_model* model = gsk_load_glb("player_input.glb");
if (model) {
printf("Model loaded with orientation: %d\n", model->orientation);
// 2. Save the model. The writer automatically handles orientation conforming.
if (gsk_write_iqm("player_output.iqm", model, false)) {
printf("IQM saved successfully.\n");
}
// 3. Cleanup
gsk_free_model(model);
}GameSkelIO is memory-first. You can load and export models directly to/from memory buffers without ever touching the disk.
#include "gameskelio.h"
#include <stdlib.h>
void process_data(const void* glb_data, size_t glb_size) {
// 1. Load from a memory buffer
gs_model* model = gsk_load_glb_buffer(glb_data, glb_size);
if (model) {
// 2. Export to an IQM buffer
size_t out_size = 0;
void* iqm_data = gsk_export_iqm_buffer(model, &out_size, false, NULL, NULL);
if (iqm_data) {
// Use iqm_data (e.g., send over network or cache)
// 3. IMPORTANT: Free the exported buffer when done
gsk_free_buffer(iqm_data);
}
gsk_free_model(model);
}
}GLB and FBX files often contain embedded textures. You can retrieve the raw binary data (e.g., PNG/JPG bytes) directly from the loaded model.
#include "gameskelio.h"
#include <stdio.h>
gs_model* model = gsk_load_glb("character_with_textures.glb");
if (model) {
// Safety: Check if the model has materials and a valid texture path
if (model->num_materials > 0 && model->materials[0].color_map) {
const char* tex_path = model->materials[0].color_map;
size_t data_size = 0;
const void* image_bytes = gsk_get_embedded_texture(model, tex_path, &data_size);
if (image_bytes) {
printf("Found embedded texture: %s (%zu bytes)\n", tex_path, data_size);
// Pass to your engine's texture loader (e.g., stbi_load_from_memory)
}
}
gsk_free_model(model);
}For engines that use frame-based animation systems, you can "bake" any sparse animation into a dense buffer of frame data.
#include "gameskelio.h"
gs_model* model = gsk_load_glb("animated_asset.glb");
if (model && model->num_animations > 0) {
// Bake the first animation clip at 30 FPS
uint32_t anim_idx = 0;
float fps = 30.0f;
gs_baked_anim* baked = gsk_bake_animation(model, anim_idx, fps);
if (baked) {
// 'baked->data' contains interleaved T3, R4, S3 floats per joint per frame
printf("Baked %u frames for %u joints\n", baked->num_frames, baked->num_joints);
// IMPORTANT: The baked animation is a new allocation and must be freed
gsk_free_baked_anim(baked);
}
gsk_free_model(model);
}A command-line utility for batch processing and reference usage.
gskelconv <input.iqm/glb/fbx> <output.iqm/glb/fbx> [flags]--qfusion: Forces a single animation stack and generates a<model>.cfgfor QFusion/Warfork compatibility.--base / --anim: (FBX only) Selective export of geometry or animations.
A specialized tool to change a model's Bind Pose while keeping animations visually invariant. Useful for standardizing disparate models into a common T-pose or A-pose for shared animation retargeting.
gsrebind <input.glb/iqm/fbx> <anim_idx_or_name> <output.glb/iqm>MIT License. See LICENSE for details.