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

- Part 1: Node based matrix constraint
- Part 2: Matrix twist calculator
**Part 3: Matrix rivet**

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

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.

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.

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.

I will be implementing this right away! Thanks again!

Very interesting! How does this compare in speed to the follicle? I’m assuming you wrote this after doing that speed test between the aim constraint rivet and the follicle…

Thanks! Yes, I did compare them, but the performance was just about the same (the variations were very small and going both ways over a few tests), which means the matrix rivet is significantly faster than the

`aimConstraint`

one. Looks like the follicle is really well optimized, which is great! The only benefits we get from this setup compared to the follicle is we get to choose which tangent we want to use as an up vector and also we have less DAG nodes in the outliner.Thanks for the reply (and for sharing all these techniques, for that matter). So, to clarify – the matrix rivet and follicle were basically the same in speed? Or at least, one or the other performed slightly faster as you did various comparisons without any consistently faster method… Did I read that correctly? Was this testing the matrix constraint with or without the added vector product? I can definitely see the benefit of being able to choose u OR v as an up vector depending on your needs (and less dag nodes is nice too).

One drawback I’ve found in my testing is that if you want to make something slide along the surface and you’re driving the param u and v values on the pointOnSurfaceInfo node, as soon as you go outside the uv range of your surface, the riveted node snaps back to origin. I threw a clamp node in there between my driver and param u/v attrs to resolve this, but it’s a little extra step I wish I didn’t have to do… This doesn’t happen with a follicle – it looks like it automatically clamps the uv values for you. But, if you’re not sliding stuff around the surfaces, this is moot!

Yes, that’s correct. Didn’t see any noticeable speed difference between the two. And the tests were done without the vector product.

About the sliding, I know what you mean, it could be a bit frustrating. What I do usually, is completely ignore that and leave it to the animators to not break it, just so we don’t have to add an extra step and also if they are pushing it to the ends of the nurbs surfaces they should expect it to break. That’s not a great solution, though, so what I could suggest is writing a node for these sliding setups. It can be faster than a follicle and have the choice for tangents.

Haha, ignore it and trust the animators to not break it – awesome (love it!).

Yeah, I tend to make a surface larger than I think an animator would slide something along it anyway to prevent that type of thing from happening (and trust them to not break it). The locator just has that “extra cushion” where it stops at the edge of the surface instead of popping to the origin.

Of course, a home-made plugin node could do the trick too! -thanks again!