Roll joints are helper joints to eliminate the 'sweet wrapper' effect that linear skinning gives when twisting along the joint axis. By distributing the rotation across a number of joints you can maintain volume in the mesh, and the more joints you use the less volume you lose.
|180 degree twist distributed over one joint|
|180 degree twist distributed over four joints|
Generally it's a bad idea to drive any roll joint directly from the rotation of another (you can get away with driving forearm roll joints from the wrist so long as you choose your wrist joint rotation orders very carefully). This is especially true for the upper arm which has such a large range of movement it is guaranteed that an axis will be crossed which will give you sudden changes in Euler rotation values, making it impossible to use them to drive a twist joint.
This way of driving upper roll joints is completely independent of the rotation values of the arm and so is completely stable at all times.
Here we have the skeleton built with two roll joints for the upper arm (in green). Note that they are leaf joints, i.e they are both children of the arm joint and have no children of their own.
I see a lot of rigs with roll joints in sequence (arm-->roll_01-->roll_02-->elbow), but in my own view I think the leaf structure is far superior.
- Much easier to set up
- Allows greater flexibility with regards to driving the joint translations/rotations (which I'll cover in my next post on improving deltoids deformation using just the arm_roll_01 joint you see here).
- Can move about independently of the underlying 'main' limb joint hierarchy without having to counteract this movement further down the hierarchy.
- Less compression-generated 'jitter' at run time. In-game animations are compressed, and the longer the joint chain the more noticeable the jitter will be at the end of the chain as all the inaccuracies add up along the length. Using leaf joints keeps the primary (arm-->elbow-->wrist) joint chain length as short as possible, thereby keeping compression artifacts to a minimum.
Building the no-flip upper arm roll joint system
Note: I will assume the top spine joint is called sternum, the upper arm joint l_arm, elbow l_elbow and the roll joints l_arm_roll_01 and l_arm_roll_02.
Create a two joint chain called l_arm_roll_vecDriverJoint. Its world space translateX value should be zero (in line with the center of the character). In Y it should be at the same height of the arm joint, and in Z should be around 20cm behind. Joint orientation and rotation orders are not really important.
Create an empty group called l_arm_roll_vecDriverJoint_grp and parent constrain it with no offset to the sternum joint.
Create an ikHandle (ikRPsolver) called l_arm_roll_vecDriverJoint_ikHandle on the joint l_arm_roll_vecDriverJoint and point constrain it to the elbow.
Parent l_arm_vecDriverJoint, vecDriverJoint_ikPV and l_arm_roll_vecDriverJoint_ikHandle to your group.
Create a new group called defDriver_l_arm_roll_grp. Parent constraint it with no offset to l_arm.
Duplicate both the roll joints and call them defDriver_l_arm_roll_01 and ..._02. Parent these to defDriver_l_arm_roll_grp.
Duplicate l_arm_roll_01 once more and call it defDriver_l_arm_roll_twistMod. Parent it to defDriver_l_arm_roll_01. This extra joint will allow us to distribute a small amount of the arm twist into the deltoid area, as if it remains completely static it looks quite unnatural. It will also have some further use which I'll cover in my next post.
Point constrain defDriver_l_arm_roll_02 to l_arm and l_elbow.
Create a locator defDriver_l_arm_roll_01_aimUp. Parent it to l_arm and set all rotate and translate attributes to zero. Now, in a single translate channel (Z in this example, but it depends on your joint orientation) move the locator behind the arm by around 40cm and down its length by 15cm.
Parent defDriver_l_arm_roll_01_aimUp to l_arm_vecDriverJoint.
Aim constraint defDriver_l_arm_roll_01 to l_elbow. Assuming your skeleton orientations are as mine your aim vector should be 0,1,0 and up vector 0,0,1. Set world up type to object up and world up object to defDriver_l_arm_roll_01_aimUp.
These constraint settings are specific to the joint orientation you have set. Essentially the aim vector should correspond to the down (twist) axis of your joint (so in my case +Y). The up vector should be the axis pointing backwards from the character, basically in the direction of the locator defDriver_l_arm_roll_01_aimUp which in my case is -X. You can see the joint axis in the picture below which should help you figure out the correct settings for your joints.
After creating the orient constraint, the rotation values for defDriver_l_arm_roll_01 should be 0,0,0. If you don't have zero rotation values you'll need to go back and check your aim and up vector axis are set correctly.
Create a multiplyDivide node called defDriver_arm_roll_twistMod_divide. Set operation to multiply and input2X to -0.1
Connect defDriver_l_arm_roll_01.rotateY to defDriver_arm_roll_twistMod_divide.input1X and defDriver_arm_roll_twistMod_divide.outputX to defDriver_arm_roll_twistMod.rotateY.
Create another multiplyDivide node called defDriver_arm_roll_divide. Set operation to divide and input2X to 2.0.
Connect defDriver_l_arm_roll_01.rotateY to defDriver_arm_roll_divide.input1X and defDriver_arm_roll_twistMod_divide.outputX to defDriver_l_arm_roll_02.rotateY.
Finally, point and orient constrain l_arm_roll_01 to defDriver_l_arm_roll_01 and do the same for the second roll joint so the skinned joints are being driven by the roll systems.
Here is a quick capture showing the system working. Note how I can drag the arm around without any concern of the rotation values and the roll joints are perfectly stable at all times.