C++ API Reference
meshOpCmd/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)
{
for (i = 0; i < fComponentIDs.length(); ++i)
{
int2 vertices;
meshFn.getEdgeVertices(fComponentIDs[i], vertices);
adjacentVertexList.append(vertices[0]);
adjacentVertexList.append(vertices[1]);
}
break;
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], true, vertexNormal,
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);
}