In this section we’ll give an overview of some fundamentals of writing NUKE plug-ins, as well as an intro to the underlying architecture and call order.
This is not going to get into that much detail - for that refer to 2D Architecture and Architecture - and is aimed mainly at those new to the NDK. If you haven’t worked within NUKE before, we highly recommend you run through some of the basic videos on The Foundry website to get a handle on common terminology, interfaces, and work practices.
NUKE is based around the premise of building a Node Graph (or DAG) of image operators to combine, produce, or otherwise operate on image data. Every object in the Node Graph is represented by a node.
A node is a block in the Node Graph that is visible to the user. Each node has a set of input arrows that connect it to other nodes, and any number of output arrows connecting it to the inputs of other nodes. The actual C++ node class is not visible to plug-in writers, so it can be changed without breaking plug-ins. Instead there are a number of access functions that retrieve information using methods on operators.
Operators, also known as Ops in the NUKE parlance, are the class that a plug-in creator generally writes. They are connected by their inputs to other Ops, not nodes. Each node creates at least one Op and, superficially, you can think of the node and Op as being equivalent. However there are many reasons a node may contain more than one Op:
- Many Ops implement themselves by building internal trees of Ops
- An Op may ask for an input Op to be created at more than one frame number or view, to allow time blending, stereoscopic or shifting effects
- Cloning creates more than one Op for the “original” node
- Looping (nyi) creates an Op for each iteration
- Readers and writers create a FileHandler based on a filename
The slightly confusing part of this is that NUKE creates an Op and interrogates it to decide on what the node is like in the Node Graph. Therefore there is always one Op per node in the Node Graph. But there maybe multiple other Ops created as well as described above.
It is very important to remember that as a plug-in writer you are writing an ‘Op’ not a node. What the user sees in the Node Graph as a set of nodes does not necessarily reflect what Ops NUKE creates to process data.
Any node in the Node Graph needs an ‘Op’ to process data. All data processing plug-ins in NUKE are derived from the Op class.
- Ops define what knobs are visible to the user in the NUKE node panel
- Ops are instanced by NUKE to process data at a given time
- Ops define how many inputs the user sees on the node
- Ops define what inputs are allowed to be connected to this Op
- Ops define how the inputs are accessed temporally
- Ops sign their parameters to return a unique hash for caching
Op should be considered a virtual base class. No NUKE Ops use it directly, they subclass one of the more specialised classes such as Iop (image processing), GeoOp (geometry processing), ParticleOp (particle processing), and DeepOp (deep image processing). In this intro, initially we’ll look at Iops to describe how image processing occurs. In the other sections, we’ll look into other sub-systems.
Knobs are the NUKE name for parameters that appear on a node.
An Op is queried by NUKE for which knobs are available on the node. The Op also tells NUKE about local parameters for each knob that can have values stored on them, frozen for a given frame (or context), for image processing.
The knob is where parameters are stored, keyframes, and animation curves. An Op itself has no notion of these concepts, it is only instanced a frozen frame (or context) for image processing.
For instance, a simple Op with one floating point knob in its control panel is defined like this in C++:
Float_Knob(f, &_value, “mygain”);
Here, NUKE creates a floating point knob for the node and uses this to track keyframes, values, and animation. The first argument, &_value, is a local parameter that NUKE sets to a given value for image processing when required.
For instance, you can set a value and key on this knob to 0 at frame 1 and 10 at frame 10.
When NUKE uses this Op for image processing for say, frame 10, it fills the _value parameter with the value 10 before it calls the Op to process the image. Or, if it needs to render frame 1 it fills _value with 0.
Now we’ll look at the typical processing events that occur when you perform some actions in NUKE to view an image.
NUKE works on a ‘pull’ system - a node at the bottom of the node tree ‘pulls’ data from its inputs, and they in turn ‘pull’ from their inputs. This occurs up the tree until NUKE hits a generator node such as a Read or CheckerBoard node.
For instance, the following occurs when you create a CheckerBoard node and views its output:
- User -> Create a CheckerBoard
- NUKE -> Loads and creates an instance of the CheckerBoard Op
- NUKE -> Calls the knobs() function on the Op to figure out what parameters the node has
- NUKE -> opens the control panel and shows the parameters for the knobs
- User -> At some point the user attaches a Viewer or output node (Write) to produce an image
- NUKE -> Looks for Ops that match the current Node Graph at the current context (frame number). If none are found, new Ops are created
- NUKE -> Places the values of the knobs into the Ops frozen at a given context (store)
- NUKE -> The output Op asks its inputs for what channels and image sizes it produces (validate). Each Op recursively calls its inputs asking for their image sizes and channels until there are no more inputs
- NUKE -> The output Op requests a certain image size and channels from all its inputs (request). Each Op recursively calls its inputs requesting a certain image size and channels required to output the image requested until there are no more inputs
- NUKE -> The output Op requests rows from all its inputs (engine). Each Op recursively calls its inputs asking for rows until there are no more inputs. Each row as it flows back down the tree to the output is modified by each Op.
The 2D Architecture section explains each of these steps in more detail.