5.3 KiB
Cattery Pattern and Gizmo Design
Cattery Directory Structure
Place plugins in ~/.nuke/Cattery/NodeName/:
NodeName/
├── NodeName.gizmo # node UI definition
├── NodeName.cat # compiled TorchScript model
├── cat.json # Cattery metadata
├── NodeName.png # icon (26x26)
└── NodeName@2x.png # retina icon (52x52)
cat.json
{
"minimum_nuke_version_required": 14.0,
"version": 1,
"category": "Depth Estimation",
"description": "NodeName - https://github.com/Biohazard-VFX/...",
"cats": [
{
"filepath": "NodeName.gizmo",
"name": "NodeName",
"icon": "NodeName.png"
}
]
}
Registration
init.py (all modes including command-line):
import nuke
nuke.pluginAddPath('./Cattery/NodeName')
menu.py (interactive only):
import nuke
toolbar = nuke.menu("Nodes")
toolbar.addCommand(
'Cattery/Category/NodeName',
'nuke.createNode("NodeName")',
icon="NodeName.png"
)
Gizmo Structure
A gizmo is a Group node containing an internal processing pipeline. Key sections: knob definitions, internal nodes, Python callbacks.
Knob Types for ML Nodes
# GPU settings
addUserKnob {6 useGPU l "Use GPU" +STARTLINE}
useGPU true
addUserKnob {26 gpuName l "" +STARTLINE}
# Model selection (Enumeration)
addUserKnob {4 model_variant l "Model" M {"Small" "Base" "Large" "Metric"}}
# Resolution control
addUserKnob {4 downrez l "Downrez" M {"1/1 (Full)" "1/2" "1/4"}}
downrez "1/2"
# Processing resolution (Enumeration with numeric comparison)
addUserKnob {4 process_res l "Process Res" M {"Auto" "336" "504" "672" "1008"}}
# Output mode
addUserKnob {4 view l "Output" M {"Depth (grayscale)" "False Color" "Confidence" "Metric Depth"}}
# Depth controls
addUserKnob {7 near l "Near" R -20 20}
near 5
addUserKnob {7 far l "Far" R -20 20}
far 0
# Toggle
addUserKnob {6 bypass_srgb l "Bypass sRGB" +STARTLINE}
addUserKnob {6 invert_map l "Invert Depth" +STARTLINE}
# Info tab
addUserKnob {20 info l "Info"}
addUserKnob {26 version l "Version" T "1.0.0"}
addUserKnob {26 author l "Author" T "Biohazard VFX"}
Internal Node Graph Pipeline
Typical ML inference gizmo pipeline:
Input1
|
AssertNukeVersion (check >= 14.0)
|
Shuffle (ensure RGBA channels exist)
|
Colorspace (linear -> sRGB, unless bypass_srgb)
| NOTE: use Colorspace node, NOT ColorMatrix
| sRGB EOTF is nonlinear, ColorMatrix can't do it
|
Reformat (downrez based on knob)
| scale expression: pow(2, parent.downrez * -1)
|
Inference (load .cat model)
| modelFile: [lsearch -inline [plugins -all NodeName.cat] *.cat]
| useGPU: {parent.useGPU}
|
Reformat (uprez back to original)
| reference Input1 dimensions: Input1.width, Input1.height
|
ChannelCopy (depth.alpha -> depth.Z)
|
Grade (near/far adjustment)
| white: {parent.near - parent.far}
| black: {parent.far}
|
[Invert] (optional, controlled by invert_map knob)
|
Switch (output mode selection)
├─ input 0: Depth grayscale
├─ input 1: False Color (Ramp gradient + STMap lookup)
├─ input 2: Confidence map
└─ input 3: Metric depth
|
Output1
False Color Visualization
Build a Turbo/viridis colormap gradient texture using stacked Ramp nodes, then use the depth map as UV coordinates via STMap:
Constant (512x64)
|
Ramp1 (blue->cyan)
|
Ramp2 (cyan->green)
|
Ramp3 (green->yellow)
|
Ramp4 (yellow->red)
|
STMap (use depth as U coordinate)
| uv: depth channel
Expression Gotchas
- Enum knobs: compare by NUMERIC INDEX, not string
- correct:
parent.process_res == 0(for "Auto") - wrong:
parent.process_res == "Auto"
- correct:
- TCL model path:
[lsearch -inline [plugins -all Name.cat] *.cat] - Reference parent input:
Input1.widthnotwidth - Downrez scale:
pow(2, parent.downrez * -1.0)
.cat File Format
.cat files are NOT just renamed .pt files. They have a FlatBuffers header (~136 bytes) prepended before the TorchScript zip archive. This header encodes channel mappings, scale factors, model ID, and zip offsets.
The ONLY way to create a valid .cat file is through Nuke's CatFileCreator node. Manual zip repacking will produce "Unrecognized filetype" errors.
.cat File Creation
Requires NukeX or Nuke Studio (CatFileCreator node not in Nuke NC):
- torch.jit.script your model -> save as .pt
- In NukeX, create CatFileCreator node
- Set torchScriptFile to your .pt file
- Set catFile to desired output path
- Set channelsIn (e.g., "rgba.red rgba.green rgba.blue")
- Set channelsOut (e.g., "rgba.alpha")
- Set modelId
- Click "Create .cat file and Inference"
Script via nuke -t:
import nuke
cat = nuke.createNode('CatFileCreator', inpanel=False)
cat['torchScriptFile'].setValue('/path/to/model.pt')
cat['catFile'].setValue('/path/to/output.cat')
cat['modelId'].setValue('NodeName')
cat['channelsIn'].setValue('rgba.red rgba.green rgba.blue')
cat['channelsOut'].setValue('rgba.alpha')
cat.knob('createCatFile').execute()
Distribution
End users just need:
- Copy
NodeName/folder to~/.nuke/Cattery/ - Restart Nuke (or Cattery > Update)
- Node appears in Cattery menu
For studio deployment:
- Set NUKE_PATH to shared network location
- Place Cattery folder in shared path
- All artists get the node automatically