# How ArmorPaint's Plugin Architecture Handles GLTF, FBX, EXR, PSD, TIFF, and SVG Imports Using the Iron Engine

> Explore ArmorPaint's C-only plugin system for seamless GLTF, FBX, EXR, PSD, TIFF, SVG import. Discover how it converts files into Iron engine-native meshes and textures.

- Repository: [Armory 3D/armorpaint](https://github.com/armory3d/armorpaint)
- Tags: internals
- Published: 2026-09-14

---

**ArmorPaint uses a C-only plugin system that registers format-specific importer callbacks during initialization, routing file data through dedicated parsers that convert GLTF, FBX, EXR, PSD, TIFF, and SVG into Iron engine-native meshes and GPU textures.**

The **armory3d/armorpaint** repository implements a lightweight plugin architecture that abstracts file format complexities from the core engine. This design allows the **Iron engine**—the low-level graphics and data layer—to consume standardized `raw_mesh_t` structures and GPU textures regardless of the original file format, enabling seamless import of industry-standard 3D and image formats.

## Plugin Registration and Callback Architecture

At startup, `plugins_init()` in [`paint/plugins/plugins.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/plugins.c) establishes a registration system using two global callback maps. These maps associate file extensions with C function pointers that handle the actual parsing.

The system distinguishes between mesh importers and texture importers:

- `import_mesh_importers` stores callbacks returning `raw_mesh_t*` for 3D geometry
- `import_texture_importers` stores callbacks returning GPU textures for image data

```c
any_map_set(import_mesh_importers, "gltf", import_gltf_glb);
any_map_set(import_mesh_importers, "fbx",  import_fbx);
any_map_set(import_texture_importers, "exr",  import_exr);
any_map_set(import_texture_importers, "psd",  import_psd);
any_map_set(import_texture_importers, "tiff", import_tiff);
any_map_set(import_texture_importers, "svg",  import_svg);

```

*Source:* [`paint/plugins/plugins.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/plugins.c) – lines 55-75 & 68-74

## File-to-Memory Pipeline

Every importer follows a consistent pattern to retrieve data from ArmorPaint's virtual file system. Rather than accessing disk directly, importers work with memory blobs managed by the engine.

The standard workflow uses `data_get_blob()` to load files into `buffer_t` structures, passes the raw bytes to format-specific parsers, then releases the temporary storage:

```c
buffer_t *b = data_get_blob(path);
void *res = io_<fmt>_parse(b->buffer, b->length, …);
data_delete_blob(path);

```

This pattern appears universally across `import_exr`, `import_psd`, `import_tiff`, `import_svg`, `import_gltf_glb`, and `import_fbx` implementations.

*Source:* [`paint/plugins/plugins.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/plugins.c) – e.g., `import_exr` lines 49-54, `import_fbx` lines 43-48

## Format-Specific Parser Implementations

Each supported format resides in its own subdirectory under `paint/plugins/`, containing C implementations that transform binary buffers into engine-compatible structures.

### GLTF and GLB Import

The GLTF importer leverages the single-file **cgltf** library ([`paint/plugins/io_gltf/cgltf.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/io_gltf/cgltf.c)). The parser extracts vertex positions, normals, UVs, and skinning data, populating a `raw_mesh_t` structure that the Iron engine can convert to GPU buffers.

