March 2019: ewertb.soundlinker.com is getting relocated to ewertb.mayasound.com, please update your bookmarks.

Maya API How-To #09

Back · Previous · Next Maya

How do I extract animated polymesh vertex coordinates?

Preamble

This How-To is the direct result of my being stonewalled for three days while trying to determine how to do this (seemingly) simple feat without causing Maya to crash and burn at every turn.

I express sincere gratitude towards Justin Miller (DreamWorks) and Mark McGrevy (SONY) for their helpful advice. Justin suggested the MDGContext technique, and Mark explained for me why my original code wasn't working. Thanks also to William McCullough for providing the Highend3D Maya forums, without which I'd still be stuck on this problem.

Method #1: Advancing Time

This technique is straight-forward and intuitive. Start at the first frame of the animation (acquired from MAnimControl::minTime()) and march through each consecutive frame until you reach the end of the animtion (MAnimControl::maxTime()). At each time stop, grab the coordinates for the vertices (using MFnMesh::getPoints()).

There seems to be a consensus amongst developers for using MGlobal::viewFrame() over MAnimControl::setCurrentTime(). The former forces a refresh of Maya's interactive view whereas the latter simply flags it as "dirty." The opinion is that ::viewFrame() is the more reliable, and who am I to argue with the experts?

The following class method can be used to query vertex coordinates at a given frame (or "time" if you prefer):

// ************************************************************************************************
//    Function: GetPointsAtTime
//
// Description: Sets Maya to the specified Time and gets the object-space vertex coordinates
//              from the specified DAG at the that Time.
//
//       Input: const MDagPath& dagPath: The DAG path for the mesh object.
//              const MTime& mayaTime: The Time at which to query the vertex coordinates.
//              MPointArray& points: Storage for the array of vertex coordinates.
//
//      Output: (MStatus): MS::kSuccess if it worked; else MS::kFailure.
// ************************************************************************************************
MStatus myPlugIn::GetPointsAtTime(
    const MDagPath& dagPath,
    const MTime& mayaTime,
    MPointArray& points )
{
  MStatus                               status = MS::kSuccess;

  points.clear();

  MFnMesh                               fnMesh;

  // Move Maya to current frame
  MGlobal::viewFrame( mayaTime );

  // You MUST reinitialize the function set after changing time!
  fnMesh.setObject( dagPath );

  // Get vertices at this time
  status = fnMesh.getPoints( points );

  return status;
}

Below is an example use of the GetPointsAtTime function:

MStatus myPlugIn::doIt( const MArgList& args )
{
  MStatus                               status = MS::kSuccess;

  MDagPath                              dagPath;

  // .. determing dagPath from current selection, or whatever .. //

  MPointArray                           points;

  MTime                                 currentTime, maxTime;

  // Get start- and end-frame from Maya
  currentTime = MAnimControl::minTime();
  maxTime = MAnimControl::maxTime();

  // Iterate through time
  while ( currentTime <= maxTime )
  {
    // Get vertices at this time
    status = GetPointsAtTime( dagPath, currentTime, points );

    // .. do something with the points here .. //

    // Advance by one frame
    currentTime++;
  }

  return status;
}

Method #2: Using Time in Context

There is one important consideration for using the "Advancing Time" technique for extracting vertex animation: Performance. Maya's API documentation warns that ::viewFrame() is slow. This is understandable as it forces a refresh for the entire dependency graph, even for the nodes that have no influence on the mesh object you are probing.

The recommended alternative is to "create an instance of the MDGContext class initialized to the time you are interested in" and "pass it to the getValue method of the MPlug class."

So how can you get vertex coordinates by querying an attribute? Every mesh has an attribute with this exact information: ‘.outMesh’. At first it may not be clear how to extract the necessary information from this plug. If you query this attribute via MGlobal::getFunctionSetList() it reveals that the MObject returned via MPlug::getValue() supports the following function sets:

[kBase, kMesh, kData, kMeshData, kGeometryData]

Thus, conveniently, you can use the MFnMesh function set and treat the plug like it was the mesh itself.

The following class method shows how to use MPlug and MDGContext to query vertex coordinates at a given time:

// ************************************************************************************************
//    Function: GetPointsAtTimeContext
//
// Description: Gets the object-space vertex coordinates from the specified DAG at
//              the specified Time.
//
//       Input: const MDagPath& dagPath: The DAG path for the mesh object.
//              const MTime& mayaTime: The Time at which to query the vertex coordinates.
//              MPointArray& points: Storage for the array of vertex coordinates.
//
//      Output: (MStatus): MS::kSuccess if it worked; else MS::kFailure.
// ************************************************************************************************
MStatus myPlugIn::GetPointsAtTimeContext(
    const MDagPath& dagPath,
    const MTime& mayaTime,
    MPointArray& points )
{
  MStatus                    status = MS::kSuccess;

  points.clear();

  MFnDependencyNode          fnDependNode( dagPath.node(), &status );

  MPlug                      plugMesh;
  MObject                    meshData;

  // Get the .outMesh plug for this mesh
  plugMesh = fnDependNode.findPlug( MString( "outMesh" ), &status );

  // Get its value at the specified Time.
  status = plugMesh.getValue( meshData, MDGContext( mayaTime ) );

  // Use its MFnMesh function set 
  MFnMesh                    fnMesh( meshData, &status );

  // And query the point coordinates
  status = fnMesh.getPoints( points );

  return status;
}

Related How-To's

30 March 2003