splitUVCmd.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: splitUVCmd.cpp
//
// MEL Command: splitUV
//
// Author: Lonnie Li
//

#include "splitUVCmd.h"
#include "splitUVNode.h"

// Function Sets
//
#include <maya/MFnDependencyNode.h>
#include <maya/MFnMesh.h>
#include <maya/MFnSingleIndexedComponent.h>

// Iterators
//
#include <maya/MItSelectionList.h>
#include <maya/MItMeshPolygon.h>

// General Includes
//
#include <maya/MGlobal.h>
#include <maya/MSelectionList.h>
#include <maya/MPlug.h>

#include <maya/MIOStream.h>

// Status Checking Macro - MCheckStatus (Debugging tool)
//
#define MCheckStatus(status,message)    \
    if( MS::kSuccess != status ) {      \
        cerr << message << "\n";        \
        return status;                  \
    }


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

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

void* splitUV::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 splitUV();
}

bool splitUV::isUndoable() const
//
//  Description:
//      this method tells Maya this command is undoable.  It is added to the 
//      undo queue if it is.
//
//  Return Value:
//      true if this command is undoable.
//
{
    return true;
}

MStatus splitUV::doIt( const MArgList& )
//
//  Description:
//      implements the MEL splitUV command.
//
//  Arguments:
//      args - the argument list that was passes to the command from MEL
//
//  Return Value:
//      MS::kSuccess - command succeeded
//      MS::kFailure - command failed (returning this value will cause the 
//                     MEL script that is being run to terminate unless the
//                     error is caught using a "catch" statement.
//
{
    MStatus status;

    // Parse the selection list for objects with selected UV components.
    // To simplify things, we only take the first object that we find with
    // selected UVs and operate on that object alone.
    //
    // All other objects are ignored and return warning messages indicating
    // this limitation.
    //
    MSelectionList selList;
    MGlobal::getActiveSelectionList( selList );
    MItSelectionList selListIter( selList );
    selListIter.setFilter( MFn::kMesh );

    // The splitUV node only accepts a component list input, so we build
    // a component list using MFnComponentListData.
    //
    // MIntArrays could also be passed into the node to represent the uvIds,
    // but are less storage efficient than component lists, since consecutive 
    // components are bundled into a single entry in component lists.
    //
    MFnComponentListData compListFn;
    compListFn.create();
    bool found = false;
    bool foundMultiple = false;

    for( ; !selListIter.isDone(); selListIter.next() )
    {
        MDagPath dagPath;
        MObject component;
        selListIter.getDagPath( dagPath, component );

        // Check for selected UV components
        //
        if( component.apiType() == MFn::kMeshMapComponent )
        {
            if( !found )
            {
                // The variable 'component' holds all selected components on the selected
                // object, thus only a single call to MFnComponentListData::add() is needed
                // to store the selected components for a given object.
                //
                compListFn.add( component );

                // Copy the component list created by MFnComponentListData into our local
                // component list MObject member.
                //
                fComponentList = compListFn.object();

                // Locally store the actual uvIds of the selected UVs so that this command
                // can directly modify the mesh in the case when there is no history and
                // history is turned off.
                //
                MFnSingleIndexedComponent compFn( component );
                compFn.getElements( fSelUVs );

                // Ensure that this DAG path will point to the shape of our object.
                // Set the DAG path for the polyModifierCmd.
                //
                dagPath.extendToShape();
                setMeshNode( dagPath );
                found = true;
            }
            else
            {
                // Break once we have found a multiple object holding selected UVs, since
                // we are not interested in how many multiple objects there are, only
                // the fact that there are multiple objects.
                //
                foundMultiple = true;
                break;
            }
        }
    }
    if( foundMultiple )
    {
        displayWarning("Found more than one object with selected UVs - Only operating on first found object.");
    }

    // Initialize the polyModifierCmd node type - mesh node already set
    //
    setModifierNodeType( splitUVNode::id );

    if( found )
    {
        if( validateUVs() )
        {
            // Now, pass control over to the polyModifierCmd::doModifyPoly() method
            // to handle the operation.
            //
            status = doModifyPoly();
            
            if( status == MS::kSuccess )
            {
                setResult( "splitUV command succeeded!" );
            }
            else
            {
                displayError( "splitUV command failed!" );
            }
        }
        else
        {
            displayError( "splitUV command failed: Selected UVs are not splittable" );
            status = MS::kFailure;
        }
    }
    else
    {
        displayError( "splitUV command failed: Unable to find selected UVs" );
        status = MS::kFailure;
    }
    
    return status;
}

MStatus splitUV::redoIt()
//
//  Description:
//      Implements redo for the MEL splitUV command. 
//
//      This method is called when the user has undone a command of this type
//      and then redoes it.  No arguments are passed in as all of the necessary
//      information is cached by the doIt method.
//
//  Return Value:
//      MS::kSuccess - command succeeded
//      MS::kFailure - redoIt failed.  this is a serious problem that will
//                     likely cause the undo queue to be purged
//
{
    MStatus status;

    // Process the polyModifierCmd
    //
    status = redoModifyPoly();

    if( status == MS::kSuccess )
    {
        setResult( "splitUV command succeeded!" );
    }
    else
    {
        displayError( "splitUV command failed!" );
    }

    return status;
}

