HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Vector parameters

Usually, parameters that represent a single vector are implemented as a single parameter with 3 component fields. However, it is also possible to implement a parameter that evaluates a vector expression instead. This section describes how to go about doing so, and also how to implement both parameter types and allow the user to select between them.

See Also
HDK_OpBasics_PopParmEvalCaching

First we show the PRM_Template entry for our vector parameter to help put the rest of the discussion into context.

static PRM_Name names[] =
{
PRM_Name("parmtype", "Parameter Type"),
PRM_Name("parmv", "Vector Parameter"),
PRM_Name("parm", "Parameter", 1),
// ...
};
static PRM_Default parmvDefaults[] =
{
PRM_Default(1, "vector3($VX, $VY, $VZ)")
};
POP_VectorExample::myTemplateList[] =
{
// ...
PRM_Template(PRM_STRING, 1, &names[1], parmvDefaults),
// ...
};
Note
This part of our example only uses names[1] from the array above. The remaining entries are there for when we want to implement switching between vector and component mode late. Passing 1 as the third argument to the PRM_Name constructor in the code above forces that parameter, which is not the parameter currently under discussion, to default to expression mode.

Next, in the class definition, make sure there is a method to evaluate the parameter as a raw string using the regular OP method.

void PARMV(UT_String &s) { STR_PARM_NE("parmv", 1, 0, 0) };
Note
The above code fragment uses a hard-coded parameter index (1) for simplicity. This does not lend itself well to incorporating changes and is not a recommended practice. Moreover, there is no relation to the index in the static names[] array declared earlier.

Add a cache variable and a method to retrieve that cache value to the class definition. For example:

POP_VPARM(myParm, getParm);

Finally, add an expression object that will be used to evaluate the vector expression.

EV_EXPRESSION* myParmExpr;

In the constructor of the new POP, allocate the expression object:

and free it in the destructor:

ev_FreeExpr(myParmExpr);
Note
The expression object does not need to be a member variable of the POP, but defining it as one allows us to avoid always allocating and releasing it.

Now we turn our attention to evaluating our vector expression parameter. Just like for normal parameter evaluation, we'll want to use a method pointer variable.

Normal parameter evaluation performs some processing with which we don't normally need to concern ourselves. However, we're explicitly working with EV_EXPRESSION objects in this example, so we'll need to take some additional steps.

We'll need to explicitly set the evaluation time (CH_Manager::setEvaluateTime()) and evaluation channel collection (CH_Manager::setEvalCollection()) in the global CH_Manager, so we'll first cache the existing values before evaluating any parameters.

Note
This management of the global CH_Manager is not incorporated directly into POP_Node::evalVector() precisely to permit factoring out some otherwise per-evaluation overhead.
int thread = context.getThread();
float prev_time = ch_mgr->getEvaluateTime(thread);
CH_Collection *prev_coll = ch_mgr->getEvalCollection(thread);

We set the evaluate time and evaluate channel collection prior to evaluating our vector expression.

ch_mgr->setEvaluateTime(t, thread);
ch_mgr->setEvalCollection(getChannels(), thread);

To evaluate our vector parameter, we'll first evaluate the raw expression string, update our expression object, and again, just like for normal parameter evaluation, we'll assign the method pointer to point to the appropriate evaluation method. We ask for the raw expression string because we do not want any local variables evaluated yet.

UT_String expr;
PARMV(expr);
ev_ChangeExpr(myParmExpr, expr);
POP_VCACHE(myParmEval, myParmExpr, getParm, myParm, POP_VectorExample);

To evaluate the parameter while iterating through the particles, use the the POP_VEVAL macro.

float x, y, z;
POP_VEVAL(myParmEval, myParmExpr, x, y, z, thread);
Note
Make sure that the current thread (OP_Context::getThread()) is available in the current scope as thread.

Finally, before terminating the cookPop() method, we'll restore the cached evaluation time and channel collection.

ch_mgr->setEvalCollection(prev_coll, thread);
ch_mgr->setEvaluateTime(prev_time, thread);

Switching between vector and component parameters

Several of the factory POPs permit the user to switch between a parameter with 3 component fields and a parameter with a single vector expression field. This capability can easily be added to your node as well.

First, we include an ordinal menu parameter to switch between the two modes.

POPvectorMenu is a predefined PRM_ChoiceList object. The mode parameter declared above defaults to 1, component mode.

We modify the PRM_Template definition for the vector expression parameter presented earlier slightly to give the dialog building code a hint that the preceding parameter will switch between us and the succeeding parameter.

PRM_Template(PRM_STRING, PRM_TYPE_VEC_SWITCHER, 1, &names[1], parmvDefaults),

Finally, we declare the tuple parameter.

PRM_Template(PRM_XYZ, 3, &names[2], parmcDefaults),

To be consistent with the vector parameter default, parmcDefaults should be declared to be something like:

static PRM_Default parmcDefaults[] =
{
PRM_Default(0, "$VX"),
PRM_Default(0, "$VY"),
PRM_Default(0, "$VZ")
};

Then, in the cookPop() method, we'll need to check the "parmtype" parameter to determine which of the two parameters we should evaluate.

if (PARMTYPE() == 0)
{
// evaluate vector parameter
}
else
{
// evaluate tuple parameter
}