Parameter Editing in the Command Panel

3DS Max Plug-In SDK

Parameter Editing in the Command Panel

See Also: Class Animatable, Custom Controls, Parameter Maps.

Overview

This section discusses the editing of an item's parameters in the command panel. Plug-In types such as procedural objects, modifiers, space warps, and controllers may all present their user interface in the command panel.

Methods Called During Parameter Editing

There are two methods that are called when a user begins and ends editing an item's parameters. In the case of procedural objects, modifier, and controllers, these methods are from class Animatable. They are named BeginEditParams() and EndEditParams(). Other plug-in types, such as utilities, may have their own version of these methods (for instance UtilityObj::BeginEditParams()). The text below discusses the more common Animatable version.

Beginning Parameter Editing

When the user begins to edit an item the BeginEditParams() method is called on it. At this point it is expected to do two things: Put up its user interface via rollup pages, and register any sub-object selection types it may have. Each of these is discussed below.

Adding rollup pages can be done using the method Interface::AddRollupPage() or by creating a parameter map (CreateCPParamMap()) which puts up the rollup pages itself.

The BeginEditParams() prototype looks like this:

virtual void BeginEditParams(IObjParam *ip,ULONG flags, Animatable *prev=NULL);

The flags parameter passed is used to indicate which branch of the command panel the item is being edited in. For instance it may be the Create branch, the Modifier branch, the Hierarchy branch, or the Motion branch.

Some items may have specific parameters that are particular to the creation process. The flags parameter indicates whether the user is in the create branch or not. For example, procedural spheres have an option to create them by dragging out the radius or the diameter. These parameters shouldn't be displayed in the Modify branch since they are only for creating. The sphere checks the flag to see if rollup should be added. Here's the code fragment from the sphere code where that is done:

void SphereObject::BeginEditParams(IObjParam *ip,ULONG flags,Animatable *prev) {

// . . .

  

  if (flags&BEGIN_EDIT_CREATE) {

   pmapCreate = CreateCPParamMap(

    descCreate,CREATEDESC_LENGH,

    this,

    ip,

    hInstance,

    MAKEINTRESOURCE(IDD_SPHEREPARAM1),

    GetString(IDS_RB_CREATIONMETHOD),

    0);

 

   pmapTypeIn = CreateCPParamMap(

    descTypeIn,TYPEINDESC_LENGH,

    this,

    ip,

    hInstance,

    MAKEINTRESOURCE(IDD_SPHEREPARAM3),

    GetString(IDS_RB_KEYBOARDENTRY),

    APPENDROLL_CLOSED);

   }

 

  pmapParam = CreateCPParamMap(

   descParam,PARAMDESC_LENGH,

   pblock,

   ip,

   hInstance,

   MAKEINTRESOURCE(IDD_SPHEREPARAM2),

   GetString(IDS_RB_PARAMETERS),

   0);

  }

Note above how the 'Creation Method' or 'Keyboard Entry' rollups are only added if the BEGIN_EDIT_CREATE flag is set. In either case the 'Parameters' rollup is added.

The following bit flags may be compared with the flag parameter to test the branch (or sub-task of the branch).

BEGIN_EDIT_CREATE

Indicates the item is being edited in the create branch.

BEGIN_EDIT_MOTION

Indicates a controller is being edited in the motion branch.

BEGIN_EDIT_HIERARCHY

Indicates a controller is being edited in the Pivot subtask of the hierarchy branch.

BEGIN_EDIT_IK

Indicates a controller is being edited in the IK subtask of the hierarchy branch.

BEGIN_EDIT_LINKINFO

Indicates a controller is being edited in the Link Info subtask of the hierarchy branch.

Inside BeginEditParams() the item is also expected to register any sub-object selection levels with the system. . For example, a modifier plug-in may have sub-object selection levels like 'Gizmo' and 'Center'. These need to appear in the sub-object drop down allowing the user to select them. In the case of a bend mofifier for example, this is accomplished by calling BeginEditParams() on the base class SimpleMod, i.e.:

void BendMod::BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev) {

 SimpleMod::BeginEditParams(ip,flags,prev);

 // . . .

What the base class SimpleMod does is the following:

void SimpleMod::BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev){

 // . . .

 // Add our sub object type

 TSTR type1(GetResString(IDS_RB_APPARATUS) ); 

 TSTR type2(GetResString(IDS_RB_CENTER) ); 

 const TCHAR *ptype[] = { type1, type2 };

 ip->RegisterSubObjectTypes( ptype, 2 );

 // . . .

Note how it retrieves two string resources using GetResString() ("Gizmo" and "Center") and registers them using Interface::RegisterSubObjectTypes().

Ending Parameter Editing

When the user is finished editing the item's parameters its EndEditParams() method is called. For example, if the user switches branches of the command panel, or selects a different object type to be created, it's called on the item currently being edited.

The prototype looks like this:

virtual void EndEditParams(IObjParam *ip, ULONG flags, Animatable *next=NULL);

This method is also passed a flag value. If the END_EDIT_REMOVEUI flag is set to the item should remove its rollup pages from the command panel. For example, when in the create branch, procedural objects can be created one after the other so there is no need to remove the UI when one instance of a class is finished with its create stage if another instance of that same class is about to be created. In such a case, END_EDIT_REMOVEUI won't be set since the user interface should stay in place.

User Interface Related Variables

The best way to handle user interface-related variables is to make them = 4) BSPSPopupOnMouseOver(event);;">class variables (by declaring them static). Class variables are shared by every instanced of the class (rather than stored with each instantiated object of the class). This way, an object doesn't suffer the overhead of these variables that are only used when it is editing. Only one item may have its parameters being edited at any one time so the class variables work well.

For example, consider a procedural sphere. It may have class variables that contain the handles to its parameter maps in the command panel. When a sphere is first created, it is immediately edited so its BeginEditParams() method is called. It creates the parameter maps and stores the handles to them in its class variables. If the user chooses to make another sphere, the first sphere's EndEditParams() method is called, but END_EDIT_REMOVEUI flag is set to FALSE, indicating that the sphere should leave the rollup pages in the command panel. A new instance of the Sphere class is then created and its BeginEditParams() method is called. It notices that the parameter map handles are not NULL so it doesn't need to create them. For instance, consider how the procedural sphere checks this in the code below. If its pointer to the 'Creation Method' and 'Parameters' parameter maps are still valid (non-NULL) it doesn't recreate the rollup pages. Rather it simply updates the parameber block pointer stored by the parameter map to point to the new sphere object.

if (pmapCreate && pmapParam) {

 // Left over from last sphere ceated

 pmapCreate->SetParamBlock(this);

 pmapTypeIn->SetParamBlock(this);

 pmapParam->SetParamBlock(pblock);

} else {

 // . . .

Interface Pointer Validity

The interface pointer passed to BeginEditParams() is usally stored by the plug-in since it is likely that the plug-in will need it to call its methods. This pointer is only valid while the item is being editied. Therefore, another important thing to do when EndEditParams() is called is to set the object's interface pointer to NULL. This pointer is only valid between the BeginEditParams() and EndEditParams() methods. A plug-in should not call methods on this pointer outside of this interval.