*Source:* [`io_gltf/cgltf.c`](https://github.com/armory3d/armorpaint/blob/main/io_gltf/cgltf.c) line 15-17

### FBX Import

For FBX files, ArmorPaint wraps the **ufbx** library in [`paint/plugins/io_fbx/io_fbx.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/io_fbx/io_fbx.c). The implementation traverses the FBX scene graph, constructs mesh geometry, and optionally handles material splits while converting coordinate systems to match the engine's expectations.

*Source:* [`io_fbx/io_fbx.c`](https://github.com/armory3d/armorpaint/blob/main/io_fbx/io_fbx.c) line 1-4

### EXR Import

OpenEXR handling uses a hand-rolled parser in [`paint/plugins/io_exr/io_exr.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/io_exr/io_exr.c). The code reads EXR headers, decompresses scanlines using the format's native compression, and immediately uploads the resulting pixel data via `gpu_create_texture_from_bytes()`.

*Source:* [`io_exr/io_exr.c`](https://github.com/armory3d/armorpaint/blob/main/io_exr/io_exr.c) line 17-22

### PSD, TIFF, and SVG Import

- **PSD**: [`paint/plugins/io_psd/io_psd.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/io_psd/io_psd.c) decodes layered Photoshop files, registering each layer as a separate texture entry in the global cache.
- **TIFF**: [`paint/plugins/io_tiff/io_tiff.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/io_tiff/io_tiff.c) handles uncompressed and LZW-compressed TIFF images, converting them to GPU-ready textures.
- **SVG**: [`paint/plugins/io_svg/io_svg.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/io_svg/io_svg.c) rasterizes vector graphics into bitmap textures that the rendering pipeline can sample directly.

## Iron Engine Integration

The parsers return data in formats expected by the Iron engine's rendering layer. **Mesh data** flows through `raw_mesh_t` structures containing short-precision arrays for positions (`posa`), normals (`nora`), UVs (`texa`), and optional vertex colors. **Texture data** bypasses intermediate structures, with importers like `import_exr` calling `gpu_create_texture_from_bytes()` to create `gpu_texture_t` objects immediately.

These objects enter the engine's global caches (`data_cached_textures`, `import_mesh_importers`), making them available to the UI and real-time rendering systems without format-specific handling in the core engine code.

## Build System Configuration

The plugin system uses [`paint/plugins/project.js`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/project.js) to declare which source files compile into the plugin module. Adding support for new formats requires only appending the directory to this configuration:

```javascript
project.add_cfiles("io_gltf/**");
project.add_cfiles("io_fbx/**");
project.add_cfiles("io_exr/**");
project.add_cfiles("io_psd/**");
project.add_cfiles("io_tiff/**");
project.add_cfiles("io_svg/**");

```

*Source:* [`paint/plugins/project.js`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/project.js) lines 5-11

## Practical Usage Examples

### Importing a GLTF Mesh

```c
char *gltf_path = "models/house.gltf";

void *(*import_gltf)(char *) = any_map_get(import_mesh_importers, "gltf");
raw_mesh_t *mesh = (raw_mesh_t *)import_gltf(gltf_path);

if (mesh) {
    gpu_mesh_t *gpu_mesh = gpu_create_mesh_from_raw(mesh);
}

```

*Relevant source:* [`plugins.c`](https://github.com/armory3d/armorpaint/blob/main/plugins.c) – `import_gltf_glb` function lines 27-41

### Importing a PSD with Layers

```c
char *psd_path = "textures/brick_wall.psd";

void *(*import_psd)(char *) = any_map_get(import_texture_importers, "psd");
void *psd_tex = import_psd(psd_path);

char *layer_key = "brick_wall.diffuse.png";
gpu_texture_t *layer_tex = any_map_get(data_cached_textures, layer_key);

```

*Relevant source:* [`plugins.c`](https://github.com/armory3d/armorpaint/blob/main/plugins.c) – `import_psd` function lines 56-72

### Importing an EXR Texture

```c
char *exr_path = "hdr/sky.exr";

void *(*import_exr)(char *) = any_map_get(import_texture_importers, "exr");
gpu_texture_t *hdr_tex = (gpu_texture_t *)import_exr(exr_path);

```

*Relevant source:* [`plugins.c`](https://github.com/armory3d/armorpaint/blob/main/plugins.c) – `import_exr` function lines 49-54

## Summary

- **ArmorPaint's plugin architecture** registers C callbacks in global maps (`import_mesh_importers` and `import_texture_importers`) during `plugins_init()` in [`paint/plugins/plugins.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/plugins.c).
- **File loading** uses the virtual file system via `data_get_blob()` to stream files into memory buffers before parsing.
- **Format-specific parsers** for GLTF (cgltf), FBX (ufbx), EXR (custom), PSD, TIFF, and SVG convert binary data into Iron engine structures.
- **Iron integration** relies on `raw_mesh_t` for geometry and `gpu_texture_t` for images, with parsers calling `gpu_create_texture_from_bytes()` to upload texture data directly.
- **Build configuration** in [`paint/plugins/project.js`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/project.js) allows extending support by adding new `io_*` directories to the C file list.

## Frequently Asked Questions

### How does ArmorPaint register new import formats?

New formats register by calling `any_map_set()` in [`paint/plugins/plugins.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/plugins.c) during the `plugins_init()` function. Developers map file extensions to importer functions that return either `raw_mesh_t*` for meshes or GPU textures for images, depending on which global map they target (`import_mesh_importers` or `import_texture_importers`).

### What data structures does the Iron engine use for imported meshes?

The Iron engine consumes ** `raw_mesh_t` ** structures containing short-precision arrays: `posa` for positions, `nora` for normals, and `texa` for UV coordinates. The engine later converts these to `gpu_mesh_t` objects via `gpu_create_mesh_from_raw()` for rendering.

### How are texture formats like EXR and PSD handled differently from meshes?

Texture importers bypass intermediate mesh structures and call ** `gpu_create_texture_from_bytes()` ** directly to create `gpu_texture_t` objects. PSD importers additionally register individual layers as separate entries in `data_cached_textures`, while EXR importers handle high dynamic range scanline decompression before GPU upload.

### Where are the importer source files located in the ArmorPaint repository?

All importer implementations reside in `paint/plugins/io_*/` directories (e.g., `paint/plugins/io_gltf/`, `paint/plugins/io_fbx/`). The central registration logic lives in [`paint/plugins/plugins.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/plugins.c), while the build system references these directories in [`paint/plugins/project.js`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/project.js).