MStatus splitUV::undoIt()
//
//  Description:
//      implements undo for the MEL splitUV command.  
//
//      This method is called to undo a previous command of this type.  The 
//      system should be returned to the exact state that it was it previous 
//      to this command being executed.  That includes the selection state.
//
//  Return Value:
//      MS::kSuccess - command succeeded
//      MS::kFailure - redoIt failed.  this is a serious problem that will
//                     likely cause the undo queue to be purged
//
{
    MStatus status;

    status = undoModifyPoly();

    if( status == MS::kSuccess )
    {
        setResult( "splitUV undo succeeded!" );
    }
    else
    {
        setResult( "splitUV undo failed!" );
    }
    
    return status;
}

MStatus splitUV::initModifierNode( MObject modifierNode )
{
    MStatus status;

    // We need to tell the splitUV node which UVs to operate on. By overriding
    // the polyModifierCmd::initModifierNode() method, we can insert our own
    // modifierNode initialization code.
    //
    MFnDependencyNode depNodeFn( modifierNode );
    MObject uvListAttr;
    uvListAttr = depNodeFn.attribute( "inputComponents" );

    // Pass the component list down to the splitUV node
    //
    MPlug uvListPlug( modifierNode, uvListAttr );
    status = uvListPlug.setValue( fComponentList );

    return status;
}

MStatus splitUV::directModifier( MObject mesh )
{
    MStatus status;

    fSplitUVFactory.setMesh( mesh );
    fSplitUVFactory.setUVIds( fSelUVs );

    // Now, perform the splitUV
    //
    status = fSplitUVFactory.doIt();

    return status;
}

// Private Methods
//
bool splitUV::validateUVs()
//
// Description:
//
//      Validate the UVs for the splitUV operation. UVs are valid only if they are shared
//      by more than one face. While the splitUVNode is smart enough to not process the
//      split if a UV is not splittable, a splitUV node is still created by the polyModifierCmd.
//      So call this method to validate the UVs before calling doModifyPoly().
//
//      validateUVs() will return true so long as there is at least one valid UV. It will
//      also prune out any invalid UVs from both the component list and UVId array.
//
{
    // Get the mesh that we are operating on
    //
    MDagPath        dagPath = getMeshNode();
    MObject         mesh = dagPath.node();

    // Get the number of faces sharing the selected UVs
    //
    MFnMesh         meshFn( mesh );
    MItMeshPolygon  polyIter( mesh );
    MIntArray       selUVFaceCountArray;

    int i;
    int j;
    int count = 0;
    int selUVsCount = fSelUVs.length();

    for( i = 0; i < selUVsCount; i++ )
    {
        for( ; !polyIter.isDone(); polyIter.next() )
        {
            if( polyIter.hasUVs() )
            {
                int polyVertCount = polyIter.polygonVertexCount();

                for( j = 0; j < polyVertCount; j++ )
                {
                    int UVIndex = 0;
                    polyIter.getUVIndex(j, UVIndex);

                    if( UVIndex == fSelUVs[i] )
                    {
                        count++;
                        break;
                    }
                }
            }
        }

        selUVFaceCountArray.append(count);
    }

    // Now, check to make sure that at least one UV is being shared by more than one
    // face. So long as we have one UV that we can operate on, we should proceed and let
    // the splitUVNode ignore the UVs which are only shared by one face.
    //
    bool isValid = false;
    MIntArray validUVIndices;

    for( i = 0; i < selUVsCount; i++ )
    {
        if( selUVFaceCountArray[i] > 1 )
        {
            isValid = true;
            validUVIndices.append(i);
        }
    }

    if( isValid )
    {
        pruneUVs( validUVIndices );
    }

    return isValid;
}

MStatus splitUV::pruneUVs( MIntArray& validUVIndices )
//
// Description:
//
//      This method will remove any invalid UVIds from the component list and UVId array.
//      The benefit of this is to reduce the amount of extra processing that the node would
//      have to perform. It will result in less iterations through the mesh as there are
//      less UVs to search for.
//
{
    MStatus status;

    unsigned i;
    MIntArray validUVIds;

    for( i = 0; i < validUVIndices.length(); i++ )
    {
        int uvIndex = validUVIndices[i];
        validUVIds.append( fSelUVs[uvIndex] );
    }

    // Replace the local int array of UVIds
    //
    fSelUVs.clear();
    fSelUVs = validUVIds;

    // Build the list of valid components
    //
    MFnSingleIndexedComponent compFn;
    compFn.create( MFn::kMeshMapComponent, &status );
    MCheckStatus( status, "compFn.create( MFn::kMeshMapComponent )" );
    status = compFn.addElements( validUVIds );
    MCheckStatus( status, "compFn.addElements( validUVIds )" );
    MObject component = compFn.object();

    // Replace the component list
    //
    MFnComponentListData compListFn;
    compListFn.create();
    status = compListFn.add( component );
    MCheckStatus( status, "compListFn.add( component )" );

    fComponentList = compListFn.object();

    return status;
}