You can create a manipulator container programmatically and add one or more base manipulators to the container. This approach involves the following steps:
MPxManipContainer
.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.
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.
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.
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);
}
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);
...
}
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);
}
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:
glFirstHandle()
– When drawing an OpenGL pickable component, a name uniquely representing the OpenGL component must be set. This method returns the unsigned integer value that represents the first available OpenGL handle to use. When one OpenGL component drawing is finished, the integer value can be incremented by one to represent the next OpenGL component.colorAndName()
– This method is used to set the color of the OpenGL component that is being drawn next. It is also used to set the OpenGL name of the component so that picking is supported.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();
}
You can use MPxManipulatorNode
to update the plug values in the following ways:
MPxManipulatorNode::doPress()
, MPxManipulatorNode::doDrag()
, and MPxManipulatorNode::doRelease()
. These methods are called when the manipulator receives a corresponding mouse event such as mouse down, mouse drag, or mouse release. Algorithms can be implemented to update the plug values when these methods are called. For example, when doPress()
is called, you can record the mouse position as the original position or when doDrag()
is called, you can record the mouse movement (direction and distance).Connect to a Dependent Node – Follow these steps to connect to a dependent node.
add*Value()
in the postConstructor()
of the manipulator node.conectPlugToValue()
in connectToDependNode()
.set*Value()
in one of the do*()
functions.To connect plugs directly to manipulator values, the manipulator values must be first created on the manipulator. There are several add*Value()
methods to add manipulator values of different types. The one-on-one association can be achieved by calling connectPlugToValue()
method within MPxManipulatorNode::connectToDependNode()
. In one of the do*()
functions, which is triggered when the manipulator is receiving the corresponding mouse event, calling set*Value()
sets the corresponding manipulator value that consequently sets the plug value.
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);
...
}