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
- Open Plugin Manager → Edit Commands → New Script. Kiosk creates a stub
.pyfile and opens it for editing. - Write a function whose name starts with
kiosk_— that prefix is how Kiosk discovers it. - The comment on the line directly above
defbecomes the command’s tooltip in the UI. - The first parameter is always
file_info— a dictionary describing the exported asset. - Return
file_infoat 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:
| Key | Description |
|---|---|
file_path | Full path to the asset file |
file_name | Filename with extension (e.g. oak_bark_4k.exr) |
base_name | Filename without extension (e.g. oak_bark_4k) |
extension | Extension without the dot (e.g. exr) |
source_name | Library / source folder name (e.g. PolyHaven) |
category_name | Active category (e.g. textures, hdri) |
tags | List of subfolder names (e.g. ["wood", "seamless"]) |
render_engine | Active 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_):
| Command | Description |
|---|---|
kiosk_import_asset | Import the file into the scene |
kiosk_create_material | Create a renderer-specific material from detected PBR textures |
kiosk_import_hdri | Load an HDRI environment |
kiosk_create_area_light | Add an area light |
kiosk_reveal_in_explorer | Open 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 type | UI control |
|---|---|
bool | Checkbox |
int | Integer spinner |
float | Decimal spinner |
str | Text 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:
| Token | Replaced 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