HDK
|
A lot of the leg work involved in creating a new operator is setting up the array of PRM_Template objects. These templates are used to create the parameter list (PRM_ParmList) for the operator as well as the parameter dialog that appears in the parameter pane for the given node type.
Let's look at an example from SOP_Star.C
First notice that PRM_Include.h is included for the various PRM_Template classes and types. Then, we have some static data that is used by the main array of PRM_Template's (also static). For each PRM_Template object, we initialize it with the desired information. There are many forms of the PRM_Template::PRM_Template() constructor along with various default arguments. Here are the main function parameters:
thetype
thetype_ext
thevectorsize
thenameptr
thedefaults
thechoicelistptr
therangeptr
thecallbackfunc
thehelptext
theexportlevel
thespareptr
In the example above there are a number of common parameter names, defaults, and ranges that start with the PRM prefix. For a detailed list of these see the PRM_Shared.h file (included by PRM_Include.h).
There are numerous predefined PRM_Type objects covering most of the commonly use parameter types declared in PRM_Type.h. They follow a common naming convention. If it ends in _E
, then it can contain non-animated expressions. If it ends in _J
, then it can be animated. If it ends with neither of these suffixes, then it is non-animatable. Some commonly used ones are:
PRM_FLT
1
, 2
, 3
, etc.PRM_ANGLE
PRM_RGB
r
, g
, b
, and a
.PRM_XYZ
x
, y
, and z
.PRM_UVW
u
, v
, and w
.PRM_DIRECTION
x
, y
, and z
.PRM_INT
1
, 2
, 3
, etc.PRM_FLT_MINMAX
, PRM_INT_MINMAX
, PRM_ANGLE_MINMAX
min
and max
. For angle parameters, it will show a 2D circle representation.PRM_STARTEND
start
and end
.PRM_BEGINEND
start
and end
.PRM_STRING
PRM_FILE
, PRM_PICFILE
, PRM_GEOFILE
PRM_ORD
PRM_TOGGLE
PRM_CALLBACK
PRM_BUTTONSTRIP
PRM_SWITCHER
In addition to these types, one may bitwise or one of the following flags:
PRM_TYPE_JOIN_NEXT
PRM_TYPE_LABEL_NONE
A detailed breakdown for a PRM_Type follows. It has three basic types (PRM_Type::PRM_BasicType):
FLOAT
STRING
ORDINAL
For each of these types, there are several possibilities to give it more semantic meaning (PRM_Type::PRM_FloatType, PRM_Type::PRM_OrdinalType, PRM_Type::PRM_StringType, PRM_Type::PRM_PathType). This allows the parameter pane to present the parameter in a way that is more suited to its real purpose. For example, if you have a string parameter, you might mark it as being a file path of some sort so that it will have a button in the parameter pane that brings up a file browser. For file paths, you can additionally specify them as particular file types so that the file browser has certain default file extensions. Additionally, there are some other modifiers on PRM_Type:
PRM_ChannelType
PRM_InterfaceType
PRM_BehaviorType
In addition to PRM_Type, one also supply an optional PRM_ExtendedType. The most widely used options for PRM_ExtendedType are:
PRM_TYPE_DYNAMIC_PATH
PRM_TYPE_DYNAMIC_PATH_LIST
PRM_TYPE_LOGARITHMIC
Switcher parameters types (PRM_SWITCHER, PRM_SWITCHER_NOREFRESH) allows one to organize the parameters that follow it into a set of folders. The number of folders is specified as the vector size. The folder labels, and the number of parameters in the folder are given by the array of defaults. Note that if there are switchers inside switchers, the nested switcher inside and all its child parameters only count as 1 entry in the PRM_Default.
Here is a simple example that creates a switcher that has 2 folders where the first folder has 1 parameter, and the second folder has no parameters:
Multi-parms are dynamically sized parameters. Each multi-parm has a variable number of child instances. Each multi-parm child instance itself consists of a fixed number of parameters (defined by an array of PRM_Template objects).
A multi-parm has a separate PRM_Template constructor so it cannot be confused with a standard parameter. Here's the constructor: (there is also a version with a PRM_Export parameter)
type
PRM_MULTITYPE_LIST
PRM_MULTITYPE_SCROLL
PRM_MULTIPARM_SWITCHER
type
templates vectorsize
name
defaultsize
defaultrange
spare
Multi-parm template lists are almost identical to a normal template list. The differences are:
Here is an example that creates a series of 2D points with a RGBA color.
You can access the value of any multi-parm by name, or by using the PRM_Name with the # in it. You should not access multi-parms by index, nor should you lookup the parameter by name and store the index. Child parameters of a multi-parm are NOT guaranteed to be stored in any particular order (especially if 2 multi-parms or nested multi-parms are used in an OP_Node).
You may access any parameter before a multi-parm by index, but parameters after a multi-parm must be accessed by name.
As a convenience, you can use the eval...Inst() and set...Inst() functions to evaluate parameters. From the sample above, this code evaluates the point position of a series of points in the multi-parm:
Note that indexing multi-parms starts at 1, not 0 by default. This so that user-friendly instance names are generated. You can override this behavior by changing the start offset in the parameter template constructor. However, this should only be done for exceptional cases or for backwards compatibility. The address of i is taken in the above example because the instance is actually a list; by default the list is 1 element long, but nested multi-parms require longer lists (you may have noticed the last parameter is 'int nestlevel = 1')
Nested multi-parms are slightly more complicated, simply because more levels of instance numbers are required. Consider the example:
Each curve contains a series of 2D points, and the node itself has a series of curves, each of which are a variable size.
To access a specific point, you need to specify both the curve index and the point index.
Finally, note that you can't export multi-parms to the Toolbox (PRM_EXPORT_TBX). They're just too big.
Although ramp parameters are internally represented as multi-parms, they have a simplified declaration using the multi-parm PRM_Template constructor. You choose the PRM_MultiType as either PRM_MULTITYPE_RAMP_RGB (color) or PRM_MULTITYPE_RAMP_FLT (float) ramps. Then you pass NULL for the template list. The vector size must be 1 and the first PRM_Default entry specifies default number of ramp points (which itself must be at least 1).
To create a parameter with a menu, a PRM_ChoiceList object needs to be supplied. The PRM_ChoiceList constructor takes various PRM_ChoiceListType flags:
PRM_CHOICELIST_REPLACE
PRM_CHOICELIST_TOGGLE
PRM_CHOICELIST_APPEND
PRM_CHOICELIST_WILD
*
as a valid value to mean all choices.PRM_CHOICELIST_SINGLE
The example below demonstrates a simple static menu setup for ordinal and string parameters.
Menus with items generated dynamically are done with a different form of the PRM_ChoiceList constructor that is given a PRM_ChoiceGenFunc callback. The callback fills out a PRM_Name array along with a trailing sentinel item. Care should be taken to NOT generate more than listsize
items.
For group menus, one can also use SOP_Node functions like in the example below.
To disable or hide parameters, one should override the OP_Parameters::updateParmsFlags() method. This method is called by the parameter pane to give the node a chance to disable or hide parameters in the UI which will not used when cooked. Parameters are usually disabled or hidden due to the values of other parameters which render them unused.
The updateParmsFlags() implementation typically evaluates some parameters to determine whether different set of parameters should be used. Then either the OP_Parameters::enableParm(), or the OP_Parameters::setVisibleState() methods should be called with a true or false value. Note that enableParm() should always be called for any parameter that might be enabled, or disabled. Likewise, setVisibleState() should always be called for any parameter that might be hidden, or made visible again. Both methods return true
if the parameter's state actually changed. updateParmsFlags should keep track of those return values and return true
also if any of the modified parameters' state changed.
It's also a good policy to always call the updateParmsFlags function of the base class and keep a track of its return value, in much the same way as the return value of the parameter modifiers. Otherwise, any parameters that the new operator might inherit, will not have their UI state properly updated.
As new features are added to your custom operator, you may find a need to change the behavior of how some of the old parameters work. However, there might be legacy scene files that you need to support which relies on a particular value of the old parameter behaving a particular way. The usual way to solve these type of problems is to create a new parameter that works similarly to the old parameter but has a different name. The old parameter is then moved into the obsolete parameter list for special handling at load time.
As Houdini loads a scene file, an obsolete parameter list is created for each node that it is loading. If it encounters a parameter on a node that does not match any of the existing parameters of that node, it will first attempt to find a matching obsolete parameter for loading. When the node is done loading, the OP_Node::resolveObsoleteParms() method is called to convert the values of the obsolete parameters into values of the current parameter list that will result in the old behavior. Finally, the obsolete parm list is destroyed.
To use obsolete parameters, first define an obsolete PRM_Template array similar to how it is done with regular parameter templates. Then when you register your operator, call OP_Operator::setObsoleteTemplates() with your obsolete template list.
Then implement OP_Node:resolveObsoleteParms() in your subclass. The important thing to note is that the obsolete parameter list is like any parameter list with its default values. The only way to know if a particular obsolete parm was found, is to test if the value of the parameter is at its default value. The example below illustrates the coding pattern for resolveObsoleteParms
.