performance

This post is a part of a three post series, where I implement popular rigging functionalities with just using maya’s native matrix nodes.

Rivets are one of those things that blew my mind the first time I learned of them. Honestly, at the time, the ability to stick an object to the deforming components of a geometry seemed almost magical. Although, the more you learn about how geometries work in Maya, the more sense rivets start to make. The stigma around them, though, has always been that they are a bit slow, since they have to wait for the underlying geometry to evaluate and only then can they evaluate as well. And even though that is still the case, it seems that since parallel was introduced the performance has increased significantly.

It is worth trying to simplify and clean rivets up, considering how handy they are for rigging setups like:
– twist distributing ribbons
– bendy/curvy limbs
– sticking objects to geometries after squash and stretch
– sticking controls to geometries
– driving joints sliding on surfaces

and others.

When I refer to the classic rivet or the aimConstraint rivet, it is this one that I am talking about. I have seen it used by many riggers and also lots of lighters as well.

The purpose of this approach is to get rid of the aimConstraint that is driving the rotation of the rivet. Additionally, I have seen a pointConstraint used as well, in order to account for the parent inverse matrix, which would also be replaced by this setup. Even though we are stripping constraints, the performance increase is not very large, so the major benefit of the matrix rivet is a cleaner graph.

TL;DR: We are going to plug the information from a pointOnSurfaceInfo node directly into a fourByFourMatrix node, in attempt to remove constraints from our rigs.

Disclaimer: Bear in mind, I will be only looking at riveting an object to a NURBS surface. Riveting to poly geo would need to be done through the same old loft setup.

Limitations: Since we are extracting our final transform values using a decomposeMatrix node, we do not have the option to use any rotation order other than XYZ, as at the moment the decomposeMatrix node does not support other orders. A way around it, though, is taking the outputQuat attribute and pluging it into an quatToEuler node which actually supports different rotate orders.

Difference between follicle and aimConstraint rivet

Matrix rivet - follicle and classical rivet differences

Matrix rivet - follicle and classic rivet graph

The locator is riveted using an aimConstraint. You can see there is a small difference in the rotations of the follicle and the locator. Why is that?

The classical rivet setup connects the tangentV and normal attributes of a pointOnSurface to the aimConstraint. The third axis is then the cross product of these two. But it seems like the follicle is actually using the tangentU vector for it’s calculations, since we get this difference between the two setups.

Choosing to plug the tangentU into the aimConstraint, instead of tangentV, results in the same behaviour as a follicle. To be honest, I am not sure which one would be preferable. In the construction of our matrix rivet, though, we have full control over that.

Why not follicles?

As I already said, in parallel, follicles are fast! Honestly, for most of my riveting needs I wouldn’t mind using a follicle. The one aspect of follicles I really dislike though, is the fact that it operates through a shape node. I understand it was not meant for rigging, and having the objects clearly recognizable both in the outliner and the viewport is important, but in my case it is just adding up to clutter. Ideally, I like avoiding unnecessary DAG nodes, since they only get in the way.

Additionally, have you had a look at the follicle shape node? I mean, there are so many hair related attributes, it is a shame to use it just for parameterU and parameterV.

Therefore, if we could use a non-DAG network of simple nodes to do the same job without any added overhead, why should we clutter our rigs?

Constructing the matrix rivet

So, the way matrices work in Maya is that the first three rows of the matrix describe the X, Y and Z axis and the fourth row is the position. Since, this is an oversimplification I would strongly suggest having a look at some matrix math resources and definitely watching the Cult of Rig streams, if you would like to learn more about matrices.

What this means to us, though, is that if we have two vectors and a position we can always construct a matrix out of them, since the cross product of the two vectors will give us a third one. So here is how our matrix construction looks like in the graph.

Matrix rivet - constructing the matrix

So, as you can see, we are utilizing the fourByFourMatrix node to construct a matrix. Additionally, we use the vectorProduct node set to Cross Product to construct our third axis out of the normal and the chosen tangent, in this case tangentV which gives us the same result as using the classic aimConstraint rivet. If we choose to use the tangentU instead, we would get the follicle‘s behaviour. Then, obviously we decompose the matrix and plug it into our riveted transform.

Optionally, similar to the first post in this series, we can use the multMatrix node to inverse the parent’s transform, if we so need to. What I usually do, though, is parent them underneath a transform that has it’s inheritTransform attribute turned off, so we can plug the world transforms directly.

It is important to note that in this case we are absolutely sure that the output matrix is orthogonal, since we know that the normal is perpendicular to both tangents. Thus, crossing it with any of the tangents, will result in a third perpendicular vector.

Skipping the vector product

Initially, when I thought of building rivets like this, I plugged the normal, tangentU and tangentV directly from the pointOnSurfaceInfo to the fourByFourMatrix. What this means, is that we have a matrix that is not necessarily orthogonal, since the tangents might very well not be perpendicular. This results in a shearing matrix. That being said though, it was still giving me proper results.

