Implementing Sub-Object Selection

A modifier registers its sub-object selection levels when Animatable::BeginEditParams() method is called. The method Interface::RegisterSubObjectTypes() can be used to handle this. It provides the system with a list of strings. These strings are the names of the sub-object selection levels that will appear in the Sub-Object drop down list. For example, the following code would register two sub-object levels, "Gizmo" and "Center".

const TCHAR *ptype[] = { "Gizmo", "Center" };
GetCOREInterface()->RegisterSubObjectTypes( ptype, 2 );

When the user presses the Sub-Object button or changes the selection in the sub-object drop down list the modifier is notified. The system calls the Modifier's ActivateSubObjSel() method. In its implementation of this method the modifier is expected to provide an instance of a class derived from CommandMode to support Move, Rotate, Uniform scale, Non-uniform scale, and Squash. These modes replace their object level counterparts although the user still uses the move/rotate/scale tool bar buttons to activate these modes.

Users will expect sub-object move, rotate and scale transformations to behave like their object level counterparts. To make this very easy to implement, there are a set of standard modes defined by the system that a developer may use. These modes handle all this logic internally -- the developer simply uses them. Most modifiers will be able to use these modes. The sample code below demonstrates how the modes are used. This example is from the implementation of class SimpleMod.

In the class declaration the mode pointers are defined:

class SimpleMod : public Modifier
{
   //...
   protected:
     static  MoveModBoxCMode *moveMode;
     static  RotateModBoxCMode *rotMode;
     static  UScaleModBoxCMode *uscaleMode;
     static  NUScaleModBoxCMode *nuscaleMode;
     static  SquashModBoxCMode *squashMode;
     static  SelectModBoxCMode *selectMode;
     //...
};

In the BeginEditParams() method the modes are allocated:

void SimpleMod::BeginEditParams( IObjParam *ip, ULONG flags,
                Animatable *prev )
{
   //...
   // Create sub object editing modes.
   moveMode   = newMoveModBoxCMode(this,ip);
   rotMode     = new RotateModBoxCMode(this,ip);
   uscaleMode   = newUScaleModBoxCMode(this,ip);
   nuscaleMode = new NUScaleModBoxCMode(this,ip);
   squashMode   = newSquashModBoxCMode(this,ip);
   //...
}

In the method ActivateSubobjSel() the modes are activated. This method is defined in BaseObject.

void SimpleMod::ActivateSubobjSel(int level, XFormModes& modes )
{
   switch ( level )
   {
     case 1: // Modifier box
      modes = XFormModes( moveMode, rotMode,
                nuscaleMode, uscaleMode,
                squashMode,NULL);
      break;
     case 2: // Modifier Center
      modes = XFormModes(moveMode,NULL,NULL,NULL,NULL,NULL);
      break;
   }
   //...
}

When the item is finished being edited the modes can be deleted:

void SimpleMod::EndEditParams(   IObjParam *ip,
                ULONG flags,
                Animatable *next)
{
   //...
   ip->DeleteMode(moveMode);
   ip->DeleteMode(rotMode);
   ip->DeleteMode(uscaleMode);
   ip->DeleteMode(nuscaleMode);
   ip->DeleteMode(squashMode);
 
   if ( moveMode ) deletemoveMode;
   moveMode = NULL;
 
   if ( rotMode ) deleterotMode;
   rotMode = NULL;
 
   if ( uscaleMode ) deleteuscaleMode;
   uscaleMode = NULL;
 
   if ( nuscaleMode ) delete nuscaleMode;
   nuscaleMode = NULL;
 
   if ( squashMode ) deletesquashMode;
   squashMode = NULL;
}

Both the standard transformation modes and custom modes are expected to treat sub-object selection in the same way 3ds Max handles object selection. This means all the keypress options, such as Ctrl to add to a selection and Alt to subtract from a selection, should work in the same manner. To avoid forcing plug-in developers to implement all this logic, a SelectionProcessor class is provided. This class represents a mouse proc derived from the MouseCallback class. When you create an instance of the SelectionProcessor class you give its constructor a pointer to another MouseCallback -- the mouse procedure to handle the mode being implemented. The SelectionProcessor receives the mouse input first. It processes the mouse input using a standard selection logic and then calls the MouseCallback it was given.

NOTE:For working with modifier and sub-object selection, two classes are defined: GenModSelectionProcessor and SubModSelectionProcessor.

See Also