MEL How-To #55

Back · Previous · Next Maya

How can a get a list of all vertex or edge components from a polymesh in proper counter-clockwise or "render" order?

A polymesh face

The image at right depicts a selected polymesh face component. If we were to query this face using the API we would learn that Maya constructs this face in the vertex order: 295, 12, 14, 296. From this, logic will yield an "edge construction order" of: 582, 19, 584, 585.


Deriving Vertex Order From A Face

polyInfo

The "polyInfo" command, when specified with its `−faceToVertex` flag, returns a string result which lists the vertex order for a face in the proper construction order. While tokenizing a string result is not an ideal way to query numeric results, it does get the job done, and is less work than futzing with vertex-face components (as described below).

string $ftv[] = `polyInfo -faceToVertex m26_section.f[278]`;
// Result: FACE    278:    295     12     14    296 //

To extract the values in order to use them, use the tokenize command on the string. In the example below the string is split by stripping its space (" "), colon (":"), and carriage-return/newline ("\n\r") characters. This results in six tokens - the first of which is the string "FACE", the second being the index of the face, and the remaining four the indices for the vertices.

string $tokens[];
tokenize $ftv[0] " :\n\r" $tokens;
// Result: 6 //

int $vertexOrder[];
clear $vertexOrder;
int $t;

for ( $t = 2; $t < `size $tokens`; $t++ )
{
  $vertexOrder[`size $vertexOrder`] = $tokens[$t];
};

print $vertexOrder;
// Prints: 295 12 14 296 //

polyListComponentConversion

You can use the polyListComponentConversion command in a round-about fashion to derive the counter-clockwise order (or "render order", or "construction order") for the edges and vertices that comprise a polymesh face component.

Joey Ponthieux (Video Applications Group, NASA Langley Research Center) struck upon using polyListComponentConversion to query "vertexFace" components and, through a series of conversions, this will yield a list of components in the desired order.

// This face
ls -sl;
// Result: m26_section.f[278] //

// Use polyListComponentConversion to get .vtxFace components
string $vtxFace[] = `polyListComponentConversion -fromFace
                                                 -toVertexFace m26_section.f[278]`;
// Result: m26_section.vtxFace[295][278]
           m26_section.vtxFace[12][278]
           m26_section.vtxFace[14][278]
           m26_section.vtxFace[296][278] //

You can see the pattern in the order emerging. Now we just need to get this represented in a more direct form. Use the polyListComponentConversion again to convert each of these back to a vertex component. You must do this conversion one component at a time; a conversion en masse will revert to numerical order, defeating the intentions of this exercise.

// An array to store our result
string $vertexOrder[];

for ( $vf in $vtxFace )
{
  // Get vertex for this vtxFace
  string $vertex[] = `polyListComponentConversion -fromVertexFace -toVertex $vf`;

  // And append to our result
  $vertexOrder[ size($vertexOrder) ] = $vertex[0];
}

print $vertexOrder;

// Result: m26_section.vtx[295]
           m26_section.vtx[12]
           m26_section.vtx[14]
           m26_section.vtx[296] //

Expanding Results In "Range" Form

The polyListComponentConversion command may return its results in compressed "range" form:

ls -sl;
// Result: m26_section.f[6] //

string $vtxFace[] = `polyListComponentConversion -fromFace
                                                 -toVertexFace m26_section.f[6]`;
// Result: m26_section.vtxFace[14][6]
           m26_section.vtxFace[13][6]
           m26_section.vtxFace[15:16][6] //

Therefore, it's likely best to throw a filterExpand in the mix:

// Use filterExpand to individualize each .vtxFace component in $vtxFace
$vtxFace = `filterExpand -sm 70 -expand true $vtxFace`;
// Result: m26_section.vtxFace[14][6] m26_section.vtxFace[13][6]
           m26_section.vtxFace[15][6] m26_section.vtxFace[16][6] //

for ( $vf in $vtxFace )
{
  // Get vertex for this vtxFace
  string $vertex[] = `polyListComponentConversion -fromVertexFace -toVertex $vf`;

  // And append to our result
  $vertexOrder[ size($vertexOrder) ] = $vertex[0];
}

print $vertexOrder;

// Result: m26_section.vtx[14]
           m26_section.vtx[13]
           m26_section.vtx[15]
           m26_section.vtx[16] //

Deriving Edge Order From A Face

polyInfo

The "polyInfo" command, when specified with its `−faceToEdge` flag, returns a string result which lists the edge order for a face in the proper construction order. While tokenizing a string result is not an ideal way to query numeric results, it does get the job done, and is less work than futzing with vertex-face components (as described below).

string $fte[] = `polyInfo -faceToEdge m26_section.f[278]`;
// Result: FACE    278:    582     19     584   585 //

To extract the values in order to use them, use the tokenize command on the string. In the example below the string is split by stripping its space (" "), colon (":"), and carriage-return/newline ("\n\r") characters. This results in six tokens - the first of which is the string "FACE", the second being the index of the face, and the remaining four the indices for the vertices.

string $tokens[];
tokenize $fte[0] " :\n\r" $tokens;
// Result: 6 //

int $edgeOrder[];
clear $edgeOrder;
int $t;

for ( $t = 2; $t < `size $tokens`; $t++ )
{
  $edgeOrder[`size $edgeOrder`] = $tokens[$t];
};

print $edgeOrder;
// Prints: 582 19 584 585 //

polyListComponentConversion

Using the $vtxFace array obtained in the above example, the edge construction order may be obtained from the polyListComponentConversion command by specifying its `−toEdge` flag.

// An array to store our result
string $edgeOrder[];

for ( $vf in $vtxFace )
{
  // Get edge for this vtxFace
  string $edge[] = `polyListComponentConversion -fromVertexFace -toEdge $vf`;

  // And append to our result
  $edgeOrder[ size($edgeOrder) ] = $edge[0];
}

