Situation:
A semi-cartoony arm, with an IK/FK setup; using separate chains for the FK, IK and bind (including around 7 forearm twist) joints.
Challenge / Problem:
I am looking for a way to enable the wrist to be twisted an indefinite (but for this post, ± 720 degrees) amount and still have the twist joints distribute the total rotation relative to the elbow; without the twist joints flipping back to the shortest distance when the rotation passes ±360degrees.
How the twist is actually implemented is not the question.
The issue is how to reliably calculate the total rotation of the wrist, around the axis/joint which would point along the length of the forearm (‘twist axis’).
Current Unsatisfactory Solutions:
- Use a direct connection from the wrist control
- Pros: No flipping of values;
- Cons: This fails when gimball lock etc. results in either: the rotation that was connected not rotating the hand around the twist axis; or a different axis rotating the hand around the twist axis.
- Use an expression, to calculate the rotation difference (of the IK wrist joint in the twist axis) since the last evaluated frame (ie.
getAttr -t (obj.lastEvaluatedFrameCustomAttr) obj.attribute), countering any sudden changes (eg. 179degrees straight to -180degrees) by adding or subtracting 360degrees to the rotation difference between the frames, and adding that rotation difference to the value used to drive the twist.
- Pros: When played from start (ie. rendered), or even scrubbed from animation start frame, the arm twist evaluates correctly with no twisting, provided the initial pose is non flipped.
- Cons: The start frame assumes the arm/wrist pose to be at a non-flipped position, or otherwise the initial offset (multiple of 360) must be set manually; The arm twist does not evaluate until keyed; If a frame is jumped to on the timeline, the arm twist may not evaluate correctly. The expression may trigger the ‘cycleCheck’ warning.
Does anyone know of a more practical method, preferably one that works without requiring keys?
(Method 2. Experimental expression ):
if ((hand_ctrl.enableNoFlip == 0) || (frame == `playbackOptions -q -ast`))
{
hand_ctrl.noFlipArmTwistRotation = wristTwist.rotateX;
}
else
{
$frameDif = frame - attrHolderJnt.prevFrame;
$rotDif = ((`getAttr wristTwist.rotateX`) - (`getAttr -t (attrHolderJnt.prevFrame) wristTwist.rotateX`));
if ($rotDif > 180)
{
hand_ctrl.noFlipArmTwistRotation += $rotDif - 360;
}
else if ($rotDif < -180)
{
hand_ctrl.noFlipArmTwistRotation += $rotDif + 360;
}
else if ($frameDif != 0)
{
hand_ctrl.noFlipArmTwistRotation += $rotDif;
}
}
attrHolderJnt.prevFrame = frame;