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();
        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 &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);
         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 )
//
// 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);
            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]); // 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;
}

Autodesk® Maya® 2010 © 1997-2009 Autodesk, Inc. All rights reserved. Generated with doxygen 1.5.6