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

# meshOpFtyAction.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 "meshOpFty.h"

    // General Includes
    //
    #include <maya/MGlobal.h>
    #include <maya/MIOStream.h>
    #include <maya/MFloatPointArray.h>

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

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

    #define CHECK_STATUS(st) if ((st) != MS::kSuccess) { break; }

    MStatus meshOpFty::doIt()
    //
    //  Description:
    //      Performs the operation on the selected mesh and components
    //
    {
        MStatus status;
        unsigned int i, j;

        // Get access to the mesh's function set
        //
        MFnMesh meshFn(fMesh);

        // The division count argument is used in many of the operations
        // to execute the operation multiple subsequent times. For example,
        // with a division count of 2 in subdivide face, the given faces will be
        // divide once and then the resulting inner faces will be divided again.
        //
        int divisionCount = 2;

        MFloatVector translation;
        if (fOperationType == kExtrudeEdges
            || fOperationType == kExtrudeFaces
            || fOperationType == kDuplicateFaces
            || fOperationType == kExtractFaces)
        {
            // The translation vector is used for the extrude, extract and
            // duplicate operations to move the result to a new position. For
            // example, if you extrude an edge on a mesh without a subsequent
            // translation, the extruded edge will be on at the position of the
            // orignal edge and the created faces will have no area.
            //
            // Here, we provide a translation that is in the same direction as the
            // average normal of the given components.
            //
            MFn::Type componentType = getExpectedComponentType(fOperationType);
            MIntArray adjacentVertexList;
            switch (componentType)
            {
            case MFn::kMeshEdgeComponent:
                for (i = 0; i < fComponentIDs.length(); ++i)
                {
                    int2 vertices;
                    meshFn.getEdgeVertices(fComponentIDs[i], vertices);
                    adjacentVertexList.append(vertices[0]);
                    adjacentVertexList.append(vertices[1]);
                }
                break;

            case MFn::kMeshPolygonComponent:
                for (i = 0; i < fComponentIDs.length(); ++i)
                {
                    MIntArray vertices;
                    meshFn.getPolygonVertices(fComponentIDs[i], vertices);
                    for (j = 0; j < vertices.length(); ++j)
                        adjacentVertexList.append(vertices[j]);
                }
                break;
            default:
                break;
            }
            MVector averageNormal(0, 0, 0);
            for (i = 0; i < adjacentVertexList.length(); ++i)
            {
                MVector vertexNormal;
                meshFn.getVertexNormal(adjacentVertexList[i], vertexNormal,
                    MSpace::kWorld);
                averageNormal += vertexNormal;
            }
            if (averageNormal.length() < 0.001)
                averageNormal = MVector(0.0, 1.0, 0.0);
            else averageNormal.normalize();
            translation = averageNormal;
        }

        // When doing an extrude operation, there is a choice of extrude the
        // faces/edges individually or together. If extrudeTogether is true and
        // multiple adjacent components are selected, they will be extruded as if
        // it were one more complex component.
        //
        // The following variable sets that option.
        //
        bool extrudeTogether = true;

        // Execute the requested operation
        //
        switch (fOperationType)
        {
        case kSubdivideEdges: {
            status = meshFn.subdivideEdges(fComponentIDs, divisionCount);
            CHECK_STATUS(status);
            break; }

        case kSubdivideFaces: {
            status = meshFn.subdivideFaces(fComponentIDs, divisionCount);
            CHECK_STATUS(status);
            break; }

        case kExtrudeEdges: {
            status = meshFn.extrudeEdges(fComponentIDs, divisionCount,
                &translation, extrudeTogether);
            CHECK_STATUS(status);
            break; }

        case kExtrudeFaces: {
            status = meshFn.extrudeFaces(fComponentIDs, divisionCount,
                &translation, extrudeTogether);
            CHECK_STATUS(status);
            break; }

        case kCollapseEdges: {
            status = meshFn.collapseEdges(fComponentIDs);
            CHECK_STATUS(status);
            break; }

        case kCollapseFaces: {
            status = meshFn.collapseFaces(fComponentIDs);
            CHECK_STATUS(status);
            break; }

        case kDuplicateFaces: {
            status = meshFn.duplicateFaces(fComponentIDs, &translation);
            CHECK_STATUS(status);
            break; }

        case kExtractFaces: {
            status = meshFn.extractFaces(fComponentIDs, &translation);
            CHECK_STATUS(status);
            break; }

        case kSplitLightning: {
            status = doLightningSplit(meshFn);
            CHECK_STATUS(status);
            break; }

        default:
            status = MS::kFailure;
            break;
        }

        return status;
    }

    MStatus meshOpFty::doLightningSplit(MFnMesh& meshFn)
    //
    //  Description:
    //      Performs the kSplitLightning operation on the selected mesh
    //      and components. It may not split all the selected components.
    //
    {
        unsigned int i, j;

        // These are the input arrays to the split function. The following
        // algorithm fills them in with the arguments for a continuous
        // split that goes through some of the selected faces.
        //
        MIntArray placements;
        MIntArray edgeIDs;
        MFloatArray edgeFactors;
        MFloatPointArray internalPoints;

        // The following array is going to be used to determine which faces
        // have been split. Since the split function can only split faces
        // which are adjacent to the earlier face, we may not split
        // all the faces
        //
        bool* faceTouched = new bool[fComponentIDs.length()];
        for (i = 0; i < fComponentIDs.length(); ++i)
            faceTouched[i] = false;

        // We need a starting point. For this example, the first face in
        // the component list is picked. Also get a polygon iterator
        // to this face.
        //
        MItMeshPolygon itPoly(fMesh);
        for (; !itPoly.isDone(); itPoly.next())
        {
            if (fComponentIDs[0] == (int)itPoly.index()) break;
        }
        if (itPoly.isDone())
        {
            // Should never happen.
            //
            delete [] faceTouched;
            return MS::kFailure;
        }

        // In this example, edge0 is called the starting edge and
        // edge1 is called the destination edge. This algorithm will split
        // each face from the starting edge to the destination edge
        // while going through two inner points inside each face.
        //
        int edge0, edge1;
        MPoint innerVert0, innerVert1;
        int nextFaceIndex = 0;

        // We need a starting edge. For this example, the first edge in the
        // edge list is used.
        //
        MIntArray edgeList;
        itPoly.getEdges(edgeList);
        edge0 = edgeList[0];

        bool done = false;
        while (!done)
        {
            // Set this face as touched so that we don't try to split it twice
            //
            faceTouched[nextFaceIndex] = true;

            // Get the current face's center. It is used later in the
            // algorithm to calculate inner vertices.
            //
            MPoint faceCenter = itPoly.center();

            // Iterate through the connected faces to find an untouched,
            // selected face and get the ID of the shared edge. That face
            // will become the next face to be split.
            //
            MIntArray faceList;
            itPoly.getConnectedFaces(faceList);
            nextFaceIndex = -1;
            for (i = 0; i < fComponentIDs.length(); ++i)
            {
                for (j = 0; j < faceList.length(); ++j)
                {
                    if (fComponentIDs[i] == faceList[j] && !faceTouched[i])
                    {
                        nextFaceIndex = i;
                        break;
                    }
                }
                if (nextFaceIndex != -1) break;
            }

            if (nextFaceIndex == -1)
            {
                // There is no selected and untouched face adjacent to this
                // face, so this algorithm is done. Pick the first edge that
                // is not the starting edge as the destination edge.
                //
                done = true;
                edge1 = -1;
                for (i = 0; i < edgeList.length(); ++i)
                {
                    if (edgeList[i] != edge0)
                    {
                        edge1 = edgeList[i];
                        break;
                    }
                }
                if (edge1 == -1)
                {
                    // This should not happen, since there should be more than
                    // one edge for each face
                    //
                    delete [] faceTouched;
                    return MS::kFailure;
                }
            }
            else
            {
                // The next step is to find out which edge is shared between
                // the two faces and use it as the destination edge. To do
                // that, we need to iterate through the faces and get the
                // next face's list of edges.
                //
                itPoly.reset();
                for (; !itPoly.isDone(); itPoly.next())
                {
                    if (fComponentIDs[nextFaceIndex] == (int)itPoly.index()) break;
                }
                if (itPoly.isDone())
                {
                    // Should never happen.
                    //
                    delete [] faceTouched;
                    return MS::kFailure;
                }

                // Look for a common edge ID in the two faces edge lists
                //
                MIntArray nextFaceEdgeList;
                itPoly.getEdges(nextFaceEdgeList);
                edge1 = -1;
                for (i = 0; i < edgeList.length(); ++i)
                {
                    for (j = 0; j < nextFaceEdgeList.length(); ++j)
                    {
                        if (edgeList[i] == nextFaceEdgeList[j])
                        {
                            edge1 = edgeList[i];
                            break;
                        }
                    }
                    if (edge1 != -1) break;
                }
                if (edge1 == -1)
                {
                    // Should never happen.
                    //
                    delete [] faceTouched;
                    return MS::kFailure;
                }

                // Save the edge list for the next iteration
                //
                edgeList = nextFaceEdgeList;
            }

            // Calculate the two inner points that the split will go through.
            // For this example, the midpoints between the center and the two
            // farthest vertices of the edges are used.
            //
            // Find the 3D positions of the edges' vertices
            //
            MPoint edge0vert0, edge0vert1, edge1vert0, edge1vert1;
            MItMeshEdge itEdge(fMesh, MObject::kNullObj );
            for (; !itEdge.isDone(); itEdge.next())
            {
                if (itEdge.index() == edge0)
                {
                    edge0vert0 = itEdge.point(0);
                    edge0vert1 = itEdge.point(1);
                }
                if (itEdge.index() == edge1)
                {
                    edge1vert0 = itEdge.point(0);
                    edge1vert1 = itEdge.point(1);
                }
            }

            // Figure out which are the farthest from each other
            //
            double distMax = edge0vert0.distanceTo(edge1vert0);
            MPoint max0, max1;
            max0 = edge0vert0;
            max1 = edge1vert0;
            double newDist = edge0vert1.distanceTo(edge1vert0);
            if (newDist > distMax)
            {
                max0 = edge0vert1;
                max1 = edge1vert0;
                distMax = newDist;
            }
            newDist = edge0vert0.distanceTo(edge1vert1);
            if (newDist > distMax)
            {
                max0 = edge0vert0;
                max1 = edge1vert1;
                distMax = newDist;
            }
            newDist = edge0vert1.distanceTo(edge1vert1);
            if (newDist > distMax)
            {
                max0 = edge0vert1;
                max1 = edge1vert1;
            }

            // Calculate the two inner points
            //
            innerVert0 = (faceCenter + max0) / 2.0;
            innerVert1 = (faceCenter + max1) / 2.0;

            // Add this split's information to the input arrays. If this is
            // the last split, also add the destination edge's split information.
            //
            placements.append((int) MFnMesh::kOnEdge);
            placements.append((int) MFnMesh::kInternalPoint);
            placements.append((int) MFnMesh::kInternalPoint);
            if (done) placements.append((int) MFnMesh::kOnEdge);

            edgeIDs.append(edge0);
            if (done) edgeIDs.append(edge1);

            edgeFactors.append(0.5f);
            if (done) edgeFactors.append(0.5f);

            MFloatPoint point1((float)innerVert0[0], (float)innerVert0[1],
                (float)innerVert0[2], (float)innerVert0[3]);
            MFloatPoint point2((float)innerVert1[0], (float)innerVert1[1],
                (float)innerVert1[2], (float)innerVert1[3]);
            internalPoints.append(point1);
            internalPoints.append(point2);

            // For the next iteration, the current destination
            // edge becomes the start edge.
            //
            edge0 = edge1;
        }

        // Release the dynamically-allocated memory and do the actual split
        //
        delete [] faceTouched;
        return meshFn.split(placements, edgeIDs, edgeFactors, internalPoints);
    }