C++ API Reference
peltOverlapCmd/peltOverlapCmd.cpp
//-
// ==========================================================================
// Copyright 1995,2006,2008 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.
// ==========================================================================
//+
//
// peltOverlapCmd.cpp
//
// This plugin checks and returns the overlapping faces in pairs for a given
// list of shading groups. The faces can be from the same mesh or different meshes, i.e.
//
// 1) Return the first overlapping faces associated to "shadingGroupName1"
// peltOverlap "shadingGroupName1";
//
// 2) Return the first 100 overlapping faces associated to "shadingGroupName1"
// and the first 100 overlapping faces associated to "shadingGroupName2".
// peltOverlap -exitAfterNthError 100 "shadingGroupName1" "shadingGroupName2";
//
#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"
//
// Command class declaration
//
class peltOverlap : public MPxCommand
{
public:
peltOverlap();
~peltOverlap() override;
MStatus doIt( const MArgList& args ) override;
static MSyntax newSyntax();
static void* creator();
private:
MStatus parseArgs( const MArgList &args );
void createBoundingCircle(const MStringArray &flattenFaces, MFloatArray &center, 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);
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 )
//
// Description
// Return the overlapping faces in pairs for given shading groups.
//
{
MStatus status = parseArgs ( args );
if ( !status ) return status;
// Iterate over shading groups
for (unsigned int i = 0; i < fShadingGroups.length(); i++ )
{
// Get a list of faces through mel because it's so much easier this way.
// It should be fine because this is not performance critical.
MStringArray faces, flattenFaces;
MGlobal::executeCommand("sets -q " + fShadingGroups[i], faces, false, false);
for(unsigned int j = 0; j < faces.length(); j++)
{
MString nodeType = MGlobal::executeCommandStringResult("nodeType " + faces[j]);
if (nodeType == "mesh") {
MGlobal::selectByName(faces[j]); // add to list
}
}
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 &center, MFloatArray &radius)
//
// Description
// Represent a face by a center and radius, i.e.
// center = {center1u, center1v, center2u, center2v, ... }
// radius = {radius1, radius2, ... }
//
{
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);
// Loop through all vertices to construct edges/rays
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)
//
// Description
// Represent a face by a series of edges(rays), i.e.
// orig = {orig1u, orig1v, orig2u, orig2v, ... }
// vec = {vec1u, vec1v, vec2u, vec2v, ... }
//
// return false if no valid uv's.
{
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());
// Loop through all vertices to construct edges/rays
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
)
// Check if there are crossing edges between two faces. Return true
// if there are crossing edges and false otherwise. A face is represented
// by a series of edges(rays), i.e.
// faceOrig[] = {orig1u, orig1v, orig2u, orig2v, ... }
// faceVec[] = {vec1u, vec1v, vec2u, vec2v, ... }
{
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) {
// Given ray1(O1, V1) and ray2(O2, V2)
// Normal of ray1 is (V1.y, V1.x)
float o2x = face2Orig[j];
float o2y = face2Orig[j+1];
float v2x = face2Vec[j];
float v2y = face2Vec[j+1];
float n2x = v2y;
float n2y = -v2x;
// Find t for ray2
// t = [(o1x-o2x)n1x + (o1y-o2y)n1y] / (v2x * n1x + v2y * n1y)
float denum = v2x * n1x + v2y * n1y;
// Edges are parallel if denum is close to 0.
if (fabs(denum) < 0.000001f) continue;
float t2 = ((o1x-o2x)* n1x + (o1y-o2y) * n1y) / denum;
if (t2 < 0.00001f || t2 > 0.99999f) continue;
// Find t for ray1
// t = [(o2x-o1x)n2x + (o2y-o1y)n2y] / (v1x * n2x + v1y * n2y)
denum = v1x * n2x + v1y * n2y;
// Edges are parallel if denum is close to 0.
if (fabs(denum) < 0.000001f) continue;
float t1 = ((o2x-o1x)* n2x + (o2y-o1y) * n2y) / denum;
// Edges intersect
if (t1 > 0.00001f && t1 < 0.99999f) return 1;
}
}
return 0;
}
void peltOverlap::numOverlapUVFaces(const MString& shadingGroup, MStringArray& flattenFaces)
//
// Description
// Return overlapping faces in pairs for given a shading group and its associated faces.
//
{
MFloatArray face1Orig, face1Vec, face2Orig, face2Vec, center, radius;
// Loop through face i
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];
// Exclude the degenerate face
// if(area(face1Orig) < 0.000001) continue;
// Loop through face j where j != 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;
// Quick rejection if bounding circles don't overlap
if (dsqr >= (ri+rj)*(ri+rj)) continue;
if(!createRayGivenFace(flattenFaces[j], face2Orig, face2Vec)) continue;
// Exclude the degenerate face
// if(area(face2Orig) < 0.000001) continue;
if (checkCrossingEdges(face1Orig, face1Vec, face2Orig, face2Vec)) {
numOverlap++;
appendToResult(flattenFaces[i]);
appendToResult(flattenFaces[j]);
continue;
}
}
}
}
//
// The following routines are used to register/unregister
// the command we are creating within Maya
//
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;
}