meshReorder/meshRemapTool.cpp

meshReorder/meshRemapTool.cpp
//
// Description: Get user selections for mesh vertex/edge remapping
//
// Author: Bruce Hickey
//
// This is added to prevent multiple definitions of the MApiVersion string.
#define _MApiVersion
#include <maya/MCursor.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnMesh.h>
#include <maya/MGlobal.h>
#include <maya/MIOStream.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MItMeshVertex.h>
#include <maya/MMatrix.h>
#include <maya/MPlug.h>
#include <maya/MPointArray.h>
#include <maya/MVector.h>
#include "meshRemapTool.h"
#include "meshMapUtils.h"
/*
Description:
This context allows you to remap vertex/edge lists from one mesh to another.
To create the meshRemapContext context and set it as the active tool,
run the following MEL commands:
meshRemapContext;
setToolTo meshRemapContext1;
Once the context has been created and activated, follow the help line prompts. You will
be directed to select 3 vertices on the source mesh, and the corresponding 3 vertices on
the target mesh. Once all 6 vertices have been selected, the meshRemap command will be invoked
to remap the target mesh's vertices.
*/
// The user Context
const double epsilon = 0.00001;
meshRemapTool::meshRemapTool()
{
setTitleString ( "Mesh Remap Tool" );
setCursor( MCursor::editCursor );
reset();
}
meshRemapTool::~meshRemapTool() {}
void* meshRemapTool::creator()
{
return new meshRemapTool;
}
void meshRemapTool::toolOnSetup ( MEvent & )
{
reset();
}
void meshRemapTool::reset()
{
fNumSelectedPoints = 0;
fSelectedPathSrc.clear();
fSelectedComponentSrc.clear();
fSelectVtxSrc.clear();
fSelectedFaceSrc = -1;
fSelectedPathDst.clear();
fSelectedComponentDst.clear();
fSelectVtxDst.clear();
fSelectedFaceDst = -1;
fCurrentHelpString = "Select 1st point on source mesh.";
helpStateHasChanged();
}
//
// Selects objects within the user defined area, then process them
//
MStatus meshRemapTool::doRelease( MEvent & event )
{
char buf[1024];
// Perform the base actions
// Get the list of selected items
//
// Get the selection's details, we must have exactly one component selected
//
MObject component;
MDagPath path;
MItSelectionList selectionIt (fSelectionList, MFn::kComponent);
MStringArray selections;
selectionIt.getStrings( selections );
// There are valid cases where only the meshes are selected.
if( selections.length() == 0 )
{
// Handling the case where the user's workflow is
// 1) select src mesh, select vtx1, vtx2, vtx3 (i.e. fNumSelectedPoints == 0)
// 2) select dst mesh, select vtx1, vtx2, vtx3 (i.e. fNumSelectedPoints == 3)
if( fNumSelectedPoints == 0 || fNumSelectedPoints == 3 )
{
MItSelectionList selectionDag (fSelectionList, MFn::kDagNode);
if (!selectionDag.isDone() && selectionDag.getDagPath(path) == MS::kSuccess)
{
path.extendToShape();
// return true there is exactly one mesh selected
if (path.hasFn(MFn::kMesh))
{
selectionDag.next();
if (selectionDag.isDone())
{
// If this is the destination mesh, make sure that
// it doesn't have history.
if (fNumSelectedPoints == 3)
{
return checkForHistory(path);
}
return MS::kSuccess;
}
}
}
}
// Handling the case where the user is doing the auto remap, i.e.
// select src mesh, and then dst mesh when fNumSelectedPoints == 0.
if( fNumSelectedPoints == 0 )
{
MItSelectionList selectionDag (fSelectionList, MFn::kDagNode);
if (!selectionDag.isDone() && selectionDag.getDagPath(path) == MS::kSuccess)
{
path.extendToShape();
// Confirm first selection is a mesh
if (path.hasFn(MFn::kMesh))
{
selectionDag.next();
if (!selectionDag.isDone() &&
selectionDag.getDagPath(path) == MS::kSuccess)
{
path.extendToShape();
// Confirm second selection is a mesh
if (path.hasFn(MFn::kMesh))
{
selectionDag.next();
// Confirm there are exactly 2 selections
if (selectionDag.isDone())
{
// Make sure that the destination mesh
// doesn't have history.
return checkForHistory(path);
}
}
}
}
}
}
}
if( selections.length() != 1 )
{
MGlobal::displayError( "Must select exactly one vertex" );
return MS::kSuccess;
}
if (selectionIt.isDone ())
{
MGlobal::displayError( "Selected item not a vertex" );
return MS::kSuccess;
}
if( selectionIt.getDagPath (path, component) != MStatus::kSuccess )
{
MGlobal::displayError( "Must select a mesh or its vertex" );
return MS::kSuccess;
}
if (!path.node().hasFn(MFn::kMesh) && !(path.node().hasFn(MFn::kTransform) && path.hasFn(MFn::kMesh)))
{
MGlobal::displayError( "Must select a mesh or its transform" );
return MS::kSuccess;
}
// If this is the first vertex of the destination mesh, make sure that
// it doesn't have history.
if ((fNumSelectedPoints == 3) && (checkForHistory(path) != MS::kSuccess))
{
return MS::kSuccess;
}
MItMeshVertex fIt ( path, component, &stat );
if( stat != MStatus::kSuccess )
{
MGlobal::displayError( "MItMeshVertex failed");
}
if (fIt.count() != 1 )
{
sprintf(buf, "Invalid selection '%s'. Vertices must be picked one at a time.", selections[0].asChar() );
return MS::kSuccess;
}
else
{
sprintf(buf, "Accepting vertex '%s'", selections[0].asChar() );
}
//
// Now that we know it's valid, process the selection, the first 3 items are the source, the second
// 3 define the target
//
if( fNumSelectedPoints < 3 )
{
fSelectedPathSrc.append( path );
fSelectedComponentSrc.append( component );
}
else
{
fSelectedPathDst.append( path );
fSelectedComponentDst.append( component );
}
//
// When each of the source/target are defined, process them. An error/invalid selection will restart the selection for
// that particular mesh.
//
if( fNumSelectedPoints == 2 )
{
if( ( stat = meshMapUtils::validateFaceSelection( fSelectedPathSrc, fSelectedComponentSrc, &fSelectedFaceSrc, &fSelectVtxSrc ) ) != MStatus::kSuccess )
{
MGlobal::displayError("Selected vertices don't define a unique face on source mesh");
reset();
return stat;
}
}
//
// Once the target is defined, invoke the command
//
if( fNumSelectedPoints == 5 )
{
if( ( stat = meshMapUtils::validateFaceSelection( fSelectedPathDst, fSelectedComponentDst, &fSelectedFaceDst, &fSelectVtxDst ) ) != MStatus::kSuccess )
{
MGlobal::displayError("Selected vertices don't define a unique face on destination mesh");
reset();
return stat;
}
executeCmd();
}
else
{
//
// We don't have all the details yet, just move to the next item
//
fNumSelectedPoints++;
}
helpStateHasChanged();
return stat;
}
MStatus meshRemapTool::checkForHistory(const MDagPath& mesh)
{
MFnMesh meshFn(mesh);
MPlug historyPlug = meshFn.findPlug("inMesh", true);
if (historyPlug.isDestination())
{
MGlobal::displayError("Destination mesh has history. Its geometry cannot be modified.");
}
return MS::kSuccess;
}
MStatus meshRemapTool::resolveMapping()
{
// Get the list of selected items
if( fSelectionList.length() != 2)
{
reset();
helpStateHasChanged();
}
MDagPath dagPath;
MObject component;
if( fSelectionList.getDagPath(0, dagPath, component) != MStatus::kSuccess)
{
MGlobal::displayError(" Invalid source mesh");
}
dagPath.extendToShape();
// Repeat this 3 times one for each vertex
fSelectedPathSrc.append(dagPath);
fSelectedPathSrc.append(dagPath);
fSelectedPathSrc.append(dagPath);
if( fSelectionList.getDagPath(1, dagPath, component) != MStatus::kSuccess)
{
MGlobal::displayError(" Invalid destination mesh");
}
dagPath.extendToShape();
// Repeat this 3 times one for each vertex
fSelectedPathDst.append(dagPath);
fSelectedPathDst.append(dagPath);
fSelectedPathDst.append(dagPath);
// Find the vertices in Object Space of the first polygon
MPointArray srcPts;
MIntArray srcVertIds, dstVertIds;
MItMeshPolygon faceIterSrc(fSelectedPathSrc[0]);
unsigned srcFaceId = faceIterSrc.index();
faceIterSrc.getPoints(srcPts, MSpace::kObject);
faceIterSrc.getVertices(srcVertIds);
// Tranfrom the vertices to dst World Space
unsigned i, j;
MMatrix m = fSelectedPathDst[0].inclusiveMatrix();
for(i = 0; i < srcPts.length(); i++)
{
srcPts[i] = srcPts[i] * m;
}
MItMeshPolygon faceIterDst(fSelectedPathDst[0]);
while (!faceIterDst.isDone())
{
MPointArray dstPts;
faceIterDst.getPoints(dstPts, MSpace::kWorld);
if (arePointsOverlap(srcPts, dstPts))
{
// Set face and vertex indices
fSelectedFaceSrc = srcFaceId;
fSelectedFaceDst = faceIterDst.index();
fSelectVtxSrc.append(srcVertIds[0]);
fSelectVtxSrc.append(srcVertIds[1]);
fSelectVtxSrc.append(srcVertIds[2]);
faceIterDst.getVertices(dstVertIds);
for (j = 0; j < dstPts.length(); j++)
{
MVector v = dstPts[j] - srcPts[0];
if (v * v < epsilon)
{
fSelectVtxDst.append(dstVertIds[j]);
break;
}
}
for (j = 0; j < dstPts.length(); j++)
{
MVector v = dstPts[j] - srcPts[1];
if (v * v < epsilon)
{
fSelectVtxDst.append(dstVertIds[j]);
break;
}
}
for (j = 0; j < dstPts.length(); j++)
{
MVector v = dstPts[j] - srcPts[2];
if (v * v < epsilon)
{
fSelectVtxDst.append(dstVertIds[j]);
break;
}
}
}
faceIterDst.next();
}
}
bool meshRemapTool::arePointsOverlap(const MPointArray& srcPts, const MPointArray& dstPts) const
{
unsigned i, j;
for(i = 0; i < 3; i++)
{
bool overlap = false;
const MPoint &pt = srcPts[i];
for(j = 0; j < dstPts.length(); j++)
{
MVector v = dstPts[j] - pt;
if (v * v < epsilon)
{
overlap = true;
break;
}
}
if (!overlap) return false;
}
return true;
}
void meshRemapTool::completeAction()
{
if( resolveMapping() != MStatus::kSuccess )
{
reset();
return;
}
executeCmd();
}
void meshRemapTool::executeCmd()
{
char cmdString[200];
sprintf(cmdString, "meshRemap %s.vtx[%d] %s.vtx[%d] %s.vtx[%d] %s.vtx[%d] %s.vtx[%d] %s.vtx[%d]",
fSelectedPathSrc[0].partialPathName().asChar(), fSelectVtxSrc[0],
fSelectedPathSrc[1].partialPathName().asChar(), fSelectVtxSrc[1],
fSelectedPathSrc[2].partialPathName().asChar(), fSelectVtxSrc[2],
fSelectedPathDst[0].partialPathName().asChar(), fSelectVtxDst[0],
fSelectedPathDst[1].partialPathName().asChar(), fSelectVtxDst[1],
fSelectedPathDst[2].partialPathName().asChar(), fSelectVtxDst[2]);
MStatus st = MGlobal::executeCommand(cmdString, true, true);
if (st)
{
MGlobal::displayInfo( "Mesh remapping complete" );
}
reset();
}
void meshRemapTool::helpStateHasChanged()
//
// Description:
// Set up the correct information in the help window based
// on the current state.
//
{
switch (fNumSelectedPoints)
{
case 0:
fCurrentHelpString = "For auto remap select source mesh and then destination mesh and Press Enter. For manual remap select 1st vertex on source mesh.";
break;
case 1:
fCurrentHelpString = "Select 2nd vertex on source mesh.";
break;
case 2:
fCurrentHelpString = "Select 3rd vertex on source mesh.";
break;
case 3:
fCurrentHelpString = "Select 1st vertex on target mesh.";
break;
case 4:
fCurrentHelpString = "Select 2nd vertex on target mesh.";
break;
case 5:
fCurrentHelpString = "Select 3rd vertex on target mesh.";
break;
default:
// nothing, shouldn't be here
break;
}
setHelpString( fCurrentHelpString );
}
MPxContext* meshRemapContextCmd::makeObj()
{
return new meshRemapTool;
}
void* meshRemapContextCmd::creator()
{
return new meshRemapContextCmd;
}
//-
// ==========================================================================
// 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.
// ==========================================================================
//+