print $edgeOrder;

// Result: m26_section.e[582]
           m26_section.e[19]
           m26_section.e[584]
           m26_section.e[585] //

Note: Joey Ponthieux explains that this conversion to edge components appears to only be reliable using Maya v3.0. In Maya 2.5 the returned edge does not necessarily correspond to the same construction order of the input vertex. In this case it is necessary to specify successive vertices and use the ‘−internal’ flag to extract the common edge.

// Modified loop to accomodate Maya v2.5
// $vertexOrder derived in method described above
for ( $v = 0; $v < size($vertexOrder); $v++ )
{
  int $nextVtx = ( $v < ( size($vertexOrder) - 1 ) ? $v + 1 : 0 );

  // Use two successive vertices to derive internal edge
  string $edge[] = `polyListComponentConversion -fromVertex -toEdge
                    -internal $vertexOrder[$v] $vertexOrder[$nextVtx]`;

  $edgeOrder[ size($edgeOrder) ] = $edge[0];
}

print $edgeOrder;

// Result: m26_section.e[582]
           m26_section.e[19]
           m26_section.e[584]
           m26_section.e[585] //

Deriving Vertex Order From An Edge

A polymesh face

The "vertex order" implied here is that which corresponds to the winding order for the face. That is, such that the indices for the vertices are ordered as required to reconstruct the face in a manner preserving its face normal.

It is possible (and likely) that an edge will be shared between two adjacent faces. Even though both faces are constructed in counter-clockwise order, their adjacent nature means that the vertex ordering for a shared edge is dependent upon for which face you are describing it. Given an edge shared by face 'A' and face 'B', the vertex order of the edge on face 'A' is exactly opposite to the vertex order of the edge on face 'B'. Therefore, for meaningful results, it is necessary to specify both an edge and a face to retrieve the vertex order for the edge.

Again referring to geometry at right, the winding order for the edges is 582, 19, 584, 585. Specifying their vertex indices in similar fashion yields an ordering, per edge, of: { 295,12 }, { 12,14 }, { 14,296 }, { 296,295 }.

Using a combination of the techniques described above it is a short stone's throw to expand the functionality of this process to derive the vertex order from an edge. The steps are as follows:

  1. Retrieve the ordered vertices for the specified face, as described in "Deriving Vertex Order From A Face".

  2. Starting at the first vertex, create pairs to determine the "internal" edge that each generates, as described in "Deriving Edge Order From A Face".

  3. Check if the derived edge matches the specified edge. If so, then the current vertex pair, in order, describes the vertex order of the specified edge within the specified face.

  4. If all vertex pairs are exhausted without finding a matching edge, then the specified edge is not a component of the specified face and the result is an empty set.


polyInfo

Left as an exercise for the reader. Ain't I a stinker?

polyListComponentConversion

The following MEL procedure takes as arguments an edge component and related face component, and returns a string array containing the ordered vertex pair for the edge.

global proc string[] edgeVertexOrder( string $inputEdge, string $inputFace )
{
  string $edgeVertexOrder[];

  string $vertexOrder[];

  // Note: polyListComponentConversion will return the components relative
  //       to the TRANSFORM node.  You may want to add an assertion/conversion
  //       for the input arguments so they are relative to a transform.
  //       If the input arguments are shape-relative then this procedure will FAIL.

  // Get vertex order of face
  string $vtxFace[] = `polyListComponentConversion -fromFace -toVertexFace $inputFace`;

  // Use filterExpand to individualize each .vtxFace component in $vtxFace
  $vtxFace = `filterExpand -sm 70 -expand true $vtxFace`;

  for ( $vf in $vtxFace )
  {
    // Get vertex for this vtxFace
    string $vertex[] = `polyListComponentConversion -fromVertexFace -toVertex $vf`;

    // And append to our result
    $vertexOrder[ size($vertexOrder) ] = $vertex[0];
  }

  // Now, check each vertex pair to see if it matches the specified edge
  for ( $v = 0; $v < size($vertexOrder); $v++ )
  {
    int $nextVtx = ( $v < ( size($vertexOrder) - 1 ) ? $v + 1 : 0 );

    // Use two successive vertices to derive internal edge

    string $edge[] = `polyListComponentConversion -fromVertex -toEdge
                    -internal $vertexOrder[$v] $vertexOrder[$nextVtx]`;

    // Does this match?
    if ( $edge[0] == $inputEdge )
    {
      $edgeVertexOrder[0] = $vertexOrder[$v];
      $edgeVertexOrder[1] = $vertexOrder[$nextVtx];

      // As soon as a match is found we can stop looking.
      break;
    }
  }

  // Warn if $inputEdge not found in $inputFace
  if ( size( $edgeVertexOrder ) == 0 )
    warning ( $inputEdge + " is not part of " + $inputFace );

  return $edgeVertexOrder;
}

An example run:

edgeVertexOrder m26_section.e[582] m26_section.f[278];
// Result: m26_section.vtx[295] m26_section.vtx[12] //

// This demonstrates that the adjacent face (.f[277]) evaluates its 
// vertex order in the opposite direction, as described in the text.
//
edgeVertexOrder m26_section.e[582] m26_section.f[277];
// Result: m26_section.vtx[12] m26_section.vtx[295] //

edgeVertexOrder m26_section.e[584] m26_section.f[277];
// Warning: m26_section.e[584] is not part of m26_section.f[277] //

Acknowledgements

  • Joey Ponthieux, Video Applications Group, NASA Langley Research Center, for demonstrating how to use "polyListComponentConversion"
  • David Biggs, for prompting me to update this How-To with "polyInfo"

Related How-To's

18 Sep 2004