一半君的总结纸

听话只听一半君

#05 How do I get the normal vector for a polygon face or vertex?

Querying Per-Vertex Per-Face Normals

There is a “polyNormalPerVertex” command which allows you to query the vertex normal for each associated face:

polyNormalPerVertex -q -xyz;
// Result: 0 1 0 0 0 1 -1 0 0 //

These results indicate that the vertex is associated with three faces. The normals are:

{ 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { -1.0, 0.0, 0.0 }

You could average these three to obtain the shared vertex normal, if desired.

Unfortunately, this doesn’t tell you which normal correlates to which associated face. To really know you must work with .vtxFace components.

// Convert a .vtx component to .vtxFace components.
string $plcc[] = `polyListComponentConversion -fv -tvf pCube1.vtx[2]`;

// Expand the list to avoid Maya's component compression
$plcc = `filterExpand -sm 70 -ex true $plcc`;

for ( $vtxFace in $plcc )
{
  // Get the per-vertex per-face normal for this component
  float $normal[3] = `polyNormalPerVertex -q -xyz $vtxFace`;
  print(  $vtxFace + ": { " + $normal[0] + ", " + $normal[1] + ", " + $normal[2] + " }\n" );
}

Example run:

pCube1.vtxFace[2][0]: { 0, 0, 1 }
pCube1.vtxFace[2][1]: { 0, 1, 0 }
pCube1.vtxFace[2][5]: { -1, 0, 0 }

Using ‘polyInfo’

This tip is credited to Joseph A. Hansen (Beyond Games).

The polyInfo command has a ‘-faceNormal’ flag which can be used to obtain the vector for a face. Two quirks with this method:

  1. The result is returned as an annotated string (actually a string array, just to be difficult); and,
  2. The normal isn’t normalized.

We can work around both of these with a little helper procedure.

polyInfo -fn pSphere1.f[7];
// Result: FACE_NORMAL      7: -0.090404 -0.135299 0.017982 //

polyInfo -fn pCube1.f[0];
// Result: FACE_NORMAL      0: 0.000000 0.000000 2.000000 //

Here’s our helper script:

proc vector translatePolyInfoNormal( string $pin )
{
  vector $normal;
  float $x;
  float $y;
  float $z;

  string $tokens[];
  int $numTokens = `tokenize $pin " " $tokens`;

  // Make sure we're looking at polyInfo data:
  if ( ( $numTokens > 3 ) && ( $tokens[0] == "FACE_NORMAL" ) )
  {
    // Maya performs data-type conversion here.
    $x = ($tokens[$numTokens-3]);
    $y = ($tokens[$numTokens-2]);
    $z = ($tokens[$numTokens-1]);

    $normal = << $x, $y, $z >>;

    // Normalize it.
    $normal = `unit $normal`;
  }

  // Return it.
  return $normal;
}

Now let’s put it to use:

string $sphereInfo[] = `polyInfo -fn pSphere1.f[7]`;
// Result: FACE_NORMAL      7: -0.090404 -0.135299 0.017982

vector $sphereNormal = translatePolyInfoNormal( $sphereInfo[0] );
// Result: <<-0.552209, -0.826438, 0.109838>>  //

string $cubeInfo[] = `polyInfo -fn pCube1.f[7]`;
// Result: FACE_NORMAL      5: -2.000000 0.000000 0.000000

vector $cubeNormal = translatePolyInfoNormal( $cubeInfo[0] );
// Result: <<-1, 0, 0>>  //

Using The Cross Product

// Pick a face, any face
string $node = "polySurface1";
int $face = 0;

// Get vertex list for face
int $vertices[] = facetVertices( $node + ".f[" + $face + "]" );

// Get coordinates for each of the three vertices
vector $vector[3];
for ( $p = 0; $p < 3; $p++ )
{
  float $coord[3];

  $command = "xform -q -ws -t " + $node + ".vtx[" + $vertices[$p] + "]";
  $coord = `eval $command`;
  $vector[$p] = << $coord[0], $coord[1], $coord[2] >>;
}

vector $v0, $v1, $v2;
// Reassign to non-array variables to allow access
// to vector components (e.g. vector.x)
$v0 = $vector[0];
$v1 = $vector[1];
$v2 = $vector[2];

// Get two vectors for surface definition, from the 3 verts.
vector $def[2];
$def[0] =
  <<     $v0.x - $v2.x,     $v0.y - $v2.y,     $v0.z - $v2.z   >>;

$def[1] =
  <<     $v1.x - $v2.x,     $v1.y - $v2.y,     $v1.z - $v2.z   >>;

// Get normal
vector $normal;

// The cross product provides the normal for the surface defined from these two vectors
$normal = `cross $def[0] $def[1]`;

This script only considers the first three vertices for the given face. If your face is planar, this is fine. If your face is comprised of more than three vertices and it is not planar then the results will be inaccurate.

Be aware that the direction of the surface normal is dependent on the direction of your edge vectors. To ensure accurate results you’ll want to make sure that the vertices are queried in render order.

Querying The ‘mesh’ Node

On a whim, I tried to see if it was possible to query one of the ‘vertexNormal’ attributes of the ‘mesh’ DG node to obtain this information directly:

  getAttr pSphere1.vn[0].vfnl[0].fnxy;
  // Result: 100000002004087730000 100000002004087730000 100000002004087730000 //

Given the results, I think not.

References for Cross Product

Vectors and Vector Arithmetic

Eric Weisstein’s World of Mathematics – Cross Product

Acknowledgement

Joseph A. Hansen, Tools Manager, Beyond Games

Related How-To’s

22 February 2003

Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: