|
STRETCHY ARM SKELETON http://www.borafx.com
PART I - CREATING A HI-RES ARM SKELETON Before we start, make sure your skeleton > joint tool settings are reseted. So, the joint orientation is XYZ with second axis world orientation is at +Y. If not, go ahead and reset the tool settings. We will start with drawing a four joint arm skeleton in the top view as shown in the picture below. The last joint is only for the joint orientation purposes. After you done name, joints as c_shoulder, c_elbow and c_wrist. This will be our main IK skeleton to control the arm rig.
Now we will build the hi-res skeleton which will be used for the skinning later on. This new skeleton will have two extra joints, between shoulder and elbow joints and same between elbow and wrist joints. We adding these joints to help the deformations around shoulder and elbow areas. So, lets duplicate the skeleton and move the new skeleton a little towards Z axis so things would be easier to select in view panels. Name the joints as hi_shoulder, hi_elbow and hi_wrist. To add the extra joints you can download and use this small [addJoint mel script]*. It simply adds entered number of joints to the selected joint hierarchy. Now, what we need to do is select the hi_shoulder joint and type `addJoint(2)` in the command line. Do same with the elbow joint; pick hi_elbow joint and run `addJoint(2)` in command line. Name the new joints as in the picture below;
[ download scene file: stretchyArm_part1.ma ] ** Now we will setup the ik solver and make some connections so that our control arm moves the hires arm properly. So lets start with opening up the skeleton > ik handle tool option box. Make ikRPsolver selected, and Snap Enable unchecked. Select c_shoulder and than c_wrist joints and create the IK handle. Than pick the IK handle and freeze its transformations, ( modify > freeze transformations ). Create a locator ( create > locator ), name it ikPole and snap it to c_shoulder joint first and than move it towards the back of the skeleton in -Z axis. Select both the locator and ikHandle and create a pole vector constrain ( constrain > pole vector ). Also don`t forget to freeze the transformations on that locator ( modify > freeze transformations ). Open up the connection editor (window > general editors > connection editor ) and make the connections as follows; c_shoulder.rotateY --> hi_shoulder.rotateY Now both skeletons should be moving almost same except control shoulders rotation along X axis. We going to fix this by getting the X rotation of c_shoulder joint, and dividing it between hi_shoulder1 and hi_shoulder2 joints. First open up the hypershade window ( window > rendering editors > hypershade ) and show bottom side only. Select c_shoulder joint and select graph > add selected to graph from hypershade menu. Now pick both hi_shoulder1 and hi_shoulder2 joints and do the same ( graph > add selected to graph ). Add a multiply divide node from the list, and in the attribute editor change its input2X to 0.5. Connect c_shoulder.rotateX to input1X of the multiply divide node. Than connect outputX of the multiply divide node to hi_shoulder1.rotateX and hi_shoulder2.rotateX.
[ download scene file: stretchyArm_part2.ma ] ** We can start working on the wrist part now. This time we want to make a setup so the extra joints we have around wrist, hi_wrist1 and hi_wrist2, rotates gradually in X axis so we can have nice twisting deformation. To fix that flipping, select wristHandle only and add it to a group node ( edit > group ). Center the group nodes pivot ( modify > center pivot ) and rotate the group node 180 degrees in X and Z axes. You can now parent the ikHandle to the wristHandle object. Select c_wrist first, than hi_wrist and do a orient constrain with maintain offset OFF and `all axes` selected. After the constrain created select the hi_wrist joint and change constrain`s interpolation type to `shortest` in the channel box. Next we need to orient both hi_wrist1 and hi_wrist2 joints to both c_wrist and hi_elbow joints. Select c_wrist first, than hi_wrist1 and do a orient constrain with maintain offset OFF and only X axis selected. Select hi_elbow first and than hi_wrist1, and create another constrain with same settings, only X axis. Set interpolation type to `average` in the channel box. Now we also need to set the constrain weights. Set C_wristW0 to 1.0 and Hi_elbowW0 to 0.5 in the constrain settings, so the c_wrist joint can have more influence on its rotation than hi_elbow. We will do the same for the hi_wrist2 joint. So, select c_wrist first, than hi_wrist2 and do a orient constrain with maintain offset OFF and only X axis selected. Select hi_elbow first and than hi_wrist2, and create another constrain with same settings, only X axis. Set the interpolation type to `average` and C_wristW0 to 0.5 and Hi_elbowW0 to 1.0 in the constrain settings. If you moved the hi_shoulder joint, snap it back to c_shoulder joint so they fully overlap. Now you can hide both the c_shoulder and the ikHandle so you wont select it by mistake.
Now, the first part of this tutorial is done. [ download scene file: stretchyArm_part3.ma ] **
PART 2 - MAKING THE ARM SKELETON STRETCHABLE Our aproach will be measuring the distance between the control skeletons last joint, c_wrist and the ikHandle, and divide it by the default length of join chain. This will give us to what proportions joints needed to be scaled to reach the ikHandle`s position. User will have an option to turn on and off stretching behaviour and define minumum / maximum distances the arm could stretch. Than we will add some controls for what I call scaling behaviour, so as the arm stretches joints will be scaled down in two other axes, which will also be keyable and adjustable. We will be using the arm we created in the last part of this tutorial, and add up to it. Ok, lets start with adding the following attributes ( modify > add attribute ) to wristHandle ;
First, connect wristHandle.defStretch to c_shoulder.scaleX and c_elbow.scaleX. So the main control skeleton will be scaled as the default stretch is changed. Create two locators ( create > locator ) and name them pos1 and pos2. We will point constrain locators to our ikHandle and c_wrist joints and use them to measure the distance between them. So, select the ikHandle, wich was parented under wristHandle, and than select pos1 and do a point constrain( constrain > point ). Do same with selecting the c_wrist joint and pos2 locator. Open up the hypershade window and make it show the bottom tabs only. In the command line run `createNode distanceBetween`. Now you should have a distanceBetween node shown in hypershade window. Select both pos1 and pos2 locators and add them to graph ( graph > add selected to graph ). Since we going to add more nodes and make connections, try not to close the hyperShade window, so you won`t get confused. Connect pos1.translate to point1 and pos2.translate to the point2 of the distanceBetween node.
Now we have the distance calculated, so lets divide it by the skeletons default length. Create a multiplyDivide node and name it unitScale. We need to calculate the default size of our arm skeleton. To do this, we will basicaly add up translateX of the c_elbow joint to the translateX of the c_wrist joint. Do this little math and type that value into input2X of the unitScale node, and change this multiply divide nodes operation type to `divide`. Connect distanceBetween1.distance to unitScale.input1X. To make the stretching blendable we will multiply this output with the autoStretch attribute. Create another multiplyDivide node and name it stretchMul. Add wristHande to the graph , and connect wristHandle.autoStretch to input1X of the stretchMul node and connect unitScale.outputX to input2X of the stretchMul node.
Now we need to add the default joint scale, 1 in most cases, to our last nodes output, so we will know what the joints scale along its direction( X axis ) should be. Create a plusMinusAverage node and name it plusOne. Connect the stretchMul. outputX to plusOne.input1D[0]. Connect wristHandle.defScale to plusOne.input1D[1]. These two connection could be tricky since the input1D is an array type connection, so if you having trouble do it from the command line as follows; connectAttr -f stretchMul.outputX plusOne.input1D[0]; Lets clamp this value so the arm won`t stretch more that the maximum stretch size defined. Create a clamp node and name it stretchClamp. Connect plusOne.output1D to stretchClamp.inputR and wristHandle.maxStretch to stretchClamp.maxR.
We need to connect the output of this clamp node( stretchClamp.outputR ) to X scale of each joint in the hi-res skeleton except the wrist joint. You can do that manually or from the command line as follows; connectAttr -f stretchClamp.outputR hi_shoulder.scaleX; [ download scene file: stretchyArm_part4.ma ] ** Now we have done with the stretching part, lets do the `scaling`, so the stretched arm would decrease in radius etc. Create a new multiplyDivide node and name it inverseStrecth. Connect stretchClamp.outputR to inverseStretch.input2X. Set inverseStretch node`s input1X to 1.0 and set operation type to `divide`. Create another multiplyDivide node named scalePow and connect wristHandle.autoScale to its input2X and inverseStretch.outputX to its input1X. Set the operation type to `power`. Now we need a final clamp node to limit min/max joint scales, so create a clamp node named scaleClamp. Connect wristHandle.minScale to scaleClamp.minR and wristHandle.maxScale to scaleClamp.maxR. Connect scalePow.outputX to scaleClamp.inputR. Than we need to connect this nodes output to Y and Z scales of each joint in the hi-res skeleton except the wrist joint. If you want to do it in command line run these lines; connectAttr -f scaleClamp.outputR hi_shoulder.scaleY;
[ download scene file: stretchyArm_part5.ma ] **
PART 3 - GETTING COLOR FEEDBACK FROM THE STRETCHED ARM When animating such a stretchy skeleton, it might be usefull to get some feedback on how much the rig is stretched. We will do this by adding a simple object to our wristHandle and change its color based on stretching distances. Lets create a nurbsSphere with 1 sections and 1 spans and name it colorGizmo. Snap it to c_wrist joint, freeze its transformations ( modify > freeze transformations ) and delete its history ( edit > delete by type > history ). Run the following command in the command line; parent -r -s colorGizmoShape wristHandle This command does what its called shapeParenting; parents colorGizmo object`s shape to the wristHandle. Now you can select same transform node wristHandle with two shapes; nurbsCurve we had earlier, and the nurbsSphere we created. You can now delete the empty transform node named colorGizmo in the outliner window. Create a surfaceShader and a remapValue node. Connect stretchClamp.outputR to remapValue`s .inputValue. Connect wristHandle.defStretch to remapValue1.inputMin and wristHandle.maxStretch to remapValue1.inputMax. First color in the remapValue node is the color to be used when the arm is relaxed, and the other is the color used when the arm is stretched to the maximum, so pick the colors as you like. Connect the remapValue`s outColor to surfaceShader`s outColor, and assign the shader to the nurbsSphere. And turn on hardware shading ( shading > hardware texturing ). Now our colorGizmo will change its color according to stretching values.
[ download scene file: stretchyArm_part6.ma ] **
Thats it. If you need to use this arm rig on your character, just make sure c_shoulder and hi_shoulder parented to the same joint in your body skeleton, and pos1 and pos2 locators are grouped and hidden so they don`t get moved / deleted by mistake. If you having troubles, please feel free to drop me a line. Stay tuned for more tutorials/tricks. Bora Dayioglu
* To use this mel script, copy it to your maya scripts folder( My Documents/maya/scripts ) . And either restart Maya or open up the script editor and source it ( select file > source script from the script editor menu ) |