[SESI logo]

Houdini Development Toolkit - Version 6.5

Side Effects Software Inc. 2004

Channel Operator

Creating a Custom CHOP

CHOP stands for Channel Operation. Each CHOP is responsible for creating or modifying channel data. All CHOPs are derived from the CHOP_Node base class. To write a custom CHOP, you need to do the following things:

Declare a New Operator
Define Parameters
Write the Cook Method

Declaring a New Operator

In order that your CHOP can be found by Houdini, it must be added to the existing operators. This is done through the newChopOperator(...) method. This function simply creates a new OP_Operator type and adds it to the incoming operator table. Let's look at an example as we go through and describe the different parts of the operator. void newChopOperator(OP_OperatorTable *table) { table->addOperator( new OP_Operator("stair", // Internal name "Stair", // UI name CHOP_Stair::myConstructor, // CHOP constructor &CHOP_Stair::myTemplatePair, // Parameters 0, // Min # of inputs 0, // Max # of inputs &CHOP_Stair::myVariablePair, // Local variables OP_FLAG_GENERATOR) // generator/source ); } The OP_Operator has eight parts.

1. The Internal Name This is used as the initial name of the CHOP when it is first created. i.e. the first stair CHOP we add will be named stair1.

2. UI name This will be the label of the CHOP in the parameter dialog. It has no use internally.

3. CHOP Constructor The method to construct the CHOP.

4. Parameters This is the list of the parameters used in the CHOP.

5. Min # of Inputs The minimum number of inputs for the CHOP. If it is a generator there are usually no inputs coming in. (However, there are cases like the constant, noise, and object CHOPs where it is desirable to have inputs.) If it is a filter type then this entry will be at least one.

6. Max # of Inputs The maximum number of sources allowed for the CHOP. This specifies how many possible inputs the CHOP can have at one time. i.e. the merge CHOP allows for 9999 total inputs.

7. Local Variables This is a list of local variables that are used within the CHOP. i.e. you may want to declare $C, or $NC for the current channel number and the total number of channels in the CHOP. If you have no local variables then enter a 0 here.

8. Generator/Source Flag If the CHOP is to be a generator CHOP (i.e. generates channels) then this flag must be set to OP_FLAG_GENERATOR. If you wish it to be a filter then nothing needs to be entered as it is the default.


Defining the Parameters

Parameters are defined through a parameter list. You should read Using Parameters for a complete description of parameters before looking at the following example. CHOP_SWITCHER2(4, "Stair", 7, "Channel"); // Names of gadgets static PRM_Name names[] = { PRM_Name("number", "Number of Stairs"), PRM_Name("height", "Stair Height"), PRM_Name("offset", "Offset"), PRM_Name("direction", "Direction"), PRM_Name("channelname", "Channel Name"), PRM_Name("start", "Start"), PRM_Name("end", "End"), // SampleRateName // ExtendLeftName // ExtendRightName // DefaultValueName }; // Menus static PRM_Name CHOPstairMenu[] = { PRM_Name("up", "Up"), PRM_Name("down", "Down"), PRM_Name(0), }; static PRM_ChoiceList stairMenu((PRM_ChoiceListType) (PRM_CHOICELIST_EXCLUSIVE | PRM_CHOICELIST_REPLACE), CHOPstairMenu); // Defaults static PRM_Default nameDefault(0,"chan1"); static PRM_Range stairRange(PRM_RANGE_RESTRICTED, 0, PRM_RANGE_UI, 10); PRM_Template CHOP_Stair::myTemplateList[] = { PRM_Template(PRM_SWITCHER, 3, &PRMswitcherName, switcher), // page 1 PRM_Template(PRM_INT_J, 1, &names[0], PRMoneDefaults, 0, &stairRange), PRM_Template(PRM_FLT_J, 1, &names[1], PRMoneDefaults, 0, &CHOP_DefaultValueRange), PRM_Template(PRM_FLT_J, 1, &names[2], PRMzeroDefaults, 0, &CHOP_DefaultValueRange), PRM_Template(PRM_ORD, 1, &names[3], PRMzeroDefaults, &stairMenu), // page 2 PRM_Template(PRM_STRING, 1, &names[4], &nameDefault, 0), PRM_Template(PRM_FLT_J, 1, &names[5], &CHOP_StartDefault, 0, &CHOP_FrameRange), PRM_Template(PRM_FLT_J, 1, &names[6], &CHOP_EndDefault, 0, &CHOP_FrameRange), PRM_Template(PRM_FLT_J, 1, &CHOP_SampleRateName, &CHOP_SampleRateDefault, 0, &CHOP_SampleRateRange), PRM_Template(PRM_ORD, 1, &CHOP_ExtendLeftName, 0, &CHOP_ExtendMenu), PRM_Template(PRM_ORD, 1, &CHOP_ExtendRightName, 0, &CHOP_ExtendMenu), PRM_Template(PRM_FLT_J, 1, &CHOP_DefaultValueName, PRMzeroDefaults, 0, &CHOP_DefaultValueRange), PRM_Template(), }; This will produce eleven parameters on two pages (four on the first page, seven on the second) along with a third page for common parameters found in every CHOP. The third page is generated automatically and does not need to be explicitly defined.

