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);
}

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