Custom Commands

Write Python commands that run inside your DCC when you export an asset.

A custom command is a plain Python function that runs inside your DCC when an asset is exported — every function whose name starts with kiosk_ is picked up automatically. Chain commands into a stack to import an asset, build a material, and set up lighting in a single click.

Custom commands are a Pro feature. To integrate a DCC Kiosk doesn’t ship with, see Custom Plugins.

Quick start

  1. Open Plugin Manager → Edit Commands → New Script. Kiosk creates a stub .py file and opens it for editing.
  2. Write a function whose name starts with kiosk_ — that prefix is how Kiosk discovers it.
  3. The comment on the line directly above def becomes the command’s tooltip in the UI.
  4. The first parameter is always file_info — a dictionary describing the exported asset.
  5. Return file_info at the end so the next command in the stack receives the data (or your modified copy).

You can put as many kiosk_ functions — and as many .py files — in your commands folder as you like. Kiosk discovers all of them.

# Print the asset path to the console
def kiosk_print_path(file_info):
    print(f"Exporting: {file_info['file_path']}")
    return file_info

file_info

Every command receives file_info — a dictionary Kiosk fills with everything it knows about the exported asset:

KeyDescription
file_pathFull path to the asset file
file_nameFilename with extension (e.g. oak_bark_4k.exr)
base_nameFilename without extension (e.g. oak_bark_4k)
extensionExtension without the dot (e.g. exr)
source_nameLibrary / source folder name (e.g. PolyHaven)
category_nameActive category (e.g. textures, hdri)
tagsList of subfolder names (e.g. ["wood", "seamless"])
render_engineActive render engine from the Plugin Manager

Tip: right-click any tile → Development → Debug File Info to print every available key for that file.

Built-in commands

These ship with Kiosk and are always available in the stack (all start with kiosk_):

CommandDescription
kiosk_import_assetImport the file into the scene
kiosk_create_materialCreate a renderer-specific material from detected PBR textures
kiosk_import_hdriLoad an HDRI environment
kiosk_create_area_lightAdd an area light
kiosk_reveal_in_explorerOpen the file location in Explorer

Stack chaining

The command stack runs top to bottom. Each command can modify file_info and return it — the modified version is passed to the next command. This lets commands cooperate: one copies the file and updates the path, the next imports from that new path.

# Copy to the project folder and update the path for the next command
def kiosk_stage_file(file_info):
    import shutil, os
    dst = os.path.join('C:/project/assets', file_info['file_name'])
    shutil.copy2(file_info['file_path'], dst)
    file_info['file_path'] = dst
    return file_info

A command that returns nothing (or None) passes file_info unchanged to the next step.

Arguments

Any parameter you add after file_info with a default value is automatically surfaced as an editable field in the Command Stack editor. Artists adjust the value per-stack — by clicking the next to the command — without ever touching the script file. Saved values travel with the stack.

The UI control is inferred from the default value’s type:

Default typeUI control
boolCheckbox
intInteger spinner
floatDecimal spinner
strText field
# Import FBX with artist-adjustable options
def kiosk_import_fbx(file_info, center_on_import=True, scale=1.0):
    import bpy
    bpy.ops.import_scene.fbx(filepath=file_info['file_path'], global_scale=scale)
    if center_on_import and bpy.context.active_object:
        bpy.context.active_object.location = (0, 0, 0)
    return file_info

Here center_on_import shows as a checkbox and scale as a decimal field.

Keep all DCC imports (import bpy, import maya.cmds, etc.) inside the function body — Kiosk reads the signature without executing the file.

Path tokens

Inside any string argument you can use these tokens — Kiosk replaces them with the asset’s values when the command runs:

TokenReplaced with
<category_name>Active category (e.g. hdri, textures)
<base_name>Filename without extension
<file_name>Full filename with extension
<source_name>Library / source folder name
<extension>File extension (e.g. exr, png)
<dcc>Host application (e.g. Blender)
# Copy the asset into a token-built subfolder
def kiosk_copy_asset(file_info, sub_path="<category_name>/<source_name>"):
    import shutil, os
    resolved = (sub_path
        .replace('<category_name>', file_info.get('category_name', ''))
        .replace('<source_name>',   file_info.get('source_name', ''))
        .replace('<base_name>',     file_info.get('base_name', ''))
        .replace('<file_name>',     file_info.get('file_name', ''))
        .replace('<extension>',     file_info.get('extension', '')))
    dst = os.path.join('C:/project', resolved)
    os.makedirs(os.path.dirname(dst), exist_ok=True)
    shutil.copy2(file_info['file_path'], dst)
    return file_info

Examples

Blender — import FBX and center at origin

# Import FBX and move to world origin
def kiosk_import_fbx_centered(file_info):
    import bpy
    bpy.ops.import_scene.fbx(filepath=file_info['file_path'])
    if bpy.context.active_object:
        bpy.context.active_object.location = (0, 0, 0)
    return file_info

Blender — create an image-texture material

# Create a Principled BSDF material with this texture
def kiosk_create_texture_material(file_info):
    import bpy
    mat = bpy.data.materials.new(name=file_info['base_name'])
    mat.use_nodes = True
    bsdf = mat.node_tree.nodes["Principled BSDF"]
    tex = mat.node_tree.nodes.new("ShaderNodeTexImage")
    tex.image = bpy.data.images.load(file_info['file_path'])
    mat.node_tree.links.new(tex.outputs["Color"], bsdf.inputs["Base Color"])
    return file_info

Cinema 4D — merge a scene file

# Merge the selected scene into the current document
def kiosk_merge_scene(file_info):
    import c4d
    c4d.documents.MergeDocument(
        doc,
        file_info['file_path'],
        c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS
    )
    c4d.EventAdd()
    return file_info

Maya — import as a reference

# Import file as a Maya reference
def kiosk_import_reference(file_info):
    import maya.cmds as cmds
    cmds.file(file_info['file_path'], reference=True, namespace=file_info['base_name'])
    return file_info

Houdini — load geometry into a new geo node

# Create a Geo node and load the file into a File SOP
def kiosk_load_geometry(file_info):
    import hou
    geo_node = hou.node("/obj").createNode("geo", file_info['base_name'])
    file_sop = geo_node.createNode("file")
    file_sop.parm("file").set(file_info['file_path'])
    file_sop.setDisplayFlag(True)
    return file_info

Branch on the active render engine

# Create a renderer-specific material
def kiosk_smart_material(file_info):
    import maya.cmds as cmds
    name = file_info['base_name']
    engine = file_info['render_engine']
    if engine == 'Arnold':
        cmds.shadingNode('aiStandardSurface', asShader=True, name=f'{name}_mtl')
    elif engine == 'VRay':
        cmds.shadingNode('VRayMtl', asShader=True, name=f'{name}_mtl')
    return file_info
behind KioskMeet the maker →