The first line determines how many pages will be available for the CHOP parameters, the number of parameters on each page and the title displayed on each tab. In this case, two pages are required so CHOP_SWITCHER2 is used. If four pages were needed, CHOP_SWITCHER4 would be used.

An array of PRM_Name type is created, holding the internal name of each parameter as well as the UI name (the title that will be displayed on the page). Some of the names are predefined common CHOPs names found in PRM_ChopShared.h and do not need to be included in the array.

The first PRM_Template entry is for the page switcher. The total number of pages including the common page must be provided.

The first parameter is an integer value with an animatable parameter (_J). Its internal name is number and its UI name is Number of Stairs. It uses a common default value of one and has a range defined as stairRange. This allows for integer values no less than zero (and up to ten showing on the slider).

The second parameter is associated with the stair height and the third is the offset. Both are floating point values with animatable parameters and use default ranges. The height defaults to one and the offset to zero.

The fourth parameter is an ordinal type. Ordinal types are usually menu types. It is very similar to the integer type except ordinals are not animatable. The fifth entry specifies a menu, stairMenu, which has been defined with two entries (Up and Down) corresponding to integer values 0 and 1.

The parameters on the second page are standard Channel parameters found in every generator CHOP. These parameters describe the channel name, start, end, sample rate and extend options.


Writing the Cook Method

The cook method basically comes in two flavours, cooks that have inputs and those that don't. All cooking involves evaluating the parameters, processing the sample data and returning the status. CHOPs without inputs generally begin the cook by calling the destroyClip method to make sure all the sample data is created fresh. Cooks that have inputs call the copyInput method which gets information from the parent clip.

Below are the two common examples of cooking CHOPs.

Generator example:

