squareScaleManip/squareScaleManip.cpp

squareScaleManip/squareScaleManip.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.
// ==========================================================================
//+
//
// squareScaleManip.cpp
// Creates manipulator node squareScaleManipulator
// Creates command squareManipCmd
//
// 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.
/*
// To show this example using MEL, run the following:
loadPlugin squareScaleManip.so;
squareManipCmd -create;
// To delete the manipulator using MEL:
squareManipCmd -delete;
*/
#include "squareScaleManip.h"
#include <maya/MIOStream.h>
#include <maya/MMatrix.h>
#include <maya/MVector.h>
#include <maya/MSelectionList.h>
#include <maya/MItSelectionList.h>
#include <maya/MFnTransform.h>
#include <maya/MGlobal.h>
#include <maya/MFnCamera.h>
#include <maya/MTemplateCommand.h>
#include <maya/MDrawRegistry.h>
#include <maya/MDrawContext.h>
#include <maya/MUserData.h>
#include <maya/MStateManager.h>
// Statics
MTypeId squareScaleManipulator::id( 0x81046 );
MString squareScaleManipulator::classification("drawdb/geometry/manip/squareScaleManipulator");
MString squareScaleManipulator::registrantId("SquareScaleManipPlugin");
const MPoint squareScaleManipulator::topLeft(-0.5f, 0.5f, 0.0f);
const MPoint squareScaleManipulator::topRight(0.5f, 0.5f, 0.0f);
const MPoint squareScaleManipulator::bottomLeft(-0.5f, -0.5f, 0.0f);
const MPoint squareScaleManipulator::bottomRight(0.5f, -0.5f, 0.0f);
//
// class implementation
//
squareScaleManipulator::squareScaleManipulator()
, activeName(0)
, topName(0)
, rightName(0)
, bottomName(0)
, leftName(0)
, mousePointGlName(MPoint())
{
// Populate initial points
topLeft.get(tl);
topRight.get(tr);
bottomLeft.get(bl);
bottomRight.get(br);
// Setup the plane with a point on the
// plane along with a normal
MPoint pointOnPlane(topLeft);
// Normal = cross product of two vectors on the plane
MVector normalToPlane =
(MVector(topLeft) - MVector(topRight)) ^
(MVector(topRight) - MVector(bottomRight));
// Necessary to normalize
normalToPlane.normalize();
// Plane defined by a point and a normal
plane.setPlane(pointOnPlane, normalToPlane);
}
squareScaleManipulator::~squareScaleManipulator()
{
}
// virtual
void squareScaleManipulator::draw(
M3dView &view,
const MDagPath &path,
{
// Are we in the right view
MDagPath dpath;
view.getCamera(dpath);
if (!shouldDraw(dpath))
{
return;
}
// do the draw using common helper method
view.beginGL();
// Get the starting value of the pickable items
MGLuint glPickableItem;
glFirstHandle(glPickableItem);
// Top
topName = glPickableItem;
// Place before you draw the manipulator component that can
// be pickable.
colorAndName(view, glPickableItem, true, mainColor());
glBegin(GL_LINES);
glVertex3fv(tl);
glVertex3fv(tr);
glEnd();
// Right
glPickableItem++;
rightName = glPickableItem;
colorAndName(view, glPickableItem, true, mainColor());
glBegin(GL_LINES);
glVertex3fv(tr);
glVertex3fv(br);
glEnd();
// Bottom
glPickableItem++;
bottomName = glPickableItem;
colorAndName(view, glPickableItem, true, mainColor());
glBegin(GL_LINES);
glVertex3fv(br);
glVertex3fv(bl);
glEnd();
// Left
glPickableItem++;
leftName = glPickableItem;
colorAndName(view, glPickableItem, true, mainColor());
glBegin(GL_LINES);
glVertex3fv(bl);
glVertex3fv(tl);
glEnd();
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)
{
// Scale nodes on the selection list. Implementation
// is very simple and will not support undo.
for (MItSelectionList iter(list); !iter.isDone(); iter.next())
{
MObject node;
MStatus status;
iter.getDependNode(node);
MFnTransform xform(node, &status);
if (MS::kSuccess == status)
{
double newScale[3];
newScale[0] = mousePointGlName.x + 1;
newScale[1] = mousePointGlName.y + 1;
newScale[2] = mousePointGlName.z + 1;
xform.setScale(newScale);
}
}
return MS::kSuccess;
}
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;
if (glActiveName(activeName))
{
// Reset draw points
topLeft.get(tl);
topRight.get(tr);
bottomLeft.get(bl);
bottomRight.get(br);
float* start = 0;
float* end = 0;
if (activeName == topName)
{
start = tl;
end = tr;
}
if (activeName == bottomName)
{
start = bl;
end = br;
}
if (activeName == rightName)
{
start = tr;
end = br;
}
if (activeName == leftName)
{
start = tl;
end = bl;
}
if (start && end)
{
// Find a vector on the plane
lineMath line;
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;
}
// Move draw points
start[0] += (float)mousePointGlName.x;
start[1] += (float)mousePointGlName.y;
start[2] +=(float) mousePointGlName.z;
end[0] += (float)mousePointGlName.x;
end[1] += (float)mousePointGlName.y;
end[2] +=(float) mousePointGlName.z;
}
}
return MS::kSuccess;
}
bool squareScaleManipulator::shouldDraw(const MDagPath& cameraPath) const
{
MStatus status;
MFnCamera camera(cameraPath, &status);
if (!status)
{
return false;
}
const char* nameBuffer = camera.name().asChar();
if (0 == nameBuffer)
{
return false;
}
if ((0 == strstr(nameBuffer,"persp")) &&
(0 == strstr(nameBuffer,"front")))
{
return false;
}
return true;
}
//
// Draw override implementation for VP2.0
//
class squareScaleManipulatorData : public MUserData
{
public:
enum ManipEdge
{
kTop,
kRight,
kBottom,
kLeft
};
squareScaleManipulatorData()
: MUserData(false) // don't delete after draw
, tl(0), tr(0), br(0), bl(0), activeEdge(kNone) {}
virtual ~squareScaleManipulatorData() {}
float* tl; float* tr; float* br; float* bl;
ManipEdge activeEdge;
};
squareScaleManipulatorOverride::squareScaleManipulatorOverride(const MObject& obj)
: MHWRender::MPxDrawOverride(obj, squareScaleManipulatorOverride::draw)
{
}
squareScaleManipulatorOverride::~squareScaleManipulatorOverride()
{
}
MHWRender::DrawAPI squareScaleManipulatorOverride::supportedDrawAPIs() const
{
}
bool squareScaleManipulatorOverride::isBounded(
const MDagPath& objPath,
const MDagPath& cameraPath) const
{
return true;
}
MBoundingBox squareScaleManipulatorOverride::boundingBox(
const MDagPath& objPath,
const MDagPath& cameraPath) const
{
return MBoundingBox(
squareScaleManipulator::topLeft,
squareScaleManipulator::bottomRight);
}
bool squareScaleManipulatorOverride::disableInternalBoundingBoxDraw() const
{
return true;
}
MUserData* squareScaleManipulatorOverride::prepareForDraw(
const MDagPath& objPath,
const MDagPath& cameraPath,
const MHWRender::MFrameContext& frameContext,
MUserData* oldData)
{
// get the node
MStatus status;
MFnDependencyNode node(objPath.node(), &status);
if (!status) return NULL;
squareScaleManipulator* manipNode =
dynamic_cast<squareScaleManipulator*>(node.userNode());
if (!manipNode) return NULL;
// access/create user data for draw callback
squareScaleManipulatorData* data =
dynamic_cast<squareScaleManipulatorData*>(oldData);
if (!data)
{
data = new squareScaleManipulatorData();
}
// populate user data from the node
data->tl = data->tr = data->br = data->bl = 0;
data->activeEdge = squareScaleManipulatorData::kNone;
if (manipNode->shouldDraw(cameraPath))
{
data->tl = manipNode->tl;
data->tr = manipNode->tr;
data->br = manipNode->br;
data->bl = manipNode->bl;
if (manipNode->activeName == manipNode->topName)
{
data->activeEdge = squareScaleManipulatorData::kTop;
}
else if (manipNode->activeName == manipNode->rightName)
{
data->activeEdge = squareScaleManipulatorData::kRight;
}
else if (manipNode->activeName == manipNode->bottomName)
{
data->activeEdge = squareScaleManipulatorData::kBottom;
}
else if (manipNode->activeName == manipNode->leftName)
{
data->activeEdge = squareScaleManipulatorData::kLeft;
}
}
return data;
}
void squareScaleManipulatorOverride::draw(
const MHWRender::MDrawContext& context,
const MUserData* data)
{
const squareScaleManipulatorData* manipData =
dynamic_cast<const squareScaleManipulatorData*>(data);
if (stateMgr && manipData &&
manipData->tl && manipData->tr && manipData->br && manipData->bl)
{
// turn off depth to get manip to draw on top of everything
const MHWRender::MDepthStencilState* oldDepthState = stateMgr->getDepthStencilState();
if (oldDepthState)
{
static const MHWRender::MDepthStencilState* depthState = NULL;
if (!depthState)
{
desc.depthEnable = false;
desc.depthWriteEnable = false;
depthState = stateMgr->acquireDepthStencilState(desc);
}
if (depthState)
{
stateMgr->setDepthStencilState(depthState);
}
}
// draw manip
static const float dormantColor[4] = { 0.39f, 0.94f, 1.0f, 1.0f };
static const float activeColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
glColor4fv(manipData->activeEdge == squareScaleManipulatorData::kTop ? activeColor : dormantColor);
glBegin(GL_LINES);
glVertex3fv(manipData->tl);
glVertex3fv(manipData->tr);
glEnd();
glColor4fv(manipData->activeEdge == squareScaleManipulatorData::kRight ? activeColor : dormantColor);
glBegin(GL_LINES);
glVertex3fv(manipData->tr);
glVertex3fv(manipData->br);
glEnd();
glColor4fv(manipData->activeEdge == squareScaleManipulatorData::kBottom ? activeColor : dormantColor);
glBegin(GL_LINES);
glVertex3fv(manipData->br);
glVertex3fv(manipData->bl);
glEnd();
glColor4fv(manipData->activeEdge == squareScaleManipulatorData::kLeft ? activeColor : dormantColor);
glBegin(GL_LINES);
glVertex3fv(manipData->bl);
glVertex3fv(manipData->tl);
glEnd();
// restore old depth state
if (oldDepthState)
{
stateMgr->setDepthStencilState(oldDepthState);
}
}
}
//
// Template command that creates and deletes the manipulator
//
class squareManipCmd;
char cmdName[] = "squareManipCmd";
char nodeName[] = "squareScaleManipulator";
class squareManipCmd : public MTemplateCreateNodeCommand<squareManipCmd,cmdName,nodeName>
{
public:
//
squareManipCmd()
{}
};
static squareManipCmd _squareManipCmd;
//
// Static methods
//
void* squareScaleManipulator::creator()
{
return new squareScaleManipulator();
}
MStatus squareScaleManipulator::initialize()
{
// No-op
return MS::kSuccess;
}
//
// Entry points
//
MStatus initializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin( obj, PLUGIN_COMPANY, "2009", "Any");
status = plugin.registerNode(
nodeName,
squareScaleManipulator::id,
squareScaleManipulator::creator,
squareScaleManipulator::initialize,
&squareScaleManipulator::classification);
if (!status) {
status.perror("registerNode");
return status;
}
status = _squareManipCmd.registerCommand( obj );
if (!status) {
status.perror("registerCommand");
return status;
}
squareScaleManipulator::classification,
squareScaleManipulator::registrantId,
squareScaleManipulatorOverride::Creator);
if (!status) {
status.perror("registerDrawOverrideCreator");
return status;
}
return status;
}
MStatus uninitializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin( obj );
status = plugin.deregisterNode( squareScaleManipulator::id );
if (!status) {
status.perror("deregisterNode");
return status;
}
status = _squareManipCmd.deregisterCommand( obj );
if (!status) {
status.perror("deregisterCommand");
return status;
}
squareScaleManipulator::classification,
squareScaleManipulator::registrantId);
if (!status) {
status.perror("deregisterDrawOverrideCreator");
return status;
}
return status;
}