squareScaleManipContext/squareScaleManipContext.cpp

squareScaleManipContext/squareScaleManipContext.cpp
//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+
/*
This example is based on the squareScaleManip example but uses
a context and context command. Template classes are used
for defining the context and context command below. If the
plug-in, context is active, selecting geometry will show
the manipulator. Only the right and left sides of the
square currently modify the geometry if moved.
Loading and unloading:
----------------------
The square scale manipulator context and tool button can be created with the
following MEL commands:
loadPlugin squareScaleManipContext;
squareScaleManipContext squareScaleManipContext1;
setParent Shelf1;
toolButton -cl toolCluster
-i1 "moveManip.xpm"
-t squareScaleManipContext1
squareManip1;
If the preceding commands were used to create the manipulator context,
the following commands can destroy it:
deleteUI squareScaleManipContext1;
deleteUI squareManip1;
If the plug-in is loaded and unloaded frequently (eg. during testing),
it is useful to make these command sequences into shelf buttons.
How to use:
-----------
Once the tool button has been created using the script above, select the
tool button then click on an object. Move the right and left edges of the
square to modify the selected object's scale.
There is code duplication between this example and squareSclaeManip. But
the important additions here are the calls to addDoubleValue() and
addDoubleValue() and the virtual connectToDependNode(). This functionality
ties the updating of the manipulator into changing a node's attribute(s).
*/
#include <maya/MIOStream.h>
#include <maya/MPxNode.h>
#include <maya/MPxManipulatorNode.h>
#include <maya/MPxSelectionContext.h>
#include <maya/MPxContextCommand.h>
#include <maya/MModelMessage.h>
#include <maya/MFnPlugin.h>
#include <maya/MGlobal.h>
#include <maya/MItSelectionList.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MDagPath.h>
#include <maya/MManipData.h>
#include <maya/MHardwareRenderer.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnTransform.h>
#include <maya/MMatrix.h>
#include <maya/MTemplateManipulator.h>
#include "squareScaleManipContext.h"
// Statics
MTypeId squareScaleManipulator::id( 0x81048 );
//
// squareScaleManipulator
//
// Utility class for returning square points
class squareGeometry
{
public:
static MPoint topLeft() {
return MPoint( -0.5f, 0.5f, 0.0f );
}
static MPoint topRight() {
return MPoint( 0.5f, 0.5f, 0.0f );
}
static MPoint bottomLeft() {
return MPoint( -0.5f, -0.5f, 0.0f );
}
static MPoint bottomRight() {
return MPoint( 0.5f, -0.5f, 0.0f );
}
};
//
// class implementation
//
squareScaleManipulator::squareScaleManipulator()
{
// Setup the plane with a point on the
// plane along with a normal
MPoint pointOnPlane(squareGeometry::topLeft());
// Normal = cross product of two vectors on the plane
MVector normalToPlane = (MVector(squareGeometry::topLeft()) - MVector(squareGeometry::topRight())) ^
(MVector(squareGeometry::topRight()) - MVector(squareGeometry::bottomRight()));
// Necessary to normalize
normalToPlane.normalize();
plane.setPlane( pointOnPlane, normalToPlane );
// Set plug indicies to a default
topIndex = rightIndex = bottomIndex = leftIndex = -1;
// initialize rotate/translate to a good default
rotateX = rotateY = rotateZ = 0.0f;
translateX = translateY = translateZ = 0.0f;
}
squareScaleManipulator::~squareScaleManipulator()
{
// No-op
}
// virtual
void squareScaleManipulator::postConstructor()
{
// In the postConstructor, the manipulator node
// is setup. Add the values that we want to
// track
MStatus status;
status = addDoubleValue( "topValue", 0, topIndex );
if ( !status )
return;
status = addDoubleValue( "rightValue", 0, rightIndex );
if ( !status )
return;
status = addDoubleValue( "bottomValue", 0, bottomIndex );
if ( !status )
return;
status = addDoubleValue( "leftValue", 0, leftIndex );
if ( !status )
return;
}
// virtual
MStatus squareScaleManipulator::connectToDependNode(const MObject &dependNode)
{
// Make sure we have a scaleX plug and connect the
// plug to the rightIndex we created in the
// postConstructor
MStatus status;
MFnDependencyNode nodeFn(dependNode,&status);
if ( ! status )
return MS::kFailure;
MPlug scaleXPlug = nodeFn.findPlug("scaleX", &status);
if ( ! status )
return MS::kFailure;
int plugIndex = 0;
status = connectPlugToValue(scaleXPlug,rightIndex, plugIndex);
if ( !status )
return MS::kFailure;
finishAddingManips();
}
// virtual
void squareScaleManipulator::draw(M3dView &view, const MDagPath &path,
{
static MGLFunctionTable *gGLFT = 0;
if ( 0 == gGLFT )
gGLFT = MHardwareRenderer::theRenderer()->glFunctionTable();
// Populate the point arrays which are in local space
float tl[4],tr[4],br[4],bl[4];
squareGeometry::topLeft().get(tl);
squareGeometry::topRight().get(tr);
squareGeometry::bottomLeft().get(bl);
squareGeometry::bottomRight().get(br);
// Depending on what's active, we modify the
// end points with mouse deltas in local
// space
MGLuint active = 0;
if ( glActiveName( active ) )
{
float *a = 0,*b = 0;
if ( active == topName )
{
a = &tl[0]; b = &tr[0];
}
if ( active == bottomName )
{
a = &bl[0]; b = &br[0];
}
if ( active == rightName )
{
a = &tr[0]; b = &br[0];
}
if ( active == leftName )
{
a = &tl[0]; b = &bl[0];
}
if ( active != 0 )
{
a[0] += (float) mousePointGlName.x; a[1] += (float) mousePointGlName.y; a[2] += (float) mousePointGlName.z;
b[0] += (float) mousePointGlName.x; b[1] += (float) mousePointGlName.y; b[2] += (float) mousePointGlName.z;
}
}
// Begin the drawing
view.beginGL();
// Push the matrix and set the translate/rotate. Perform
// operations in reverse order
DegreeRadianConverter convert;
gGLFT->glPushMatrix();
gGLFT->glTranslatef( translateX, translateY, translateZ );
gGLFT->glRotatef( (float) convert.radiansToDegrees(rotateZ), 0.0f, 0.0f, 1.0f );
gGLFT->glRotatef( (float) convert.radiansToDegrees(rotateY), 0.0f, 1.0f, 0.0f );
gGLFT->glRotatef( (float) convert.radiansToDegrees(rotateX), 1.0f, 0.0f, 0.0f );
// Get the starting index of the first pickable component
MGLuint glPickableItem;
glFirstHandle( glPickableItem );
// Top
topName = glPickableItem;
// Place before you draw the manipulator component that can
// be pickable.
colorAndName( view, glPickableItem, false, mainColor() );
gGLFT->glBegin( MGL_LINES );
gGLFT->glVertex3fv( tl );
gGLFT->glVertex3fv( tr );
gGLFT->glEnd();
// Right
glPickableItem++;
rightName = glPickableItem;
colorAndName( view, glPickableItem, true, mainColor() );
gGLFT->glBegin( MGL_LINES );
gGLFT->glVertex3fv( tr );
gGLFT->glVertex3fv( br );
gGLFT->glEnd();
// Bottom
glPickableItem++;
bottomName = glPickableItem;
colorAndName( view, glPickableItem, false, mainColor() );
gGLFT->glBegin( MGL_LINES );
gGLFT->glVertex3fv( br );
gGLFT->glVertex3fv( bl );
gGLFT->glEnd();
// Left
glPickableItem++;
leftName = glPickableItem;
colorAndName( view, glPickableItem, true, mainColor() );
gGLFT->glBegin( MGL_LINES );
gGLFT->glVertex3fv( bl );
gGLFT->glVertex3fv( tl );
gGLFT->glEnd();
// Pop matrix
gGLFT->glPopMatrix();
// End the drawing
view.endGL();
}
// virtual
MStatus squareScaleManipulator::doPress( M3dView& view )
{
// Reset the mousePoint information on
// a new press
mousePointGlName = MPoint::origin;
updateDragInformation();
return MS::kSuccess;
}
// virtual
MStatus squareScaleManipulator::doDrag( M3dView& view )
{
updateDragInformation();
return MS::kSuccess;
}
// virtual
MStatus squareScaleManipulator::doRelease( M3dView& view )
{
return MS::kSuccess;
}
void squareScaleManipulator::setDrawTransformInfo( double rotation[3], MVector translation )
{
rotateX = (float) rotation[0]; rotateY = (float) rotation[1]; rotateZ = (float) rotation[2];
translateX = (float) translation.x; translateY = (float) translation.y; translateZ = (float) translation.z;
}
MStatus squareScaleManipulator::updateDragInformation()
{
// Find the mouse point in local space
MPoint localMousePoint;
MVector localMouseDirection;
if ( MS::kFailure == mouseRay( localMousePoint, localMouseDirection) )
return MS::kFailure;
// Find the intersection of the mouse point with the
// manip plane
MPoint mouseIntersectionWithManipPlane;
if ( ! plane.intersect( localMousePoint, localMouseDirection, mouseIntersectionWithManipPlane ) )
return MS::kFailure;
mousePointGlName = mouseIntersectionWithManipPlane;
MGLuint active = 0;
if ( glActiveName( active ) )
{
float start[4],end[4];
if ( active == topName )
{
squareGeometry::topLeft().get(start);
squareGeometry::topRight().get(end);
}
if ( active == bottomName )
{
squareGeometry::bottomLeft().get(start);
squareGeometry::bottomRight().get(end);
}
if ( active == rightName )
{
squareGeometry::topRight().get(start);
squareGeometry::bottomRight().get(end);
}
if ( active == leftName )
{
squareGeometry::topLeft().get(start);
squareGeometry::bottomLeft().get(end);
}
if ( active != 0 )
{
lineMath line;
// Find a vector on the plane
MPoint a( start[0], start[1], start[2] );
MPoint b( end[0], end[1], end[2] );
MPoint vab = a - b;
// Define line with a point and a vector on the plane
line.setLine( start, vab );
MPoint cpt;
// Find the closest point so that we can get the
// delta change of the mouse in local space
if ( line.closestPoint( mousePointGlName, cpt ) )
{
mousePointGlName.x -= cpt.x;
mousePointGlName.y -= cpt.y;
mousePointGlName.z -= cpt.z;
}
double minChangeValue = minOfThree( mousePointGlName.x, mousePointGlName.y, mousePointGlName.z );
double maxChangeValue = maxOfThree( mousePointGlName.x, mousePointGlName.y, mousePointGlName.z );
if ( active == rightName )
{
setDoubleValue( rightIndex, maxChangeValue );
}
if ( active == leftName )
{
setDoubleValue( rightIndex, minChangeValue );
}
}
return MS::kSuccess;
}
return MS::kFailure;
}
//
// squareScaleManipContext
//
class squareScaleManipContext;
char contextName[] = "squareScaleManipContext";
char manipulatorNodeName[] = "squareScaleContextManipulator";
class squareScaleManipContext :
public MTemplateSelectionContext<contextName, squareScaleManipContext,
MFn::kTransform, squareScaleManipulator, manipulatorNodeName >
{
public:
squareScaleManipContext() {}
virtual ~squareScaleManipContext() {}
// Only work on scaleX
virtual void namesOfAttributes(MStringArray& namesOfAttributes)
{
namesOfAttributes.append("scaleX");
}
// firstObjectSelected will be set so that we can
// determine translate and rotate. We then push
// this info into the manipulator using the
// manipulatorClassPtr pointer
virtual void setInitialState()
{
MStatus status;
MFnTransform xform( firstObjectSelected, &status );
if ( MS::kSuccess != status )
return;
MTransformationMatrix xformMatrix = xform.transformation(&status);
if ( MS::kSuccess != status )
return;
MTransformationMatrix rotateTranslateMatrix = xformMatrix.asRotateMatrix();
rotateTranslateMatrix.setTranslation( xformMatrix.getTranslation(MSpace::kWorld), MSpace::kWorld );
MMatrix matrix = rotateTranslateMatrix.asMatrix();
cout << matrix << endl;
double rotation[3];
xformMatrix.getRotation( rotation, ro );
MVector translation;
translation = xformMatrix.getTranslation( MSpace::kWorld );
manipulatorClassPtr->setDrawTransformInfo( rotation, translation );
}
};
//
// Setup the context command which makes the context
//
class squareScaleManipContextCommand;
char contextCommandName[] = "squareScaleManipContext";
class squareScaleManipContextCommand :
public MTemplateContextCommand<contextCommandName, squareScaleManipContextCommand, squareScaleManipContext >
{
public:
squareScaleManipContextCommand() {}
virtual ~squareScaleManipContextCommand() {}
};
static squareScaleManipContextCommand _squareScaleManipContextCommand;
//
// Static methods for the manipulator node
//
void* squareScaleManipulator::creator()
{
return new squareScaleManipulator();
}
MStatus squareScaleManipulator::initialize()
{
return MS::kSuccess;
}
//
// Entry points
//
MStatus initializePlugin(MObject obj)
{
MStatus status;
MFnPlugin plugin(obj, PLUGIN_COMPANY, "2009", "Any");
status = _squareScaleManipContextCommand.registerContextCommand( obj );
if (!status)
{
MString errorInfo("Error: registering context command : ");
errorInfo += contextCommandName;
return status;
}
status = plugin.registerNode(manipulatorNodeName, squareScaleManipulator::id,
&squareScaleManipulator::creator, &squareScaleManipulator::initialize,
if (!status)
{
MString str("Error registering node: ");
str+= manipulatorNodeName;
return status;
}
return status;
}
MStatus uninitializePlugin(MObject obj)
{
MStatus status;
MFnPlugin plugin(obj);
status = _squareScaleManipContextCommand.deregisterContextCommand( obj );
if (!status)
{
MString errorInfo("Error: deregistering context command : ");
errorInfo += contextCommandName;
return status;
}
status = plugin.deregisterNode(squareScaleManipulator::id);
if (!status)
{
MString str("Error deregistering node: ");
str+= manipulatorNodeName;
return status;
}
return status;
}