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

Each OP_Node can have local variables which are defined over the scope of its cook (i.e. active when the cook method has been called). These variables are defined in the OP_Operator constructor (which defines the operator). Currently variables can be floating point or string valued.

The OP_Operator constructor takes an array of CH_LocalVariable's. This class simply contains a string (representing the variable name) and a unique integer (used for evaluation). So, for example, to define 4 local variables for an operator, we might have the following code:

// First, let us get a bunch of unique integers
enum {
VAR_PT, // Current point
VAR_NPT, // Total number of points
VAR_ID, // Particle ID
VAR_LIFE, // Life time
};
// Next, we define our local variable table
SOP_MyNode::myVariables[] = {
{ "PT", VAR_PT },
{ "NPT", VAR_NPT },
{ "ID", VAR_ID },
{ "LIFE", VAR_LIFE },
{ 0 } // End the table with a null entry
};

During evaluation of the parameters of the OP_Node, these variables become "active". If the variable is found in an expression, the subclassed version of OP_Parameters::evalVariableValue() will be invoked. It is your responsibility to return a value for the variable in question. For local string variables, override the string version of OP_Parameters::evalVariableValue().

Continuing from the above example, we have:

bool
SOP_MyNode::evalVariableValue(fpreal &val, int index, int thread)
{
GA_Offset ppt_offset;
int id;
if (!GAisValid(myCurrPoint)) // Sorry, we're in an invalid state
return false;
switch (index)
{
case VAR_PT:
val = fpreal(myCurrPoint);
return true;
case VAR_NPT:
val = fpreal(myTotalPoints);
return true;
case VAR_ID:
if (myInputGeo && myInputIdHandle.isValid())
{
ppt_offset = myInputGeo->pointOffset(myCurrPoint);
id = myInputIdHandle(ppt_offset);
val = fpreal(id);
return true;
}
return false;
case VAR_LIFE:
if (myInputGeo && myInputLifeHandle.isValid())
{
ppt_offset = myInputGeo->pointOffset(myCurrPoint);
life = myInputLifeHandle(ppt_offset);
val = life.x() / life.y();
return true;
}
return false;
}
// Not one of our variables, must delegate to the base class.
return SOP_Node::evalVariableValue(val, index, thread);
}

The code for cooking might look something like:

SOP_MyNode::cookMySop(OP_Context &context)
{
fpreal t = context.getTime();
...
// Now, we loop through each point, doing expression
// evaluation INSIDE the loop.
for (myCurrPoint = GA_Index(0); myCurrPoint < myTotalPoints; myCurrPoint++)
{
// When evaluating parms, it's possible that the expression
// would use one of our local variables, so
pos.x() = evalFloat("posx", 0, t);
...
}
myCurrPoint = GA_INVALID_INDEX; // Make sure to flag that we're done cooking
...
}

The reason for setting myCurrPoint to GA_INVALID_INDEX outside of the loop is that occasionally, it is possible for your evalVariableValue method to be called out of context. The reason for this is complicated, but it has to do with displaying the values of the parameters in the dialog boxes (i.e. the parameters need to be evaluated, but the OP_Node isn't being cooked).