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:
The result is returned as an annotated string (actually a string array, just to be difficult); and,
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:<>//
string $cubeInfo[] = `polyInfo -fn pCube1.f[7]`;
// Result: FACE_NORMAL 5: -2.000000 0.000000 0.000000
vector $cubeNormal = translatePolyInfoNormal( $cubeInfo[0] );
// Result:<>//
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
{
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
M Casco Associates - Vector Arithmetic
Eric Weisstein's World of Mathematics - Cross Product
Acknowledgement
Joseph A. Hansen, Tools Manager, Beyond Games
Related How-To's
How can I ensure that a polygon face component is planar?
How can I determine if a polygon face component is planar?
How can a get a list of all vertex or edge components from a polymesh in proper counter-clockwise or "render" order?