Accessing Mesh Data [MEL]


 

 

 

A polygonal surface in maya is of type MFn::kMesh and is usually parented under a transform. In the above hyper-graph view, "pCube1" is the transform, "pCubeShape1" is the mesh surface itself, and that is connected to the surface shader "initialShadingGroup". As with all shapes in maya, there is no obvious connection between the transform and the surface visible in the hypergraph.

 

 

 

 

 

Finding all Meshes in the Scene

When retrieving mesh data, you will need to use either MItDependencyNodes or MItDagNode in order to loop through a list of meshes.

It is worth querying isIntermediateObject() before exporting any mesh data, if the function returns true then the node will be part of the scenes history.

 

 

 

 

 


#include<maya/MFnMesh.h>
#include<maya/MItDag.h>

// create an iterator to go through all meshes
MItDag it(MItDag::kDepthFirst,MFn::kMesh);

while(!it.isDone())
{
 

// attach the function set to the object
MFnMesh
fn(it.item());

// only want non-history items
if( !fn.isIntermediateObject() ) {

  // print mesh name
cout
<<"Mesh "<< fn.name().asChar() <<endl;

}

// get next mesh
it.next();

}




 

Vertex Co-ordinates

 

Vertex co-ordinates are stored in a single array. This array is then later referenced by the faces using a zero based index.

 

To get access to the vertex data, Simply call the getPoints members function of MFnMesh.

 

 

 

 

 

 

 


#include<maya/MFnMesh.h>

void outputMeshVertices(MObject& obj)
{
 

// attach the function set to the object
MFnMesh
fn(obj);

// this will hold the returned vertex positions
MPointArray vts;

// use the function set to get the points

fn.getPoints(vts);

// write number of verts
cout
<< "NumVerts " << vts.length() << endl;

// only want non-history items
for(int i=0;i!=vts.length();++i) {

  // print vertex
cout << vts[i].x <<" "
<< vts[
i].y <<" "
<< vts[
i].z << "\n";

}

}


 

 

Vertex Normals

 

Normal vectors are stored in a single array. This array is then later referenced by the faces using a zero based index.

 

The getNormals function of MFnMesh allows you to retrieve an array of normals used in this mesh.

 

 

 

 

 

 

 


#include<maya/MFnMesh.h>

void outputMeshNormals(MObject& obj)
{
 

// attach the function set to the object
MFnMesh
fn(obj);

// this will hold the returned vertex positions
MFloatVectorArray nmls;

// use the function set to get the points

fn.getNormals(nmls);

// write number of verts
cout
<< "NumNorms" << nmls.length() << endl;

// only want non-history items
for(int i=0;i!=nmls.length();++i) {

  // print normal
cout << nmls[i].x <<" "
<< nmls[
i].y <<" "
<< nmls[
i].z << "\n";

}

}




 

Texture Co-ordinates

 

A mesh may hold a varying number of uv sets. This means that you will have to deal with the possibility of no texture co-ordinates on a mesh, or optionally, more than one set.

A small problem rears it's ugly head here. If the mesh has no uv sets, the MayaAPI will still return one to you. To get around this problem, it is worth querying the length of the first uv set to see if it actually contains any data before writing out that UV Set.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


#include<maya/MFnMesh.h>

bool outputMeshTexCoords(MObject& obj)
{
 

// attach the function set to the object
MFnMesh
fn(obj);

MStringArray uvsets;

// get the names of the uv sets on the mesh
fn.getUVSetNames(uvsets);

// This is an annoying hack. if the mesh has no uv
// coords, it always seems to return a uv set. We
// therefore want to check for this so it causes us
// less hassle when writing the indices later

if( !uvsets.length() || !fn.numUVs(uvsets[0]) ) {

  cout << "0\n";

}

cout << uvsets.length() << endl;

// write each tex coord
for(int j=0;j!=uvsets.length();++j) {

 

//output uvset name
cout
<< uvsets[i].asChar() << endl;

// two arrays for the uv texturecoords
MFloatArray u_coords;
MFloatArray v_coords;

// get the uv data

fn.getUVs(u_coords,v_coords,&uvsets[i]);

// write number of texture coordinate
cout << "NumUvs "
<< fn.
numUVs(uvsets[i]) << "\n";

// write each tex coord
for(int i=0;i!=fn.numUVs(uvsets[i]);++i) {

  // print tex coord
cout << u_coords[i] <<" "
<< v_coords[
i] <<"\n";

}

}

}





 

Face Data

 

Accessing face data from a mesh requires the use of an additional iterator class, MItMeshPolygon.

 

Each face is comprised of a number of indices that reference the vertex, normal and the various uv sets applied to the mesh.

One small annoyance is a little problem to do with UV coordinates. If a mesh is created with no uv sets, it actually creates a single uv set regardless. This uv set contains no texture coord data, and any attempt to access a UV index for that uv set will fail. To get around the problem, a single usage flag (bUvs) has been used to check to see if the first uv set actually contains any uvs.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


#include<maya/MFnMesh.h>
#include<maya/MStringArray.h>
#include<maya/MItMeshPolygon.h>

