How to Implement Custom Plugins in ArmorPaint: A Complete Developer Guide

ArmorPaint implements a lightweight plugin system where custom functionality is added by placing MiniC script files in the plugins/ folder and registering callbacks via plugin_create() and plugin_notify_on_*() functions.

ArmorPaint's plugin architecture is deliberately minimal yet powerful, allowing developers to extend the 3D painting software without recompiling the engine. According to the armory3d/armorpaint source code, the system uses an embedded MiniC interpreter to evaluate plugin scripts stored in the repository's plugins/ directory. This guide explains how to implement custom plugins in ArmorPaint by leveraging the core API found in paint/sources/plugin.c and configuration management in paint/sources/config.c.

How the ArmorPaint Plugin System Works

ArmorPaint's plugin system operates through a straightforward callback-based architecture. When you implement custom plugins in ArmorPaint, you create MiniC scripts that define a plugin_t object and register function pointers for UI updates, game tick updates, and cleanup operations.

The engine maintains a global map g_plugins that stores active plugin handles, with lifecycle management handled by plugin_start() and plugin_stop() in paint/sources/plugin.c.

The Five-Stage Plugin Lifecycle

Understanding the execution flow is essential for stable plugin development:

  1. Script Loading: plugin_start() constructs the path plugins/<name>, reads the file into a blob, and executes it via minic_eval_named(blob, name) at lines 13-22 in paint/sources/plugin.c.

  2. Object Creation: Inside the script, plugin_create() allocates a plugin_t structure, registers the plugin name in g_plugins, and returns a handle (lines 6-10 in paint/sources/plugin.c).

  3. Callback Registration: The script registers event handlers using plugin_notify_on_ui(), plugin_notify_on_update(), and plugin_notify_on_delete(), which store function pointers in the plugin_t struct (lines 43-53 in paint/sources/plugin.c).

  4. Execution: During the main UI loop in paint/sources/base.c, ArmorPaint iterates through g_plugins, invoking on_ui during frame rendering, on_update each tick, and on_delete during unload.

  5. Cleanup: plugin_stop() looks up the plugin, executes the on_delete callback if present, frees the MiniC context, and removes the entry from the global map (lines 30-41 in paint/sources/plugin.c).

Creating Your First ArmorPaint Plugin

To implement custom plugins in ArmorPaint, create a MiniC file in the plugins/ directory. Here is a minimal "Hello World" implementation that adds a UI button and logs messages:

/* File: plugins/hello.c */
/* 1. Create the plugin object */
var plug = plugin_create();

/* 2. Register a UI callback */
plugin_notify_on_ui(plug, function() {
    // Add a button to the side panel
    var btn = ui_button("Say Hello");
    if (ui_clicked(btn)) {
        console_log("Hello from my custom plugin!");
    }
});

/* 3. Register an update callback (runs every tick) */
plugin_notify_on_update(plug, function() {
    // Simple per-frame log (remove in production!)
    console_log("Tick …");
});

/* 4. Optional cleanup when disabled */
plugin_notify_on_delete(plug, function() {
    console_log("Good-bye from hello plugin");
});

Save this code as plugins/hello.c. The plugin_create() function initializes the plugin context, while plugin_notify_on_ui() injects custom interface elements into ArmorPaint's side panel.

Enabling Your Plugin

Enable your implementation through the Settings interface or programmatically:

  1. Open Settings → Plugins in ArmorPaint
  2. Click Enable and type hello (the filename without extension)
  3. The engine calls config_enable_plugin() in paint/sources/config.c (lines 78-89), which pushes the name to g_config->plugins and invokes plugin_start()

The UI button appears immediately in the side panel, and the console displays "Tick …" every frame until disabled.

Advanced Plugin Development Patterns

For production implementations, study the reference UV Unwrap plugin located at paint/plugins/uv_unwrap/uv_unwrap.c. This demonstrates how to implement custom plugins in ArmorPaint that interact with the engine's mesh processing API.

The UV Unwrap plugin calls proc_uv_unwrap() and manipulates material slots, showing how to access core painting functionality beyond simple UI elements. Key techniques include:

  • Mesh Processing: Call engine procedures like proc_uv_unwrap() directly from MiniC callbacks
  • State Management: Store persistent data in global script variables between update calls
  • Conditional UI: Use ui_clicked() and related functions to handle user input events

When implementing complex tools, register only the callbacks you need. If your plugin performs background processing without UI elements, omit plugin_notify_on_ui() to reduce frame overhead.

Managing Plugin Lifecycle Programmatically

The configuration system in paint/sources/config.c provides functions for dynamic plugin management:

  • config_enable_plugin(name): Adds the plugin to the user configuration and triggers plugin_start()
  • config_disable_plugin(name): Removes the plugin from g_config->plugins and calls plugin_stop()

These functions are useful for creating plugin managers or dependency systems where one plugin enables others based on project requirements.

Summary

  • ArmorPaint uses a file-based plugin system where MiniC scripts placed in plugins/ are evaluated by the MiniC interpreter at runtime
  • plugin_create() in paint/sources/plugin.c allocates the plugin_t object and registers it in the global g_plugins map
  • Callbacks are registered via plugin_notify_on_ui(), plugin_notify_on_update(), and plugin_notify_on_delete() to hook into the main application loop
  • plugin_start() and plugin_stop() handle script evaluation and cleanup, including MiniC context management at lines 13-22 and 30-41 respectively
  • Enable plugins through Settings → Plugins or programmatically via config_enable_plugin() in paint/sources/config.c
  • Reference the UV Unwrap plugin at paint/plugins/uv_unwrap/uv_unwrap.c for advanced mesh processing examples

Frequently Asked Questions

What programming language do ArmorPaint plugins use?

ArmorPaint plugins are written in MiniC, a lightweight C-like scripting language interpreted at runtime. The engine evaluates plugin scripts using minic_eval_named() as implemented in paint/sources/plugin.c, allowing immediate execution without compilation or engine recompilation.

Where should I place my custom plugin files?

Place your MiniC script files in the plugins/ folder within the ArmorPaint directory. The plugin_start() function constructs paths using the pattern plugins/<name> and automatically loads the script, as defined in paint/sources/plugin.c lines 13-22.

How do I add buttons or UI elements to ArmorPaint?

Register a UI callback using plugin_notify_on_ui(plugin_handle, function). Inside the callback function, use UI construction functions like ui_button() and ui_clicked() to create interactive elements. The engine invokes this callback during every UI frame in the main loop defined in paint/sources/base.c.

Can plugins access ArmorPaint's mesh and texture data?

Yes. Advanced plugins like the UV Unwrap example demonstrate access to the engine's procedural mesh API through functions like proc_uv_unwrap(). Plugins can interact with material slots, mesh data, and painting operations by calling the engine's exposed C functions from MiniC callbacks.

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 →