OP_ERROR CHOP_Stair::cookMyChop(OP_Context &context) { CL_Track *track; float samplerate; float *data; UT_String name; int nchan; char *cnames[MAX_CHANS]; ... destroyClip(); samplerate = RATE(context.myTime); if(UTequalZero(samplerate)) { addError(CHOP_ERROR_ZERO_SAMPLE_RATE); return error(); } myClip->setSampleRate(samplerate); // evaluate parameters CHAN_NAME(name, context.myTime); nchan = name.expandArrays(cnames, MAX_CHANS); my_NC = nchan; ... // create the tracks for (my_C=0; my_C < nchan; my_C++) { track = myClip->addTrack(cnames[my_C]); track->...; ... data = track->getData(); ... } return error(); } Filter example: OP_ERROR CHOP_Stair::cookMyChop(OP_Context &context) { CL_Track *track; const CL_Clip *parent_clip; const CL_Track *parent_track; ... parent_clip = copyInput(context, 0, 1, 1); // data and slerps // evaluate parameters my_NC = myClip->getNumTracks(); ... // modify the tracks for (my_C=0; my_C < my_NC; my_C++) { ... track = myClip->getTrack(my_C); parent_track = parent_clip->getTrack(my_C); ... track->...; } return error(); } Please see the CHOP_Node base class for more information on the class and its methods.


Cooking and Changing Handles

Handles allow for interactive modification of parameters in the graph window. They appear as boxes or squares and may have bars in horizontal or vertical directions depending on their function. CHOPs are not required to have handles.

Handles are based on CHOP_Handle.h and required methods for cooking and recognizing changes.

cookMyHandles example:

#define OFFSET_HANDLE 1 void CHOP_Stair::cookMyHandles(OP_Context &context) { CHOP_Handle *handle; CHOP_HandleLook hlook; float start, end; float xoffset, yoffset; destroyHandles(); xoffset = (end - start) * 0.75 + start; yoffset = OFFSET(context.myTime); hlook = myChannels->getChannel("offset") ? HANDLE_GUIDE : HANDLE_BOX; handle = new CHOP_Handle(this, "offset", OFFSET_HANDLE, xoffset, yoffset, hlook, HANDLE_VERTICAL, (HANDLE_BAR | HANDLE_LABEL_RIGHT | HANDLE_LABEL_BOTTOM)); myHandles.append(handle); ... }

This creates a handle which modifies the offset parameter and allows for motion in the vertical direction only. The handle is positioned three quarters of the way between the start and end of the clip.

The general form of the CHOP_Handle allows for many variations:

CHOP_Handle( CHOP_Node *dad, // the parent const char *name, // the handle label int id, // used to determine changes float xoffset, // the handle's x position float yoffset, // the handle's y position CHOP_HandleLook look, // what the handle looks like CHOP_HandleMotion motion, // how the handle moves int flags) // other handle descriptions

CHOP_HandleLook can be one of three types:

HANDLE_GUIDE - a line that cannot be moved
HANDLE_BOX - a filled in square
HANDLE_SQUARE - a square outline.

CHOP_HandleMotion can be one of three types as well:

HANDLE_HORIZONTAL - a vertical line that slides horizontally
HANDLE_VERTICAL - a horizontal line that slides vertically
HANDLE_PLANE - a square that allows both types of motion

Other flags further describe the appearance of the handle:

HANDLE_LABEL_LEFT - place the label to the left of the handle
HANDLE_LABEL_RIGHT - place the label to the right of the handle
HANDLE_LABEL_TOP - place the label above the handle
HANDLE_LABEL_BOTTOM - place the label below the handle
HANDLE_BAR - allows the handle to be used in bar mode
HANDLE_GRAPH - allows the handle to be used in graph mode
HANDLE_WIDTH_END - the handle is at the end of the clip

The DEFAULT_FLAGS are set to position the label below and to the left of the handle in graph mode.

Along with a cook method, a method to handle changes in the CHOP (including changes to the handles) is also necessary.

handleChanged example:

float CHOP_Stair::handleChanged(CHOP_Handle *handle, CHOP_HandleData *hdata) { float result = 0.0f; OP_Network *net = 0; float offset; if (hdata->shift) { setCurrent(1); if (net = getParent()) net->pickRequest(this, 0); } switch(handle->getId()) { case START_HANDLE: offset = CL_RINT(hdata->xoffset); if (!hdata->shift) SET_START(myHandleCookTime, offset); result = toUnit(offset, hdata->unit); break; case END_HANDLE: offset = CL_RINT(hdata->xoffset); if (!hdata->shift) SET_END(myHandleCookTime, offset); result = toUnit(offset, hdata->unit, 1); break; case OFFSET_HANDLE: if (!hdata->shift) SET_OFFSET(myHandleCookTime, hdata->yoffset); result = hdata->yoffset; break; } ... return result; }

This example checks for changes in three handles and modifies the parameter associated with the affected handle. Handles are selected based on their Id numbers. Any changes in the CHOP from something other than a handle should also be included in this method.

When dealing with handles, it is important to make sure the units are correct. The xoffset is always in samples and should be rounded off to the nearest integer value just to be safe. In order to return the proper value, the toUnit method must be used. This converts the xoffset value to the type of unit currently displayed by the graph. If the handle is at the end of an interval (has a HANDLE_WIDTH_END flag), a 1 must be provided as the third parameter. Only values in the x-plane need to be converted to the proper units.


Table of Contents
Operators | Surface Operations | Particle Operations | Composite Operators | Channel Operators
Material & Texture | Objects | Command and Expression | Render Output |
Mantra Shaders | Utility Classes | Geometry Library | Image Library | Clip Library
Customizing UI | Questions & Answers

Copyright © 2004 Side Effects Software Inc.
477 Richmond Street West, Toronto, Ontario, Canada M5V 3E7