def addFKChain(self, startJoint, endJoint, parent): '''Create chain of FK ctrls on given joints, returns list of created ctrls - parent = parent of the first ctrl ''' RIGLOG.debug('adding FKChain') jointList = mpJoint.getJointList(startJoint, endJoint) ctrlParent = parent fkCtrls = [] prevCtrl = None for idx, joint in enumerate(jointList): zero, fkCtrl = self.addCtrl('%02d' % idx, type='FK', shape='sphere', parent=ctrlParent, xform=joint) #first ctrl should do translation of joint, so chain moves with ctrls if idx == 0: cmds.pointConstraint(fkCtrl, joint, mo=True) cmds.orientConstraint(fkCtrl, joint, mo=True) #every joint driven by ori cns ctrlParent = fkCtrl mpAttr.lockAndHide(fkCtrl, 't') #translate not needed on fk chain fkCtrls.append(fkCtrl) if prevCtrl: mpRig.addPickParent(fkCtrl, prevCtrl) #for pickwalk later prevCtrl = fkCtrl mpRig.addSnapParent(fkCtrl, joint) #for snapping FK to IK return fkCtrls
def build(self): FKChain.build(self) #add offset between each ctrl curls = list() for idx, ctrl in enumerate(self.ctrls): #get zero null: zero = cmds.listRelatives(ctrl, p=1)[0] self.name.desc = 'Curl%02d' % idx curlNull = cmds.group(em=True, n=self.name.get(), p=zero) cmds.xform(curlNull, ws=True, m=cmds.xform(zero, q=True, ws=True, m=True)) cmds.parent(ctrl, curlNull) curls.append(curlNull) #make curl ctrl curlZero, curlCtrl = self.addCtrl('curl', type='FK', shape='spoon', parent=self.pinParent, xform=self.ctrls[0], size=3.5) mpAttr.lockAndHide(curlCtrl, 't') #connect curl ctrl for curl in curls: for axis in 'xyz': cmds.connectAttr(curlCtrl + '.r%s' % axis, curl + '.r%s' % axis)
def lock(self): '''Lock and hide nodes that shouldn't be touched''' RIGLOG.info('locking rig') cmds.setAttr(self.rigNode + '.rigVersion', lock=True) cmds.setAttr(self.rigNode + '.buildDate', lock=True) cmds.setAttr(self.rigNode + '.builtBy', lock=True) for node in (self.limbNode, self.geoNode, self.skeletonNode, self.rigNode): mpAttr.lockAndHide(node, ['t', 'r', 's', 'v']) for limb in self.limbs: mpAttr.lockAndHide(limb.limbNode, ['t', 'r', 's', 'v'])
def addCtrl(ctrlname, shape='sphere', size=1.0, segments=13, parent=None, color=None, shapeXform=None, xform=None): '''make a ctrl with a given shape out of a curve, parented under a zero null shape: sphere,cube,circle,cross,pyramid,line,spoon size: defaults to 1.0 segments: used on round shapes, defaults to 13 parent: object to parent under (if any) color: maya color name or color index shapeXform: a matrix or object (uses worldMatrix) to transform ctrl shape with. This is only cosmetic. xform: an object, vector, or matrix to xform the transform and rotationOrder of the ctrl ''' #get shape with name crv = getCurve(shape, size=size, segments=segments) #rename and make zero null crv = cmds.rename(crv, ctrlname) zero = cmds.createNode('transform', n=crv + "_Zero") attr.hideAnimChannels(zero) #lock scale #In the rare cases that a ctrl must be scaled this can simply be unlocked. #Otherwise this saves a lot of headache later IMO. attr.lockAndHide(crv, 's') #parent under zero null so it's local xforms are zero cmds.parent(crv, zero) #set color based on argument, or position (based on name) if not specified if not color: color = getPositionColor(ctrlname) setColor(ctrlname, color) #do transforming (if given) if shapeXform: cluster = cmds.cluster(crv) xfMatrix = rigmath.Transform(shapeXform) cmds.xform(cluster, ws=True, m=xfMatrix.get()) cmds.delete(crv, ch=True) else: shapeXform = rigmath.Transform() if parent: cmds.parent(zero, parent) #store shape transform for later, if needed cmds.addAttr(crv, dt='matrix', ln='shapeMatrix') shapeXform.scale(size) cmds.setAttr(crv + '.shapeMatrix', *shapeXform.get(), type='matrix') #flag setAsCtrl(crv) if xform: #if name of node then math matrix and rotate order if isinstance(xform, str) and cmds.objExists(xform): matchMatrix = cmds.xform(xform, q=True, ws=True, m=True) attr.matchAttr(xform, zero, "rotateOrder") attr.matchAttr(xform, crv, "rotateOrder") #otherwise just match matrix. #wrapping a Transform here for convenience else: try: matchMatrix = rigmath.Transform(xform).get() except RuntimeError: raise RuntimeError( "Couldn't find object or transform %s to match" % xform) cmds.xform(zero, ws=True, m=matchMatrix) return (zero, crv)
def addFKIKChain(self, startJoint, endJoint, localParent, worldParent): '''Create a chain of FK ctrls with a blended IKRP solver. Requires three or more joints in chain. Also creates a "stub" joint with an SC solver at the end of the chain, so that the last joint's rotation is blended between the IK end ctrl and the last FK ctrl properly. - localParent = drives translation of FK chain always, rotation when local space is on. IK ignores. - worldParent = drives rotation when 'world' space is blended on. Drives IK translate and rotate. Returns list of [FkCtrl1,FKCtrl2,...,IKAim,IKEnd] ''' jointList = mpJoint.getJointList(startJoint, endJoint) if len(jointList) < 3: raise RuntimeError('FKIKChain needs at least three joints') fkCtrls = self.addFKChain(startJoint, endJoint, localParent) aimCtrl, endCtrl = self.addIKChain(startJoint, endJoint, worldParent) #lock mid ctrls axes so the FK system can only rotate on one plane. #This is needed so the IK system, which is always on a plane, can snap to the FK system #First find which axis will be locked by checking its local axes against the #normal vector of the chain. The highest dot product is the most parallel. #Note: I may need to lock joint DoF as well, sometimes Maya injects tiny rotation values there #when in IK mode. midJointIdx = int(len(jointList) / 2) midJoint = jointList[midJointIdx] startV = mpMath.Vector(startJoint) midV = mpMath.Vector(midJoint) endV = mpMath.Vector(endJoint) chainMid = midV - startV chainEnd = endV - startV chainMid.normalize() chainEnd.normalize() chainNormal = chainMid.cross(chainEnd) chainNormal.normalize() axes = ['x', 'y', 'z'] for ctrl in fkCtrls[1:-1]: ctrlXform = mpMath.Transform(ctrl) dots = list() dots.append(abs(ctrlXform.xAxis().dot(chainNormal))) dots.append(abs(ctrlXform.yAxis().dot(chainNormal))) dots.append(abs(ctrlXform.zAxis().dot(chainNormal))) del axes[dots.index(max(dots))] for axis in axes: mpAttr.lockAndHide(ctrl, ['r%s' % axis]) #constrain fk ctrls to local/world firstFKZero = cmds.listRelatives(fkCtrls[0], p=True)[0] cmds.parentConstraint(localParent, firstFKZero, mo=True) #make a stub joint and an SCIKsolver #This is using Maya's built in 'ikBlend' blends rotate on the last joint self.name.desc = 'ikStub' cmds.select(cl=True) stubJoint = cmds.joint(n=self.name.get()) cmds.parent(stubJoint, endJoint) stubPos = endV + ((endV - midV) * 0.5) cmds.xform(stubJoint, t=stubPos.get(), ws=True) self.name.desc = 'iKStubHandle' stubHandle, stubEffector = cmds.ikHandle(n=self.name.get(), solver='ikSCsolver', sj=endJoint, ee=stubJoint) self.name.desc = 'stubEffector' stubEffector = cmds.rename(stubEffector, self.name.get()) cmds.parent(stubHandle, self.noXform) cmds.parentConstraint(endCtrl, stubHandle, mo=True) mpCache.flag(stubJoint, False) #don't want stub joints saved in jointSRTs #Construct the blend FKIKblender = self.addAttrLimb(ln=mpName.FKIKBLENDATTR, at='float', min=0, max=1, dv=0, k=True) effector, handle = mpJoint.getIKNodes(endJoint) cmds.connectAttr(FKIKblender, handle + '.ikBlend') cmds.connectAttr(FKIKblender, stubHandle + '.ikBlend') #switch ctrl vis for ctrl in (aimCtrl, endCtrl): shape = cmds.listRelatives(ctrl, s=True)[0] mpAttr.connectWithAdd(FKIKblender, shape + '.v', 0.4999999) for ctrl in fkCtrls: shape = cmds.listRelatives(ctrl, s=True)[0] adder = mpAttr.connectWithAdd(FKIKblender, shape + '.v', -0.4999999) mpAttr.connectWithReverse(adder + '.output', shape + '.v', force=True) #setup IK->FK snapping messages #Since the IK end ctrl and the last FK ctrl can have totally different oris, #make a null matching the IK's ori under the FK ctrl to act as a snap target self.name.desc = 'ikEndSnap' endSnapNull = cmds.group(em=True, n=self.name.get(), p=fkCtrls[-1]) cmds.xform(endSnapNull, ws=True, m=cmds.xform(endCtrl, q=True, ws=True, m=True)) mpRig.addSnapParent(endCtrl, endSnapNull) mpRig.addSnapParent(aimCtrl, fkCtrls[0]) mpRig.addSnapParent(aimCtrl, fkCtrls[1]) mpRig.addSnapParent(aimCtrl, fkCtrls[2]) #cleanup for hideObject in (handle, stubHandle, stubJoint): cmds.setAttr(hideObject + '.v', 0) mpAttr.lockAndHide(hideObject, 'v') fkCtrls.extend([aimCtrl, endCtrl]) return fkCtrls
def build(self): jointList = mpJoint.getJointList(self.startJoint, self.endJoint) if len(jointList) < 5: raise RuntimeError( 'LegFKIK requires at least 5 joints in chain: hip/knee/ankle/ball/toetip. Got %s' % len(jointList)) #start with standard blend setup and FKIKChain self.addPinBlend() legCtrls = self.addFKIKChain(jointList[0], jointList[-3], self.pinBlend, self.pinWorld) #add FK offsets to ball ballFKZero, ballFKCtrl = self.addCtrl('%02d' % len(jointList[:-2]), type='FK', shape='sphere', parent=legCtrls[-3], xform=jointList[-2]) mpAttr.lockAndHide(ballFKCtrl, 't') cmds.orientConstraint(ballFKCtrl, jointList[-2], mo=True) #addFKIKChain returns list with [...,lastFK,IKAim,IKEnd], so grabbing last FK for pick parent: mpRig.addPickParent(ballFKCtrl, legCtrls[-3]) #create classic 'reverse foot' setup if self.heel: heelPos = mpMath.Transform(self.heel) else: RIGLOG.warning( 'No ".heel" joint specified on LegFKIK, guessing pivot') #take the ball->toe length, double it, and go back from the ball that much heelOffset = mpMath.Vector(jointList[-2]) #ball heelOffset -= mpMath.Vector(jointList[-1]) #minus toe heelOffset *= 2 heelOffset + mpMath.Vector(jointList[-2]) #add back to ball heelPos = mpMath.Transform( jointList[-2]) #heel equal ball plus our new vector heelPos += heelOffset #Make ik single chains for ball and toe self.name.desc = 'ikHandleBall' ballHandle, ballEffector = cmds.ikHandle(n=self.name.get(), solver='ikSCsolver', sj=jointList[-3], ee=jointList[-2]) self.name.desc = 'ikHandleToe' toeHandle, toeEffector = cmds.ikHandle(n=self.name.get(), solver='ikSCsolver', sj=jointList[-2], ee=jointList[-1]) self.name.desc = 'ballEffector' ballEffector = cmds.rename(ballEffector, self.name.get()) cmds.parent(ballHandle, self.noXform) self.name.desc = 'toeEffector' toeEffector = cmds.rename(toeEffector, self.name.get()) cmds.parent(toeHandle, self.noXform) #Make foot controls #These transforms are for cosmetic SRT of the foot controls anklePos = mpMath.Vector(jointList[-3]) heelVec = mpMath.Vector(heelPos) footCtrlXform = mpMath.Transform() footCtrlXform.scale(2, 0.4, 4) footCtrlXform.translate(0, heelVec.y - anklePos.y, 0) toeCtrlXform = mpMath.Transform() toeCtrlXform.setFromEuler(0, 90, 0) footZero, footCtrl = self.addCtrl('Foot', shape='cube', type='IK', parent=self.pinWorld, xform=mpMath.Vector(jointList[-3]), shapeXform=footCtrlXform) heelZero, heelCtrl = self.addCtrl('Heel', shape='circle', type='IK', parent=footCtrl, xform=heelPos, shapeXform=toeCtrlXform) toeTipZero, toeTipCtrl = self.addCtrl('ToeTip', shape='circle', type='IK', parent=heelCtrl, xform=jointList[-1], shapeXform=toeCtrlXform) ballZero, ballCtrl = self.addCtrl('Ball', shape='circle', type='IK', parent=toeTipCtrl, xform=jointList[-2], shapeXform=toeCtrlXform) cmds.parentConstraint(ballCtrl, ballHandle, mo=True) cmds.parentConstraint(toeTipCtrl, toeHandle, mo=True) #Blend already exists, but this will grab the right attr FKIKblender = self.addAttrLimb(ln=mpName.FKIKBLENDATTR, at='float', min=0, max=1, dv=0, k=True) cmds.connectAttr(FKIKblender, ballHandle + '.ikBlend') cmds.connectAttr(FKIKblender, toeHandle + '.ikBlend') #constrain legIK endctrl to new foot ctrl ankleIKCtrl = legCtrls[-1] mpAttr.unlockAndShow(ankleIKCtrl, 'r') cmds.parentConstraint(ballCtrl, ankleIKCtrl, mo=True) #swap out foot for old IK handle ctrl #First retrieve effector, handle, aim, and endNull from IK system effector, handle = mpJoint.getIKNodes(jointList[-3]) handleCns = cmds.listConnections(handle + '.tx', s=1, d=0)[0] endNull = cmds.listConnections(handleCns + '.target[0].targetTranslate', s=1, d=0)[0] endNullCns = cmds.listConnections(endNull + '.tx', s=1, d=0)[0] aimCtrl = legCtrls[-2] aimCtrlZero = cmds.listRelatives(aimCtrl, p=True)[0] #delete old aimCtrl blend cns cmds.delete(cmds.listConnections(aimCtrlZero + '.tx', s=1, d=0)[0]) cmds.pointConstraint(self.pinWorld, footCtrl, aimCtrlZero, mo=True) #delete old IK ctrl and wire IK to new foot ctrl cmds.delete(endNullCns) cmds.parentConstraint(ballCtrl, endNull, mo=True) self.deleteCtrl(legCtrls[-1]) #add new pickwalk/snap info mpRig.addPickParent(footCtrl, legCtrls[-2]) mpRig.addPickParent(ballCtrl, footCtrl) mpRig.addPickParent(toeTipCtrl, ballCtrl) mpRig.addPickParent(heelCtrl, toeTipCtrl) #Make some nulls to act as snap targets. This is because IK and FK controls might have different axis order or initial positions. self.name.desc = 'ikAnkleSnapTarget' ikAnkleSnapTarget = cmds.group(em=True, n=self.name.get(), p=legCtrls[-3]) cmds.xform(ikAnkleSnapTarget, ws=True, m=cmds.xform(footCtrl, ws=True, q=True, m=True)) mpRig.addSnapParent(footCtrl, ikAnkleSnapTarget) self.name.desc = 'ikBallSnapTarget' ikBallSnapTarget = cmds.group(em=True, n=self.name.get(), p=ballFKCtrl) cmds.xform(ikBallSnapTarget, ws=True, m=cmds.xform(ballCtrl, ws=True, q=True, m=True)) mpRig.addSnapParent(ballCtrl, ikBallSnapTarget) self.name.desc = 'fkBallSnapTarget' fkBallSnapTarget = cmds.group(em=True, n=self.name.get(), p=ballCtrl) cmds.xform(fkBallSnapTarget, ws=True, m=cmds.xform(ballFKCtrl, ws=True, q=True, m=True)) mpRig.addSnapParent(ballFKCtrl, fkBallSnapTarget) self.name.desc = 'ikToeSnapTarget' ikToeSnapTarget = cmds.group(em=True, n=self.name.get(), p=ballFKCtrl) cmds.xform(ikToeSnapTarget, ws=True, m=cmds.xform(toeTipCtrl, ws=True, q=True, m=True)) mpRig.addSnapParent(toeTipCtrl, ikToeSnapTarget) #add new ctrls to vis switch for ctrl in (footCtrl, heelCtrl, toeTipCtrl, ballCtrl): shape = cmds.listRelatives(ctrl, s=True)[0] mpAttr.connectWithAdd(FKIKblender, shape + '.v', 0.4999999) for ctrl in [ballFKCtrl]: shape = cmds.listRelatives(ctrl, s=True)[0] adder = mpAttr.connectWithAdd(FKIKblender, shape + '.v', -0.4999999) revNode = mpAttr.connectWithReverse(adder + '.output', shape + '.v', force=True) #cleanup for item in [ballHandle, toeHandle, handle]: mpAttr.visOveride(item, 0)
def build(self): jointList = mpJoint.getJointList(self.startJoint, self.endJoint) if len(jointList) < 5: raise RuntimeError( 'LegFKIK requires at least 5 joints in chain: hip/knee/ankle/ball/toetip. Got %s' % len(jointList)) #Internally the dog leg is driven by a 3 joint ik system made here. #This three joint IK has the same total length as hip->ball joint chain. length = mpJoint.getChainLength(jointList[0], jointList[-2]) halfLength = length / 2.0 hip = mpMath.Vector(jointList[0]) knee = mpMath.Vector(jointList[1]) ball = mpMath.Vector(jointList[-2]) legBaseV = ball - hip legBaseMidV = legBaseV * 0.5 binormalLength = -1 * math.sqrt( 0.5 * legBaseV.length()**2 / halfLength**2) #Find the isoceles triange that is coplanar with the leg chain, #and whose tall legs sum to the same length as the dogleg. #The top vertex of this triangle is where we want the mid joint on our #three joint system kneeV = knee - hip kneeV.normalize() ballV = ball - hip ballV.normalize() chainNormal = kneeV.cross(ballV) chainNormal.normalize() chainBinorm = chainNormal.cross(kneeV) chainBinorm *= binormalLength midJointPos = (legBaseMidV + chainBinorm) + hip #make driver joints cmds.select(cl=True) self.name.desc = 'driverJoint01' driverJnt1 = cmds.joint(n=self.name.get(), p=hip.get()) self.name.desc = 'driverJoint02' driverJnt2 = cmds.joint(n=self.name.get(), p=midJointPos.get()) self.name.desc = 'driverJoint03' driverJnt3 = cmds.joint(n=self.name.get(), p=ball.get()) #start with standard blend setup and FKIKChain on hip->ankle self.addPinBlend() legCtrls = self.addFKIKChain(jointList[0], jointList[-3], self.pinBlend, self.pinWorld) ikEnd = legCtrls[-1] effector, handle = mpJoint.getIKNodes(jointList[-3]) #add FK offsets to ball ballFKZero, ballFKCtrl = self.addCtrl('%02d' % len(jointList[:-2]), type='FK', shape='sphere', parent=legCtrls[-3], xform=jointList[-2]) mpAttr.lockAndHide(ballFKCtrl, 't') cmds.orientConstraint(ballFKCtrl, jointList[-2], mo=True) mpRig.addPickParent(ballFKCtrl, legCtrls[-3]) #Make IK Chain on driver joints oldpart = self.name.part self.name.part += "Driver" driverAimCtrl, driverEndCtrl = self.addIKChain(driverJnt1, driverJnt3, self.pinWorld) self.name.part = oldpart #set ctrl flags false on driver ctrls. #This way animators will never touch them and they won't snap/reset/etc. driverEndNull = cmds.listRelatives(driverEndCtrl, p=True)[0] mpCtrl.setAsCtrl(driverEndCtrl, False) mpCtrl.setAsCtrl(driverAimCtrl, False) #and delete shapes so they aren't visible for ctrl in (driverEndCtrl, driverAimCtrl): self.ctrls.remove(ctrl) cmds.delete(cmds.listRelatives(ctrl, s=True)[0]) #make single chain on ankle->ball and ball->tip for ankle roll and toe tap self.name.desc = 'ikHandleBall' ballHandle, ballEffector = cmds.ikHandle(n=self.name.get(), solver='ikSCsolver', sj=jointList[-3], ee=jointList[-2]) self.name.desc = 'ikHandleToe' toeHandle, toeEffector = cmds.ikHandle(n=self.name.get(), solver='ikSCsolver', sj=jointList[-2], ee=jointList[-1]) self.name.desc = 'ballEffector' ballEffector = cmds.rename(ballEffector, self.name.get()) cmds.parent(ballHandle, self.noXform) self.name.desc = 'toeEffector' toeEffector = cmds.rename(toeEffector, self.name.get()) cmds.parent(toeHandle, self.noXform) #Make a ankle roll ctrl and toe tap ctrl #These transforms are for cosmetic SRT of the foot controls footCtrlXform = mpMath.Transform() footCtrlXform.scale(2, 0.4, 4) toeCtrlXform = mpMath.Transform() toeCtrlXform.setFromEuler(0, 90, 0) footZero, footCtrl = self.addCtrl('Foot', shape='cube', type='IK', parent=self.pinWorld, xform=mpMath.Vector(jointList[-2]), shapeXform=footCtrlXform) toeTipZero, toeTipCtrl = self.addCtrl('ToeTip', shape='circle', type='IK', parent=footCtrl, xform=jointList[-1], shapeXform=toeCtrlXform) ballZero, ballCtrl = self.addCtrl('Ball', shape='circle', type='IK', parent=toeTipCtrl, xform=jointList[-2], shapeXform=toeCtrlXform) #parent constrain single chains under new ctrls cmds.parentConstraint(ballCtrl, ballHandle, mo=True) cmds.parentConstraint(toeTipCtrl, toeHandle, mo=True) cmds.parentConstraint(footCtrl, driverEndNull, mo=True) #parent constrain ankle IK to new foot ctrls driverEffector, driverHandle = mpJoint.getIKNodes(driverJnt3) mpAttr.unlockAndShow(ikEnd, 'r') ikEndNull = cmds.listRelatives(ikEnd, p=True)[0] cmds.parentConstraint(ballCtrl, ikEndNull, mo=True) cmds.parentConstraint(toeTipCtrl, driverHandle, mo=True) #parent constrain ball ctrl to driver knee joint cmds.parentConstraint(driverJnt2, ballZero, mo=True) #Blend already exists, but this will grab the right attr FKIKblender = self.addAttrLimb(ln=mpName.FKIKBLENDATTR, at='float', min=0, max=1, dv=0, k=True) cmds.connectAttr(FKIKblender, ballHandle + '.ikBlend') cmds.connectAttr(FKIKblender, toeHandle + '.ikBlend') #add new pickwalk/snap info mpRig.addPickParent(footCtrl, legCtrls[-2]) mpRig.addPickParent(ballCtrl, footCtrl) mpRig.addPickParent(toeTipCtrl, ballCtrl) #Make some nulls to act as snap targets. This is because IK and FK controls might have different axis order or initial positions. self.name.desc = 'ikAnkleSnapTarget' ikAnkleSnapTarget = cmds.group(em=True, n=self.name.get(), p=jointList[-2]) cmds.xform(ikAnkleSnapTarget, ws=True, m=cmds.xform(footCtrl, ws=True, q=True, m=True)) mpRig.addSnapParent(footCtrl, ikAnkleSnapTarget) self.name.desc = 'ikBallSnapTarget' ikBallSnapTarget = cmds.group(em=True, n=self.name.get(), p=ballFKCtrl) cmds.xform(ikBallSnapTarget, ws=True, m=cmds.xform(ballCtrl, ws=True, q=True, m=True)) mpRig.addSnapParent(ballCtrl, ikBallSnapTarget) self.name.desc = 'fkBallSnapTarget' fkBallSnapTarget = cmds.group(em=True, n=self.name.get(), p=jointList[-2]) cmds.xform(fkBallSnapTarget, ws=True, m=cmds.xform(ballFKCtrl, ws=True, q=True, m=True)) mpRig.addSnapParent(ballFKCtrl, fkBallSnapTarget) self.name.desc = 'ikToeSnapTarget' ikToeSnapTarget = cmds.group(em=True, n=self.name.get(), p=ballFKCtrl) cmds.xform(ikToeSnapTarget, ws=True, m=cmds.xform(toeTipCtrl, ws=True, q=True, m=True)) mpRig.addSnapParent(toeTipCtrl, ikToeSnapTarget) #add new ctrls to vis switch for ctrl in (footCtrl, toeTipCtrl, ballCtrl): shape = cmds.listRelatives(ctrl, s=True)[0] mpAttr.connectWithAdd(FKIKblender, shape + '.v', 0.4999999) for ctrl in [ballFKCtrl]: shape = cmds.listRelatives(ctrl, s=True)[0] adder = mpAttr.connectWithAdd(FKIKblender, shape + '.v', -0.4999999) mpAttr.connectWithReverse(adder + '.output', shape + '.v', force=True) #cleanup for item in [ballHandle, toeHandle, handle]: mpAttr.visOveride(item, 0) mpAttr.visOveride(driverJnt1, 0)