# Data Structure for Storing Mesh Data in ArmorPaint: A Complete Technical Guide

> Discover the ArmorPaint mesh data structure, a technical guide detailing C structs for geometry, GPU resources, and scene integration. Learn how ArmorPaint handles 3D models.

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

---

**ArmorPaint represents 3D geometry using a hierarchical system of C structs that separate logical mesh description (`mesh_data_t`), GPU runtime resources (`mesh_data_runtime_t`), and scene integration handles (`mesh_object_t`).**

The open-source 3D painting software ArmorPaint (armory3d/armorpaint) implements a specialized data structure for storing mesh data that cleanly separates CPU-side geometry definitions from GPU-ready buffers. This design enables efficient memory management and real-time rendering while supporting dynamic mesh import from formats like OBJ and FBX.

## Core Mesh Data Structures

The engine defines three primary structs in [`base/sources/engine.h`](https://github.com/armory3d/armorpaint/blob/main/base/sources/engine.h) that work together to represent a complete mesh.

### mesh_data_t: Logical Geometry Description

The **`mesh_data_t`** struct serves as the authoritative source for mesh geometry, storing all vertex attributes and metadata before GPU upload.

Key fields include:
- **`char *name`** – Human-readable identifier for the mesh.
- **`float scale_pos`** and **`float scale_tex`** – Scale factors applied to position and UV coordinates during rendering.
- **`vertex_array_t_array_t *vertex_arrays`** – Dynamic array containing all vertex attributes (positions, normals, UVs, colors).
- **`u32_array_t *index_array`** – Index buffer defining triangle topology.
- **`mesh_data_runtime_t *_`** – Lazy-initialized pointer to the GPU-side representation.

### mesh_data_runtime_t: GPU Resources

When a mesh is first rendered, ArmorPaint creates a **`mesh_data_runtime_t`** instance containing the actual GPU buffers.

Important members:
- **`gpu_buffer_t *vertex_buffer`** and **`gpu_buffer_t *index_buffer`** – Direct handles to GPU memory.
- **`gpu_vertex_structure_t structure`** – Vertex layout description for the graphics pipeline.
- **`bool owns_arrays`** – Boolean flag indicating whether the engine should free CPU-side arrays when the mesh is destroyed, preventing double-free errors during cleanup.

### mesh_object_t: Scene Integration

To place a mesh in the world, the engine wraps `mesh_data_t` inside **`mesh_object_t`**, which binds geometry to materials and transforms.

Critical fields:
- **`object_t *base`** – Generic object header containing transform matrices and hierarchy information.
- **`mesh_data_t *data`** – Pointer to the logical mesh data.
- **`shader_data_t *material`** – Reference to the shader material used for rendering.
- **`f32 camera_dist`** – Cached distance to camera, utilized for level-of-detail (LOD) calculations.

## Collection Management Containers

Beyond individual meshes, ArmorPaint manages collections through specialized dynamic-array wrappers defined in [`paint/sources/types.h`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/types.h).

The **`mesh_data_t_array_t`** struct maintains a list of all logical meshes in the project via a `mesh_data_t **buffer` pointer, while **`mesh_object_t_array_t`** tracks all scene instances using `mesh_object_t **buffer`. The global scene state (`scene_t` in [`engine.h`](https://github.com/armory3d/armorpaint/blob/main/engine.h)) stores these collections in `any_array_t *mesh_datas`, enabling iteration over all geometry without traversing the scene graph.

## Mesh Creation and Import Pipeline

Creating a usable mesh follows a strict three-stage pipeline implemented across [`paint/sources/util_mesh.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/util_mesh.c) and [`paint/sources/io/import_mesh.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/io/import_mesh.c).

First, construct a raw mesh from application data:

```c
raw_mesh_t *raw = plugin_make_raw_mesh(
    "my_mesh",                // name identifier
    posa,                     // i16_array_t * positions (short4norm format)
    nora,                     // i16_array_t * normals (short4norm format)
    inda,                     // u32_array_t * triangle indices
    1.0f);                    // scale_pos factor

```

Next, convert the raw data into the logical structure:

```c
mesh_data_t *mesh = import_mesh_raw_mesh(raw);

```

Finally, instantiate the mesh in the scene with material binding:

```c
mesh_object_t *obj = scene_add_mesh_object(
    mesh,                     // logical mesh data
    default_material,         // shader_data_t * material pointer
    NULL);                    // parent object (NULL for root level)

```

To modify mesh properties after creation, access the data directly through the scene object:

```c
obj->data->scale_pos = 2.5f;   // Enlarge mesh by 2.5x

```

For batch operations, iterate the project's mesh registry:

```c
for (int i = 0; i < project->mesh_datas->length; ++i) {
    mesh_data_t *m = project->mesh_datas->buffer[i];
    printf("Mesh %s has %u vertices\n",
           m->name,
           (unsigned)m->vertex_arrays->buffer[0]->values->length);
}

```

## Key Source Files and Implementation Details

The mesh data structure implementation spans multiple files in the `base/` and `paint/` directories:

- **[`base/sources/engine.h`](https://github.com/armory3d/armorpaint/blob/main/base/sources/engine.h)** – Contains the core struct definitions for `mesh_data_t`, `mesh_data_runtime_t`, and `mesh_object_t`.
- **[`paint/sources/types.h`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/types.h)** – Defines dynamic-array wrappers `mesh_data_t_array_t` and `mesh_object_t_array_t`.
- **[`paint/sources/util_mesh.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/util_mesh.c)** – Implements buffer building utilities, raw mesh conversion, and memory cleanup routines.
- **[`paint/sources/io/import_mesh.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/io/import_mesh.c)** – Handles parsing of external files (OBJ, FBX) and population of `mesh_data_t` fields.
- **[`paint/sources/render/make_mesh.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/render/make_mesh.c)** – Creates renderable `mesh_object_t` instances from logical mesh data.
- **[`paint/sources/minic_api_list.h`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/minic_api_list.h)** – Exposes public C-API functions including `mesh_data_create()` and `mesh_object_set_data()`.

## Summary

- **ArmorPaint uses a three-tier architecture**: `mesh_data_t` stores logical geometry, `mesh_data_runtime_t` manages GPU buffers, and `mesh_object_t` handles scene integration.
- **Lazy initialization** defers GPU resource creation until first render via the `_` pointer in `mesh_data_t`.
- **Dynamic array wrappers** in [`paint/sources/types.h`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/types.h) provide type-safe collections for mesh management.
- **Explicit ownership flags** (`owns_arrays`) prevent memory leaks during CPU-to-GPU data transitions.
- **Scale factors** (`scale_pos`, `scale_tex`) allow non-destructive vertex deformation at the data structure level.

## Frequently Asked Questions

### What distinguishes mesh_data_t from mesh_object_t in ArmorPaint?

`mesh_data_t` contains the actual geometry data—vertices, indices, and attributes—that can be shared across multiple instances, while `mesh_object_t` represents a specific instance in the scene with its own transform, material assignment, and camera distance calculation. Multiple `mesh_object_t` instances can reference the same `mesh_data_t` to optimize memory usage for repeated geometry.

### How does ArmorPaint handle the transition from CPU data to GPU buffers?

The transition occurs lazily through the `mesh_data_runtime_t` struct. When a mesh is first rendered, the engine checks if the `_` pointer in `mesh_data_t` is null; if so, it creates GPU buffers via [`util_mesh.c`](https://github.com/armory3d/armorpaint/blob/main/util_mesh.c) and stores them in `mesh_data_runtime_t`, including the `gpu_vertex_structure_t` layout description required by the rendering pipeline.

### Where does ArmorPaint store the complete list of meshes in a project?

The global project state maintains a `mesh_data_t_array_t` accessible through `project->mesh_datas`, defined in [`paint/sources/types.h`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/types.h). This array holds pointers to all `mesh_data_t` structures loaded in the current session, allowing plugins and tools to iterate geometry without traversing the scene hierarchy.

### What mesh file formats can populate these data structures?

According to the source in [`paint/sources/io/import_mesh.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/io/import_mesh.c), the engine parses external files including OBJ and FBX formats, converting them into the internal `mesh_data_t` representation through the `import_mesh_raw_mesh()` function, which normalizes position and normal data into the engine's native `i16_array_t` (short4norm) format.