I. General Introduction
A manipulator is a node that draws itself using 3D graphical elements that respond to user events. While it is possible to change a node’s attribute values using Channel Box, Graph Editor and etc., manipulators provides a more intuitive way of changing attribute values: Manipulators provide a series of visual controls that users can interactively move or adjust. The modification of these visual controls subsequently is translated by manipulators into value changes and then applied to the nodes attributes. The attribute values are modified directly by the manipulator and not through the standard plug and connection mechanism used by other dependency graph nodes.
Using the manipulator, it is easier than typing modified values in the editors such as Attribute Editor, Channel Box and etc. Users can see the immediate visual feedback during operations. Therefore, manipulators quite often are used as an interactive tool for animators to manipulate a specific object or custom nodes in a scene. With custom manipulators, you will be able to create all types of interactive custom tools (modeling tool, rigging tool, etc…) for animators in different stages of animation production pipeline.
There are several ways to create and activate manipulators: First, you can create a manipulator on any node in the current scene at any time. You can also create and assign a manipulator for a specific type of object. Another approach is to create a context tool and associate the manipulator to the context tool so that whenever it is active, the manipulator is active and ready to use. Manipulators are only active when the “Show Manipulator Tool” or the associated context is active, and the object that they correspond to is selected. For more information, see Section 5 “Register Manipulators”.
When manipulators are created, even though they are nodes, they are not visible in the Hypergraph or Outliner, and they are not added to the Maya selection list. Additionally, their attributes are not accessible from MEL or the attribute editor, and they are not written to file.
Manipulators are designed to operate on data types, ranging from integer and floating point values to matrix data and can operate on one or more attribute values at the same time.
II. Maya Base Manipulators
Maya defines a set of simple manipulators, called base manipulators. These operate on a range of data types from a single boolean, integer, or floating point value, to vectors of floating point values of different lengths. The OpenMaya API supports 12 base manipulator classes that can be combined to form a composite manipulator. All the function set classes for these Maya base manipulators are derived from MFnManip3D.
FreePointTriadManip
The
FreePointTriadManip provides a moveable point that can be moved anywhere. It
has axes for constrained x, y, and z movement and obeys grid snapping, point
snapping, and curve snapping. The FreePointTriadManip generates the 3D position
of the point. It is useful for specifying the position of an object in space.
One thing to note is that the FreePointTriadManip is not corresponding to Maya’s internal moveManip, and it is actually a subset of the moveManip.
The API class for FreePointTriadManip is MFnFreePointTriadManip. This function set class can be used to create the manipulator and set up some properties of this manipulator. For example, MFnFreePointTriadManip::setDrawAxes() can be used to set whether or not to draw the axes of the FreePointTriadManip. MFnFreePointTriadManip::setSnapMode() can be used to set whether or not the FreePointTriadManip should be in snap mode etc...
RotateManip
The RotateManip corresponds
to Maya built-in rotate manipulator, and it allows you to manipulate a 3d
rotation vector. The manipulator
consists of three constrained-axis rotation rings, a view rotation ring, and an
invisible trackball that allows the user to rotate in arbitrary directions on
the sphere. It supports the 3 rotation modes of the built-in rotate manipulator
(object space, global, gimbal) and allows constrained rotation on the x, y, z
and viewing axes. The vector generated by the manipulator is an Euler rotation
that is suitable for input to a rotation plug.
The API class for RotateManip is MFnRotateManip. Simliar to MFnFreePointTriadManip, it provides functions to create the manipulator and set up properties.
ScaleManip
The ScaleManip
corresponds to Maya built-in scale manipulator and lets you manipulate relative
x, y, and z scaling values. The scale manipulator provides a central handle for
proportional scaling, and x, y, and z axis handles for non-proportional scaling
on each axis. The vector generated by the manipulator is a relative scaling
vector that is suitable for input to a scale plug.
The API class for scaleManip is MFnScaleManip. Simliar to MFnFreePointTriadManip, it provides functions to create the manipulator and set up properties.
DirectionManip
The DirectionManip lets
you specify a direction as defined by the vector from the start point to the
manipulator position. It uses a FreePointTriadManip to specify the end point of
a vector relative to a given start point. This manipulator generates a vector
from the start point to the end point.
The API class for DirectionManip is MFnDirectionManip. Simliar to MFnFreePointTriadManip, it provides functions to create the manipulator and set up properties of the manipulator.
DistanceManip
The DistanceManip lets
you manipulate a point that is constrained to move along a line. The distance
value is calculated from the start point of the line to the manipulated point.
This manipulator generates a single floating point value. Scaling factors can
be used to determine how long the manipulator appears when it is drawn. The API
class for DistanceManip is MFnDistanceManip.
PointOnCurveManip
The PointOnCurveManip lets you manipulate a point constrained to move along a curve, in order to specify the "u" curve parameter value. This manipulator generates a single floating point value corresponding to the curve parameter. The API class for PointOnCurveManip is MFnPointOnCurveManip.
PointOnSurfaceManip
The PointOnSurfaceManip
lets you manipulate a point constrained to move along a surface, in order to
specify the (u, v) surface parameter values. This manipulator generates two
floating point values corresponding to the surface (u, v) parameters. The API
class for PointOnSurfaceManip is MFnPointOnSurfaceManip.
DiscManip
The DiscManip lets you
rotate a disc in order to specify a rotation about an axis. This manipulator
generates a single floating point value corresponding to the rotation. The API
class for DiscManip is MFnDiscManip.
CircleSweepManip
The CircleSweepManip
lets you manipulate a point constrained to move around a circle, in order to
specify a sweep angle. This manipulator generates a single floating point value
corresponding to the sweep angle. Although very similar to DiscManip, it
provides more options for specifying rotating angles. The API class for CircleSweepManip
is MFnCircleSweepManip.
ToggleManip
The ToggleManip lets
you switch between two modes or some on/off state. It is drawn as a circle with
or without a dot. When the mode is on, the dot is drawn in the circle and when
the mode is off, the circle is drawn without the dot. This manipulator
generates a boolean value corresponding to whether or not the mode is on or
off. The API class for ToggleManip is MFnToggleManip.
StateManip
The StateManip lets you
switch between multiple states. It is drawn as a circle with a notch. Each
click on the circle increments the value of the state (modulo the maximum
number of states). This manipulator generates an integer value corresponding to
the state of the manipulator. The API class for StateManip is MFnStateManip.
CurveSegmentManip
The CurveSegmentManip lets you manipulate two points on a curve to specify a curve segment. This manipulator generates two floating point values, which correspond to the parameters of the start and end of the curve segment. The API class for CurveSegmentManip is MFnCurveSegmentManip.
III. Custom Manipulators
There are several ways to work with manipulators, you could either create a base manipulator on any node in the current scene on the fly, or you could also create a manipulator container and compose a custom manipulator by adding Maya base manipulators into the container, furthermore, you can also create a brand new type of manipulator node by using MPxManipulatorNode class and defining custom drawing and picking of the manipulator node. Before we jump into more advanced topics of creating a manipulator with MPxManipulatorNode, let’s first examine how to work with Maya base manipulators to create the simplest manipulator on a node.
a. Working with Maya Base Manipulators
In the “Maya Base Manipulators” section, we listed all the function set classes for Maya base manipulators. The function set classes are responsible to create the base manipulators with corresponding create() functions, and also set up some properties of the base manipulators depending on their individual types. Most importantly, the function set classes are used to set up relationships between values of visual controls on the manipulators and values of the attributes (plugs) on nodes. In other words, these base manipulator function set classes are responsible to communicate with attribute (plugs) on nodes to set their values based on visual control values on manipulator and also to set manipulator visual control values appropriately with respect to the values of the attributes (plugs). In the following section, we will take a closer look at how this communication works. We will use MFnDistanceManip as an example.
i. Communication between Manipulators and Plugs on Nodes
The communication between manipulators and nodes can be done in one of two ways: simple one-to-one associations, or through the use of more complex conversion functions.
The properties of the visual controls on a manipulator are represented by float values or other types of data values, and these properties are all registered on the manipulator with unique identifiers. The values are called manip values, and the unique identifiers are manip indices. Some of the values define the key property relate directly to an affordance of the manipulator. For example, the MFnDistanceManip::distanceIndex() returns the index of the manipulator property that relates directly to the distance of a DiscManip. Other values do not relate to an affordance of the manipulator, but provide important information on the position or orientation of the manipulator such as MFnDistanceManip::startPointIndex() and MFnDistanceManip::directionIndex().
As the name indicates, one-to-one mapping basically synchronizes manip values with attribute/plug values directly. In all the function set classes for Maya base manipulators, there is always one function that has some form of “connectToXXXPlug”. This is the function to establish the one-to-one mapping relationship between manip values and attribute/plug values. In other words, the manip value equals to the attribute/plug value, and vice versa.
In the case of MFnDistanceManip, the function MFnDistanceManip::connectToDistancePlug() sets up the one-to-one relationship between the manip value of distance manip and the corresponding “distance” attribute/plug on a node. The following code in devkit project footPrintManip demonstrates a good example of using this one-to-one mapping.
MFnDistanceManip distanceManipFn (fDistanceManip) ; MFnDependencyNode nodeFn (node) ; MPlug sizePlug =nodeFn.findPlug ("size", &stat) ; if ( stat != MStatus::kFailure ) { distanceManipFn.connectToDistancePlug (sizePlug) ; … }
The following graph demonstrates the one-to-one mapping relationship between the distance manip value and the value of “size” plug on a node.
After you set up the one-to-one mapping relationship,
whenever the manip value changes, the corresponding plug/attribute value
changes with it and vice versa.
One-to-one mapping is a very straightforward and convenient approach to set up relationship between manip values and attribute/plug values, however it may not be acceptable for more complex requirements. For example, in the above case what if you want to set up the manip value to be 5 * the value of plug “size”? There is no way you can set this up with one-to-one mapping. Also, when you move the node, the distance manipulator will appear not showing up at the center of the node, and there is no way to make the manipulator move along with the position of the node. In situations where the position of a manipulator needs to be affected by the position of an object, or a group of manipulators need to move together in a specific way; a new approach (conversion functions) needs to be implemented.
Conversion Functions
Conversion functions are used to set up more complex relationship between manip values and attribute/plug values. As the name indicates, they are responsible to convert manip values to attribute/plug values, and vice versa. In C++, they are implemented as callback methods.
There are two types of conversion callback functions:
plugToManip conversion
A plugToManip conversion callback is used to convert attribute/plug values to corresponding manip values. The implementation of plugToManip conversion is achieved by calling MPxManipContainer::addPlugToManipConversionCallback(). Using manipulator container is the recommended approach to create a custom manipulator from May base manipulators. In the following section “Manipulator Containers”, all aspects of using MPxManipContainer class will be discussed in detail.
As discussed earlier, every property of visual controls has a corresponding index registered within the manipulator. In order to set up plugToManip conversion relationship, when MPxManipContainer::addPlugToManipConversionCallback() is called, the index of the manip value needs to be passed in. In every base manipulator function set class, there are functions to retrieve different indices for individual properties.
The following code in devkit project footPrintManip illustrates how to retrieve index of the starting point of the manipulator and pass it into addPlugToManipConversionCallback function call. The second parameter of this function call is the callback conversion function, which is responsible to calculate the manip value of this starting point based on some attribute/plug values.
unsigned startPointIndex =distanceManipFn.startPointIndex () ; addPlugToManipConversionCallback (startPointIndex, (plugToManipConversionCallback)&footPrintLocatorManip::startPointCallback) ;
The plugToManip conversion callback is responsible to access attribute/plug values, calculate manip values based on custom algorithm and return them. In this specific case, the callback function doesn’t request attribute/plug values, instead, it retrieves the node translation in world space and return this value to Maya. By doing so, it actually sets the start point manip value to be the same as the node translation. The outcome of this setting is that whenever the node moves, the manipulator moves along with it and always appears in the center of the node.
MManipData footPrintLocatorManip::startPointCallback (unsigned index) const { MFnNumericData numData ; MObject numDataObj =numData.create (MFnNumericData::k3Double) ; MVector vec =nodeTranslation () ; numData.setData (vec.x, vec.y, vec.z) ; return (MManipData (numDataObj)) ; }
One thing to note is that the conversion callback returns a data type called MManipData. By returning this to Maya, the conversion callback sets the manip value to what the MManipData represents. MManipData can represent data that is either simple or complex. When it comes to complex data types (such as such as matrices, curves, and arrays of data), MFnData or its derived classes need to be used to create the data.
manipToPlug conversion
A manipToPlug conversion callback is used to convert manip values to attribute/plug values. The implementation of manipToPlug conversion is achieved by calling MPxManipContainer::addManipToPlugConversionCallback().The corresponding plug needs to be passed in as the first parameter because the callback needs to set attribute/plug values, it needs to know which attribute/plug values to change.
In general, manipToPlug conversions are less commonly used and there are several examples in the devkit. The following code from rotateManip example project demonstrates using this technique with the node’s “rotate” plug passed in.
MFnDependencyNode nodeFn (node) ; MPlug rPlug =nodeFn.findPlug ("rotate", &stat) ; … rotatePlugIndex =addManipToPlugConversionCallback (rPlug, (manipToPlugConversionCallback)&exampleRotateManip::rotationChangedCallback) ;
The addManipToPlugConversionCallback function call returns the index (“rotatePlugIndex”) to identify which plug has been registered with this callback. When the actual callback function is invoked, the index of the plug value to be calculated will be passed in as the parameter of the function call. The callback function then needs to distinguish if the passed-in value is the index that is registered for this callback. If there is more than one plug registered with this callback, a condition statement can be used to distinguish different passed-in plug index and calculate corresponding manip values. In the rotationChangedCallback() below, the passed-in “index” is compared with “rotatePlugIndex”. If they are equal, getConverterManipValue() is called with rotate manip index to retrieve rotate manip value. By returning the rotate manip value to Maya as a MManipData object, this callback function is setting the rotate plug value to be the same as the rotate manip value. Depending on your specific requirement, more complex value relationship between rotate plug value and rotate manip value can be set up with this conversion callback function.
MManipData exampleRotateManip::rotationChangedCallback (unsigned index) { MObject obj =MObject::kNullObj ; // If we entered the callback with an invalid index, print an error and // return. Since we registered the callback only for one plug, all // invocations of the callback should be for that plug. if ( index != rotatePlugIndex ) { MGlobal::displayError ("Invalid index in rotation changed callback!") ; // For invalid indices, return vector of 0's MFnNumericData numericData ; obj =numericData.create (MFnNumericData::k3Double) ; numericData.setData (0.0, 0.0, 0.0) ; return (obj); } MFnNumericData numericData ; obj =numericData.create (MFnNumericData::k3Double) ; MEulerRotation manipRotation ; if ( !getConverterManipValue (rotateManip.rotationIndex (), manipRotation) ) { MGlobal::displayError ("Error retrieving manip value") ; numericData.setData (0.0, 0.0, 0.0) ; } else { numericData.setData (manipRotation.x, manipRotation.y, manipRotation.z) ; } return MManipData (obj) ; }
For more complete and in-depth explanation, you can go to Maya API documentationà“API Guide”à “Manipulators”à “Communication between manipulators and nodes”.
Tips:
- C++
Conversion Functions vs. Python Conversion Functions
When looking at the MPxManipContainer class documentation, people usually get confused by the many conversion and conversion callback function names. Here is a clarification: If you are writing python plug-in, addManipToPlugConversion() and addPlugToManipConversion() (that are specifically designed for writing python plug-ins) are the only functions to register conversion callback functions. If you are writing C++ plug-ins, you can either use addPlugToManipConversionCallback()/addManipToPlugConversionCallback() functions or addManipToPlugConversion()/addPlugToManipConversion() functions to register conversion callback functions. - Manip
index vs. plug index
Properties of visual controls have individual index registered within the manipulator. For example, MFnDistanceManip::startPointIndex() returns the index of the start point of the DistanceManip, MFnRotateManip::rotationIndex() returns the index of rotation manip Value of the manipulator, etc. In order to specify which manip value is set up with plugToManip conversion callback, manip index needs to used when addPlugToManipConversionCallback() or addPlugToManipConversion() is called.
There is another concept of index called plug index that is returned by addManipToPlugConversionCallback() and addManipToPlugConversion() to identify which plug has been registered with manipToPlug conversion callback. This index usually is saved as a member variable and whenever the callback function is invoked, it is used to compare with the requested-value plug index to decide what values need to return for the requested-value plug.
Manip indexes also are used to retrieve manip values from getConverterManipValue() function call, and plug indexes are used to retrieve plug values from getConverterPlugValue() function call.
b. Manipulator Containers
It is recommended to create a manipulator container and add one or more base manipulators to the container to compose a new type of manipulator. It is also the most common way to create a custom manipulator. This approach involves the following steps:
i. create a custom manip container class derived from MPxManipContainer
ii. add base manipulators to the manip container class
iii. define associations between the manipulator and the attribute on the nodes they affect
Let’s examine these steps in detail:
i. Create a custom manip container class derived from MPxManipContainer
In order to create a custom manipulator you need to derive from MPxManipContainer and implement some common functions of MPxNode such as the create() and initialize() functions. Also when registering this custom node with MFnPlugin::registerNode in initializePlugin() function, MPxNode::kManipContainer needs to used.
ii. Add base manipulators to the manip container class
Maya base manipulators are added into custom manipulator container classes in the MPxManipContainer::createChildren() function. This method is usually called by Maya after this custom manip container class is set up. MPxManipContainer class provides a set of member functions to add individual Maya base manipulators, most of them named like this “addXXXManip” with XXX being the manip name. The function returns an MDagPath object representing the created base manipulator, which you can then use the corresponding function set class to operate with later. Here is an example in the devkit project footPrintManip adding distance manip into a custom manip 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) ; }
iii. Define associations between the manipulator and the attribute on the nodes they affect
As its name indicates, MPxManipContainer::connectToDependNode() method connects the manipulator to the dependency node. It is also where the association is made between the manipulator and the attribute(s)/plug(s) that it will communicate with. All the operations to set up communication between manipulators and attribute(s)/plugs (including one-on-one mapping and conversion functions) need to put into this function call.
After mapping relationships between manipulators and plugs are set up, there are two additional methods that need to be called. The methods are MPxManipContainer::finishAddingManips() and MPxManipContainer::connectToDependNode() and must be called in that order. Also, MPxManipContainer::finishAddingManips() must be called after all calls to connect to plugs have been made. MPxManipContainer::finishAddingManips must be called only once. Devkit sample footPrintManip demonstrates the following operations: creating a distance manip; applying MFnDistanceManip function set class on the created distance manip; setting up one-on-one mapping relationship between plug “size” and distance manip value; and setting up plugToManip conversion to set up manip 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) ; }
c. Custom Drawing of Custom Manipulators
Constructing a manipulator by creating a manipulator container and adding one or more base manipulators to it is the most common approach to create a custom manipulator, and Maya base manipulators provide a great variety of possibilities of custom manipulator. However there are still situations where you want to have more customized control over different perspectives of a manipulator. For example, with the above approach, the shape and drawing of custom manipulators is limited/restricted to Maya base manipulators because MPxManipContainer::draw() function only provides a M3dView parameter to add supplementary drawing onto the manipulator. The custom manipulator’s shape is already decided by the combination of Maya base manipulator shapes that are added into the current manip container. Also, you don’t have control over the selection on the manipulator components and the selecting behavior when user clicks on the manipulator that is defined by Maya base manipulators internal implementation.
A new manipulator class MPxManipulatorNode was introduced with Maya 2009 to provide a new way to implement custom manipulators with custom OpenGL drawing code. It offers developers with options for selecting manipulator components (activating different handles) on custom manipulators. This class can either work alone or work with the MPxManipContainer class.
i. Custom Drawing
MPxManipulatorNode::draw() method is overridden to implement custom drawing of this custom manipulator. In OpenGL, drawing and picking is done together. This method is also responsible to set up for selection. Several functions are important to use within this method:
glFirstHandle(): When drawing a OpenGL pickable component, a name uniquely representing the OpenGL component must be set. This method returns the unsigned int value which represents the first available OpenGL handle to use. When one OpenGL component drawing is finished, the int value can be added by 1 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 can be supported. The second parameter “glName” is the OpenGL name that represents the component you are going to draw in the code next. The last parameter “colorIndex” presents the color you want to use on the GL component. In MPxManipulatorNode class, there are several color methods which return color indexes that Maya use to allow custom manipulators to have a similar look.
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 () ; }
ii. Updating Attributes/Plugs Values on Nodes
There are 2 ways for MPxManipulatorNode to update attributes/plugs values:- Update attribute/plugs values directly by
implementing functions related with mouse movement event:
In MPxManipulator class there are three functions to use to watch for mouse movement events:
MPxManipulatorNode::doPress (M3dView &view);
MPxManipulatorNode::doDrag (M3dView &view);
MPxManipulatorNode::doRelease (M3dView &view);
These are called when the manipulator receives a corresponding mouse event including mouse down, mouse drag and mouse release. Algorithms can be implemented to update attributes/plugs values when these functions are called. For example, when doPress() gets called, record mouse position as original position; when doDrag() gets called, record mouse movement (direction and distance); when doRelease() gets called, calculate the actual mouse movement values and apply to node attributes/plugs. - Connect to a dependent node:
In order to connect to a dependent node, the following steps need to be taken:
Call add*Value() in the postConstructor() of the manipulator node;
Call conectPlugToValue() in connectToDependNode();
Call set*Value() in one of the do*() functions;
In order to connect attributes/plugs directly to manipulator values, manipulator values need to be created first on the manipulator. There are several add*Value() to add manipulator values of different types. After the manipulator values are created and added onto the manipulator, one-to-one mapping relationship can be achieved by calling connectPlugToValue() method within MPxManipulatorNode::connectToDependNode(). In one of the do*() function, which gets triggered when the manipulator is receiving corresponding mouse event, calling set*Value() will set the corresponding manipulator value that will consequently set the attributes/plugs value which is connected to current manipulator value.
V. Apply Manipulators
After a custom manipulator is created, there are two ways to apply the manipulator to nodes. The manipulator can either be applied to work only on one specific type of node, or it can be added to a custom context/tool so that it is applicable to any node.
a. Apply on one type of node
In order to make a custom manipulator work on one specific type of node, there are several steps to set up the relationship between custom manipulator and node type:
i. The manipulator must be named after the node type appended with “Manip”: For example, if the node type name is “myNode”, the custom manipulator name should be “myNodeManip”.
ii. Register the node type in the manipulator connect table in node class initialization. In the node class initialize() function, add one function call:
MPxManipContainer::addToManipConnectTable(id); where “id” is the custom node type ID.
After you select the custom node in the Maya scene and go to “Show Manipulator Tool”, the custom manipulator will show up on the custom node.
b. Apply in user-defined context
Another approach to apply custom manipulators is adding them into a custom context. Custom contexts are proxy classes where you can implement interactive tools based on mouse events occurring within an interactive panel in Maya.
In MPxContext class, there are two functions to work with manipulators: addManipulators() and deleteManipulators(). MPxContext::addManipulators() takes a parameter of MObject that represents a custom manipulator. MPxManipContainer::newManipulator() or MPxManipulatorNode::newManipulator() can be used to create a custom manipulator that is passed into MPxContext::addManipulator() call.
In addition, MPxSelectionContext::toolOnSetup() and MPxContext::toolOffCleanup() should be overridden so that toolOnSetup adds a callback to update custom manipulators to work on different objects, and toolOffCleanup removes the callback when the current tool is inactive.
VI. Manipulator Examples
Here is the list of manipulator examples available in the Maya installation devkit folder:
customAttrManip:
This plug-in demonstrates how to create user-defined manipulators on custom attributes of nodes within a user-defined context. This custom manipulator is called customAttrManip and is composed of three DistanceManips.
customTriadManip:
This plug-in demonstrates how to create user-defined manipulators from a user-defined context and apply the manipulator to custom attributes defined on a custom transform node. The custom transform node has three custom attributes define, TnoiseX, TnoiseY, and TnoiseZ. Three distance base manips are defined as the custom manipulator and get attached to the noise attributes when selected. The attachment of the manipulator is performed by an event callback that is registered for toolOnSetup and SelectionChanged events.
moveManip:
It shows how to create a manipulator from a context. The user-defined manipulator in moveToolManip.cpp is called moveManip and consists of two base manipulators: a FreePointTriadManip and a DistanceManip.
footPrintManip:
This plug-in example demonstrates how to use the Show Manipulator Tool with a user-defined node and a user-defined manipulator. The user-defined manipulator consists of a DistanceManip. It also demonstrates how to write plugToManip conversion callback functions so that DistanceManip is always following the location of the foot.
rotateManip:
This plug-in demonstrates the different modes of the rotate base manipulator. The user-defined manipulator in rotateManip.cpp consists of a rotate base manipulator and a state base manipulator. The state manipulator is used to control the mode of the rotate manipulator: object mode, world space mode, gimbal mode, and object mode with snapping.
componentScaleManip:
This plug-in demonstrates how to use the scale base manipulator and also demonstrates a method for manipulating components. The plug-in componentScaleManip.cpp consists of a scale base manipulator. The manipulator works by attaching manipToPlug conversion callbacks for every selected vertex. The conversion function computes the new vertex positions using stored initial vertex positions and the scale manipValue.
surfaceBumpManip:
This plug-in example demonstrates how the pointOnSurface base manipulator can be used to modify vertices close to the param manipValue. The plug-in uses a manipToPlug conversion function as a callback to update vertex positions on the NURBS surface.
swissArmyManip:
This plug-in is an example of a user-defined manipulator, that is comprised of a variety of the base manipulators: CircleSweepManip, DirectionManip, DiscManip, DistanceManip, FreePointTriadManip, StateManip, ToggleManip, RotateManip, ScaleManip.
lineManip:
This example demonstrates how to use the MPxManipulatorNode class along with a command to create a user defined manipulator. The manipulator created is a simple line which is an OpenGL pickable component. As you move the pickable component, selected transforms have their scale attribute modified. The line's movements are restricted in a plane. A corresponding command is used to create and delete the manipulator node and to support undo/redo etc.
squareScaleManip:
This example demonstrates how to use the MPxManipulatorNode class along with a command to create a user defined manipulator. The manipulator created is a simple square with the 4 sides as OpenGL pickable components. As you move the pickable component, selected transforms have their scale attribute modified. A corresponding command is used to create and delete the manipulator node and to support undo/redo etc.
Find here a conpanion sample for this post.
This article was written by Naiqi Weng who is a member of the ADN team working with me at Autodesk. Naiqi is an expert on the Maya and MotionBuilder API.
Great write-up on manips. Appreciate all the 'behind the scenes' info.
Posted by: Eric Pavey | October 22, 2012 at 09:12 AM
Thank you for posting this! Appreciated as well ;)
Posted by: Jens Jebens | October 25, 2012 at 03:10 PM