- Main Page
- Related Pages
- Modules
- Classes
- Examples

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

    #include "meshRemapCmd.h"
    #include "meshMapUtils.h"

    #include <maya/MItSelectionList.h>
    #include <maya/MItMeshPolygon.h>
    #include <maya/MArgList.h>
    #include <maya/MStatus.h>
    #include <maya/MFnMesh.h>
    #include <maya/MDagPathArray.h>
    #include <maya/MObjectArray.h>
    #include <maya/MIOStream.h>

    // CONSTRUCTOR DEFINITION:
    meshRemapCommand::meshRemapCommand()
    {
        fColorArrays = NULL;
        fRepArray = NULL;
        fClampedArray = NULL;
        fUArrays = NULL;
        fVArrays = NULL;
        fUVCountsArrays = NULL;
        fUVIdsArrays = NULL;
    }

    // DESTRUCTOR DEFINITION:
    meshRemapCommand::~meshRemapCommand()
    {
        reset();
    }

    // METHOD FOR CREATING AN INSTANCE OF THIS COMMAND:
    void* meshRemapCommand::creator()
    {
        return new meshRemapCommand;
    }

    MStatus meshRemapCommand::parseArgs(const MArgList& args)
    {
        MSelectionList list;
        MString err;
        MStatus stat;

        if( args.length() != 6 )
        {
            displayError("6 vertices must be specified");
            return MS::kFailure;
        }

        int argIdx = 0;
        for (unsigned int i = 0; i < 2; i++)
        {
            MObjectArray selectedComponent(3);
            MDagPathArray selectedPath;

            selectedPath.setLength(3);

            for (unsigned int j = 0; j < 3; j++)
            {
                MString arg;

                if( ( stat = args.get( argIdx, arg )) != MStatus::kSuccess )
                {
                    displayError( "Can't parse arg");
                    return stat;
                }

                list.clear();
                if (! list.add( arg ) )
                {
                    err = arg + ": no such component";
                    displayError(err);
                    return MS::kFailure; // no such component
                }

                MItSelectionList selectionIt (list, MFn::kComponent);
                if (selectionIt.isDone ())
                {
                    err = arg + ": not a component";
                    displayError (err);
                    return MS::kFailure;
                }

                if( selectionIt.getDagPath (selectedPath[j], selectedComponent[j]) != MStatus::kSuccess )
                {
                    displayError( "Can't get path for");
                    return stat;
                }

                if (!selectedPath[j].node().hasFn(MFn::kMesh) && !(selectedPath[j].node().hasFn(MFn::kTransform) && selectedPath[j].hasFn(MFn::kMesh)))
                {
                    err = arg + ": Invalid type!  Only a mesh or its transform can be specified!";
                    displayError (err);
                    return MStatus::kFailure;
                }

                argIdx++;
            }

            if( i == 0 )
            {
                if( ( stat = meshMapUtils::validateFaceSelection( selectedPath, selectedComponent, &fFaceIdxSrc, &fFaceVtxSrc ) ) != MStatus::kSuccess )
                {
                    displayError("Selected vertices don't define a unique face on source mesh");
                    return stat;
                }

                fDagPathSrc = selectedPath[0];
            }
            else
            {
                if( ( stat = meshMapUtils::validateFaceSelection( selectedPath, selectedComponent, &fFaceIdxDst, &fFaceVtxDst ) ) != MStatus::kSuccess )
                {
                    displayError("Selected vertices don't define a unique face on target mesh");
                    return stat;
                }

                fDagPathDst = selectedPath[0];
            }
        }

        if( fDagPathSrc == fDagPathDst )
        {
            displayError("Cannot use one mesh for both source and target");
            return stat;
        }

        return MStatus::kSuccess;
    }

    // FIRST INVOKED WHEN COMMAND IS CALLED, PARSING THE COMMAND ARGUMENTS, INITIALIZING DEFAULT PARAMETERS, THEN CALLING redoIt():
    MStatus meshRemapCommand::doIt(const MArgList& args)
    {
        MStatus  stat = MStatus::kSuccess;

        if ( ( stat = parseArgs( args ) ) != MStatus::kSuccess )
        {
            displayError ("Error parsing arguments");
            return stat;
        }

        return redoIt();
    }

    MStatus meshRemapCommand::redoIt()
    {
        int i;
        MStatus  stat = MStatus::kSuccess;

        fDagPathSrc.extendToShape();
        MFnMesh theMeshSrc( fDagPathSrc, &stat );
        if( stat != MStatus::kSuccess )
        {
            displayError(" MFnMesh creation");
            return stat;
        }

        MFloatPointArray    origVertices;
        stat = theMeshSrc.getPoints (origVertices, MSpace::kObject );
        if( stat != MStatus::kSuccess )
        {
            displayError(" MFnMesh getPoints");
            return stat;
        }

        //
        // Traverse the source
        //

        //
        // Initialize the traversal flags and CV mappings for this shape
        MIntArray           faceTraversal( theMeshSrc.numPolygons(), false );
        MIntArray           cvMapping( theMeshSrc.numVertices(), -1 );
        MIntArray           cvMappingInverse( theMeshSrc.numVertices(), -1 );

        //
        //  Starting with the user selected face, recursively rebuild the entire mesh
        //

        MIntArray           newPolygonCounts;
        MIntArray           newPolygonConnects;
        MFloatPointArray    newVertices;

        stat = meshMapUtils::traverseFace( fDagPathSrc, fFaceIdxSrc, fFaceVtxSrc[0], fFaceVtxSrc[1], faceTraversal,
                        cvMapping, cvMappingInverse,
                        newPolygonCounts, newPolygonConnects,
                        origVertices, newVertices );
        if ( stat != MStatus::kSuccess )
        {
            displayError(" could not process all the mesh faces.");
            return stat;
        }

        // reuse newPolygonCounts and newPolygonConnects for the src mesh.
        newPolygonCounts.clear();
        newPolygonConnects.clear();
        MItMeshPolygon polyIter(fDagPathSrc.node());
        while(!polyIter.isDone())
        {
            newPolygonCounts.append(polyIter.polygonVertexCount());
            for(i = 0; i < (int) polyIter.polygonVertexCount(); i++)
            {
                newPolygonConnects.append(polyIter.vertexIndex(i));
            }
            polyIter.next();
        }

        //
        // Now, traverse the destination
        //

        fDagPathDst.extendToShape();
        MFnMesh theMeshDst( fDagPathDst, &stat );
        if( stat != MStatus::kSuccess )
        {
            displayError(" MFnMesh creation");
            return stat;
        }

        MFloatPointArray    origVerticesDst;
        stat = theMeshDst.getPoints (origVerticesDst, MSpace::kObject );
        if( stat != MStatus::kSuccess )
        {
            displayError(" MFnMesh getPoints");
            return stat;
        }

        // Initialize the traversal flags and CV mappings for this shape
        MIntArray           faceTraversalDst( theMeshDst.numPolygons(), false );
        MIntArray           cvMappingDst( theMeshDst.numVertices(), -1 );
        MIntArray           cvMappingInverseDst( theMeshDst.numVertices(), -1 );

        //
        //  Starting with the user selected face, recursively rebuild the entire mesh
        //

        MIntArray           newPolygonCountsDst;
        MIntArray           newPolygonConnectsDst;
        MFloatPointArray    newVerticesDst;

        stat = meshMapUtils::traverseFace( fDagPathDst, fFaceIdxDst, fFaceVtxDst[0], fFaceVtxDst[1], faceTraversalDst,
                        cvMappingDst, cvMappingInverseDst,
                        newPolygonCountsDst, newPolygonConnectsDst,
                        origVerticesDst, newVerticesDst );
        if ( stat != MStatus::kSuccess )
        {
            displayError(" could not process all the mesh faces.");
            return stat;
        }

        //
        // Use the generated maps to build a new CV list that will remap
        // the destination to the source topology
        //
        for( i = 0; i < theMeshDst.numVertices(); i++ )
        {
            newVerticesDst[cvMappingInverse[i]] = origVerticesDst[cvMappingInverseDst[i]];
        }

        // Prepare for undo. Must collect the mesh information here before it is modified by createInPlace() call.
        fVertices.copy(origVerticesDst);
        MItMeshPolygon polyIterDst(fDagPathDst.node());
        while(!polyIterDst.isDone())
        {
            fPolygonCounts.append(polyIterDst.polygonVertexCount());
            for(i = 0; i < (int)polyIterDst.polygonVertexCount(); i++)
            {
                fPolygonConnects.append(polyIterDst.vertexIndex(i));
            }
            polyIterDst.next();
        }

        // Store Colors for undo
        fColorSetNames.clear();
        theMeshDst.getColorSetNames(fColorSetNames);
        int numColorSets = fColorSetNames.length();
        fClampedArray = new bool[numColorSets];
        fRepArray = new MFnMesh::MColorRepresentation[numColorSets];
        fColorArrays = new MColorArray[numColorSets];

        for ( i = 0; i < numColorSets; i++ )
        {
            fClampedArray[i] = theMeshDst.isColorClamped(fColorSetNames[i]);
            fRepArray[i] = theMeshDst.getColorRepresentation(fColorSetNames[i]);
            theMeshDst.getVertexColors(fColorArrays[i], &(fColorSetNames[i]));
        }

        // Store UVs for undo
        fUVSetNames.clear();
        theMeshDst.getUVSetNames(fUVSetNames);
        int numUVSets = fUVSetNames.length();
        fUArrays = new MFloatArray[numUVSets];
        fVArrays = new MFloatArray[numUVSets];
        fUVIdsArrays = new MIntArray[numUVSets];
        fUVCountsArrays = new MIntArray[numUVSets];
        fUVIdsArrays = new MIntArray[numUVSets];

        for ( i = 0; i < numUVSets; i++ )
        {
            theMeshDst.getAssignedUVs(fUVCountsArrays[i], fUVIdsArrays[i], &(fUVSetNames[i]));
            theMeshDst.getUVs(fUArrays[i], fVArrays[i], &(fUVSetNames[i]));
        }

        //
        //  Create the mesh. The old approach of using copyInPlace is doing referenced counting of the mesh, which is
        //  not what we want and has caused strange problem, i.e. reorder not working on a remapped mesh.
        //
        stat = theMeshDst.createInPlace( newVerticesDst.length(), newPolygonCounts.length(), newVerticesDst, newPolygonCounts, newPolygonConnects );
        if ( stat != MStatus::kSuccess )
        {
            displayError(" mesh copy failed.");
            reset();
            return stat;
        }

        // Copy Colors from src to dst mesh
        MStringArray colorSetNamesSrc;
        theMeshSrc.getColorSetNames(colorSetNamesSrc);
        int numColorSetsSrc = colorSetNamesSrc.length();
        for (i = 0; i < numColorSetsSrc; i++)
        {
            MIntArray mapping(fVertices.length());;
            if (i == 0)
            {
                for (unsigned j = 0; j < fVertices.length(); j++)
                {
                    mapping[j] = j;
                }
            }

            MColorArray colorArraySrc;
            bool        clampedSrc;
            MFnMesh::MColorRepresentation repSrc;
            clampedSrc = theMeshSrc.isColorClamped(colorSetNamesSrc[i]);
            repSrc = theMeshSrc.getColorRepresentation(colorSetNamesSrc[i]);
            theMeshSrc.getVertexColors(colorArraySrc, &(colorSetNamesSrc[i]));

            theMeshDst.createColorSet(colorSetNamesSrc[i], NULL, clampedSrc, repSrc);
            if (colorArraySrc.length() > 0 && colorArraySrc.length() == fVertices.length())
            {
                theMeshDst.setVertexColors(colorArraySrc, mapping, NULL, repSrc);
            }
        }

        // Copy UVs from src to dst mesh
        MStringArray uvSetNamesSrc;
        theMeshSrc.getUVSetNames(uvSetNamesSrc);
        int numUVSetsSrc = uvSetNamesSrc.length();
        MString defaultUVSetName;
        theMeshDst.getCurrentUVSetName(defaultUVSetName);
        for (i = 0; i < numUVSetsSrc; i++)
        {
            MFloatArray uArraySrc;
            MFloatArray vArraySrc;
            theMeshSrc.getUVs(uArraySrc, vArraySrc, &(uvSetNamesSrc[i]));

            if (uvSetNamesSrc[i] != defaultUVSetName)
            {
                theMeshDst.createUVSet(uvSetNamesSrc[i]);
            }
            if (uArraySrc.length() > 0 && uArraySrc.length() == vArraySrc.length())
            {
                theMeshDst.setUVs(uArraySrc, vArraySrc, &(uvSetNamesSrc[i]));
                MIntArray uvCounts;
                MIntArray uvIds;
                theMeshSrc.getAssignedUVs(uvCounts, uvIds, &(uvSetNamesSrc[i]));
                theMeshDst.assignUVs(uvCounts, uvIds, &(uvSetNamesSrc[i]));
            }
        }

        return stat;
    }

    MStatus meshRemapCommand::undoIt()
    {
        int i;
        MStatus  stat = MStatus::kSuccess;
        MFnMesh theMesh( fDagPathDst );

        stat = theMesh.createInPlace( fVertices.length(), fPolygonCounts.length(), fVertices, fPolygonCounts, fPolygonConnects );

        // Restore Colors from cached data for undo
        int numColorSets = fColorSetNames.length();
        for (i = 0; i < numColorSets; i++)
        {
            MIntArray mapping(fVertices.length());;
            if (i == 0)
            {
                for (unsigned j = 0; j < fVertices.length(); j++)
                {
                    mapping[j] = j;
                }
            }

            theMesh.createColorSet(fColorSetNames[i], NULL, fClampedArray[i], fRepArray[i]);
            if (fColorArrays[i].length() > 0 && fColorArrays[i].length() == fVertices.length())
            {
                theMesh.setVertexColors(fColorArrays[i], mapping, NULL, fRepArray[i]);
            }
        }

        // Restore UVs from cached data for undo
        int numUVSets = fUVSetNames.length();
        MString defaultUVSetName;
        theMesh.getCurrentUVSetName(defaultUVSetName);
        for (i = 0; i < numUVSets; i++)
        {
            if (fUVSetNames[i] != defaultUVSetName)
            {
                theMesh.createUVSet(fUVSetNames[i]);
            }
            if (fUArrays[i].length() > 0 && fUArrays[i].length() == fVArrays[i].length())
            {
                theMesh.setUVs(fUArrays[i], fVArrays[i], &(fUVSetNames[i]));
                theMesh.assignUVs(fUVCountsArrays[i], fUVIdsArrays[i], &(fUVSetNames[i]));
            }
        }

        reset();

        return stat;
    }

    void meshRemapCommand::reset()
    {
        fVertices.clear();
        fPolygonCounts.clear();
        fPolygonConnects.clear();

        fColorSetNames.clear();
        fUVSetNames.clear();
        if (fClampedArray != NULL)
        {
            delete [] fClampedArray;
            fClampedArray = NULL;
        }
        if (fRepArray != NULL)
        {
            delete [] fRepArray;
            fRepArray = NULL;
        }
        if (fUArrays != NULL)
        {
            delete [] fUArrays;
            fUArrays = NULL;
        }
        if (fVArrays != NULL)
        {
            delete [] fVArrays;
            fVArrays = NULL;
        }
        if (fColorArrays != NULL)
        {
            delete [] fColorArrays;
            fColorArrays = NULL;
        }
        if (fUVCountsArrays != NULL)
        {
            delete [] fUVCountsArrays;
            fUVCountsArrays = NULL;
        }
        if (fUVIdsArrays != NULL)
        {
            delete [] fUVIdsArrays;
            fUVIdsArrays = NULL;
        }
    }