Parameter Maps
See Also: Parameter Blocks and Maps in Release 3 and Later, Class IParamMap, Class IParamArray, Class IParamBlock, Class ParamBlockDescID, Class ParamUIDesc.
Overview
Parameter Maps are used to minimize the programming effort required to manage the user interface parameters of a plug-in. A simple plug-in, such as the procedural sphere, has a user interface consisting of controls like spinners, radio buttons and check boxes. Each of these controls has a one-to-one correspondence with a variable or parameter in a parameter block. Parameter maps can be used to map the UI controls to a parameter handler of the appropriate data type. The use of parameter maps has several advantages:
For standard processing, the developer will not need to write message-processing code to handle events generated by the user. For example, if a developer does not use parameter maps they must provide a dialog proc which processes the messages sent by the controls as the user works with them. For instance the spinner control sends a message when the user operates the up and down arrows or enters new values. Parameter maps handle this message processing internally.
The developer is freed from dealing with the complexity of having to manage controllers to control the animation of user interface values. The appropriate type of controller is assigned and managed by the parameter block which the parameter map controls. The developer simply uses GetValue() and SetValue() methods to retrieve and store values.
Undo and Redo are handled automatically for changes to a parameter's value.
The Loading and Saving of the parameters to and from disk is handled automatically.
The following user interface controls are available for use with parameter maps:
Spinner Custom Control
The spinner control is used to provide input of values limited to a fixed type. For example, the control may be limited to the input of only positive integers. The input options are integer, float, universe (world space units), positive integer, positive float, positive universe, and time. This control allows the user to increment or decrement a value by clicking on the up or down arrows. The user may also click and drag on the arrows to interactively adjust the value. The Ctrl key may be held to accelerate the value changing speed, while the Alt key may be held to decrease the value changing speed.
Radio Button
Radio buttons are used to provide the user with a single boolean choice, or when used in groups, to select among several options.
Single Check Box
Check boxes are used to provide the user with a single boolean choice.
Multiple Check Box
Multiple Check boxes are also supported. This interface allows each bit of a single integer to control a different check box.
Color Swatch Custom Control
The Color Swatch control presents the user with the standard 3ds max modeless color selector when the user clicks on the control. The color swatch control displays the currently selected color.
Note that you may use a combination of parameter maps and other techniques to manage your plug-in's user interface. If you don't specifically inform the parameter map about a UI control, it will be ignored. If your plug-in has UI controls that require special processing, a mechanism is provided to allow you to do so. If you wish to use other types of controls such as custom buttons, toolbars or image controls see the Advanced Topics section Custom Controls
Basic Concepts of Parameter Maps
A parameter map may be considered a table of parameter handlers. The parameter map provides the processing required to map the UI controls to the variables these controls affect. The parameter map operates on a = 4) BSPSPopupOnMouseOver(event);;">virtual array of parameters. Each control is one element of the virtual array. Each rollup page in the command panel is treated as its own virtual array.
For processing parameter blocks, the parameter map manages everything. As a user operates the UI controls of the plug-in, the parameter map processes the messages sent by the controls and stores any values in the parameter block which need to be set.
For processing other variables, the developer needs to provide a way for the parameter map to get and set the i-th element of the virtual array. To do this, the developer assigns an integer index to each parameter. The developer also implements a set of GetValue() and SetValue() methods. One parameter to these methods is an index to which parameter to get or set. If the parameter map needs the i-th variable to be stored, the developer stores it. It the parameter map needs to retrieve the i-th value the developer supplies it. A section below discusses how this is done.
These are the primary classes involving parameter maps the developer should be aware of:
Class ParamUIDesc - These are descriptors that define the properties of the UI control such as its type (spinner, radio button, check box, etc.), which resource ID it refers to (which control in the dialog template), and which index into the virtual array to use.
Class ParamBlockDescID - This class describes each parameter of a parameter block by its type (int, float, point, color), whether it may be animated or is constant, and a ID used to help manage backwards compatibility of parameter blocks.
Class IParamArray - This is the base class from which parameter maps and parameter blocks are derived. This class represents a virtual array of parameters.
Class IParamMap - This class provides a set of methods to work with the parameter map, for example, retrieving a pointer to the parameter block used.
Using Parameter Maps
This section discusses how to use parameter maps to manage the user interface parameter of a plug-in. The example of the procedural sphere object is examined to see how it uses parameter maps. Parameter maps handle most of the command panel user interface processing of the procedural sphere. Shown below are the three rollup pages of the command panel added by the procedural sphere. Except for the "Create" button, all the controls are managed by the parameter map.
The sphere object has two types of parameters managed by parameter maps.
One type of parameter is a simple non-animated value which is driven by a control in the user interface. An example of this is the value associated with the creation method radio buttons in the UI of the sphere. These buttons control if the sphere is created by dragging it radius or its diameter. This is simply a variable which contains the number of the radio button selected by the user (0 or 1). The parameter map allows the variable to be updated as the user chooses radio buttons.
Another type of value is a parameter of a parameter block. Parameter blocks are the mechanism used by 3ds max to manage the animation of parameters. If a parameter is animated, it must have a controller to control the animation. For example, a floating point value, like the radius of the sphere, uses a floating point controller. The primary purpose of parameter blocks is to manage the complexity of handling different controllers for different parameters. The parameter map updates parameter in the parameter block resulting from any user input, like adjusting the radius spinner.
The following is a description of how the procedural sphere uses parameter maps. It describes how the parameter map was set up and initialized.
The SphereObject class is sub-classed from IParamArray.
class SphereObject : public GenSphere, public IParamArray {
...
};
This is done so the sphere may use the virtual array mechanism to allow the parameter map to work with its variables. The details of the way the parameter map access these variables is described later in this section.
Declare the UI Variables
The developer must declare several = 4) BSPSPopupOnMouseOver(event);;">class variables which are pointers to IParamMaps. Each parameter map manages a single rollup page in the command panel.
static IParamMap *pmapCreate;
static IParamMap *pmapParam;
The developer must also declare class variables needed for the parameters in the user interface.
static int dlgSegments;
static int dlgCreateMeth;
static int dlgSmooth;
static Point3 crtPos;
static float crtRadius;
Describe the Controls
The developer must define the ParamUIDesc arrays to establish the properties of the UI controls such as their type (spinner, radio button, check box, etc.), which resource ID they refer to, and which index into the virtual array they use. Below is the ParamUIDesc array for the Parameters rollup page. It calls several overloaded constructors passing the values needed to describe the controls. See the ParamUIDesc Reference section for detailed information on the constructors.
static int squashIDs[] = {IDC_HEMI_CHOP,IDC_HEMI_SQUASH};
static ParamUIDesc descParam[] = {
// Radius
ParamUIDesc(
PB_RADIUS, // Virtual array index
EDITTYPE_UNIVERSE, // Type of value to edit
IDC_RADIUS,IDC_RADSPINNER, // Resource IDs
MIN_RADIUS,MAX_RADIUS, // Upper and lower limits on the value
SPIN_AUTOSCALE), // Scale factor for up/down arrow clicks
// Segments
ParamUIDesc(
PB_SEGS,
EDITTYPE_INT,
IDC_SEGMENTS,IDC_SEGSPINNER,
(float)MIN_SEGMENTS,(float)MAX_SEGMENTS,
0.1f),
// Smooth
ParamUIDesc(PB_SMOOTH,TYPE_SINGLECHEKBOX,IDC_OBSMOOTH),
// Hemisphere
ParamUIDesc(
PB_HEMI,
EDITTYPE_FLOAT,
IDC_HEMISPHERE,IDC_HEMISPHERESPINNER,
0.0f,1.0f,
0.005f),
// Chop/squash
ParamUIDesc(PB_SQUASH,TYPE_RADIO,squashIDs,2),
// Recenter
ParamUIDesc(PB_RECENTER,TYPE_SINGLECHEKBOX,IDC_HEMI_RECENTER)
};
#define PARAMDESC_LENGH 6
Set Up the Parameter Block
Several of the controls used allow animated values. For example, the radius, and segments parameters may be animated. The developer uses a parameter block to store these values. The developer must define the parameter block descriptor which describes the properties of each parameter.
Prototype:
class ParamBlockDescID {
public:
ParamType type;
UserType *user;
BOOL animatable;
DWORD id;
};
This class is initialized by passing four values per parameter. These values are:
ParamType type
The Parameter Type - The following are the types which may be used:
TYPE_INT - Integers values.
TYPE_FLOAT - Floating point values.
TYPE_POINT3 - Point values.
TYPE_RGBA - Colors values - Red, Green, Blue and Alpha.
TYPE_BOOL - Boolean values.
UserType *user
This 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 just a constant value should be stored.
DWORD id
This is an ID assigned to each parameter. This is used for backwards compatibility if you change the parameter block structure in the future. There is a mechanism that allows older format parameter blocks to be converted to a newer format using these IDs to match corresponding parameters between the new and old format. This is described below under Backwards Compatibility.
Create an Array of Descriptors
The developer must create an array of the descriptors. Items in the parameter block are referred to by index. The index is derived from the order in which the descriptors appear in the ParamBlockDescID array.
#define PB_RADIUS 0
#define PB_SEGS 1
#define PB_SMOOTH 2
#define PB_HEMI 3
#define PB_SQUASH 4
#define PB_RECENTER 5
static ParamBlockDescID descVer1[] = {
{ TYPE_FLOAT, NULL, TRUE, 0 }, // Radius
{ TYPE_INT, NULL, TRUE, 1 }, // Segs
{ TYPE_INT, NULL, TRUE, 2 }, // Smooth
{ TYPE_FLOAT, NULL, TRUE, 3 }, // Hemi
{ TYPE_INT, NULL, FALSE, 4 }, // Squash
{ TYPE_INT, NULL, FALSE, 5 } }; // Recenter
#define PBLOCK_LENGTH 6
For more detailed information see the Advanced Topics section Parameter Blocks.
Create the Parameter Block and Make a Reference to it
A parameter block must be created from the parameter block descriptors. This is done in the constructor of the sphere object. A reference is made to the parameter block as well. For more information on references see the Advanced Topic section References. After the parameter block is created, default values are initialized. This is done using the SetValue() method of the parameter block. The developer must pass the index of the parameter, the time to set the value at, and the value to set.
SphereObject::SphereObject()
{
MakeRefByID(FOREVER, 0,
CreateParameterBlock(descVer1, PBLOCK_LENGTH, CURRENT_VERSION));
assert(pblock);
pblock->SetValue(PB_RADIUS,0,crtRadius);
pblock->SetValue(PB_SMOOTH,0,dlgSmooth);
pblock->SetValue(PB_SEGS,0,dlgSegments);
pblock->SetValue(PB_SQUASH,0,0);
}
Add the Rollup Page to the Command Panel
When the user may edit the sphere's parameters the developer must add the rollup pages to the command panel. This is done using the CreateCPParamMap() method. This method creates a parameter map to handle the display of parameters in the command panel. Shown below are two samples from the sphere's BeginEditParams() method.
This first call to CreateCPParamMap() manages the variables of the SphereObject (not the parameter block).
The CreateCPParamMap() method takes several arguments. The first is the array of ParamUIDescs, one element for each control to be managed. The second is the number of items in this array. The third parameter is a pointer to the virtual array of parameters. The example below uses the this pointer of the sphere object to indicate which parameter array. The this pointer means the SphereObject itself is the IParamArray pointer (the SphereObject was derived from IParamArray). In this way, the parameter map may access the variables of the sphere object. The fourth parameter is the interface pointer passed into the BeginEditParams() method. The fifth parameter is the DLL instance handle of the plug-in. The sixth parameter is the dialog template for the rollup page (created using the resource editor). The next parameter is the title displayed in the rollup page title bar. The final parameter is a set of flags to control settings of the rollup page. After this call finishes, the "Creation Method" rollup has been added to the command panel.
pmapCreate = CreateCPParamMap(
descCreate,CREATEDESC_LENGH,
this,
ip,
hInstance,
MAKEINTRESOURCE(IDD_SPHEREPARAM1),
_T("Creation Method"),
0);
The example below is similar, however it uses the parameter block pointer pblock to indicate which virtual array to manage. The parameters in this case are all stored as part of the parameter block (the IParamBlock class is derived from IParamArray). After this call finishes, the "Parameters" rollup has been added to the command panel.
pmapParam = CreateCPParamMap(
descParam,PARAMDESC_LENGH,
pblock,
ip,
hInstance,
MAKEINTRESOURCE(IDD_SPHEREPARAM2),
_T("Parameters"),
0);
Allowing the Parameter Map to Access plug-in Variables
The developer must implement the GetValue() and SetValue() methods of the IParamArray class to allow the parameter map to manage the SphereObject variables. The virtual array mechanism works by using an index to specify which parameters are to be retrieved and set. Shown below are samples for the integer creation method variable. Each method uses a switch statement that checks the index into the virtual array and gets or sets the appropriate variable. Note that the way the developer assigns the index to the variables is important since this is an index into the virtual array. Start at 0 and assign each one 0, 1, 2, 3...
BOOL SphereObject::SetValue(int i, TimeValue t, int v)
{
switch (i) {
case PB_CREATEMETHOD: dlgCreateMeth = v; break;
}
return TRUE;
}
BOOL SphereObject::GetValue(int i, TimeValue t, int &v, Interval &ivalid)
{
switch (i) {
case PB_CREATEMETHOD: v = dlgCreateMeth; break;
}
return TRUE;
}
Removing the Rollup Page from the Command Panel
When the user is done editing the sphere's parameters, the DestroyCPParamMap() method is used to remove the rollup page from the command panel and release the controls associated with the parameter map. It is called from the EndEditParams() method.
Prototype:
void DestroyCPParamMap(IParamMap *m);
if (pmapCreate) DestroyCPParamMap(pmapCreate);
DestroyCPParamMap(pmapParam);
pmapCreate = NULL;
pmapParam = NULL;
Processing Controls not managed by the Parameter Map
Some controls may need to be processed by the developer directly. The sphere has a 'Create' button in the 'Keyboard Entry' rollup. Button controls are not processed by parameter maps. In order to process the messages sent when the user operates the button, the developer needs to derive a class from ParamMapUserDlgProc and set it as the parameter map's user = 4) BSPSPopupOnMouseOver(event);;">callback using SetUserDialogProc().
class SphereTypeInDlgProc : public ParamMapUserDlgProc {
public:
SphereObject *so;
SphereTypeInDlgProc(SphereObject *s) {so=s;}
BOOL DlgProc(TimeValue t,IParamMap *map,
HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
void DeleteThis() {delete this;}
};
The SetUserDlgProc() method of IParamMap is used to set the callback to handle the messages. Note that the callback is called after the default processing is complete.
pmapTypeIn->SetUserDlgProc(new SphereTypeInDlgProc(this));
For more information on Dialog Procs see the Advanced Topics section on Custom Controls.
Providing a Name and Dimension for the Parameter Block Parameters
The developer must provide a name for each parameter used in a parameter block. This name is needed because the animated parameters show up in the track view and must be labeled. The parameter block takes care of displaying them in the track view, but has no idea of the name for each one, it only knows them by index. The developer provides the name to display by implementing the method GetParameterName() and returning the name of the parameter whose index into the parameter block is passed.
TSTR SphereObject::GetParameterName(int pbIndex)
{
switch (pbIndex) {
case PB_RADIUS:
return TSTR(_T("Radius"));
case PB_HEMI:
return TSTR(_T("Hemisphere"));
case PB_SEGS:
return TSTR(_T("Segments"));
case PB_SMOOTH:
return TSTR(_T("Smooth"));
default:
return TSTR(_T(""));
}
}
The developer must provide a dimension for each parameter used in a parameter block. This dimension is basically the type and magnitude of the value stored in the parameter block. Pre-defined constants are used as the return values. For more information, see the Reference section ParamDimension.
ParamDimension *SphereObject::GetParameterDim(int pbIndex)
{
switch (pbIndex) {
case PB_RADIUS:
return stdWorldDim;
case PB_HEMI:
return stdNormalizedDim;
case PB_SEGS:
return stdSegmentsDim;
case PB_SMOOTH:
return stdNormalizedDim;
default:
return defaultDim;
}
}
Backwards Compatibility
As development evolves on a plug-in, the use of its parameter blocks may change. For example, more parameters may be added or some may be deleted. 3ds max files that were saved using the old version of the plug-in would not normally load properly under the new format. In order to provide backward compatibility with the older format a mechanism exists to allow the old format to be converted to the new format.
To accomplish the conversion you must define both the old and the new format of the parameter block.
static ParamBlockDescID descVer0[] = {
{ TYPE_FLOAT, NULL, TRUE, 0 },
{ TYPE_INT, NULL, TRUE, 1 },
{ TYPE_INT, NULL, TRUE, 2 } };
static ParamBlockDescID descVer1[] = {
{ TYPE_FLOAT, NULL, TRUE, 0 },
{ TYPE_INT, NULL, TRUE, 1 },
{ TYPE_INT, NULL, TRUE, 2 },
{ TYPE_FLOAT, NULL, TRUE, 3 },
{ TYPE_INT, NULL, FALSE, 4 },
{ TYPE_INT, NULL, FALSE, 5 } };
#define PBLOCK_LENGTH 6
The fourth argument of the ParamBlockDescID is the ID of the parameter. These are used to match the old version of the parameter to the new version. The code which does the conversion matches the IDs between the two versions, for example, it will match the ID=1 of the old version to the ID=1 of the new version.
You create an array of the class ParamVersionDesc, one element for each old version of the parameter block. This structure describes a version of the parameter block. You also create an instance of ParamVersionDesc for the new version.
// Array of old versions
static ParamVersionDesc versions[] = {
ParamVersionDesc(descVer0,3,0)
};
#define NUM_OLDVERSIONS 1
// Current version
static ParamVersionDesc curVersion(descVer1,PBLOCK_LENGTH,1);
#define CURRENT_VERSION 1
When the plug-in is loaded, its Load() method is called. From within this method you call a method of the ILoad class which registers the parameter block post load callback object. The callback object is an instance of the class ParamBlockPLCB. This callback creates a new parameter block. The new parameter block inherits any parameters from the old parameter block whose parameter IDs match. In this way the older format is automatically converted to the new format by matching the corresponding IDs.
IOResult SphereObject::Load(ILoad *iload)
{
iload->RegisterPostLoadCallback(
new ParamBlockPLCB(versions,NUM_OLDVERSIONS,&curVersion,this,0));
return IO_OK;
}
See also: Loading and Saving, ParamBlockPLCB.