How the UV Unwrap Plugin Integrates into ArmorPaint's Editing Workflow

The UV unwrap plugin integrates into ArmorPaint through a three-stage pipeline where proc_uv_unwrap is registered globally in plugins.c, exposed via a UI button in tab_plugins.c that merges scene objects, executes the unwrapping algorithm, and redistributes the generated UV coordinates back to individual paint objects.

The UV unwrap plugin in the armory3d/armorpaint repository provides automatic texture coordinate generation via a modular architecture located in paint/plugins/uv_unwrap/. Rather than running as a built-in core feature, this functionality is dynamically registered within the global mesh processing system and executed through a temporary mesh merging workflow. This design allows the plugin to process complex multi-object scenes while maintaining compatibility with ArmorPaint's editing architecture.

Plugin Registration and Global Registration

ArmorPaint utilizes a registration-based plugin system that maps string identifiers to function pointers, enabling runtime discovery of mesh processing utilities.

Global Registration in plugins.c

The integration begins in paint/plugins/plugins.c, where the unwrapping function is inserted into the global util_mesh_unwrappers map during initialization. This registration makes the plugin discoverable by both the UI system and programmatic callers.

any_map_set(util_mesh_unwrappers, "uv_unwrap", proc_uv_unwrap);

This line stores the address of proc_uv_unwrap under the key "uv_unwrap", allowing any system component to retrieve and invoke the unwrapping routine by name without direct linking to the plugin implementation.

Header Exposure

The entry point is declared in paint/plugins/uv_unwrap/uv_unwrap.h, exposing the function signature for external modules:

void proc_uv_unwrap(raw_mesh_t *mesh);

UI Integration and Workflow Execution

The primary integration point occurs in paint/sources/ui/tab_plugins.c, where the UV Unwrap button orchestrates the data preparation and result application.

The Merge-Process-Distribute Pattern

When a user clicks the UV Unwrap button, the system executes plugin_uv_unwrap_button, which implements a three-phase workflow:

  1. Merge: All paint objects are consolidated into a single temporary mesh using util_mesh_merge(g_project->_->paint_objects).
  2. Process: The merged raw_mesh_t structure is passed to proc_uv_unwrap for algorithm execution.
  3. Distribute: Generated UV coordinates are copied back to each original object, and meshes are rebuilt with mesh_data_build.
void plugin_uv_unwrap_button() {
    util_mesh_merge(g_project->_->paint_objects);
    if (g_context->merged_object == NULL) return;

    raw_mesh_t *mesh = ALLOC_INIT(raw_mesh_t, {
        .posa = clone_i16_array(g_context->merged_object->data->vertex_arrays->buffer[0]->values),
        .nora = clone_i16_array(g_context->merged_object->data->vertex_arrays->buffer[1]->values),
        .texa = NULL,
        .inda = clone_u32_array(g_context->merged_object->data->index_array)
    });

    // Execute the unwrapping algorithm
    proc_uv_unwrap(mesh);

    // Copy results back to individual paint objects...
    util_uv_uvmap_cached = false; // Invalidate UV cache to refresh display
}

Per-Object Unwrapping

For targeted operations, the system provides plugin_uv_unwrap_per_object_button, which processes a single mesh_object_t without merging:

void plugin_uv_unwrap_per_object_button(mesh_object_t *mo) {
    raw_mesh_t *mesh = clone_mesh_to_raw(mo->data);
    proc_uv_unwrap(mesh);
    
    mo->data->vertex_arrays->buffer[0]->values = mesh->posa;
    mo->data->vertex_arrays->buffer[1]->values = mesh->nora;
    mo->data->vertex_arrays->buffer[2]->values = mesh->texa;
    mo->data->index_array = mesh->inda;
    
    mesh_data_build(mo->data);
    util_uv_uvmap_cached = false;
}

This variant clones the specific object's geometry, performs the unwrapping, and directly replaces the mesh's vertex arrays with the results.

Algorithm Implementation and Execution

The core unwrapping logic resides in proc_uv_unwrap within paint/plugins/uv_unwrap/uv_unwrap.c. This function receives the prepared raw_mesh_t and executes the complete pipeline including chart generation, packing, and UV coordinate assignment.

void proc_uv_unwrap(raw_mesh_t *mesh) {
    double t = iron_time();
    // Decode mesh, compute face normals, build charts
    // Detect folds, pack charts into atlas, rotate/scale UVs
    // Write final UV coordinates into mesh->texa
    iron_log("Unwrapped in %fs\n", iron_time() - t);
}

The function operates directly on the raw_mesh_t structure, filling the .texa (texture coordinates) buffer that was initially NULL during allocation. Upon completion, the caller distributes these coordinates back to the scene objects as demonstrated in the UI button implementations.

Summary

  • Registration: The plugin registers proc_uv_unwrap in the global util_mesh_unwrappers map in paint/plugins/plugins.c, making it accessible via the key "uv_unwrap".
  • Data Flow: The UI layer in paint/sources/ui/tab_plugins.c merges multiple paint objects into a temporary raw_mesh_t, executes the algorithm, and distributes results back to individual meshes.
  • Dual Interface: Both global scene unwrapping (merge-based) and per-object unwrapping (direct clone) are supported through separate button handlers.
  • Cache Invalidation: After UV generation, util_uv_uvmap_cached is set to false to force UI refresh and display the new coordinates.
  • Modular Design: The separation of registration, UI orchestration, and algorithm execution allows the UV unwrap plugin to function as a first-class citizen within ArmorPaint's editing workflow.

Frequently Asked Questions

How do I access the UV unwrap plugin in ArmorPaint?

Navigate to the Plugins tab in the user interface and click the UV Unwrap button. This triggers plugin_uv_unwrap_button in paint/sources/ui/tab_plugins.c, which merges your current paint objects, computes UV coordinates, and applies them to your mesh. You can also unwrap individual objects by using the per-object variant if available in your build.

What mesh format does the plugin use internally?

The plugin operates on raw_mesh_t structures defined in the ArmorPaint source. This structure contains Int16 position arrays (posa), normal arrays (nora), index arrays (inda), and a nullable texture coordinate array (texa). The UI layer clones your scene geometry into this format before calling proc_uv_unwrap, ensuring the algorithm works on a consistent intermediate representation regardless of the original object type.

Can the UV unwrap plugin be called from scripts or other code?

Yes. Because the plugin registers itself in the global util_mesh_unwrappers map using any_map_set(util_mesh_unwrappers, "uv_unwrap", proc_uv_unwrap), you can retrieve the function pointer programmatically using the key "uv_unwrap" and invoke it on a properly constructed raw_mesh_t. This allows scripted tools and other plugins to leverage the unwrapping algorithm without direct UI interaction.

Why does the plugin merge objects before unwrapping?

The global UV Unwrap button calls util_mesh_merge to create a unified mesh surface that spans all selected paint objects. This approach ensures that UV charts are packed efficiently across the entire scene without overlapping UV islands between different objects. After processing, the coordinates are distributed back to each original mesh, preserving the scene hierarchy while optimizing texture space utilization.

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 →