There have been times when I was rigging a character and I and thought to myself, "I wish I could save my joint data or skin weights without using a Maya Binary or an Image file." when it comes to exporting and importing skin weights Maya does have a built in command to handle this. However, using Maya's vanilla export and import weights command has always been hit or miss in my experience. I thought it would be a good idea to take some time to explain the benefits serializing data as shown to me by Technical Director Nick Woo!
Note, this post isn't a guided tutorial with code snippets. I will make a video tutorial on how to create a tool like this later for my YouTube Channel.
The Issues With Maya's Native Export/Import Skin Weights Tool
In a class that I was demonstrating this function to, a student asked me "What's wrong with the way Maya deals with exporting/importing the skin weight values?" This prompted a valuable dialogue for the class. While there isn't anything innately wrong with how Maya handles it, it is prone to error.
This is because the command outputs the weight values into an image file. More often than not image files fall victim to something called compression. If an image file is being compressed and transferred enough times you will most likely end up with a degraded image. Another inherent problem with baking weight values into this format is that they depend on the UV's of the model. Which can be changed at anytime for any reason. This is where data serialization comes in and saves the day.
What to expect from our custom skin weight export tool
One of the first things we will notice about the export weights command, is that it outputs an image file for every bound joint multiplied by the number bound mesh. That means if our rig has 50 joints and the model has 5 separate meshes, the command will export 250 image files. That's a lot of files to keep track of!
We can write a tool that records each mesh's vertices, its given influences (joints), and the weight values applied the vertices. All of that data will be contained in a single file. Whats more important is that this method of exporting weights is as accurate as it gets. Instead of relying on pixel values on an image file this method provides the exact weight values, and it's not all tied to UV's. Essentially we recreate the smooth skin spreadsheet from the component editor window. With some extra data to help properly reconstruct the skin cluster node.
What data needs to be serialized
In order to successfully reconstruct the skin cluster node we need to serialize the required data. First off, we need the shape node of the bound mesh. we will also need to query a list of all the influences. For each influence we need to get the weight values. Then the blend weight values, just in case we are taking advantage of dual quaternion method. Which if we are dealing twisting mesh parts then dual quaternion is what I would recommend using. Next The last two data points we need are the skinning method value and the normalize weights value.
We should construct our data object to look something like this.
Data = {
Shape Node [array]
Weights [array]
Blend Weights [array]
Skinning Method [string]
Normalize Weights [string]
}
Now lets look at how to access the different data values.
How to access the data
There are multiple ways to write a tool that writes the required data to disk. You could write a python script that accesses the data from the skin cluster node. this can be achieved by just importing the Maya command module. This involves higher level access to the Maya Application Core. Which will work, but it is not as efficient as it could be. We want to be able to write our tool in Python with low level access to the Maya Application Core. The lower the access the faster the execution of the code will be. So we will write the tool using Maya Python API.
I would also highly recommend writing this tool using Object Oriented Programming. This makes it so we can handle the data more cleanly in my opinion. Using classes instead of functional programming also allows us to update and maintain our code with so much more ease. It's also how my teacher taught me to write these kinds of tools. If it's good enough for Nick. it's good enough for us.
The Shape Node
Accessing the shape node is actually straight forward. This is where we can use a simple python function to access a list of selected objects in the scene. The function will parse through the selection and return the shape node directly attached to the skin cluster node.
A brief list of node types to filter:
- nurbsCurve Nodes
- nurbsSurface Nodes
- transform Nodes
- intermediate Nodes
Weights
This is where we will start to bring in Maya Python API. For gathering the weights we will need to use the Maya Python API SkinCluster function set. First we need to construct the function that will gather the weight values for each vertex per influence. To do this we'll need access to the deformerSet node and the skinCluster node. The deformer set stores the components of the deformed object as members in this node. We'll use the getMembers() method from the MFnSet function set to pull a list of the members from the deformerSet node.
Now that we have a list of the members we can get the weight values for each of them. This will be another function that we have to construct. To access the weights we'll be specifically using the MFnSkinCluster.getWeights() method. Take the time to note and fully understand how the weight array is packed. if we don't unpack the data correctly the tool will not work as intended and the remapped data will be useless. Also consider removing the namespace for each influence. This should make this tool more adaptable for different kinds of pipelines.
- Iterate over each influence.
- remove namespace.
- gather weights for EACH component.
Blend Weights
Gathering the data for this is relatively straight forward. We will use the MFnSkinCluster.getBlendWeights() method. next let’s iterate over each component to gather this data and store it in a Python list.
Skinning Method & Normalize Weights
These last two data points are easy. All we need to do is write a simple python function that will look up these attributes in the skinCluster node. Once the attributes have been parsed we will store their values as a string in Python.
Actually exporting and importing the data object
Now for actually exporting the skin cluster data. We'll need to work with the JSON library. The huge python dictionary that we constructed with all the skin cluster data is going to be considered a data object. Now we just use JSON's dump function to write that data object to disk.
As for importing and reconstructing the node we need to keep two scenarios in mind. Does the mesh we want to import our data to already have a skinCluster node attached? Or does it have clean history and needs to have a new skinCluster node created.
If the selected mesh already has a skinCluster node attached to it then we should only have to update the node with the values in the JSON file. Essentially we will just be using setWeights() and setMembers() methods instead.
If the selected mesh has a clean history and no skinCluster node is attached. Then we need to create a new skinCluster node with the appropriate influences and weight values. And then we import the skin cluster data into the appropriate nodes.
Wrapping everything up for the user
After we have finished accessing all of the data, and creating all the necessary functions, we simply write a GUI wrapper for our script. This will make it so riggers can intuitively work with our tool. I remember my teacher Nick telling me it's always good practice provide team members with a GUI for any tool you make.
Now, Maya allows for multiple ways to wrap our code. We can use Maya's UI class in the API. Or we can use PyQt and PySide 2. I cannot recommend using PyQt enough. "Why PyQt?" you may ask. Because PyQt is the sleek and beautiful GUI that Maya has been written with since 2013. Trust me, it's worth learning.
Komentáře