Manipulator Containers

You can create a manipulator container programmatically and add one or more base manipulators to the container. This approach involves the following steps:

  1. Create a manipulator container class derived from MPxManipContainer.
  2. Add the base manipulators to the manipulator container class.
  3. Define associations between the manipulator and the attribute on the nodes.

The parent class of user-defined container manipulators is MPxManipContainer. There are a number of methods in MPxManipContainer that allow you to add a variety of base manipulators to the container. You also need to implement a number of methods in the custom manipulator.

The necessary methods are:

You can also override the draw method to customize the way your container manipulator is drawn.

creator method

The creator method needs to return a new instance of the manipulator, and it is registered in the initializePlugin() function with a call to the MFnPlugin::registerNode() method.

NOTE: Because this method is static and registered, and not derived, the name of the method does not need to be creator, although by convention the name creator is typically used.

initialize method

The initialize method performs any necessary initializations for the manipulator as well as calling the method in the parent class MPxManipContainer::initialize(). Similar to the creator method, the initialize method is static and registered rather than derived.

createChildren method

The base manipulators are added to the manipulator container classes using the MPxManipContainer::createChildren() method.

For example, in the moveManip::createChildren() method:

MStatus moveManip::createChildren()
{
    ...

    fDistanceManip = addDistanceManip(manipName,
    distanceName); 
    fFreePointManip = addFreePointTriadManip(pointManipName,
    pointName);

    ...

}

This method is usually called after the custom manipulator container class is set up. The MPxManipContainer class provides a set of member functions to add individual base manipulators, most of them are named as addXYZManip, where XYZ represents the manipulator name. The function returns an MDagPath object representing the created base manipulator. Following is an example from the footPrintManip plug-in for adding distance manipulator to a custom manipulator container.

MStatus footPrintLocatorManip::createChildren() {
    MStatus stat =MStatus::kSuccess;
 
    MString manipName("distanceManip");
    MString distanceName("distance");
 
    MPoint startPoint (0.0, 0.0, 0.0);
    MVector direction (0.0, 1.0, 0.0);
    fDistanceManip =addDistanceManip(manipName,distanceName);
 
    MFnDistanceManip distanceManipFn(fDistanceManip);
    distanceManipFn.setStartPoint(startPoint);
    distanceManipFn.setDirection(direction);
     
    return(stat);
}

connectToDependNode method

The connectToDependNode() method is where the association is made between the manipulator and the plug with which it will communicate. All the operations to set up communication between manipulators and plugs (including one-on-one association and conversion functions) must be put in this method. After mapping relationships between manipulators and plugs are set, there are two additional methods to be called. These methods are MPxManipContainer::finishAddingManips() and MPxManipContainer::connectToDependNode(), and must be called in that order. It is important to note that MPxManipContainer::finishAddingManips() must be called after all calls to connect to plugs have been made. Furthermore, finishAddingManips() must be called only once.

For example, in the moveManip::connectToDependNode() method:

MStatus moveManip::connectToDependNode(const MObject &node)
{
    ...
    distanceManipFn.connectToDistancePlug(syPlug);
    ...
    freePointTriadManipFn.connectToPointPlug(tPlug);
    ...
    finishAddingManips();
    ...
    MPxManipContainer::connectToDependNode(node);
    ...
}
NOTE: Although connectToDependNode() is a virtual method, you will usually be responsible for calling this method yourself when you are using the manipulator within a custom context. For more information, see Connect Manipulators to the Show Manipulator Tool.

The footPrintManip plug-in example demonstrates the following operations: creating a distance manipulator, applying MFnDistanceManip function set class on the created distance manipulator, setting up one-on-one association between plug and distance manipulator value, and setting up plugToManip conversion to set the manipulator start point position.

