Parameter Blocks

3DS Max Plug-In SDK

Parameter Blocks

See Also: Parameter Blocks and Maps in Release 3 and Later, Parameter Maps, Class IParamBlock, Class ParamDimension, Class GetParamName, Class GetParamDim.

Overview

The parameter block class provides a mechanism for storing values for a plug-ins parameters. When a parameter block is created, the developer specifies the number of parameters and the types of each parameter. Parameter types consist of a range of built in types like integer, float, 3D Point, and Color.

Parameters may be animated or constant. In order for a parameter to be animatable, it must have a controller to control the animation. Different parameter types require different controller types. For example, a floating point value, like the angle parameter for the bend modifier, requires a floating point controller. A node transformation matrix requires a transform controller. The most common controllers are interpolating or 'key frame' controllers.

One of the main purposes of parameter blocks is to manage the complexity of maintaining different controllers for different parameters. To access the values in the parameter block, the plug-in developer uses GetValue() and SetValue() methods, each take a TimeValue as a parameter. The parameter block stores values in an efficient a manner as possible. If the value hasn't yet been animated, that is, SetValue() hasn't been called with time not equal to 0, then a constant value is stored. When SetValue() is called with time not equal to 0, and MAXs 'Animate' button is on, a new instance of the default controller for the parameter type is plugged in to the parameter block and initialized with the parameter's values. Plug-ins are required to display their animated parameters in the track view. If all the plug-in's parameters are handled by a parameter block, then the parameter block will take care of this task as well.

Parameter Blocks in Use

In this section we'll explore the definition and creation of parameter blocks. Then we'll look at the method of setting and retrieving values from the parameter block. Finally we'll look at how parameter blocks and references may be used together for processing animated user interface parameters. Note: This section provides only a minimal description of the reference mechanism. For an in depth discussion of references, see the Advanced Topics section References.

Creating a Parameter Block

Throughout this section an example of a plug-in with three parameters is used. An angle, a length, and a integer count parameter. A parameter block descriptor is used to describe each parameter. It is initialized by passing three arguments per parameter. (Note that another version uses four arguments. The version with four arguments is used to help maintain backward compatibility. The two descriptors are otherwise the same).

Prototype:

class ParamBlockDesc {

 public:

  ParamType type;

  UserType *user;

  BOOL animatable;

 };

// This version of the descriptor has an ID for each parameter.

class ParamBlockDescID {

 public:

  ParamType type;

  UserType *user;

  BOOL animatable;

  DWORD id;

 };

The arguments to ParamBlockDesc are:

ParamType type

The Parameter Type - The following types may be used:

image\bullet.gif TYPE_INT - Integers values.

image\bullet.gif TYPE_FLOAT - Floating point value.

image\bullet.gif TYPE_POINT3 - Point values.

image\bullet.gif TYPE_RGBA - Colors values - Red, Green, Blue and Alpha.

image\bullet.gif TYPE_BOOL - Boolean values.

UserType *user

The next value is NOT USED - it must always be passed as NULL.

BOOL animatable

This is a flag indicating if the parameter may be animated or not. Pass TRUE if the value may be animated and FALSE if it is constant.

DWORD id (Second version only)

This is an ID used to identify this parameter. This provides a solution to the problem of backwards compatibility. If you alter the parameter structure of your plug-in in the future (by adding or deleting parameter for example) previously saved 3ds max files will be incompatible. You can however use a mechanism which uses these IDs to convert older versions to the current version. See the Advanced Topics section on Parameter Maps for more detail.

Items in the parameter block are referred to by index. The index is derived from the order in which the descriptors appear in the ParamBlockDesc array.

// Parameter block indices

#define PB_INDEX_ANGLE 0

#define PB_INDEX_LENGTH 1

#define PB_INDEX_COUNT 2

ParamBlockDesc pdesc[] = {

 { TYPE_FLOAT, NULL, TRUE }, // Angle

 { TYPE_FLOAT, NULL, TRUE }, // Length

 { TYPE_INT, NULL, FALSE } // Count

};

A parameter block is created from this array by calling CreateParameterBlock(). This function requires two values, a pointer to the ParamBlockDesc array and a count of the number of parameters. (Note there is an alternate version for use with the backwards compatibility mechanism discussed above that uses three arguments. The third argument is used to indicate a version of the parameter block. See the Advanced Topics section on Parameter Maps for more information).

Prototype:

IParamBlock *CreateParameterBlock(ParamBlockDesc *pdesc, int count);

or

IParamBlock *CreateParameterBlock(ParamBlockDescID *pdesc,

 int count,DWORD version);

IParamBlock *pblk = CreateParameterBlock(pdesc, 3);

The function returns a pointer to the parameter block it creates.

Retrieving Parameter Block Values

Whenever the developer needs to retrieve a value from the parameter block, the GetValue() method is used. There are overloaded functions for each type of value to retrieve (int, float, Point3, and RGBA). Each method has four parameters. Below is the float version.

BOOL GetValue( int i, TimeValue t, float &v,

 Interval &ivalid );

The i parameter is the integer index of the parameter to retrieve. This is the index into the ParamBlockDesc array.

If the parameter is animated it will be varying over time. The t parameter specifies at what time to retrieve the value.

The v parameter is a C++ reference to a float. The value is returned through v.

