This post was originally going to be entitled ‘Why Everyone Writes their Own Skin Exporter’. Maya’s smooth skin weight export tool hasn’t changed since Maya 4.0 when it was introduced 15 years ago. It saves out greyscale weightmaps in UV space, one image per joint influence per mesh. The only update they have done in 15 years is change the slider to go from a max of 1024 pixels to 4096, and now 8192!
Autodesk understands the need and importance of a skin weight exporter, they even ship it as a C++ example in their Maya Developer Kit / SDK. So why are they still shipping this abomination above?
Like blind data discussed before, in pythonland we cannot set skinweights in one go, we must iterate through the entire mesh setting skin weights one vertex at a time. This means a Maya feature or C++ plugin can save and load skin weights in seconds that take python 15 minutes.
I have written lots of skin weight save/load crap in my time, and as I sat down and started to do that very thing in an instructional blog post one night, we had an interesting discussion in the office the next day. ADSK added ‘Export Deformer Weights‘ in 2011, but it has never really worked. I don’t know a single person who uses it. But it does save and load ‘deformer’ weights via the C++ API –so there’s real promise here!
Save/load skin weights fast without writing a custom Maya plugin? This is kinda like the holy grail, which is ridiculous, but you should see the lengths people go to eek out a little more performance! My personal favorite was MacaroniKazoo back in 2010 reaching in and setting the skinCluster weight list directly by hand using an unholy conglomeration of python API and MEL commands. Tyler Thornock has a post that builds on this here.
So I asked the guys if anyone had looked at Export Deformer Weights recently, everyone either hadn’t heard of it, heard it was shit, or had some real first-hand experience of it being “A bit shit.” But still –the promise was there!
Export Deformer Weights: Broken and Backwards
So, first thing’s first, I made an awesome test case. I am going to go over all the gotchas and things that are broken, but if you don’t want to take this voyage of discovery, skip this section.
I open the deformer weight export tool, and just wow.. I mean the UI team really likes it’s space:
I save my skin weights to an XML file, delete history on the mesh, and open the import UI:
Gotcha 1) It requires a deformer to load weights onto. You need to re-skin your mesh.
I re-skinned my mesh and loaded the XML using Index.. drumroll..
Well, this is definitely not applying the weights back by vertex index. I decided to try ‘Nearest’:
Gotcha 2) Of the options [Index, Nearest, Over], ‘Index’ is somehow lossy, and anything other than ‘Index’ seems to crash often, ‘Nearest’ seems totally borked. (above)
So this was when I just began to think this was a complete waste of my time. I was pretty annoyed that they even shipped a tool like this, something that is so needed and so important, yet crashes frequently and completely trashes your data when it does work.
Not Taking ‘Broken and Backwards’ for an Answer
I am already invested and, not understanding how loading weights by point index could be lossy and broken, I decided to look at the XML file. The tool writes out one XML file per skinCluster, here’s a rundown of the file format:
Mesh Info (Shape) – the vertices of the shape are stored local space x,y,z and corresponding index
<?xml version="1.0"?> <deformerWeight> <headerInfo fileName="C:/Users/chris.evans/Desktop/test_sphere.xml" worldMatrix="1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 "/> <shape name="pSphereShape1" group="7" stride="3" size="382" max="382"> <point index="0" value=" 0.148778 -0.987688 -0.048341"/> <point index="1" value=" 0.126558 -0.987688 -0.091950"/> <point index="2" value=" 0.091950 -0.987688 -0.126558"/> ...
Joints (Weights) – There is one block per joint that calls out each vertex that it have influences for on the shape
<weights deformer="skinCluster1" source="root" shape="pSphereShape1" layer="0" defaultValue="0.000" size="201" max="380"> <point index="0" value="0.503"/> <point index="1" value="0.503"/> <point index="2" value="0.503"/> ...
And that’s it, not a lot of data, nothing about the skinCluster attributes or options, no support for spaces like UV or world. (odd, since it’s had UV support for 15 years)
Next I decided to run the tool again and see what command it was calling, I then looked up the command documentation and here’s where it gets interesting, go ahead, take a look!
So now I am hooked, someone is putting some thought into this –at least on some level.
I don’t at all understand why the UI has none of these options, but I need to get this working. If you read through the docs, the command also supports:
- Exporting multiple skinClusters/shapes/deformers per XML file
- Exporting skinCluster/deformer attributes like ‘skinningMethod’ and ‘envelope’
- Local and world space positions with a positional tolerance
“Someone is putting some thought into this”
So I started trying to figure out why a file format that explicitly knows every influence of every vertex by index and inf name, doesn’t load weights properly. After some trials I hit gotcha #3:
Gotcha 3) Of the options the weights only load properly if the skinCluster has the *exact* same influences it was saved with. Which really makes no sense, because the file format has the name of every joint in the old skinCluster.
So now I had it working, time to wrap it and make it useful.
The Documentation is a Lie.
So, first thing’s first, I did a speed test.
Importing with deformerWeights was about 125 times faster: Gold mine.
But I just couldn’t get some of the flags to work, I thought I was just a moron, until I finally tried the code example in the ADSK Maya documentation, which FAILS. Let’s first look at the -vc flag, which is required to load using ‘bilinear’ or ‘barycentric’ mapping/extrapolation:
cmds.deformerWeights ("testWeights.xml", ex=True, vc=True, deformer="skinCluster1") # Error: Invalid flag 'vc' # Traceback (most recent call last): # File "<maya console>", line 1, in <module> # TypeError: Invalid flag 'vc' #
Gotcha 4) The python examples do not work. -vertexConnections flag doesn’t work, -attribute flag doesn’t work, so no saving skincluster metadata like ‘skinningMethod’, etc. Because of that, ‘barycentric’ and other methods that need vertex connection info do not work. The ‘deformer’ flag shows that it takes a list of deformers and writes them all to one file, but this is not true, it takes a single string name of a deformer.
I now know why the UI doesn’t have all these cool options! –they don’t work!
Gotcha 5) It doesn’t take a file path, to save a file to a path you need to specify the filename, and then the path separate.
cmds.deformerWeights ("testWeights.xml", path='d:\\myWeight\\export\\folder\\', ex=True, deformer="skinCluster1")
Perhaps someone fixed this stuff, documented it, and then reverted the fix, but this has been around since 2011.. I tried the above in maya 2016 latest service pack and all my links above are to that version of the documentation.
I wasn’t really intending to write this much, so now that we know this can import weights 125 times faster, we’ll make a tool to utilize it. Stay tuned!