Matrix rivet - skipping the vectorProduct

Then, I added it to my modular system to test it on a couple of characters and it kept giving me steadily good results – 1 to 1 with the behaviour of a follicle or aimConstraint rivet, depending on the order I plug the tangents in.

What this means, then, is that the decomposeMatrix node separates all the shearing from the matrix and thus returns the proper rotation as if the matrix is actually orthogonal.

If that is the case, then we can safely skip the vectorProduct and still have a working rivet, considering we completely disregard the outputShear attribute of the decomposeMatrix.

Since, I do not understand how that shearing is being extracted, though, I will be keeping an eye on the behaviour of the rivets in my rigs, to see if there is anything dodgy about it. So far, it has proved to be as stable as anything else.

Conclusion

If you are anything like me, you would really like the simplicity of the graph, as we literally are taking care of the full matrix construction ourselves. What is more, there are no constraints, nor follicle shapes in the outliner, which again, I find much nicer to look at.

This matrix series has been loads of fun for me to write, so I will definitely be trying to come up with other interesting functions we could use matrices for.

The classical rivet was a really popular rigging thing a few years ago (and long before that it seems). I am by no means a seasoned rigger, but whenever I would look for facial rigging techniques the rivet would keep coming up. What is more, barely if ever people suggested using follicle to achieve the result, generally because the classical rivet evaluates faster. So, I thought I’d do a maya performance test to compare them.

Table of contents

Prerequisites

I will be looking into the performance of a follicle and a classical rivet, both on a NURBS sphere and on a poly sphere. NURBS because I tend to use a lot of ribbons and poly, because it’s a popular feature for attaching objects to meshes.

I will be using Maya 2017’s Evaluation Toolkit to run the performance test, as it gives nice output for each evaluation method, even though I cannot imagine using anything but parallel.

The way the tests are going to work is, I will create two files, each containing the same geometry with 10 rivets. In one file I will use follicles and in the other the classical setup. The deformation on the geometry will just be keyed vertices and it will be identical for each setup, so we can be sure that the only difference between the two files is the riveting setup.

Then, the test will be done in a new scene where I will reference the file to test a 100 times. For each setup I will run the evaluation manager’s performance test and take the results and compare them.

Okay, let us have a look then.

NURBS

Classical rivet setup

So, the way this one works is I just loop from 1 to 10 and I create a pointOnSurfaceInfo node with parameterU set to iterator * .1 and parameterV set to .5. Then, I plug the output position directly to a locator’s translate attr. Additionally, the output position, normal vector and a tangent vector go into an aimConstraint which constraints the rotation of the locator.

Follicle setup

This one is fairly straightforward, I just created 10 follicles, with parameterU set to iterator * .1 and V to .5.

Results

Bear in mind, EMS refers to serial evaluation and EMP is parallel.

NURBS surface
Classical Rivet
Playback Speeds
===============
    DG  = 13.1694 fps
    EMS = 11.1359 fps
    EMP = 20.7469 fps
-----------------------------
Follicle
Playback Speeds
===============
    DG  = 11.3208 fps
    EMS = 12.6263 fps
    EMP = 27.8293 fps

Even though I expected the follicle to be faster I was surprised by how much. It is important to note that we have 10 * 100 = 1000 rivets in the scene, which is obviously a big number. Therefore, in a more realistic example the difference is going to be more negligible, but still 7.8fps is quite a bit.

What is also quite interesting is that in DG the follicle is slower than the classic rivet. So, the stigma of the old days that the classical rivet is faster, seem to be deserved, but parallel changes everything.

Polygons

Classical rivet setup

So, when it comes to polys the classical rivet gets a bit more complicated, which I would imagine results in a larger slowdown as well. The way this setup works is, we grab 10 couples of edges, which in turn produce 10 surfaces through a loft node. Maintaining history, the nurbs surfaces will follow the poly geometry. So, we can perform the same rivet setup as before on the nurbs.

Follicle setup

On a mesh with proper UVs the follicles are again trivial to set up. We just loop 10 times and create a follicle with the appropriate U and V parameters.

Results
Polygon geometry
Follicle
Playback Speeds
===============
    DG  = 1.7313 fps
    EMS = 3.32005 fps
    EMP = 9.79112 fps
-------------------------
Classical rivet
Playback Speeds
===============
    DG  = 1.05775 fps
    EMS = 1.52022 fps
    EMP = 3.31053 fps

As expected, follicles are again quite a bit faster. I am saying as expected, as not only do we have a riveting setup as in the NURBS case, but also there is the edges and the loft which add to the slowdown. I am assuming, that is why even in DG the classical rivet is still slower.

Conclusion

So, the conclusion is pretty clear – follicle rivets are much faster than classical rivets in the latest maya versions which include the parallel evaluation method.