The ivalid parameter is a C++ reference to an Interval. The GetValue() method updates the interval passed in. This method is frequently used by developers to 'whittle' down an interval. When a parameter of a parameter block is animated, for any given time there is a interval over which the parameter is constant. If the parameter is constantly changing the interval is instantaneous. If the parameter does not change for a certain period the interval will be longer. If the parameter never changes the interval will be FOREVER. By passing an interval to the GetValue() method you ask the parameter block to 'intersect' the interval passed in with the interval of the parameter. Intersecting two intervals means returning a new interval whose start value is the greater of the two, and whose end value is smaller of the two. In this way, the resulting interval represents a combined period of constancy for the two intervals.

This technique is used frequently to compute a validity interval for an object. The developer starts an interval off as FOREVER, then intersects this interval with each of its animated parameters (by calling GetValue()). GetValue() 'whittles' down the interval with each call. When all the parameters have been intersected the result is the overall validity interval of an object at a specific time. For more information see the Advanced Topics section on Intervals.

The return value is TRUE if a value was retrieved. Otherwise it is FALSE.

Setting Parameter Block Values

Whenever the developer needs to store a value into the parameter block, the SetValue() method is used. There are overloaded functions for each type of value to set (int, float, Point3, and RGBA). Each method has three parameters. Below is the float version.

BOOL SetValue( int i, TimeValue t, float v );

The i parameter is the integer index of the parameter to set. This is the index into the ParamBlockDesc array of the parameter.

The t parameter specifies at what time to set the value.

The value to store is passed in v.

The return value is TRUE if the value was set. It is FALSE otherwise.

Setting Values and Handling References

The IParamBlock class is derived from class ReferenceTarget. Since parameter blocks are reference targets, plug-ins may create references to them. By creating a reference to the parameter block, the plug-in may be notified whenever any of the parameters are set to new values.

The sample code below creates a parameter block from the array of descriptors and then makes a reference to the parameter block.

//This is the index of the Param Blk reference.

#define PARAM_BLK_REF 0

MakeRefByID(

 FOREVER, // Interval - always use FOREVER.

 PARAM_BLK_REF, // Index of the PB reference.

 CreateParameterBlock(pdesc, 3) // Create the PB

);

When values of the parameter block are changed, the plug-in needs to be informed. This notification is done by calling the ReferenceMaker method NotifyDependents(). The code fragment below shows how this is done. Whenever our sample plug-in needs to adjust its angle parameter it calls the function below. This function calls the SetValue() method of the parameter block and then calls NotifyDependents().

void Sample::SetAngle(TimeValue t, float r)

 {

 // pblock is a pointer to our param blk

 pblock->SetValue(PB_INDEX_ANGLE, t, r);

 NotifyDependents(FOREVER, PART_OBJ, REFMSG_CHANGE);

 }

When the NotifyDependents() method is called after a parameter has been changed, the plug-in is notified via a REFMSG_CHANGE message sent to its NotifyRefChanged() method. Here the developer may respond to the change in the parameter's value.

There are two other messages which may be sent to this method to handle parameter block processing. These are REFMSG_GET_PARAM_NAME and REFMSG_GET_PARAM_DIM.

The message REFMSG_GET_PARAM_DIM is sent by the system when it needs a parameter dimension for the parameter. Any parameter that can be controlled by a controller has a dimension. This dimension can be considered a unit of measure. It describes its type and its order of magnitude. When a controller needs to display the parameter values (for example in the function curve editor) it converts the value using its parameter dimension Convert() function. It can also convert back using the Unconvert() function.

There are several default parameter dims implemented. See ParamDimension for details.

The REFMSG_GET_PARAM_NAME messages is sent by the system when it needs a name for the parameter. For example, in the track view a name for the parameter needs to be displayed. When this message is received from the system the name is provided. The parameter which it needs the name for is passed in the partID.

The plug-in implements this method to receive messages. See below for how the messages associated with the parameter block are handled.

RefResult Sample::NotifyRefChanged(

 Interval changeInt,

 RefTargetHandle hTarget,

 PartID& partID,

 RefMessage message)

 {

 switch (message) {

  case REFMSG_CHANGE:

   // Code to handle the changed parameter...

   break;

  case REFMSG_GET_PARAM_DIM:

  // When a client of a parameter block receives the

  // REFMSG_GET_PARAM_DIM message, the partID

  // field is set to point at one of these structs.

  // The client should set dim to point at its dim descriptor.

   GetParamDim *gpd = (GetParamDim*)partID;

   // Based on which index it wants...

   switch (gpd->index) {

    // ...provide the appropriate dim

    case PB_INDEX_ANGLE:

     gpd->dim = stdAngleDim;

     break;

    case PB_INDEX_LENGTH:

     gpd->dim = stdWorldDim;

     break;

    case PB_INDEX_COUNT:

     gpd->dim = stdSegmentsDim;

     break;

     }

   return REF_STOP;

  case REFMSG_GET_PARAM_NAME:

   // When a client of a parameter block receives the

   // REFMSG_GET_PARAM_NAME message, the

   // partID field is set to point at one of these

   // structures. The client should fill in the parameter name.

   GetParamName *gpn = (GetParamName*)partID;

   switch (gpn->index) {

    case PB_INDEX_ANGLE:

     gpn->name = _T("Angle");

     break;

    case PB_INDEX_LENGTH:

     gpn->name = _T("Length");

     break;

    case PB_INDEX_COUNT:

     gpn->name = _T("Count");

     break;

    }

   return REF_STOP;

  }

 return(REF_SUCCEED);

 }

See also: GetParamName, GetParamDim.