transformDrawNode/transformDrawNode.cpp

transformDrawNode/transformDrawNode.cpp
//-
// ==========================================================================
// Copyright 2015 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.
// ==========================================================================
//+
/*
transformDrawNode uses MPxDrawOverride to draw texts about transformation of
all mesh shapes in the viewport via MPxDrawOverride::addUIDrawables. By setting
isAlwaysDirty to false in MPxDrawOverride constructor, the draw override will
be updated (via prepareForDraw()) only when the node is marked dirty via DG
evaluation or dirty propagation. Additional callbacks are also added to
explicitly mark the node as being dirty (via MRenderer::setGeometryDrawDirty())
for certain circumstances. Note that the draw callback in MPxDrawOverride
constructor is set to NULL in order to achieve better performance.
*/
#include <maya/MCallbackIdArray.h>
#include <maya/MDagMessage.h>
#include <maya/MDrawRegistry.h>
#include <maya/MFnDagNode.h>
#include <maya/MFnEnumAttribute.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnPlugin.h>
#include <maya/MFrameContext.h>
#include <maya/MItDag.h>
#include <maya/MPxDrawOverride.h>
#include <maya/MPxLocatorNode.h>
#include <maya/MVectorArray.h>
#include <stdio.h>
using namespace MHWRender;
class transformDrawNode : public MPxLocatorNode
{
public:
enum ETransformType
{
kTranslate,
kRotate,
kScale,
kShear
};
static void* creator() { return new transformDrawNode(); }
static MStatus initialize();
// Registration
static char* className;
static MTypeId id;
static MString drawDbClassification;
static MString drawRegistrantId;
// Attributes
static MObject aTextColor;
static MObject aTransformType;
private:
transformDrawNode();
virtual ~transformDrawNode();
static void AllDagChangesCallback(
MDagPath &child,
MDagPath &parent,
void *clientData);
static void WorldMatrixModifiedCallback(
MObject &transformNode,
MDagMessage::MatrixModifiedFlags &modified,
void *clientData);
void processDagMessage(bool refreshWorldMatrixCbIds);
MCallbackId fAllDagChangesCbId;
MCallbackIdArray fWorldMatrixModifiedCbIds;
};
// Registration
char* transformDrawNode::className = "transformDrawNode";
MTypeId transformDrawNode::id(0x80034);
MString transformDrawNode::drawDbClassification("drawdb/geometry/transformDrawNode");
MString transformDrawNode::drawRegistrantId(transformDrawNode::className);
// Attributes
MObject transformDrawNode::aTextColor;
MObject transformDrawNode::aTransformType;
MStatus transformDrawNode::initialize()
{
aTextColor = nAttr.create("textColor", "tc", MFnNumericData::k3Float);
nAttr.setDefault(1.0f, 1.0f, 1.0f);
nAttr.setUsedAsColor(true);
MPxNode::addAttribute(aTextColor);
aTransformType = eAttr.create("transformType", "tt", kTranslate);
eAttr.addField("Translate", kTranslate);
eAttr.addField("Rotate", kRotate);
eAttr.addField("Scale", kScale);
eAttr.addField("Shear", kShear);
MPxNode::addAttribute(aTransformType);
return MS::kSuccess;
}
transformDrawNode::transformDrawNode() : MPxLocatorNode()
{
AllDagChangesCallback, this);
}
transformDrawNode::~transformDrawNode()
{
if (fWorldMatrixModifiedCbIds.length() > 0)
{
MMessage::removeCallbacks(fWorldMatrixModifiedCbIds);
}
if (fAllDagChangesCbId != 0)
{
MMessage::removeCallback(fAllDagChangesCbId);
}
}
void transformDrawNode::processDagMessage(bool refreshWorldMatrixModifiedCbIds)
{
// Explicitly mark the node as being dirty on certain DAG message callbacks
// so that the draw override can be updated.
MRenderer::setGeometryDrawDirty(thisMObject());
if (refreshWorldMatrixModifiedCbIds)
{
MStatus status;
if (status)
{
// Remove existing callbacks
if (fWorldMatrixModifiedCbIds.length() > 0)
{
MMessage::removeCallbacks(fWorldMatrixModifiedCbIds);
fWorldMatrixModifiedCbIds.clear();
}
// Add new callbacks
for ( ;!dagIt.isDone(); dagIt.next() )
{
MDagPath dagPath;
status = dagIt.getPath(dagPath);
if (status)
{
dagPath, WorldMatrixModifiedCallback, this);
fWorldMatrixModifiedCbIds.append(id);
}
}
}
}
}
void transformDrawNode::AllDagChangesCallback(
MDagPath &child,
MDagPath &parent,
void *clientData)
{
// We need to refresh the world matrix modified callbacks because the DAG
// is just changed.
transformDrawNode *node = static_cast<transformDrawNode*>(clientData);
if (node) node->processDagMessage(true);
}
void transformDrawNode::WorldMatrixModifiedCallback(
MObject &transformNode,
MDagMessage::MatrixModifiedFlags &modified,
void *clientData)
{
// We don't need to refresh the world matrix modified callbacks because the
// DAG is not changed.
transformDrawNode *node = static_cast<transformDrawNode*>(clientData);
if (node) node->processDagMessage(false);
}
class transformDrawData : public MUserData
{
public:
transformDrawData()
: MUserData(false)
, fTextColor(1.0f, 1.0f, 1.0f, 1.0f)
, fTransformType(transformDrawNode::kTranslate)
{
}
virtual ~transformDrawData() {}
MColor fTextColor;
transformDrawNode::ETransformType fTransformType;
MVectorArray fPositions;
MVectorArray fVectors;
};
class transformDrawOverride : public MPxDrawOverride
{
public:
static MPxDrawOverride* creator(const MObject& obj)
{
return new transformDrawOverride(obj);
}
virtual DrawAPI supportedDrawAPIs() const { return kAllDevices; }
virtual bool hasUIDrawables() const { return true; }
const MDagPath& objPath,
const MDagPath& cameraPath,
const MFrameContext& frameContext,
MUserData* oldData);
virtual void addUIDrawables(
const MDagPath& objPath,
MUIDrawManager& drawManager,
const MFrameContext& frameContext,
const MUserData* data);
private:
// By setting isAlwaysDirty to false in MPxDrawOverride constructor, the
// draw override will be updated (via prepareForDraw()) only when the node
// is marked dirty via DG evaluation or dirty propagation. Additional
// callbacks are also added to explicitly mark the node as being dirty (via
// MRenderer::setGeometryDrawDirty()) for certain circumstances. Note that
// the draw callback in MPxDrawOverride constructor is set to NULL in order
// to achieve better performance.
transformDrawOverride(const MObject& obj) : MPxDrawOverride(obj, NULL, false) {}
virtual ~transformDrawOverride() {}
};
MUserData* transformDrawOverride::prepareForDraw(
const MDagPath& objPath,
const MDagPath& cameraPath,
const MFrameContext& frameContext,
MUserData* oldData)
{
MStatus status;
MObject obj = objPath.node(&status);
if (!status) return NULL;
transformDrawData* tdData = dynamic_cast<transformDrawData*>(oldData);
if (!tdData)
{
tdData = new transformDrawData();
}
// Text color
{
MPlug plug(obj, transformDrawNode::aTextColor);
MObject o = plug.asMObject();
MFnNumericData nData(o);
nData.getData(tdData->fTextColor.r, tdData->fTextColor.g, tdData->fTextColor.b);
}
// Transform type
{
MPlug plug(obj, transformDrawNode::aTransformType);
tdData->fTransformType = (transformDrawNode::ETransformType)plug.asInt();
}
tdData->fPositions.clear();
tdData->fVectors.clear();
if (status)
{
for ( ; !dagIt.isDone(); dagIt.next() )
{
MDagPath dagPath;
status = dagIt.getPath(dagPath);
if (!status)
{
status.perror("MItDag::getPath");
continue;
}
MObject transformNode = dagPath.transform(&status);
if (!status)
{
status.perror("MDagPath::transform");
continue;
}
MFnDagNode transform(transformNode, &status);
if (!status)
{
status.perror("MFnDagNode constructor");
continue;
}
MTransformationMatrix matrix(transform.transformationMatrix());
MVector vec = matrix.getTranslation(MSpace::kWorld);
tdData->fPositions.append(vec);
double tmp[3];
switch (tdData->fTransformType)
{
case transformDrawNode::kRotate:
matrix.getRotation(tmp, order);
vec = MVector(tmp);
break;
case transformDrawNode::kScale:
matrix.getScale(tmp, MSpace::kWorld);
vec = MVector(tmp);
break;
case transformDrawNode::kShear:
matrix.getShear(tmp, MSpace::kWorld);
vec = MVector(tmp);
break;
default:
// Don't reset vec so that translation is drawn by default.
break;
}
tdData->fVectors.append(vec);
}
}
return tdData;
}
void transformDrawOverride::addUIDrawables(
const MDagPath& objPath,
MUIDrawManager& drawManager,
const MFrameContext& frameContext,
const MUserData* data)
{
const transformDrawData* tdData = dynamic_cast<const transformDrawData*>(data);
if (!tdData) return;
drawManager.beginDrawable();
drawManager.setColor(tdData->fTextColor);
for (unsigned int i = 0; i < tdData->fVectors.length(); i++)
{
MPoint pos(tdData->fPositions[i]);
MVector vec(tdData->fVectors[i]);
char tmpStr[128] = {0};
sprintf(tmpStr, "(%.3f, %.3f, %.3f)", vec.x, vec.y, vec.z);
MString text(tmpStr);
drawManager.text(pos, text, MUIDrawManager::kCenter);
}
drawManager.endDrawable();
}
MStatus initializePlugin( MObject obj )
{
MFnPlugin plugin( obj, PLUGIN_COMPANY, "1.0", "Any" );
MStatus status = plugin.registerNode(
transformDrawNode::className,
transformDrawNode::id,
transformDrawNode::creator,
transformDrawNode::initialize,
&transformDrawNode::drawDbClassification);
if (!status)
{
status.perror("registerNode");
return status;
}
status = MDrawRegistry::registerDrawOverrideCreator(
transformDrawNode::drawDbClassification,
transformDrawNode::drawRegistrantId,
transformDrawOverride::creator);
if (!status)
{
status.perror("registerDrawOverrideCreator");
return status;
}
return status;
}
MStatus uninitializePlugin( MObject obj )
{
MFnPlugin plugin( obj );
MStatus status = MDrawRegistry::deregisterDrawOverrideCreator(
transformDrawNode::drawDbClassification,
transformDrawNode::drawRegistrantId);
if (!status)
{
status.perror("deregisterDrawOverrideCreator");
return status;
}
status = plugin.deregisterNode(transformDrawNode::id);
if (!status)
{
status.perror("deregisterNode");
return status;
}
return status;
}