Hello -
I'm writing a deformer that modifies UVs on a target mesh based on the UVs of an input source mesh. It works but I'm finding that the UVs do not update correctly in response to changes in the weights (via the Edit Membership tool or painted weights). The deformer is called and the UVs are being computed correctly but the call to setUVs seems to be ignored. The UVs update correctly when the source mesh changes so the code is, for the most part, working.
I tried some experiments using the plug.connectedTo() but that puts me outside the deformer and breaks the deformer chain. Any changes I make are overwritten by other deformers down the line.
Is there some special handling that I need to do in order to push the changes out after a change in the weights?
Is there a way that I can get the DAG path or node for this mesh data object without reaching outside of the deformer?
In a nutshell here is what I'm doing:
MPlug outGeomPlugArray(thisMObject(), outputGeom );
MPlug outGeomPlug = outGeomPlugArray.elementByLogicalIndex( multiIndex );
MDataHandle outGeomHandle = data.outputValue( outGeomPlug );
MObject targetMeshObj = outGeomHandle.asMesh();
// ... build UV arrays based on source mesh UVs and weight map
targetMeshFn.setUVs( uArray, vArray );
targetMeshFn.assignUVs( uvCounts, uvIds );
targetMeshFn.updateSurface();
Am I doing the update or retreiving the mesh object incorrectly?
Gory details of the deformer are below if anyone wants to dive in. Error handling and computation details are removed for readability.
Thanks in advance for any advice or ideas!
MStatus
uvDeformer::deform( MDataBlock& dataBlock, MItGeometry& geomIter, const MMatrix& localToWorldM, unsigned int multiIdx )
{
MFloatArray uArray( numUVs );
MFloatArray vArray( numUVs );
MFloatArray new_uArray( numUVs );
MFloatArray new_vArray( numUVs );
// --- Use MFnMesh for outputGeom instead of geomIter so we can setUVs
MPlug outGeomPlugArray(thisMObject(), outputGeom );
MPlug outGeomPlug = outGeomPlugArray.elementByLogicalIndex( multiIdx );
MDataHandle outGeomHandle = data.outputValue( outGeomPlug ); // kMeshData
// --- Get mesh data object and set up iterator
MObject targetMeshObj = outGeomHandle.asMesh();
MFnMesh targetMeshFn( targetMeshObj );
MItMeshPolygon targetPolyIt( targetMeshObj );
// --- Get the painted weights
std::map weightMap;
for( geomIter.reset(); !geomIter.isDone(); geomIter.next() )
weightMap[ geomIter.index() ] = weightValue( dataBlock, multiIdx, geomIter.index() vertIdx );
// --- get current UVs
int numUVs = targetMeshFn.numUVs();
targetMeshFn.getUVs( uArray, vArray );
// --- keep track of face UV counts and faceVert UV ids for assignUVs
int faceIdx = 0;
int faceUVId = 0;
int numPolys = targetPolyIt.count();
int numFaceVerts = targetMeshFn.numFaceVertices();
MIntArray uvCounts( numPolys );
MIntArray uvIds( numFaceVerts );
// --- chug through all the verts computing new UV
for ( targetPolyIt.reset(); !targetPolyIt.isDone(); targetPolyIt.next() )
{
if ( targetPolyIt.hasUVs() )
{
unsigned int numPolyVerts = targetPolyIt.polygonVertexCount();
uvCounts[ faceIdx ] = numPolyVerts;
for ( unsigned int polyFaceVertIdx = 0; polyFaceVertIdx < numPolyVerts; polyFaceVertIdx++ )
{
int uvIdx;
targetPolyIt.getUVIndex( polyFaceVertIdx, uvIdx );
// --- only want to do each uv once
if ( uvAlreadyComputed( uvIdx ) )
continue;
int vertIdx = targetPolyIt.vertexIndex ( polyFaceVertIdx );
float weight = weightMap[ vertIdx ];
// --- munge this UV based on source mesh input and weight
float2 newUV;
computeNewUV( newUV, weight... etc );
// --- update arrays
new_uArray[ uvIdx ] = newUV[0];
new_vArray[ uvIdx ] = newUV[1];
uvIds[ faceUVId++ ] = uvIdx;
}
}
else
{
uvCounts[ faceIdx ] = 0;
}
faceIdx++;
}
// --- Now push everything out to mesh
//
// This works when source input changes but is ignored if membership or painted weight changes
//
targetMeshFn.setUVs( new_uArray, new_vArray );
targetMeshFn.assignUVs( uvCounts, uvIds );
targetMeshFn.updateSurface();
outGeomHandle.setClean();
}