MStatus footPrintLocatorManip::connectToDependNode (const MObject &node) {
    MStatus stat;
 
    // Get the DAG path
    MFnDagNode dagNodeFn(node);
    dagNodeFn.getPath(fNodePath);
 
    // Connect the plugs
    
    MFnDistanceManip distanceManipFn(fDistanceManip);
    MFnDependencyNode nodeFn(node);
 
    MPlug sizePlug =nodeFn.findPlug("size", &stat);
    if ( stat != MStatus::kFailure ) {
       distanceManipFn.connectToDistancePlug (sizePlug);
       unsigned startPointIndex =distanceManipFn.startPointIndex();
       addPlugToManipConversionCallback (startPointIndex,
       (plugToManipConversionCallback)&footPrintLocatorManip::startPointCallback);
       finishAddingManips();
       MPxManipContainer::connectToDependNode(node);
    }
    return(stat);
}

draw method

The draw method is an optional method which can be used to customize the drawing of a container manipulator. If you are overriding the draw method, you must first call MPxManipContainer::draw() to draw all the children.

For example, the moveManip::draw() method draws a label in addition to the base manipulators.

void moveManip::draw(M3dView &view,
                     const MDagPath &path,
                     M3dView::DisplayStyle style,
                     M3dView::DispalyStatus status)
{
    MPxManipContainer::draw(view, path, style, status);
    view.beginGL();
    MPoint textPos(0, 0, 0);
    char str[100];
    sprintf(str, "Stretch Me!");
    MString distanceText(str);
    view.drawText(distanceText, textPos, M3dView::kLeft);
    view.endGL();
}

The shape and drawing of custom manipulators is restricted to base manipulators because the MPxManipContainer::draw() method only provides a M3dView parameter to add supplementary drawing to the manipulator. The custom manipulator’s shape is already decided by the combination of base manipulator shapes that are added to the current manipulator container. However, there are situations where you want to have more control over different perspectives of a manipulator. The MPxManipulatorNode class, which was introduced in Maya 2009, provides a new way to implement custom manipulators with custom OpenGL drawing code. It offers options for selecting components (activating different handles) in custom manipulators. This class can either work alone or work with the MPxManipContainer class.

The MPxManipulatorNode::draw() method is overridden to implement custom drawing. In OpenGL, drawing and picking are done together. There are several important methods like the following:

void triadScaleManip::draw (M3dView &view, const MDagPath &path, M3dView::DisplayStyle style,
         M3dView::DisplayStatus status) {
    ……
 
    // Begin the drawing
    view.beginGL();
 
    // Place before you draw the manipulator component that can
    // be pickable.
    MGLuint glPickableItem;
    glFirstHandle(glPickableItem);
 
    // Top
    topName =glPickableItem;
    colorAndName(view, glPickableItem, true, mainColor());
    gGLFT->glBegin(GL_LINES);
    gGLFT->glVertex3fv(tl);
    gGLFT->glVertex3fv(tr);
    gGLFT->glEnd();
 
    // Right
    glPickableItem++;
    rightName =glPickableItem;
    colorAndName(view, glPickableItem, true, mainColor());
    gGLFT->glBegin(GL_LINES);
    gGLFT->glVertex3fv(tr);
    gGLFT->glVertex3fv(br);
    gGLFT->glEnd();
 
    // ...
 
    // End the drawing
    view.endGL();
}

Updating Plug Values on Nodes

You can use MPxManipulatorNode to update the plug values in the following ways:

Registration/Deregistration

Because manipulator containers are derived from nodes, user-defined manipulators can be registered and deregistered like any other node, with the exception that the MPxNode::Type argument in MFnPlugin::registerNode() is set to MPxNode::kManipContainer instead of the default MPxNode::kDependNode.

For example, in moveToolManip.cpp:

MStatus initializePlugin(MObject obj)
{
        ...
        plugin.registerNode("moveManip", 
        moveManip::id,
        &moveManip::creator,
        &moveManip::initialize,
        MPxNode::kManipContainer);
        ...
}
MStatus uninitializePlugin(MObject obj)
{
    ...
    plugin.deregisterNode(moveManip::id);
    ...
}