Good news, MASH only uses Maya’s default data types, so all MASH data can be accessed via normal Maya APIs.
Ordinarily this isn’t needed as MASH provides two built in ways to extract data:
- For the non scripter there’s the Breakout node, which can feed MASH data into float/vector attributes in Maya via connections.
- The Python node gives users access to the raw data which they can extract and manipulate in any way they like.
However, for the performance conscious, or for those wanting to write their own MASH nodes or export MASH data in some custom/ game engine format, it might help to know a few things about Maya’s data types and APIs, so here are some simple recipes to get you going. In the following example we’ll get a MASH node (a Waiter), get it’s output data, and print a list of all the channels it contains along with the MASH point positions.
Before we get started, please remember, data getting/setting in Maya should only be done with connections otherwise Parallel Evaluation performance will suffer, so what I’m about to show you should never be run in an expression node.
MASH Data can only be extracted with either the CAPI or PyAPI 1.0. The PyAPI 2.0 isn’t finished yet and thus can’t be used with MASH.
Step 1: Get the MObject for the MASH node.
1 2 3 4 5 6 7 8 9 10 |
import maya.OpenMaya as om # Create an empty selection list sel = om.MSelectionList() # Add the waiter (named "MASH") to the selection list sel.add("MASH") # Create an empty MObject mashNode = om.MObject() # Get the Waiter's node object, and save it to the empty MObject sel.getDependNode( 0, mashNode ) |
Step 2: Get a list of channel data and print it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# Attach a function set to the MASH node mashNodeFn = om.MFnDependencyNode(mashNode) # Find the attribte that we want pointsAttribute = mashNodeFn.attribute("inputPoints") # Get the attribute plug pointsPlug = om.MPlug(mashNode, pointsAttribute) # Get the plug data handleData = pointsPlug.asMDataHandle().data(); # Attach an array attribute function set to the handle data inputPointsData = om.MFnArrayAttrsData(handleData) # Get the names of every channel in the data channels = inputPointsData.list() # Print them print channels |
Step 3: Get a specific channel. In C++ getting the channel data type is trivial, however I’ve never managed to do this in Python (it seems the data type enum get’s mangled when the Python API is auto-generated), so a bit of educated guessing on your part may be needed.
A. Double channel eg. Visibility.
1 2 3 4 5 |
# Get the visibility double data and return a copy of it # WARNING NO ERROR CHECHING doubleList = inputPointsData.getDoubleData("visibility")[:] # Print it print doubleList |
B. Vector array, e.g. Position.
First we need a function to copy the MVectorArray into a Python list:
1 2 3 4 5 6 7 8 9 10 |
def channelToList(channel): if channel == None or channel.length() == 0: return if channel.__class__.__name__ == "MVectorArray": listOfVectors = [] for i in range (0, channel.length(), 1): vec = (channel[i].x, channel[i].y, channel[i].z) listOfVectors.append(vec) return listOfVectors |
Now we can use that to get the data:
1 2 3 4 5 6 |
# Get the dynamic array data dynamicArray = inputPointsData.getVectorData("position") # Copy it to a list of tuples listOfVectors = channelToList(dynamicArray) # Print it print listOfVectors |
And you’re done. In C++, everything’s easier, you would check the channel data type first like so and then just copy the array:
1 2 3 4 5 |
MVectorArray position_pp; if (inputPointsData.checkArrayExist("position", dataType) && (dataType == MFnArrayAttrsData::kVectorArray)) { position_pp = inputPointsData.getVectorData("position"); } |
Any questions let me know.