Skip to content

Plugins

Freebird allows users to extend its functionality by creating custom plugins. A plugin is a Python file or package that defines new features, such as buttons in the Freebird main menu, custom commands, or integrations with other tools.

Where to Put Plugins

Freebird plugins live in ~/.freebird/plugins:

  • On Windows, this is typically C:\Users\YourName\.freebird\plugins.

  • On Linux, this is typically /home/yourname/.freebird/plugins.

Freebird loads plugins from that folder when Blender starts, and when you click Reload All in Freebird Settings.

A plugin can be either:

  • a single Python file such as ~/.freebird/plugins/hello.py
  • a folder such as ~/.freebird/plugins/hello_plugin/ containing __init__.py

Plugin Structure

To be treated as a plugin, the top-level file must define fb_info with a name field.

fb_info = {"name": "My plugin"}

register() and unregister() methods are optional. If they exist, Freebird calls them when plugins are loaded or unloaded.

Install a Plugin

Place your plugin file or folder in the ~/.freebird/plugins directory, then start Blender or click Reload All in Freebird Settings.

  1. Open Freebird Settings in Blender.
  2. Click Open Plugins Folder.
  3. Add a Python file or package folder inside the ~/.freebird/plugins folder.
  4. Start Blender, or click Reload All if Blender is already open.
  5. Open the Freebird main menu and select CUSTOM to see your plugin buttons.

You can also click Plugins Guide from Freebird Settings to open this page.

Debugging and Logging

If your plugin fails to load, you should see an error message in Freebird's log file. You can see the log file by clicking Freebird Settings > App Log.

To debug a plugin, you can add print() statements to your plugin code (which will send to Blender's console), or use Freebird's logging utility (which will write to Freebird's log file):

from freebird.utils import log

log.info("This is a log message from my plugin")

API: Add a button to the Main Menu Launcher

Import the launcher API from freebird.api:

from freebird.api import add_launcher_button, remove_launcher_button
add_launcher_button(
    "my_plugin",  # plugin_id
    "remesh",  # button_id
    "REMESH",  # button label
    onclick=run_remesh,  # function to call when the button is clicked
)

plugin_id and button_id must be non-empty strings. label must be a non-empty string. onclick must be callable.

You can also (optionally) provide an icon parameter. This provides the absolute path to the icon file. A default icon will be used if icon is not provided or if the provided path is invalid.

Example 1: Simple "Hello" Plugin

Example file: ~/.freebird/plugins/hello.py

import os

from freebird.api import add_launcher_button, remove_launcher_button

fb_info = {"name": "Hello plugin"}


def on_launcher_click():
    print("Hello from a Freebird plugin")


def register():
    icon_path = os.path.join(os.path.dirname(__file__), "icon.png")
    add_launcher_button(
        "hello_plugin",
        "hello",
        "HELLO",
        onclick=on_launcher_click,
        icon=os.path.abspath(icon_path),
    )


def unregister():
    remove_launcher_button("hello_plugin", "hello")

Example 2: Remesh Plugin

Save it as: ~/.freebird/plugins/remesh.py

import bpy

from freebird.api import add_launcher_button, remove_launcher_button
from freebird.utils import log

fb_info = {"name": "Remesh Selected Meshes"}


PLUGIN_ID = "remesh_plugin"
BUTTON_ID = "remesh"


def run_remesh():
    selected_meshes = [
        obj for obj in bpy.context.selected_objects
        if obj.type == "MESH"
    ]

    if not selected_meshes:
        log.info("No mesh objects selected")
        return

    for obj in selected_meshes:
        try:
            bpy.context.view_layer.objects.active = obj
            obj.select_set(True)

            modifier = obj.modifiers.new(name="FreebirdRemesh", type="REMESH")
            modifier.mode = "VOXEL"
            modifier.voxel_size = 0.1

            bpy.ops.object.modifier_apply(modifier=modifier.name)

            log.info(f"Remeshed object: {obj.name}")

        except Exception as e:
            log.error(f"Failed to remesh {obj.name}: {e}")

    log.info("Finished remeshing selected meshes")


def register():
    add_launcher_button(
        PLUGIN_ID,
        BUTTON_ID,
        "REMESH",
        onclick=run_remesh,
    )


def unregister():
    remove_launcher_button(PLUGIN_ID, BUTTON_ID)