Stretchy Arm Script
Stretchy Arm Automating Script
For this assignment I followed the tutorial on how to manually set up this stretchy arm and then I followed the tips to get started video. The second video walks through setting up the joints into groups so that you can more easily work with the three joints of the arm.
The video also goes through a script on how to rename the joints.
Functions
find_and_rename_joints()
import maya.cmds as cmds
import string
def find_and_rename_joints(bodyPart, side): # "arm", "leg", etc., "Lf" or "Rt"
letters = list(string.ascii_uppercase)
joints = []
childJoints = []
joints.append(cmds.ls(sl=1) [0])
childJoints = cmds.listRelatives(ad=1, type="joint")
childJoints.reverse()
for i in range(2):
joints.append(childJoints[i])
armJoints = []
for i,joint in enumerate(joints):
armJoint = cmds.rename(joint, bodyPart + "J" + letters[i] + side + "_1")
armJoints.append(armJoint)
if bodyPart == "arm":
shoulderJoint = armJoints[0]
elbowJoint = armJoints[1]
wristJoint = armJoints[2]
return shoulderJoint, elbowJoint, wristJoint
elif bodyPart == "leg":
hipJoint = armJoints[0]
kneeJoint = armJoints[1]
ankleJoint = armJoints[2]
return hipJoint, kneeJoint, ankleJoint
shoulderJoint, elbowJoint, wristJoint = find_and_rename_joints("arm", "Lf")
This first function is what handles finding and renaming the joints that we are working with. It has parameters that allow the user to enter what body part this is and what side of the body this is. The function returns the three joints in a list. This function can be expanded to include more body parts as seen with the leg if else statement.
make_distDim()
The next function is make_distDim()
def make_distDim():
# making distDim
elbowDist = cmds.getAttr(elbowJoint + ".tx")
wristDist = cmds.getAttr(wristJoint + ".tx")
totalArmLength = elbowDist + wristDist
null = cmds.group(em=1)
wristPos = cmds.xform(wristJoint, q=1, t=1, ws=1)
cmds.setAttr(null+".t", wristPos[0], wristPos[1], wristPos[2])
distDim = cmds.distanceDimension(sp = [0,0,0], ep = [1,0,0])
locatorTrans = cmds.listConnections(distDim)
locatorShape = cmds.listConnections(distDim, sh=1)
cmds.disconnectAttr(locatorShape[0] + ".worldPosition[0]", distDim + ".startPoint")
cmds.disconnectAttr(locatorShape[1] + ".worldPosition[0]", distDim + ".endPoint")
cmds.connectAttr(shoulderJoint+".t", distDim + ".startPoint")
cmds.connectAttr(null+".t", distDim + ".endPoint")
cmds.delete(locatorTrans)
return elbowDist, wristDist, totalArmLength, null, distDim
make_distDim()
This function makes the distance dimension tool and connects the start point to the shoulder and the end point to a null group that is point constrained to the wrist joint. This serves to get the distance value between the shoulder and wrist joint, this will be used to make a condition for the arm stretch as well as a ratio for how much to move the elbow and wrist joints to stretch the arm.
This function also gets the default length of the elbow and wrist joints as well as their combined length.
The function returns the distance tool, the length of the elbow and wrist and their total length as well as the null group as it will be used again for the IK handle controller.
IKHandle_and_CNTL()
def IKHandle_and_CNTL():
# Making an IK handle
IKHandle = cmds.ikHandle(sj = shoulderJoint, ee = wristJoint, s=1)
# Creating a controler for the IK Handle
CNTL = cmds.circle(nr = (0,0,1), c = (0,0,0), r = 1.5)
cmds.setAttr(CNTL[0] + ".rx", 90.0)
# moving controler to where the wrist joint and IK handle are
wristPos = cmds.xform(wristJoint, q=1, t=1, ws=1)
cmds.setAttr(CNTL[0] + ".t", wristPos[0], wristPos[1], wristPos[2])
# freezing transformations and deleting history
cmds.makeIdentity(CNTL[0], apply=True)
cmds.delete(CNTL[0], constructionHistory=True)
# parent constraining the Ik handle to the controler
cmds.parentConstraint(CNTL[0], IKHandle[0])
# point constraning the null to the controler
cmds.pointConstraint(CNTL[0], null)
IKHandle_and_CNTL()
This function makes the IK handle as well as a nurbs circle to act as the controller.
This function uses the cmds.ikHandle function. Since this entire script is meant to work when the shoulder joint is selected we just needed to specify what the start point of the ik handle would be, I also specified the shoulder joint was the end effector just in case.
Next the function creates a controller for the ik handle.
make_connections()
def make_connections():
# Setting up conditions
elbowDist = cmds.getAttr(elbowJoint + ".tx")
wristDist = cmds.getAttr(wristJoint + ".tx")
totalArmLength = elbowDist + wristDist
distance = cmds.getAttr(distDim + ".distance")
# creating the condition node
conditionNode = cmds.createNode("condition")
cmds.setAttr(conditionNode + ".operation", 2)
# connecting the distance to the first term of the condition node and the total arm length to the second term
cmds.connectAttr(distDim + ".distance", conditionNode + ".firstTerm")
cmds.setAttr(conditionNode + ".secondTerm", totalArmLength)
# creating multiply and divide nodes
divideNode = cmds.createNode("multiplyDivide")
cmds.setAttr(divideNode + ".operation", 2)
cmds.connectAttr(distDim + ".distance", divideNode + ".input1X")
cmds.setAttr(divideNode + ".input2X", totalArmLength)
multiplyNode = cmds.createNode("multiplyDivide")
# if condition is true
cmds.connectAttr(divideNode + ".outputX", multiplyNode + ".input1X")
cmds.connectAttr(divideNode + ".outputX", multiplyNode + ".input1Y")
# if condition is false use default values
cmds.setAttr(multiplyNode + ".input2X", elbowDist)
cmds.setAttr(multiplyNode + ".input2Y", wristDist)
# if condition is true
cmds.connectAttr(multiplyNode + ".outputX", conditionNode + ".colorIfTrueR")
cmds.connectAttr(multiplyNode + ".outputY", conditionNode + ".colorIfTrueG")
# if condition is false
cmds.setAttr(conditionNode + ".colorIfFalseR", elbowDist)
cmds.setAttr(conditionNode + ".colorIfFalseG", wristDist)
cmds.connectAttr(conditionNode + ".outColorR", elbowJoint + ".translateX")
cmds.connectAttr(conditionNode + ".outColorG", wristJoint + ".translateX")
make_connections()
This function makes the condition node and the multiply and divide node. The condition node is set to greater than, the distDim is set to the first term and the totalArmLength is set to the second term. This maes is so the condition is true when the distDim is greater than the totalArmLength. The Divide node is used to make a ratio of distDim / totalArmLength that will be multiply to the length of the elbow and wrist joints so they stretch proportionally. The multiply node is used to multiply the ratio. The elbow length is added to the x inputs of the multiply node, the wrists length is added to the y input of the multiply node, and the output of the divide node is connected to both the x and y input. The outputs are then connected to the condition nodes colorIfTrue inputs. X goes to R and Y goes to G. The original lengths are set to the colorIfFalse values to maintain the length of the joints if the condition is false.
Trouble Shooting
I got the script to be mostly functional. I ran into an issue where when the condition for the stretchy arm is met, the wrist joint jumps to where the elbow joint is. The elbow joint was behaving as wanted but the wrist joint was not. I figured out that I accientdly wrote these lines:
cmds.connectAttr(divideNode + ".outputX", multiplyNode + ".input1X")
cmds.connectAttr(divideNode + ".outputX", multiplyNode + ".input1Y")
as
cmds.connectAttr(divideNode + ".outputX", multiplyNode + ".input1X")
cmds.connectAttr(divideNode + ".outputY", multiplyNode + ".input1Y")
so I was not using the output from the divide node which gave me the ratio on the Y input of the multiple node. This is what was causing the issue.
Comments
Post a Comment