cleanPerFaceAssignmentCmd.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.
// ==========================================================================
//+

//
// File: cleanPerFaceAssignmentCmd.cpp
// MEL Command: cleanPerFaceAssignment
// Author: Hiroyuki Haga
// 

#include "cleanPerFaceAssignmentCmd.h"

// Include Maya libary: General
#include <maya/MGlobal.h>
#include <maya/MDagPath.h>
#include <maya/MPlug.h>
#include <maya/MObjectArray.h>
#include <maya/MIntArray.h>
#include <maya/MStringArray.h>

// Include Maya libary: Selection
#include <maya/MSelectionList.h>
#include <maya/MItSelectionList.h>

// Include Maya libary: Nodes
#include <maya/MFnDependencyNode.h>

// Include Maya libary: Polygons
#include <maya/MFnMesh.h>
#include <maya/MItMeshPolygon.h>

// Include Maya libary: Sets
#include <maya/MFnSet.h>

#ifdef DEBUG
#include <MIOStream.h>
#endif

MStatus cleanPerFaceAssignment::doIt( const MArgList& )
//
//  Description:
//      cleanPerfaceAssignment does the followings
//          1. It traces material connections
//          2. If multiple conections to one material is found, it combines them.
//          3. It changes the assignment order
//          4. it returns a string array that can be executed as a MEL command
//
//  Arguments:
//      NONE
//
//  Return Value:
//      It always returns MS:kSuccess as there's no error handling
//
{
    MStatus stat = MS::kSuccess;


    //  Get the selection list and using the iterator to trace them
    MSelectionList list;
    MGlobal::getActiveSelectionList( list );
    MItSelectionList listIt( list);

    for ( listIt.reset(); !listIt.isDone() ; listIt.next() ){

        // Get the DAG path of each element and extend the path to its shape node
        MDagPath path;
        listIt.getDagPath( path );
        path.extendToShape();
    
        // if a DAG Path is MFn::kMesh, it tries to obtain its material

        if ( path.apiType() == MFn::kMesh ){

            // Create a function set for poly geometries, find faces that are assigned as a set
            MFnMesh meshFn( path );
            MObjectArray sets, comps;

            unsigned int instanceNumber = path.instanceNumber();
            meshFn.getConnectedSetsAndMembers( instanceNumber, sets, comps, 1 );

#ifdef _DEBUG
            // Debug output
            for ( unsigned int i = 0; i < sets.length() ; i++ ){
                MFnSet setFn ( sets[i]);
                MItMeshPolygon faceIt( path, comps[i] );

                cout << "-------------->" << endl;
                cout << setFn.name() << endl;
                cout << "FaceCount:" << faceIt.count() << endl;
                for ( faceIt.reset() ; !faceIt.isDone() ; faceIt.next() ){
                    cout << faceIt.index() << " ";
                }
                cout << endl;
                cout << "<--------------" << endl;
                
            }
#endif // _DEBUG


            // Variable declaration 
            MStringArray SGNames;       // Stores Shading Group name
            MIntArray sameConnectionFlag;   // a flag to check if Shading Group has same names
            MIntArray sameSGFaceCount;  // storoes the number of faces included in each shading group
            MStringArray memberFaceNames;   // stores names of faces included in each shading group

            sameConnectionFlag.clear();
            sameSGFaceCount.clear();

            // ------------------------------------------------
            // 1    Initialization
            // ------------------------------------------------

            // initialization of variables
            for ( unsigned int i = 0; i < sets.length() ; i++ ){

                // SGName Initialization
                //  Append Shading Group name
                MFnSet setFn ( sets[i] );
                SGNames.append( setFn.name() );

                // sameConnectionFlag Initialization
                //  set -1�@
                sameConnectionFlag.append( -1 );
                
                
                // sameSGFaceCount Initialization
                //  Set the number of faces included in a Shading Group
                MItMeshPolygon tempFaceIt ( path, comps[i] );               
                sameSGFaceCount.append( tempFaceIt.count() );


                // memberFaceNames Initialization
                //  Stores names of faces included in a Shading Group in a way MEL can use
                MString aMemberFaceName;

                MString pathName = path.fullPathName();
                bool optimizeList = true; 
                if (optimizeList)
                {
                    int lastIndices[2] = { -1, -1 };
                    int currentIndex = -1;
                    bool haveAFace = false;
                    for ( ;!tempFaceIt.isDone() ; tempFaceIt.next() )
                    {
                        if (lastIndices[0] == -1)
                        {
                            lastIndices[0] = tempFaceIt.index();
                            lastIndices[1] = tempFaceIt.index();
                        }
                        else
                        {
                            currentIndex = tempFaceIt.index();

                            // Hit non-contiguous face #. split out a new string
                            if (currentIndex > lastIndices[1] + 1)
                            {
                                aMemberFaceName += (pathName + ".f[" + lastIndices[0] + ":" + lastIndices[1] + "] ");
                                lastIndices[0] = currentIndex;
                                lastIndices[1] = currentIndex;
                            }
                            else
                                lastIndices[1] = currentIndex;
                        }
                        haveAFace = true;
                    }
                    // Only one member. Add it.
                    if (haveAFace)
                    {
                        aMemberFaceName += (pathName + ".f[" + lastIndices[0] + ":" + lastIndices[1] + "] ");
                    }
                }
                else
                {
                    for ( ;!tempFaceIt.isDone() ; tempFaceIt.next() ){
                        aMemberFaceName += (path.fullPathName() + ".f[" + tempFaceIt.index() + "] ");
                    }
                }
                memberFaceNames.append( aMemberFaceName );

            }
            
    
            // ------------------------------------------------
            // 2    Finding redundant connections
            // ------------------------------------------------

            // Scan for multiple connections to a shading group and if it exists
            //  combine them
            for ( unsigned int i = 0; i < sets.length() ; i++ ){
                MFnSet setFn ( sets[i]);
                if ( sameConnectionFlag[i] == -1 ){

                    for ( unsigned int j = 0; j < sets.length() ; j++ ){
                        if ( i != j && sameConnectionFlag[j] == -1 ) {
                            MFnSet tempSetFn ( sets[j] );

                            if ( setFn.name() == tempSetFn.name() ){
                                sameConnectionFlag[j] = i;
                                sameSGFaceCount[i] += sameSGFaceCount[j];
                                sameSGFaceCount[j] = 0;
                                memberFaceNames[i] += memberFaceNames[j];
                                memberFaceNames[j] = "";
                            }
                        }

                    }
                    sameConnectionFlag[i] = i;
                }
            }

            // delete empties
            unsigned int tail = 0;
            while( tail < sameConnectionFlag.length() ){
                if ( sameSGFaceCount[tail] == 0 ){
                    SGNames.remove( tail );
                    memberFaceNames.remove( tail );
                    sameConnectionFlag.remove( tail );
                    sameSGFaceCount.remove( tail );

                } else {
                    tail++;
                }
            }


            // ------------------------------------------------
            // 3    Sorting array
            // ------------------------------------------------

            // Sorting preprocess
            //      Simply assign ID from the top
            MIntArray sortMapper;
            for ( unsigned int i = 0; i < sameSGFaceCount.length() ; i++ ){
                sortMapper.append( i );
            }
            bool needToSort = true;
            
            // Begin sorting
            //      in the order of number of faces 
            while ( needToSort ){
                unsigned int sortCount = 0;
                if ( sameSGFaceCount.length() > 1 ){
                    for( unsigned int i = 0; i < ( sameSGFaceCount.length() -1 ) ; i++ ){
                        if ( sameSGFaceCount[sortMapper[i]] < sameSGFaceCount[sortMapper[i+1]] ){
                            int tmp = sortMapper[i];
                            sortMapper[i] = sortMapper[i+1];
                            sortMapper[i+1] = tmp;
                            sortCount++;
                        } else {

                        }

                    }
                }else {
                    break;
                }

                
                // Check if sorting is completed
                if ( sortCount > 0 ){
                    needToSort = true; //another sorting required
                } else {
                    needToSort = false;//no sorting required and exit loop
                }


            }

            // store the sorted arrary

            //  Variable declaration
            MStringArray sortedSGNames;
            MStringArray sortedMemberFaceNames;
            MIntArray   sortedSGFaceCount;

            //  Store
            for ( unsigned int i = 0; i < sortMapper.length(); i++ ){
                sortedSGNames.append( SGNames[sortMapper[i]] );
                sortedSGFaceCount.append( sameSGFaceCount[sortMapper[i]] );
                sortedMemberFaceNames.append( memberFaceNames[sortMapper[i]]);
            }

            

#ifdef _DEBUG

            // Debug information output
            for ( unsigned int i = 0; i < sortedSGFaceCount.length() ; i++ ){
                cout << sortedSGNames[i] << "  " << sortedSGFaceCount[i] << endl;
                cout << sortedMemberFaceNames[i] << endl;
            }
            
            for ( unsigned int i = 0; i < sortMapper.length() ; i++ ){
                cout << i << " -- " << sortMapper[i] << endl;
            }
#endif // _DEBUG
            
            // ------------------------------------------------
            // 4    Output
            // ------------------------------------------------
            //  This outputs a MEL executable string array
            //  This is to enable step by step excution when it can't be solved to normally 
            
            bool executeImmediate = true;
            for ( unsigned int i = 0; i < sortedSGFaceCount.length() ; i++ ){
                
                MString resultString;
                if ( i == 0 ){

                    //�@Add object base selection MEL command 
                    resultString = ("select -r " + path.fullPathName()  +";" );
                } else {
                    //�@Add component base selection MEL command 
                    resultString = ("select -r " + sortedMemberFaceNames[i] + ";");
                }

                if (executeImmediate )
                    MGlobal::executeCommand( resultString );

                // Add a meterial assignemnet MEL comand 
                if (executeImmediate)
                {
                    resultString = ("sets -e -forceElement " + sortedSGNames[i] + ";");
                    MGlobal::executeCommand( resultString );
                }
                else
                    resultString += ("sets -e -forceElement " + sortedSGNames[i] + ";");

                // Append to the result
                if (!executeImmediate)
                    appendToResult( resultString );
            }

        }

    }
    return stat; // TODO: Error handling/Additional ASSERT required�BCurrently it always returns MS::kSuccess
}


void* cleanPerFaceAssignment::creator()
//
//  Description:
//      this method exists to give Maya a way to create new objects
//      of this type. 
//
//  Return Value:
//      a new object of this type
//
{
    return new cleanPerFaceAssignment();
}

cleanPerFaceAssignment::cleanPerFaceAssignment()
//
//  Description:
//      cleanPerFaceAssignment constructor
//
{}

cleanPerFaceAssignment::~cleanPerFaceAssignment()
//
//  Description:
//      cleanPerFaceAssignment destructor
//
{
}