#include <maya/MIOStream.h>
#include <maya/MFnPlugin.h>
#include <maya/MString.h>
#include <maya/MFloatArray.h>
#include <maya/MArgList.h>
#include <maya/MPxCommand.h>
#include <maya/MSyntax.h>
#include <maya/MArgDatabase.h>
#include <maya/MGlobal.h>
#include <maya/MDagPath.h>
#include <maya/MItSelectionList.h>
#include <maya/MSelectionList.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnSet.h>
#include <maya/MPlugArray.h>
#include <maya/MPlug.h>
#include <math.h>
#define kExitFlag "-ea"
#define kExitFlagLong "-exitAfterNthPairs"
class peltOverlap : public MPxCommand
{
public:
peltOverlap();
virtual ~peltOverlap();
MStatus doIt( const MArgList& args );
static MSyntax newSyntax();
static void* creator();
private:
MStatus parseArgs( const MArgList &args );
void createBoundingCircle(const MStringArray &flattenFaces, MFloatArray ¢er, MFloatArray &radius);
bool createRayGivenFace(const MString &face, MFloatArray &orig, MFloatArray &vec);
float area(const MFloatArray &orig);
unsigned int checkCrossingEdges(MFloatArray &face1Orig,
MFloatArray &face1Vec,
MFloatArray &face2Orig,
MFloatArray &face2Vec);
void numOverlapUVFaces(const MString &shadingGroup, MStringArray &flattenFaces);
unsigned int fNthPairs;
MStringArray fShadingGroups;
};
peltOverlap::peltOverlap()
: fNthPairs (1)
{}
peltOverlap::~peltOverlap() {}
void* peltOverlap::creator()
{
return new peltOverlap;
}
MSyntax peltOverlap::newSyntax()
{
MSyntax syntax;
syntax.addFlag(kExitFlag, kExitFlagLong, MSyntax::kUnsigned);
syntax.setObjectType(MSyntax::kStringObjects);
return syntax;
}
MStatus peltOverlap::parseArgs( const MArgList& args )
{
MStatus status = MS::kSuccess;
MArgDatabase argData(syntax(), args);
if (argData.isFlagSet(kExitFlag)) {
status = argData.getFlagArgument(kExitFlag, 0, fNthPairs);
if (status != MS::kSuccess) {
MGlobal::displayError("-ea/exitAfterNthPairs is missing an int argument");
return status;
}
}
status = argData.getObjects(fShadingGroups);
if (status != MS::kSuccess || fShadingGroups.length() < 1) {
MGlobal::displayError("Missing shading group(s) input");
status = MS::kFailure;
}
return status;
}
MStatus peltOverlap::doIt( const MArgList& args )
{
MStatus status = parseArgs ( args );
if ( !status ) return status;
for (unsigned int i = 0; i < fShadingGroups.length(); i++ )
{
MStringArray faces, flattenFaces;
MGlobal::executeCommand("sets -q " + fShadingGroups[i], faces, false, false);
MGlobal::clearSelectionList();
for(unsigned int j = 0; j < faces.length(); j++)
{
MString nodeType = MGlobal::executeCommandStringResult("nodeType " + faces[j]);
if (nodeType == "mesh") {
MGlobal::selectByName(faces[j]);
}
}
MGlobal::executeCommand("ConvertSelectionToFaces");
MGlobal::executeCommand("ls -sl -flatten", flattenFaces, false, false);
numOverlapUVFaces(fShadingGroups[i], flattenFaces);
}
return MS::kSuccess;
}
void peltOverlap::createBoundingCircle(const MStringArray &flattenFaces, MFloatArray ¢er, MFloatArray &radius)
{
center.setLength(2 * flattenFaces.length());
radius.setLength(flattenFaces.length());
for(unsigned int i = 0; i < flattenFaces.length(); i++) {
MSelectionList selList;
selList.add(flattenFaces[i]);
MDagPath dagPath;
MObject comp;
selList.getDagPath(0, dagPath, comp);
MItMeshPolygon iter(dagPath, comp);
MFloatArray uArray, vArray;
iter.getUVs(uArray, vArray);
float cu = 0.f;
float cv = 0.f;
unsigned int j;
for(j = 0; j < uArray.length(); j++) {
cu += uArray[j];
cv += vArray[j];
}
cu = cu / uArray.length();
cv = cv / vArray.length();
float rsqr = 0.f;
for(j = 0; j < uArray.length(); j++) {
float du = uArray[j] - cu;
float dv = vArray[j] - cv;
float dsqr = du*du + dv*dv;
rsqr = dsqr > rsqr ? dsqr : rsqr;
}
center[2*i] = cu;
center[2*i+1] = cv;
radius[i] = sqrt(rsqr);
}
}
bool peltOverlap::createRayGivenFace(const MString &face, MFloatArray &orig, MFloatArray &vec)
{
MSelectionList selList;
selList.add(face);
MDagPath dagPath;
MObject comp;
selList.getDagPath(0, dagPath, comp);
MItMeshPolygon iter(dagPath, comp);
MFloatArray uArray, vArray;
iter.getUVs(uArray, vArray);
if (uArray.length() == 0 || vArray.length() == 0) return false;
orig.setLength(2 * uArray.length());
vec.setLength( 2 * uArray.length());
float u = uArray[uArray.length() - 1];
float v = vArray[vArray.length() - 1];
for(unsigned int j = 0; j < uArray.length(); j++) {
orig[2*j] = uArray[j];
orig[2*j+1] = vArray[j];
vec[2*j] = u - uArray[j];
vec[2*j+1] = v - vArray[j];
u = uArray[j];
v = vArray[j];
}
return true;
}
float peltOverlap::area(const MFloatArray &orig)
{
float sum = 0.f;
unsigned int num = orig.length() / 2;
for (unsigned int i = 0; i < num; i++) {
unsigned int idx = 2 * i;
unsigned int idy = (i + 1 ) % num;
idy = 2 * idy + 1;
unsigned int idy2 = (i + num - 1) % num;
idy2 = 2 * idy2 + 1;
sum += orig[idx] * (orig[idy] - orig[idy2]);
}
return fabs(sum) * 0.5f;
}
unsigned int peltOverlap::checkCrossingEdges(
MFloatArray &face1Orig,
MFloatArray &face1Vec,
MFloatArray &face2Orig,
MFloatArray &face2Vec
)
{
unsigned int face1Size = face1Orig.length();
unsigned int face2Size = face2Orig.length();
for (unsigned int i = 0; i < face1Size; i += 2) {
float o1x = face1Orig[i];
float o1y = face1Orig[i+1];
float v1x = face1Vec[i];
float v1y = face1Vec[i+1];
float n1x = v1y;
float n1y = -v1x;
for (unsigned int j = 0; j < face2Size; j += 2) {
float o2x = face2Orig[j];
float o2y = face2Orig[j+1];
float v2x = face2Vec[j];
float v2y = face2Vec[j+1];
float n2x = v2y;
float n2y = -v2x;
float denum = v2x * n1x + v2y * n1y;
if (fabs(denum) < 0.000001f) continue;
float t2 = ((o1x-o2x)* n1x + (o1y-o2y) * n1y) / denum;
if (t2 < 0.00001f || t2 > 0.99999f) continue;
denum = v1x * n2x + v1y * n2y;
if (fabs(denum) < 0.000001f) continue;
float t1 = ((o2x-o1x)* n2x + (o2y-o1y) * n2y) / denum;
if (t1 > 0.00001f && t1 < 0.99999f) return 1;
}
}
return 0;
}
void peltOverlap::numOverlapUVFaces(const MString& shadingGroup, MStringArray& flattenFaces)
{
MFloatArray face1Orig, face1Vec, face2Orig, face2Vec, center, radius;
unsigned int numOverlap = 0;
createBoundingCircle(flattenFaces, center, radius);
for(unsigned int i = 0; i < flattenFaces.length() && numOverlap < fNthPairs; i++) {
if(!createRayGivenFace(flattenFaces[i], face1Orig, face1Vec)) continue;
const float cui = center[2*i];
const float cvi = center[2*i+1];
const float ri = radius[i];
for(unsigned int j = i+1; j < flattenFaces.length() && numOverlap < fNthPairs; j++) {
const float &cuj = center[2*j];
const float &cvj = center[2*j+1];
const float &rj = radius[j];
float du = cuj - cui;
float dv = cvj - cvi;
float dsqr = du*du + dv*dv;
if (dsqr >= (ri+rj)*(ri+rj)) continue;
if(!createRayGivenFace(flattenFaces[j], face2Orig, face2Vec)) continue;
if (checkCrossingEdges(face1Orig, face1Vec, face2Orig, face2Vec)) {
numOverlap++;
appendToResult(flattenFaces[i]);
appendToResult(flattenFaces[j]);
continue;
}
}
}
}
MStatus initializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");
status = plugin.registerCommand( "peltOverlap",
peltOverlap::creator,
peltOverlap::newSyntax);
if (!status) {
status.perror("registerCommand");
return status;
}
return status;
}
MStatus uninitializePlugin( MObject obj)
{
MStatus status;
MFnPlugin plugin( obj );
status = plugin.deregisterCommand( "peltOverlap" );
if (!status) {
status.perror("deregisterCommand");
return status;
}
return status;
}