#include "polyModifierCmd.h"
#include <maya/MGlobal.h>
#include <maya/MFloatVector.h>
#include <maya/MObjectArray.h>
#include <maya/MPlugArray.h>
#include <maya/MIOStream.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnDagNode.h>
#include <maya/MFnNumericData.h>
#ifdef _DEBUG
# define MCheckStatus(status,message) \
if( MS::kSuccess != status ) { \
MString error("Status failed: "); \
error += message; \
MGlobal::displayError(error); \
return status; \
}
#else
# define MCheckStatus(status,message)
#endif
#ifdef _DEBUG
# define MAssert(state,message) \
if( !state ) { \
MString error("Assertion failed: "); \
error += message; \
MGlobal::displayError(error); \
return; \
}
#else
# define MAssert(state,message)
#endif
#ifdef _DEBUG
# define MStatusAssert(state,message) \
if( !state ) { \
MString error("Assertion failed: "); \
error += message; \
MGlobal::displayError(error); \
return MS::kFailure; \
}
#else
# define MStatusAssert(state,message)
#endif
polyModifierCmd::polyModifierCmd()
{
fDagPathInitialized = false;
fModifierNodeTypeInitialized = false;
fModifierNodeNameInitialized = false;
}
polyModifierCmd::~polyModifierCmd()
{}
MStatus polyModifierCmd::initModifierNode( MObject )
{
return MS::kSuccess;
}
MStatus polyModifierCmd::directModifier( MObject )
{
return MS::kSuccess;
}
MStatus polyModifierCmd::doModifyPoly()
{
MStatus status = MS::kFailure;
if( isCommandDataValid() )
{
collectNodeState();
if( !fHasHistory && !fHasRecordHistory )
{
MObject meshNode = fDagPath.node();
cacheMeshData();
cacheMeshTweaks();
status = directModifier( meshNode );
}
else
{
MObject modifierNode;
createModifierNode( modifierNode );
initModifierNode( modifierNode );
status = connectNodes( modifierNode );
}
}
return status;
}
MStatus polyModifierCmd::redoModifyPoly()
{
MStatus status = MS::kSuccess;
if( !fHasHistory && !fHasRecordHistory )
{
MObject meshNode = fDagPath.node();
status = directModifier( meshNode );
}
else
{
if( !fHasHistory )
{
fDagModifier.doIt();
}
status = fDGModifier.doIt();
}
return status;
}
MStatus polyModifierCmd::undoModifyPoly()
{
MStatus status = MS::kSuccess;
if( !fHasHistory && !fHasRecordHistory )
{
status = undoDirectModifier();
}
else
{
fDGModifier.undoIt();
if( !fHasHistory )
{
status = undoCachedMesh();
MCheckStatus( status, "undoCachedMesh" );
fDagModifier.undoIt();
}
status = undoTweakProcessing();
MCheckStatus( status, "undoTweakProcessing" );
}
return status;
}
bool polyModifierCmd::isCommandDataValid()
{
bool valid = true;
if( fDagPathInitialized )
{
fDagPath.extendToShape();
if( !fDagPath.isValid() || fDagPath.apiType() != MFn::kMesh )
{
valid = false;
}
}
else
{
valid = false;
}
if( !fModifierNodeTypeInitialized && !fModifierNodeNameInitialized )
{
valid = false;
}
return valid;
}
void polyModifierCmd::collectNodeState()
{
MStatus status;
fDagPath.extendToShape();
MObject meshNodeShape = fDagPath.node();
MFnDependencyNode depNodeFn;
depNodeFn.setObject( meshNodeShape );
MPlug inMeshPlug = depNodeFn.findPlug( "inMesh" );
fHasHistory = inMeshPlug.isConnected();
fHasTweaks = false;
MPlug tweakPlug = depNodeFn.findPlug( "pnts" );
if( !tweakPlug.isNull() )
{
MAssert( (tweakPlug.isArray()),
"tweakPlug.isArray() -- tweakPlug is not an array plug" );
MPlug tweak;
MFloatVector tweakData;
int i;
int numElements = tweakPlug.numElements();
for( i = 0; i < numElements; i++ )
{
tweak = tweakPlug.elementByPhysicalIndex( i, &status );
if( status == MS::kSuccess && !tweak.isNull() )
{
getFloat3PlugValue( tweak, tweakData );
if( 0 != tweakData.x ||
0 != tweakData.y ||
0 != tweakData.z )
{
fHasTweaks = true;
break;
}
}
}
}
int result;
MGlobal::executeCommand( "constructionHistory -q -tgl", result );
fHasRecordHistory = (0 != result);
}
MStatus polyModifierCmd::createModifierNode( MObject& modifierNode )
{
MStatus status = MS::kFailure;
if( fModifierNodeTypeInitialized || fModifierNodeNameInitialized )
{
if( fModifierNodeTypeInitialized )
{
modifierNode = fDGModifier.createNode( fModifierNodeType, &status );
}
else if( fModifierNodeNameInitialized )
{
modifierNode = fDGModifier.createNode( fModifierNodeName, &status );
}
MFnDependencyNode depNodeFn( modifierNode );
MObject inMeshAttr;
MObject outMeshAttr;
inMeshAttr = depNodeFn.attribute( "inMesh" );
outMeshAttr = depNodeFn.attribute( "outMesh" );
if( inMeshAttr.isNull() || outMeshAttr.isNull() )
{
displayError( "Invalid Modifier Node: inMesh and outMesh attributes are required." );
status = MS::kFailure;
}
}
return status;
}
MStatus polyModifierCmd::processMeshNode( modifyPolyData& data )
{
MStatus status = MS::kSuccess;
MFnDagNode dagNodeFn;
data.meshNodeShape = fDagPath.node();
dagNodeFn.setObject( data.meshNodeShape );
MStatusAssert( (0 < dagNodeFn.parentCount()),
"0 < dagNodeFn.parentCount() -- meshNodeshape has no parent transform" );
data.meshNodeTransform = dagNodeFn.parent(0);
data.meshNodeDestPlug = dagNodeFn.findPlug( "inMesh" );
data.meshNodeDestAttr = data.meshNodeDestPlug.attribute();
return status;
}
MStatus polyModifierCmd::processUpstreamNode( modifyPolyData& data )
{
MStatus status = MS::kSuccess;
MFnDependencyNode depNodeFn;
MFnDagNode dagNodeFn;
MPlugArray tempPlugArray;
if( fHasHistory )
{
data.meshNodeDestPlug.connectedTo( tempPlugArray, true, false);
MStatusAssert( (tempPlugArray.length() == 1),
"tempPlugArray.length() == 1 -- 0 or >1 connections on meshNodeShape.inMesh" );
data.upstreamNodeSrcPlug = tempPlugArray[0];
data.upstreamNodeShape = data.upstreamNodeSrcPlug.node();
depNodeFn.setObject( data.upstreamNodeShape );
data.upstreamNodeSrcAttr = data.upstreamNodeSrcPlug.attribute();
fDGModifier.disconnect( data.upstreamNodeSrcPlug,
data.meshNodeDestPlug );
}
else
{
dagNodeFn.setObject( data.meshNodeShape );
data.upstreamNodeTransform = dagNodeFn.duplicate( false, false );
dagNodeFn.setObject( data.upstreamNodeTransform );
MStatusAssert( (0 < dagNodeFn.childCount()),
"0 < dagNodeFn.childCount() -- Duplicate meshNode transform has no shape." );
data.upstreamNodeShape = dagNodeFn.child(0);
status = fDagModifier.reparentNode( data.upstreamNodeShape, data.meshNodeTransform );
MCheckStatus( status, "reparentNode" );
status = fDagModifier.doIt();
MCheckStatus( status, "fDagModifier.doIt()" );
dagNodeFn.setObject( data.upstreamNodeShape );
dagNodeFn.setIntermediateObject( true );
data.upstreamNodeSrcAttr = dagNodeFn.attribute( "outMesh" );
data.upstreamNodeSrcPlug = dagNodeFn.findPlug( "outMesh", &status );
status = fDagModifier.deleteNode( data.upstreamNodeTransform );
MCheckStatus( status, "deleteNode" );
status = fDagModifier.doIt();
MCheckStatus( status, "fDagModifier.doIt()" );
dagNodeFn.getPath( fDuplicateDagPath );
}
return status;
}
MStatus polyModifierCmd::processModifierNode( MObject modifierNode,
modifyPolyData& data )
{
MStatus status = MS::kSuccess;
MFnDependencyNode depNodeFn ( modifierNode );
data.modifierNodeSrcAttr = depNodeFn.attribute( "outMesh" );
data.modifierNodeDestAttr = depNodeFn.attribute( "inMesh" );
return status;
}
MStatus polyModifierCmd::processTweaks( modifyPolyData& data )
{
MStatus status = MS::kSuccess;
fTweakIndexArray.clear();
fTweakVectorArray.clear();
if( fHasTweaks )
{
MFnDependencyNode depNodeFn;
MPlug meshTweakPlug;
MPlug upstreamTweakPlug;
MObject tweakNodeTweakAttr;
MPlug tweak;
MPlug tweakChild;
MObject tweakData;
MObjectArray tweakDataArray;
MFloatVector tweakVector;
MIntArray tweakSrcConnectionCountArray;
MPlugArray tweakSrcConnectionPlugArray;
MIntArray tweakDstConnectionCountArray;
MPlugArray tweakDstConnectionPlugArray;
MPlugArray tempPlugArray;
unsigned i;
unsigned j;
unsigned k;
data.tweakNode = fDGModifier.createNode( "polyTweak" );
depNodeFn.setObject( data.tweakNode );
data.tweakNodeSrcAttr = depNodeFn.attribute( "output" );
data.tweakNodeDestAttr = depNodeFn.attribute( "inputPolymesh" );
tweakNodeTweakAttr = depNodeFn.attribute( "tweak" );
depNodeFn.setObject( data.meshNodeShape );
meshTweakPlug = depNodeFn.findPlug( "pnts" );
MStatusAssert( (meshTweakPlug.isArray()),
"meshTweakPlug.isArray() -- meshTweakPlug is not an array plug" );
unsigned numElements = meshTweakPlug.numElements();
for( i = 0; i < numElements; i++ )
{
tweak = meshTweakPlug.elementByPhysicalIndex(i);
if( !tweak.isNull() )
{
unsigned logicalIndex = tweak.logicalIndex();
tweak.getValue( tweakData );
tweakDataArray.append( tweakData );
getFloat3PlugValue( tweak, tweakVector );
fTweakIndexArray.append( logicalIndex );
fTweakVectorArray.append( tweakVector );
MStatusAssert( (tweak.isCompound()),
"tweak.isCompound() -- Element tweak plug is not compound" );
unsigned numChildren = tweak.numChildren();
for( j = 0; j < numChildren; j++ )
{
tweakChild = tweak.child(j);
if( tweakChild.isConnected() )
{
tempPlugArray.clear();
if( tweakChild.connectedTo( tempPlugArray, false, true ) )
{
unsigned numSrcConnections = tempPlugArray.length();
tweakSrcConnectionCountArray.append( numSrcConnections );
for( k = 0; k < numSrcConnections; k++ )
{
tweakSrcConnectionPlugArray.append( tempPlugArray[k] );
fDGModifier.disconnect( tweakChild, tempPlugArray[k] );
}
}
else
{
tweakSrcConnectionCountArray.append(0);
}
tempPlugArray.clear();
if( tweakChild.connectedTo( tempPlugArray, true, false ) )
{
MStatusAssert( (tempPlugArray.length() == 1),
"tempPlugArray.length() == 1 -- 0 or >1 connections on tweakChild" );
tweakDstConnectionCountArray.append(1);
tweakDstConnectionPlugArray.append( tempPlugArray[0] );
fDGModifier.disconnect( tempPlugArray[0], tweakChild );
}
else
{
tweakDstConnectionCountArray.append(0);
}
}
else
{
tweakSrcConnectionCountArray.append(0);
tweakDstConnectionCountArray.append(0);
}
}
}
}
MPlug polyTweakPlug( data.tweakNode, tweakNodeTweakAttr );
unsigned numTweaks = fTweakIndexArray.length();
int srcOffset = 0;
int dstOffset = 0;
for( i = 0; i < numTweaks; i++ )
{
tweak = polyTweakPlug.elementByLogicalIndex( fTweakIndexArray[i] );
tweak.setValue( tweakDataArray[i] );
MStatusAssert( (tweak.isCompound()),
"tweak.isCompound() -- Element plug, 'tweak', is not compound" );
unsigned numChildren = tweak.numChildren();
for( j = 0; j < numChildren; j++ )
{
tweakChild = tweak.child(j);
if( 0 < tweakSrcConnectionCountArray[i*numChildren + j] )
{
for( k = 0;
k < (unsigned) tweakSrcConnectionCountArray[i*numChildren + j];
k++ )
{
fDGModifier.connect( tweakChild,
tweakSrcConnectionPlugArray[srcOffset] );
srcOffset++;
}
}
if( 0 < tweakDstConnectionCountArray[i*numChildren + j] )
{
fDGModifier.connect( tweakDstConnectionPlugArray[dstOffset],
tweakChild );
dstOffset++;
}
}
}
}
return status;
}
MStatus polyModifierCmd::connectNodes( MObject modifierNode )
{
MStatus status;
modifyPolyData data;
status = processMeshNode( data );
MCheckStatus( status, "processMeshNode" );
status = processUpstreamNode( data );
MCheckStatus( status, "processUpstreamNode" );
status = processModifierNode( modifierNode,
data );
MCheckStatus( status, "processModifierNode" );
status = processTweaks( data );
MCheckStatus( status, "processTweaks" );
if( fHasTweaks )
{
MPlug tweakDestPlug( data.tweakNode, data.tweakNodeDestAttr );
status = fDGModifier.connect( data.upstreamNodeSrcPlug, tweakDestPlug );
MCheckStatus( status, "upstream-tweak connect failed" );
MPlug tweakSrcPlug( data.tweakNode, data.tweakNodeSrcAttr );
MPlug modifierDestPlug( modifierNode, data.modifierNodeDestAttr );
status = fDGModifier.connect( tweakSrcPlug, modifierDestPlug );
MCheckStatus( status, "tweak-modifier connect failed" );
}
else
{
MPlug modifierDestPlug( modifierNode, data.modifierNodeDestAttr );
status = fDGModifier.connect( data.upstreamNodeSrcPlug, modifierDestPlug );
MCheckStatus( status, "upstream-modifier connect failed" );
}
MPlug modifierSrcPlug( modifierNode, data.modifierNodeSrcAttr );
MPlug meshDestAttr( data.meshNodeShape, data.meshNodeDestAttr );
status = fDGModifier.connect( modifierSrcPlug, meshDestAttr );
MCheckStatus( status, "modifier-mesh connect failed" );
status = fDGModifier.doIt();
return status;
}
MStatus polyModifierCmd::cacheMeshData()
{
MStatus status = MS::kSuccess;
MFnDependencyNode depNodeFn;
MFnDagNode dagNodeFn;
MObject meshNode = fDagPath.node();
MObject dupMeshNode;
MPlug dupMeshNodeOutMeshPlug;
dagNodeFn.setObject( meshNode );
dupMeshNode = dagNodeFn.duplicate();
MDagPath dupMeshDagPath;
MDagPath::getAPathTo( dupMeshNode, dupMeshDagPath );
dupMeshDagPath.extendToShape();
depNodeFn.setObject( dupMeshDagPath.node() );
dupMeshNodeOutMeshPlug = depNodeFn.findPlug( "outMesh", &status );
MCheckStatus( status, "Could not retrieve outMesh" );
status = dupMeshNodeOutMeshPlug.getValue( fMeshData );
MCheckStatus( status, "Could not retrieve meshData" );
MGlobal::deleteNode( dupMeshNode );
return status;
}
MStatus polyModifierCmd::cacheMeshTweaks()
{
MStatus status = MS::kSuccess;
fTweakIndexArray.clear();
fTweakVectorArray.clear();
if( fHasTweaks )
{
MFnDependencyNode depNodeFn;
MObject meshNode = fDagPath.node();
MPlug meshTweakPlug;
MPlug tweak;
MPlug tweakChild;
MObject tweakData;
MObjectArray tweakDataArray;
MFloatVector tweakVector;
MPlugArray tempPlugArray;
unsigned i;
depNodeFn.setObject( meshNode );
meshTweakPlug = depNodeFn.findPlug( "pnts" );
MStatusAssert( (meshTweakPlug.isArray()),
"meshTweakPlug.isArray() -- meshTweakPlug is not an array plug" );
unsigned numElements = meshTweakPlug.numElements();
for( i = 0; i < numElements; i++ )
{
tweak = meshTweakPlug.elementByPhysicalIndex(i);
if( !tweak.isNull() )
{
unsigned logicalIndex = tweak.logicalIndex();
getFloat3PlugValue( tweak, tweakVector );
fTweakIndexArray.append( logicalIndex );
fTweakVectorArray.append( tweakVector );
}
}
}
return status;
}
MStatus polyModifierCmd::undoCachedMesh()
{
MStatus status;
MStatusAssert( (fHasRecordHistory), "fHasRecordHistory == true" );
if( !fHasHistory )
{
MFnDependencyNode depNodeFn;
MString meshNodeName;
MObject meshNodeShape;
MPlug meshNodeDestPlug;
MPlug meshNodeOutMeshPlug;
MObject dupMeshNodeShape;
MPlug dupMeshNodeSrcPlug;
meshNodeShape = fDagPath.node();
dupMeshNodeShape = fDuplicateDagPath.node();
depNodeFn.setObject( meshNodeShape );
meshNodeName = depNodeFn.name();
meshNodeDestPlug = depNodeFn.findPlug( "inMesh", &status );
MCheckStatus( status, "Could not retrieve inMesh" );
meshNodeOutMeshPlug = depNodeFn.findPlug( "outMesh", &status );
MCheckStatus( status, "Could not retrieve outMesh" );
depNodeFn.setObject( dupMeshNodeShape );
dupMeshNodeSrcPlug = depNodeFn.findPlug( "outMesh", &status );
MCheckStatus( status, "Could not retrieve outMesh" );
if( fHasTweaks )
{
MDGModifier dgModifier;
dgModifier.connect( dupMeshNodeSrcPlug, meshNodeDestPlug );
status = dgModifier.doIt();
MCheckStatus( status, "Could not connect dupMeshNode -> meshNode" );
MString cmd( "dgeval -src " );
cmd += meshNodeName;
cmd += ".inMesh";
status = MGlobal::executeCommand( cmd, false, false );
MCheckStatus( status, "Could not force DG eval" );
dgModifier.undoIt();
}
else
{
MObject meshData;
status = dupMeshNodeSrcPlug.getValue( meshData );
MCheckStatus( status, "Could not retrieve meshData" );
status = meshNodeOutMeshPlug.setValue( meshData );
MCheckStatus( status, "Could not set outMesh" );
}
}
return status;
}
MStatus polyModifierCmd::undoTweakProcessing()
{
MStatus status = MS::kSuccess;
if( fHasTweaks )
{
MFnDependencyNode depNodeFn;
MObject meshNodeShape;
MPlug meshTweakPlug;
MPlug tweak;
MObject tweakData;
meshNodeShape = fDagPath.node();
depNodeFn.setObject( meshNodeShape );
meshTweakPlug = depNodeFn.findPlug( "pnts" );
MStatusAssert( (meshTweakPlug.isArray()),
"meshTweakPlug.isArray() -- meshTweakPlug is not an array plug" );
unsigned i;
unsigned numElements = fTweakIndexArray.length();
for( i = 0; i < numElements; i++ )
{
tweak = meshTweakPlug.elementByLogicalIndex( fTweakIndexArray[i] );
getFloat3asMObject( fTweakVectorArray[i], tweakData );
tweak.setValue( tweakData );
}
}
return status;
}
MStatus polyModifierCmd::undoDirectModifier()
{
MStatus status;
MFnDependencyNode depNodeFn;
MFnDagNode dagNodeFn;
MObject meshNode = fDagPath.node();
depNodeFn.setObject( meshNode );
if( fHasTweaks )
{
depNodeFn.setObject( meshNode );
MPlug meshNodeInMeshPlug = depNodeFn.findPlug( "inMesh", &status );
MCheckStatus( status, "Could not retrieve inMesh" );
MString meshNodeName = depNodeFn.name();
dagNodeFn.setObject( meshNode );
MObject dupMeshNode = dagNodeFn.duplicate();
MDagPath dupMeshDagPath;
MDagPath::getAPathTo( dupMeshNode, dupMeshDagPath );
dupMeshDagPath.extendToShape();
depNodeFn.setObject( dupMeshDagPath.node() );
MPlug dupMeshNodeOutMeshPlug = depNodeFn.findPlug( "outMesh", &status );
MCheckStatus( status, "Could not retrieve outMesh" );
status = dupMeshNodeOutMeshPlug.setValue( fMeshData );
MDGModifier dgModifier;
dgModifier.connect( dupMeshNodeOutMeshPlug, meshNodeInMeshPlug );
status = dgModifier.doIt();
MCheckStatus( status, "Could not connect dupMeshNode -> meshNode" );
MString cmd("dgeval -src ");
cmd += meshNodeName;
cmd += ".inMesh";
status = MGlobal::executeCommand( cmd, false, false );
MCheckStatus( status, "Could not force DG eval" );
dgModifier.undoIt();
MGlobal::deleteNode( dupMeshNode );
status = undoTweakProcessing();
}
else
{
depNodeFn.setObject( meshNode );
MPlug meshNodeOutMeshPlug = depNodeFn.findPlug( "outMesh", &status );
MCheckStatus( status, "Could not retrieve outMesh" );
status = meshNodeOutMeshPlug.setValue( fMeshData );
MCheckStatus( status, "Could not set meshData" );
}
return status;
}
MStatus polyModifierCmd::getFloat3PlugValue( MPlug plug, MFloatVector & value )
{
MObject object;
plug.getValue( object );
MFnNumericData numDataFn( object );
numDataFn.getData( value[0], value[1], value[2] );
return MS::kSuccess;
}
MStatus polyModifierCmd::getFloat3asMObject( MFloatVector value, MObject& object )
{
MFnNumericData numDataFn;
numDataFn.create( MFnNumericData::k3Float );
numDataFn.setData( value[0], value[1], value[2] );
object = numDataFn.object();
return MS::kSuccess;
}