The link provided above does indeed tell you what you want to know. I had to do this quite recently so here’s some snippets and such for completeness sake.
NOTE: I am assuming you want per vertex tangent matrices ignoring UV seams.
This is about getting an arbitrary average normal for a vertex and a relevant tangent matrix based on that normal.
If uvs are important you want to use face-vertices and maya’s MFnMesh::getNormals / MFnMesh::getTangents functions.
This is not a very trivial thing. If you construct the tangents by yourself rather than having maya do it you’ll be getting the most stable results. A clean UV map is a requirement however…
Maya’s mesh lacks a large amount of self awareness, most data is per face-vertex but there is no proper transitioning between vertex and face-vertices and not good way to get averaged data per vertex.
Best is to use a MItMeshPolygon to iterate the polygons.
MObject sInMeshAttr is the typed kMesh attribute, MDataBlock data is the io data as passed to compute
MItMeshPolygon inMeshIter(data.inputValue(sInMeshAttr).asMesh());
Then we need to iterate the individual triangles to get accurate tangent data
MIntArray parsed;
while(!inMeshIter.isDone())
{
MPointArray points;
MIntArray vertices;
inMeshIter.getTriangles(points, vertices);
MFloatArray u;
MFloatArray v;
inMeshIter.getUVs(u, v);
Now that we know the points, uvs ad vertex indices per triangle vertex we can start getting the tangent per triangle
and use that tangent for all vertices in the triangle. If a vertex is split and has multiple we can only use one of the
uv-space-triangles to get a 3D tangent per vertex.
This bit iterates each triangle of the poylgon’s triangulation (3 points)
and extracts the barycentric coordinates for the tangent.
for(unsigned int i = 0 ; i < vertices.length() ; i += 3)
{
// Taking UV coordinates [i-(i+2)] as our triangle and
// the unitX (1,0) as point to get barycentric coordinates for
// we can get the U direction in barycentric using the function
// from this site:
// http://www.blackpawn.com/texts/pointinpoly/
double u02 = (u[i + 2] - u[i]);
double v02 = (v[i + 2] - v[i]);
double u01 = (u[i + 1] - u[i]);
double v01 = (v[i + 1] - v[i]);
double dot00 = u02 * u02 + v02 * v02;
double dot01 = u02 * u01 + v02 * v01;
double dot11 = u01 * u01 + v01 * v01;
double d = dot00 * dot11 - dot01 * dot01;
double u = 1.0;
double v = 1.0;
if(d != 0.0)
{
u = (dot11 * u02 - dot01 * u01) / d;
v = (dot00 * u01 - dot01 * u02) / d;
}
Now to get the 3D tangent all we need to do is apply the barycentric coordinates to the 3D points:
MVector tangent = points[i + 2] * u + points[i + 1] * v - points[i] * (u + v);
Next we iterate over the three vertices individually.
for(unsigned int j = i; j < i + 3 ; ++j)
Here we use MFnMesh::getVertexNormal for the average normal (whether you want angle-weighted depends on what you’re doing, I often don’t use them).
Having the average normal and triangle tangent we can use the cross product for the binormal, cross the normal & binormal again to get a proper
perpendicular tangent, because the normal is average and the tangent is not the initial tangent was wrong.
inMesh.getVertexNormal(vertices[j], false, normal);
binormal = tangent ^ normal;
binormal.normalize();
tangent = binormal ^ normal;
tangent.normalize();
Last we can store the vertex index in ‘parsed’ and skip future faces that use the same vertex
parsed.append(vertices[j]);
A check for that list then needs to be at the start of the per-vertex loop.
The matrix data is now simply:
{{tangent[0], tangent[1], tangent[2], 0},
{binormal[0], binormal[1], binormal[2], 0},
{normal[0], normal[1], normal[2], 0},
{point[0], point[1], point[2], 0}}
Hope that helps…