void outputMeshFaces(MObject& obj)
{
 

// we need to get the uv sets used on this mesh. This
// will tell us how many uv indices to read from the
// faces
MFnMesh fn(obj);
MStringArray sets;

// get the names of the uv sets on the mesh
fn.getUVSetNames(sets);

// set a usage flag for texture coords
bool
bUvs = ((sets.length()!=0) && (fn.numUVs()>0));

// attach an iterator to the mesh

MItMeshPolygon
itPoly(obj);

// write each tex coord
while(!itPoly.isDone()) {

 

int vc = itPoly.polygonVertexCount();

//print num of verts on this poly
cout << vc << " " << endl;

// print all vertex,normal and uv indices
for(int i=0;i<vc;++i) {

 

// output indices for this vertex

cout <<itPoly.vertexIndex(i) <<" "
<<itPoly.
normalIndex(i) ;

if(bUvs) {

 

// have to get the uv index seperately
int uv_index;

// ouput each uv index

for(int k=0;k<sets.length();++k) {

 

itPoly.getUVIndex(i,uv_index,&sets[k]);

cout << " " << uv_index;

}

}

cout << endl;

}

}

}


 

 

 

Mesh Instances

Now accessing mesh data so far has been fairly trivial. When we start trying to access material & parenting information however, things become a little bit more complicated. The primary problem is that all DAG nodes within maya can have more than one DAG path (ie, they may be instanced a number of times).

The hypergraph view below illustrates this situation, the mesh pCubeShape1 has two instances. The first uses lambert1 and the transform pCube1; the second uses lambert2 and the transform pCube2.

If we attached an MFnDagNode function set to the mesh, then the member function parentCount() would return 2. This effectivly lets us determine how many instances of this object there are. We can then make use of the parent() funtion to return the parent transforms for each instance. i.e. calling parent(0) would return the MObject for pCube1, whilst parent(1) would return the MObject for pCube2.

Each instance may apply a number of different materials to different faces. We can use the getConnectedShaders member function of MFnMesh to find out this shader information for each instance.

 

 



 

 


#include < maya/MFnMesh.h >
#include < maya/MFnDependencyNode.h >
#include < maya/MPlugArray.h >
#include < maya/MObjectArray.h >
#include < maya/MIntArray.h >

MString GetShaderName(MObject shadingEngine)
{
	// attach a function set to the shading engine
	MFnDependencyNode fn( shadingEngine );

	// get access to the surfaceShader attribute. This will be connected to
	// lambert , phong nodes etc.
	MPlug sshader = fn.findPlug("surfaceShader");

	// will hold the connections to the surfaceShader attribute
	MPlugArray materials;

	// get the material connected to the surface shader
	sshader.connectedTo(materials,true,false);

	// if we found a material
	if(materials.length())
	{
		MFnDependencyNode fnMat(materials[0].node());
		return fnMat.name();
	}
	return "none";
}


void OutputMeshInstances(MObject mesh)
{
	MFnMesh fnMesh(mesh);

	// get the number of instances
	int NumInstances = fnMesh.parentCount();

	cout << "\tnumMeshInstances " << NumInstances << endl;

	// loop through each instance of the mesh
	for(int i=0;i< NumInstances;++i)
	{
		// attach a function set to this instances parent transform
		MFnDependencyNode fn( fnMesh.parent(i) );

		// write the name of the parent transform
		cout << "\t\tparent " << fn.name().asChar() << endl;

		// this will hold references to the shaders used on the meshes
		MObjectArray Shaders;

		// this is used to hold indices to the materials returned in the object array
		MIntArray    FaceIndices;

		// get the shaders used by the i'th mesh instance
		fnMesh.getConnectedShaders(i,Shaders,FaceIndices);

		switch(Shaders.length()) {

		// if no shader applied to the mesh instance
		case 0:
			{
				cout << "\t\tmaterials 0\n";
			}
			break;

		// if all faces use the same material
		case 1:
			{
				cout << "\t\tmaterials 1\n";
				cout << "\t\t\t"
					<< GetShaderName( Shaders[0] ).asChar()
					<< endl;
			}
			break;

		// if more than one material is used, write out the face indices the materials
		// are applied to.
		default:
			{
				cout << "\t\tmaterials " << Shaders.length() << endl;

				// i'm going to sort the face indicies into groups based on
				// the applied material - might as well... ;)
				vector< vector< int > > FacesByMatID;

				// set to same size as num of shaders
				FacesByMatID.resize(Shaders.length());

				// put face index into correct array
				for(int j=0;j < FaceIndices.length();++j)
				{
					FacesByMatID[ FaceIndices[j] ].push_back(j);
				}

				// now write each material and the face indices that use them
				for(int j=0;j < Shaders.length();++j)
				{
					cout << "\t\t\t"
						<< GetShaderName( Shaders[j] ).asChar()
						<< "\n\t\t\t"
						<< FacesByMatID[j].size()
						<< "\n\t\t\t\t";

					vector< int >::iterator it = FacesByMatID[j].begin();
					for( ; it != FacesByMatID[j].end(); ++it )
					{
						cout << *it << " ";
					}
					cout << endl;
				}
			}
			break;
		}
	}
}
			  


 

 

What Next?

Material Data

Transformation Data

Blend Shape Deformers

Soft Skinned Mesh Surfaces

Rigid Skinned Mesh Surfaces

Lattice Deformers

index

Rob Bateman [2004]

[HOME] [MEL] [API]