HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Writing a VOP

Introduction

The following steps are necessary to create a custom VOP node:

  1. Create a new class which inherits from VOP_Node
  2. Define any parameters found on the node.
  3. Add the new node type to the table of other VOP node types.
  4. Override the inputs/outputs query methods.
  5. Override the methods to generate code on cooking.

Each of these steps is discussed in more detail below. Alternatively, a full example can be found in VOP/VOP_Switch.C and VOP/VOP_Switch.h.

Inheriting from VOP_Node

The first step in created a custom VOP node is to inherit from the main VOP node class, VOP_Node

class VOP_Switch : public VOP_Node
{
public:
/// Creates an instance of this node with the given name in the given network.
static OP_Node *myConstructor(OP_Network *net,
const char *name,
OP_Operator *entry);
/// Our parameter templates.
static PRM_Template myTemplateList[];
protected:
VOP_Switch(OP_Network *net, const char *name, OP_Operator *entry);
virtual ~VOP_Switch();
};

Defining Node Parameters

The parameters on a VOP node are defined in much the same way they are defined on other custom nodes. First, the labels and optionally default value objects are created for all parameters, and then a single static parameter template table is populated with corresponding templates:

// Define our parameter names
static PRM_Name theSwitcherName("switcher", "Switcher Index");
static PRM_Name theOutOfBoundsName("outofbounds",
"Out Of Bounds Behavior");
// Define the choices for our menu
static PRM_Name theOutOfBoundsChoices[] =
{
PRM_Name("last", "Output Last Input Value"),
PRM_Name("zero", "Output Zero"),
};
// Define the choices' corresponding enum values.
enum
{
};
static PRM_ChoiceList theOutOfBoundsMenu(PRM_CHOICELIST_SINGLE,
theOutOfBoundsChoices);
PRM_Template VOP_Switch::myTemplateList[] =
{
PRM_Template(PRM_INT, 1, &theSwitcherName, PRMzeroDefaults),
PRM_Template(PRM_ORD, 1, &theOutOfBoundsName, PRMzeroDefaults,
&theOutOfBoundsMenu),
// List terminator
};

Adding a New VOP Type

To be able to use the newly created VOP node type in Houdini, it must be added to the VOP types table first. This is done in the newDriverOperator() method which has the table passed to it as an argument:

{
op = new VOP_Operator("hdkswitch", // internal name
"HDK Switch", // UI name
VOP_Switch::myConstructor, // How to create one
VOP_Switch::myTemplateList, // parm definitions
0, // Min # of inputs
VOP_VARIABLE_INOUT_MAX, // Max # of inputs
"*", // vopnet mask
0, // Local variables
OP_FLAG_UNORDERED, // Special flags
1); // # of outputs
table->addOperator(op);
}

This method is called by Houdini after loading the library file with the custom VOP node. Note that we create a VOP_Operator instead of the base OP_Operator. The VOP_Operator provides more latitude to specialize our new type. In particular, we can restrict it to be available only in certain VOPNET contexts as well as to provide more than one output.

Overriding Input/Output Methods

Each VOP node must override several methods related to its inputs and outputs which provide information regarding the number of its fixed and variable inputs, their labels, types, etc. These are:

Generating Code

Whenever a VOP node cooks, its VOP_Node::getCode(UT_String &codestr) function is called. The callee expects to find the code generated by each node in the codestr (note that it should be overwritten with code, not appended to).

To reference existing inputs, outputs, and parameters in the generated code, you should prefix them with $ symbol. For example, an input named "Cd" becomes "$Cd", a parameter named "parm1" becomes "$parm1", etc. An extremely simple example can be seen below. It simply sets the output of a node to its input value:

void VOP_Example::getCode(UT_String &codestr)
{
// Get our output and input names
UT_String inputName, outputName;
getOutputName(outputName, 0);
getInputName(inputName, 0);
codestr.sprintf("$%s = $%s;\n", outputName, inputName);
}

Examples

An example of a custom VOP node can be found in VOP/VOP_Switch.C and VOP/VOP_Switch.h.

For a more advanced example of creating a custom VOP context that is completely user-defined, please see VOP/VOP_CustomContext.C and VOP/VOP_CustomContext.h.