示例#1
0
def build( root, fingerDict, side='rt', cleanUp=1 ):
    '''
    Builds an fk hand with extendable knuckles
    
    Args:
        root - node at the location of the wrist
        fingerDict - dictionary with keys for the name of each digit and values which are the root node for each digit:
            e.g. { 'thumb':'lf_thumb1_defJnt', 'index':'lf_index1_defJnt' }
            
            For each key in fingerDict a digit will be created with segments based on the number of child joints in the chain.
            
            Any joints with the same worldSpace location as their child will become 'extend' joints
    
    '''
    # Validate arguments
    if type(root) != type('hello') and type(root) != type(u'hello'):
        return showDialog( 'Argument Error', 'You must supply a root of type string or unicode' )
    
    if type(fingerDict) != type({}):
        return showDialog( 'Argument Error', "fingerDict must be supplied as a dictionary/nwith a key for the name of each digit and a root node as its value/n e.g.{ 'thumb':'lf_thumb1_defJnt', 'index':'lf_index1_defJnt' }")
    
    # Build and align root groups
    xformGrp = cmds.group(empty=1)
    xformGrp = cmds.rename(xformGrp, common.getName( node=xformGrp, side=side, rigPart='hand', function='xform', nodeType='grp'))
    common.align(node=xformGrp, target=root)
    
    rigGrp = cmds.duplicate(xformGrp)
    rigGrp = cmds.rename(rigGrp, common.getName( node=rigGrp, side=side, rigPart='hand', function='rig', nodeType='grp'))
    cmds.parent( rigGrp, xformGrp )
    
    # Root joint
    cmds.select(None)
    rootJnt = cmds.joint()
    rootJnt = cmds.rename(rootJnt, common.getName( node=rootJnt, side=side, rigPart='hand', function='root', nodeType='jnt'))
    common.align(rootJnt, xformGrp)
    cmds.parent(rootJnt, rigGrp)
    cmds.setAttr('%s.jointOrient' % rootJnt, 0, 0, 0 )
    
    # Build fingers
    for key in fingerDict.keys():
        fing = buildFinger( side=side, name=key, rootJnt=fingerDict[key], rootGrp=xformGrp, cleanUp=cleanUp )
        cmds.parent(fing['jnts'][0], rigGrp)
        
    # Cleanup
    if cleanUp:
        cmds.setAttr( '%s.visibility' % rigGrp, 0 )
        common.attrCtrl(nodeList=[rigGrp], attrList=['visibility'])
        
    return {'systemGrp':xformGrp}
示例#2
0
def build( side = None, jntFoot = None, ctrlFoot = None, ikHandleLeg = None, mesh = None, stretchLoc = None, cleanUp = 0 ):
	'''
	builds a reverse foot!
	creates roll pivots, adds attributes to foot control and sets up the contact lattice.
	
	function requires the side, foot joint (in richards limb module the extra_jnt),
	foot control and ik handle to be supplied.
	The character's mesh is needed for contact lattice deformer and will be used for getting
	the pivot locations from a set of specific vertices.
	
	DO NOT FORGET: If the rig is build before there is a skincluster attached to the mesh
	you will need to reorder the deformers.
	
	returns a dictionary containing corresponding objects for these keys: 
	joints, pivots, ikHandles, lattice, systemsGrp, ctrls
	'''
	
		
	# abort if not all arguments were supplied
	if not jntFoot or not ctrlFoot or not ikHandleLeg or not mesh or not stretchLoc:
		return cmds.warning( 'Argument Error: Please supply all arguments (side, joint_footExtraEnd, control, ikHandle, mesh).' )
	
	
	# vertex indices for left / right
	pivotVertsLeft = [196, 199, 29, 28]
	latticeVertIdsLeft = ['0:1', '8:9', '28:29', '39:42', '47:48', '51:52', '55:58', '63:64', '67:70', '196', '199', '206:207', '211:212', '241:242', '253', '264:265', '268:273', '278:279', '282:285', '290:291', '294:297', '302:303', '409', '414', '418', '434', '437:438', '447:448']
	pivotVertsRight = [219, 230, 36, 35]
	latticeVertIdsRight = ['14:15', '24:25', '35:36', '43:46', '49:50', '53:54', '59:62', '65:66', '71:74', '219', '230:232', '237:238', '248:249', '258', '261:262', '266:267', '274:277', '280:281', '286:289', '292:293', '298:301', '304:305', '424:426', '430:431', '441:443']
	
	# set side relevant data, abort if no side specified
	if side in ['lf', 'left', 'lft']:
		pivotVerts = pivotVertsLeft
		latticeVertIds = latticeVertIdsLeft
		sideColor = 'blue'

	elif side in ['rt', 'right', 'rgt']:
		pivotVerts = pivotVertsRight
		latticeVertIds = latticeVertIdsRight
		sideColor = 'red'
	
	else:
		return cmds.warning( 'No valid side specified' )
	
	
	# create output dictionary
	outDict = {}
	
	
	# add attributes to foot control, if attribute already exists skip it
	attrs = ['roll', 'rollBreak', 'side', 'toeTap', 'toeRaise', 'toeTwist', 'heelTwist', 'squashGroundContact']
	for attr in attrs:
		if ( cmds.attributeQuery( attr, node=ctrlFoot, exists=True ) ) == True:
			print( 'Attribute %s already exists on %s.' % ( attr, ctrlFoot ) )
		else:
			cmds.addAttr( ctrlFoot, longName=attr, attributeType='float', keyable = True )
			
	
	useDefaultPivots = False
	
	# create pivot locators
	if useDefaultPivots == True:
	
		# set default pivot positions
		pivotOffset = 0.5
		jntFootPos = cmds.xform( jntFoot, q = True, translation = True, ws = True )
		pivotFrontPos = [ jntFootPos[0], jntFootPos[1], jntFootPos[2] + pivotOffset ]
		pivotRearPos = [ jntFootPos[0], jntFootPos[1], jntFootPos[2] - pivotOffset ]
		pivotLeftPos = [ jntFootPos[0] + pivotOffset, jntFootPos[1], jntFootPos[2] ]
		pivotRightPos = [ jntFootPos[0] - pivotOffset, jntFootPos[1], jntFootPos[2] ]
	
	else:
	
		# get pivot positions from vertices
		pivotFrontPos = cmds.xform( mesh + '.vtx[%s]' % pivotVerts[0], q = True, ws = True, t = True )
		pivotRearPos = cmds.xform( mesh + '.vtx[%s]' % pivotVerts[1], q = True, ws = True, t = True )
		pivotLeftPos = cmds.xform( mesh + '.vtx[%s]' % pivotVerts[2], q = True, ws = True, t = True )
		pivotRightPos = cmds.xform( mesh + '.vtx[%s]' % pivotVerts[3], q = True, ws = True, t = True )
	
	pivotFront = cmds.spaceLocator()[0]
	pivotFront = cmds.rename( pivotFront, common.getName( node=pivotFront, side=side, rigPart='foot', function='front_piv', nodeType='loc') )
	cmds.xform( translation = pivotFrontPos )
	
	pivotRear = cmds.spaceLocator()[0]
	pivotRear = cmds.rename( pivotRear, common.getName( node=pivotRear, side=side, rigPart='foot', function='rear_piv', nodeType='loc') )
	cmds.xform( translation = pivotRearPos )
	
	pivotLeft = cmds.spaceLocator()[0]
	pivotLeft = cmds.rename( pivotLeft, common.getName( node=pivotLeft, side=side, rigPart='foot', function='left_piv', nodeType='loc') )
	cmds.xform( translation = pivotLeftPos )
	
	pivotRight = cmds.spaceLocator()[0]
	pivotRight = cmds.rename( pivotRight, common.getName( node=pivotRight, side=side, rigPart='foot', function='right_piv', nodeType='loc') )
	cmds.xform( translation = pivotRightPos )
	
	cmds.parent( pivotFront, pivotRear )
	cmds.parent( pivotRear, pivotLeft )
	cmds.parent( pivotLeft, pivotRight )
	
	pivotLocs = [pivotFront, pivotRear, pivotLeft, pivotRight]
	for pivotLoc in pivotLocs: cmds.setAttr( cmds.listRelatives( pivotLoc )[0] + '.localScale', .1, .1, .1 ) # reduce local scale
	
	#outDict['pivots'] = pivotLocs | done after creating ball locator
	
	
	# create ball and toe joint
	cmds.select( clear = True )
	
	jntAnkle = cmds.joint()
	jntAnkle = cmds.rename(jntAnkle, common.getName( node=jntAnkle, side=side, rigPart='ankle', function='driven', nodeType='jnt'))
	cmds.xform( jntAnkle, ws = True, translation = ( cmds.xform( ikHandleLeg, q = True, t = True, ws = True ) ) )
	cmds.setAttr( jntAnkle + '.radius', .75 * cmds.getAttr( jntFoot + '.radius' ) )	
	
	jntBall = cmds.joint()
	jntBall = cmds.rename(jntBall, common.getName( node=jntBall, side=side, rigPart='ball', function='driven', nodeType='jnt'))
	cmds.xform( jntBall, ws = True, t = ( cmds.xform( jntFoot, q = True, ws = True, t = True ) ) )
	cmds.xform( jntBall, ws = True, relative = True, t = ( 0, 0, .5 * cmds.xform( pivotFront, q = True, t = True, ws = True )[2] ) )
	cmds.setAttr( jntBall + '.radius', .75 * cmds.getAttr( jntFoot + '.radius' ) )
	
	jntToe = cmds.joint()
	jntToe = cmds.rename(jntToe, common.getName( node=jntToe, side=side, rigPart='toe', function='driven', nodeType='jnt'))
	cmds.xform( jntToe, t = ( 0, 0, .5 * cmds.xform( pivotFront, q = True, t = True, ws = True )[2] ), relative = True, ws = True )
	cmds.setAttr( jntToe + '.radius', cmds.getAttr( jntBall + '.radius' ) )
	
	grpBufJnt = cmds.group ( empty = True )
	grpBufJnt = cmds.rename( grpBufJnt, common.getName( node=grpBufJnt, side=side, rigPart='foot', function='jnt_buf', nodeType='grp' ) )
	common.align( grpBufJnt, jntFoot, translate = True, orient = False )
	cmds.parentConstraint( jntFoot, grpBufJnt, mo = False )
	cmds.parent( jntAnkle, grpBufJnt )
	
	grpJnts = cmds.group( empty = True )
	grpJnts = cmds.rename( grpJnts, common.getName( node=grpJnts, side=side, rigPart='foot', function='jnts', nodeType='grp' ) )
	common.align( grpJnts, ctrlFoot, translate = True, orient = False )
	cmds.parent( grpBufJnt, grpJnts )
	
	outDict['joints'] = [ jntAnkle, jntBall, jntToe ]
	
	
	# create toe IK handles
	ikHandleBall, ikEffectorBall = cmds.ikHandle( startJoint = jntAnkle, endEffector = jntBall, solver = 'ikSCsolver', name = '%s_ball_ikHandle' % side )
	ikHandleToe, ikEffectorToe = cmds.ikHandle( startJoint = jntBall, endEffector = jntToe, solver = 'ikSCsolver', name = '%s_toe_ikHandle' % side )
	cmds.parent( ikHandleBall, pivotRear )
	cmds.parent( ikHandleToe, pivotFront )
	
	outDict['ikHandles'] = [ ikHandleLeg, ikHandleBall, ikHandleToe ]
	
	
	# create ball pivot locator
	pivotBall = cmds.spaceLocator( name = common.getName( side=side, rigPart='foot', function='ball_piv', nodeType='loc') )[0]
	cmds.xform( pivotBall, ws = True, translation = cmds.xform( jntBall, q = True, ws = True, t = True ) )
	pivotBall = cmds.parent( pivotBall, pivotFront )[0]
	pivotLocs.insert( 0, pivotBall ) 
	
	outDict['pivots'] = pivotLocs
	
	
	# create simple clamps and connect roll, side attribute to pivot locators
	rollLimit = 180.0
	cmds.addAttr( ctrlFoot + '.side', edit = True, min = -1 * rollLimit, max = rollLimit )
	
	clampRollRear = cmds.shadingNode( 'clamp', asUtility = True )
	clampRollRear = cmds.rename( clampRollRear, common.getName( node=clampRollRear, side=side, rigPart='rear', function='roll', nodeType='clamp') )
	cmds.setAttr( clampRollRear + '.minR', -rollLimit )
	cmds.connectAttr( ctrlFoot + '.roll', clampRollRear + '.inputR' )
	cmds.connectAttr( clampRollRear + '.outputR', pivotRear + '.rotateX' )
	
	clampRollLeft = cmds.shadingNode( 'clamp', asUtility = True )
	clampRollLeft = cmds.rename( clampRollLeft, common.getName( node=clampRollLeft, side=side, rigPart='left', function='side', nodeType='clamp') )
	cmds.setAttr( clampRollLeft + '.minR', -rollLimit )
	cmds.connectAttr( ctrlFoot + '.side', clampRollLeft + '.inputR' )
	cmds.connectAttr( clampRollLeft + '.outputR', pivotLeft + '.rotateZ' )
	
	clampRollRight = cmds.shadingNode( 'clamp', asUtility = True )
	clampRollRight = cmds.rename( clampRollRight, common.getName( node=clampRollRight, side=side, rigPart='right', function='side', nodeType='clamp') )
	cmds.setAttr( clampRollRight + '.maxR', rollLimit )
	cmds.connectAttr( ctrlFoot + '.side', clampRollRight + '.inputR' )
	cmds.connectAttr( clampRollRight + '.outputR', pivotRight + '.rotateZ' )
	
	# setup foot roll
	cmds.setAttr( ctrlFoot + '.rollBreak', 30.0 )
	cmds.addAttr( ctrlFoot + '.rollBreak', edit = True, min = 0.0, max = rollLimit )
	cmds.addAttr( ctrlFoot + '.roll', edit = True, min = -rollLimit, max = rollLimit )

	clampRollBall = cmds.shadingNode( 'clamp', asUtility = True )
	clampRollBall = cmds.rename( clampRollBall, common.getName( node=clampRollBall, side=side, rigPart='ball', function='roll', nodeType='clamp') )
	cmds.connectAttr( ctrlFoot + '.rollBreak', clampRollBall + '.maxR' )
	cmds.connectAttr( ctrlFoot + '.roll', clampRollBall + '.inputR' )

	pmaRollBall = cmds.shadingNode( 'plusMinusAverage', asUtility = True )
	cmds.setAttr( pmaRollBall + '.operation', 2 )
	cmds.setAttr( pmaRollBall + '.input1D[0]', 180 )
	cmds.connectAttr( ctrlFoot + '.rollBreak', pmaRollBall + '.input1D[1]' )

	remapRollBall = cmds.shadingNode( 'remapValue', asUtility = True )
	cmds.setAttr( remapRollBall + '.outputMin', -1 )
	cmds.setAttr( remapRollBall + '.outputMax', 1 )
	cmds.setAttr( remapRollBall + '.value[0].value_Position', 0 )
	cmds.setAttr( remapRollBall + '.value[0].value_FloatValue', 1 )
	cmds.setAttr( remapRollBall + '.value[1].value_Position', 1 )
	cmds.setAttr( remapRollBall + '.value[1].value_FloatValue', 0 )
	cmds.connectAttr( ctrlFoot + '.rollBreak', remapRollBall + '.inputMin' )
	cmds.connectAttr( pmaRollBall + '.output1D', remapRollBall + '.inputMax' )

	multRollBall = cmds.shadingNode( 'multiplyDivide', asUtility = True )
	cmds.connectAttr( clampRollBall + '.outputR', multRollBall + '.input1X' )
	cmds.connectAttr( remapRollBall + '.outValue', multRollBall + '.input2X' )
	cmds.connectAttr( multRollBall + '.outputX', pivotBall + '.rotateX' )
			
	clampRollFront = cmds.shadingNode( 'clamp', asUtility = True )
	clampRollFront = cmds.rename( clampRollFront, common.getName( node=clampRollFront, side=side, rigPart='toe', function='roll', nodeType='clamp') )
	pmaRollFront = cmds.shadingNode( 'plusMinusAverage', asUtility = True )
	pmaRollFront = cmds.rename( pmaRollFront, common.getName( node=pmaRollFront, side=side, rigPart='toe', function='roll', nodeType='pma') )

	cmds.connectAttr( ctrlFoot + '.rollBreak', clampRollFront + '.minR' )
	cmds.connectAttr( ctrlFoot + '.roll', clampRollFront + '.inputR' )
	cmds.setAttr( clampRollFront + '.maxR', rollLimit )
	cmds.connectAttr( clampRollFront + '.outputR', pmaRollFront + '.input1D[0]' )
	
	cmds.setAttr( pmaRollFront + '.operation', 2 )
	cmds.connectAttr( ctrlFoot + '.rollBreak', pmaRollFront + '.input1D[1]' )
	cmds.connectAttr( pmaRollFront + '.output1D', pivotFront + '.rotateX' )
	
	# connect twist attributes
	cmds.connectAttr( ctrlFoot + '.toeTwist', pivotFront + '.rotateY' )
	cmds.connectAttr( ctrlFoot + '.heelTwist', pivotRear + '.rotateY' )
	
	# create pivot for toe raise, parent ball ikHandle and connect
	pivToeRaise = common.insertGroup( pivotFront )
	pivToeRaise = cmds.rename( pivToeRaise, common.getName( node=pivToeRaise, side=side, rigPart='toe', function='raise_piv', nodeType='grp') )
	cmds.parent( ikHandleBall, pivotFront )
	cmds.connectAttr( ctrlFoot + '.toeRaise', pivToeRaise + '.rotateX' )
	
	# create pivot for toe tap and connect
	pivToeTap = cmds.group( ikHandleToe )
	pivToeTap = cmds.rename( pivToeTap, common.getName( node=pivToeTap, side=side, rigPart='toe', function='tap_piv', nodeType='grp') )
	cmds.xform( pivToeTap, preserve = True, ws = True, piv = cmds.xform( jntBall, q = True, translation = True, ws = True) )
	cmds.connectAttr( ctrlFoot + '.toeTap', pivToeTap + '.rotateX' )
	
	
	# create lattice deformer
	latticeVerts = []
	for vertId in latticeVertIds:
		latticeVerts.append( '%s.vtx[%s]' % (mesh, vertId) )
	cmds.select( latticeVerts )
	
	ffd, ffdLattice, ffdBase = cmds.lattice( divisions =(3, 2, 4), objectCentered = True, outsideLattice = 2, ofd = 0.25 )
	ffd = cmds.rename( ffd, common.getName( node=ffd, side=side, rigPart='foot', function='contact', nodeType='ffd') )
	ffdLattice = cmds.rename( ffdLattice, common.getName( node=ffdLattice, side=side, rigPart='foot', function='contact', nodeType='ffdlattice') )
	ffdBase = cmds.rename( ffdBase, common.getName( node=ffdBase, side=side, rigPart='foot', function='contact', nodeType='ffdbase') )
	cmds.addAttr( ctrlFoot + '.squashGroundContact', edit = True, min = 0.0 ) #, max = 1.0
	cmds.setAttr( ctrlFoot + '.squashGroundContact',  1.0 )
	cmds.connectAttr( ctrlFoot + '.squashGroundContact', ffd + '.envelope' )
	
	
	outDict['lattice'] = ffd, ffdLattice, ffdBase
	
	
	# set up ground contact control
	ctrlGround = controls.Control( side = side, rigPart = "foot", function = "contact", nodeType = "ctrl", size = 1.5, color = sideColor, aimAxis = "x" )
	ctrlGround.squareCtrl()
	cmds.rotate( 0, 45, 0, ctrlGround.control + '.cv[0:4]', relative = True, objectSpace = True )
	cmds.xform( ctrlGround.control, ws = True, t = cmds.xform( jntFoot, q = True, ws = True , t = True ) )
	
	cmds.addAttr( ctrlFoot, longName = 'showGroundCtrl', attributeType = 'bool', keyable = True )
	cmds.connectAttr( ctrlFoot + '.showGroundCtrl', ctrlGround.control + '.visibility' )
	cmds.setAttr( ctrlGround.control + '.scaleX', lock = True, keyable = False, channelBox = False )
	cmds.setAttr( ctrlGround.control + '.scaleY', lock = True, keyable = False, channelBox = False )
	cmds.setAttr( ctrlGround.control + '.scaleZ', lock = True, keyable = False, channelBox = False )
	cmds.setAttr( ctrlGround.control + '.visibility', keyable = False, channelBox = False )
	cmds.addAttr( ctrlGround.control, longName = 'followFoot', attributeType = 'float', keyable = True, min = 0.0, max = 1.0, dv = 1.0 )
	
	ctrlGrpGround = common.insertGroup( ctrlGround.control )
	constGrpGround = cmds.pointConstraint( ctrlFoot, ctrlGrpGround, mo = False, skip = 'y' )[0]
	cmds.setAttr( constGrpGround + '.enableRestPosition', 0 )
	cmds.connectAttr( ctrlGround.control + '.followFoot', constGrpGround + '.' + cmds.pointConstraint( constGrpGround, q = True, weightAliasList = True )[0] )
	
	
	# create base and top lattice ctrl joints
	topLatticePoints =  ['[1][1][3]', '[0][1][2]', '[0][1][1]', '[1][1][0]', '[2][1][1]', '[2][1][2]']
	baseLatticePoints = ['[1][0][3]', '[0][0][2]', '[0][0][1]', '[1][0][0]', '[2][0][1]', '[2][0][2]']
	centerLatticePoints = [ '[1][0][1]', '[1][0][2]' ]
	latticeJntRotations = [0, -65, -115, -180, -245, -295] 
	
	grpLatticeJnts = cmds.group( empty = True )
	grpLatticeJnts = cmds.rename( grpLatticeJnts, common.getName( node=grpLatticeJnts, side=side, rigPart='foot', function='ffd_jnts', nodeType='grp' ) )
	common.align( grpLatticeJnts, ctrlFoot, translate = True, orient = False )
	
	topLatticeJnts = []
	baseLatticeJnts = []
	for latticePoints in [ baseLatticePoints, topLatticePoints ]:
		i = 0
		if latticePoints == topLatticePoints: pos = 'top'
		if latticePoints == baseLatticePoints: pos = 'base'
		for latticePoint in latticePoints:
			i += 1
			cmds.select( clear = True )
			latticeJnt = cmds.joint()
			latticeJnt = cmds.rename( latticeJnt, common.getName( node=latticeJnt, side=side, rigPart='foot', function='ffd_%s%s' % ( pos,i ), nodeType='jnt' ) )
			cmds.xform( latticeJnt, ws = True, t = cmds.xform( '%s.pt%s' % ( ffdLattice, latticePoint ), q = True, ws = True, t = True ) )
			cmds.setAttr( latticeJnt + '.radius', .5 )
			cmds.setAttr( latticeJnt + '.rotateY', latticeJntRotations[i-1] )
			latticeJnt = cmds.parent( latticeJnt, grpLatticeJnts )[0]
			common.insertGroup( latticeJnt )
			if pos == 'top': topLatticeJnts.append( latticeJnt )
			if pos == 'base': baseLatticeJnts.append( latticeJnt )
	
	# center lattice joint
	cmds.select( clear = True )
	centerLatticeJnt = cmds.joint()
	centerLatticeJnt = cmds.rename( centerLatticeJnt, common.getName( node=centerLatticeJnt, side=side, rigPart='foot', function='ffd_center', nodeType='jnt' ) )
	cmds.setAttr( centerLatticeJnt + '.radius', .5 )
	clpPos1 = cmds.xform( '%s.pt%s' % ( ffdLattice, centerLatticePoints[0] ), q = True, ws = True, t = True )
	clpPos2 = cmds.xform( '%s.pt%s' % ( ffdLattice, centerLatticePoints[1] ), q = True, ws = True, t = True )
	clpPos = [0,0,0]
	clpPos[0] = clpPos1[0] + 0.5 * ( clpPos2[0] - clpPos1[0] )
	clpPos[1] = clpPos1[1] + 0.5 * ( clpPos2[1] - clpPos1[1] )
	clpPos[2] = clpPos1[2] + 0.5 * ( clpPos2[2] - clpPos1[2] )
	cmds.xform( centerLatticeJnt, ws = True, t = clpPos)
	centerLatticeJnt = cmds.parent( centerLatticeJnt, grpLatticeJnts)[0]
	common.insertGroup( centerLatticeJnt )
	baseLatticeJnts.append( centerLatticeJnt )
	
	# create ground reader locators
	grpReadLocs = cmds.group( empty = True )
	grpReadLocs = cmds.rename( grpReadLocs, common.getName( node=grpReadLocs, side=side, rigPart='foot', function='read_locs', nodeType='grp' ) )
	
	i = 0
	for latticeJnt in baseLatticeJnts:
		i += 1
		
		locBase = cmds.spaceLocator()
		cmds.setAttr( cmds.listRelatives( locBase )[0] + '.localScale', .2, .2, .2  )
		locBase = cmds.rename( locBase, common.getName( node=locBase, side=side, rigPart='ground', function='base%s' % i, nodeType='loc' ) )
		cmds.setAttr( locBase + '.visibility', 0 )
		
		locReader = cmds.spaceLocator()
		cmds.setAttr( cmds.listRelatives( locReader )[0] + '.localScale', .2, .2, .2  )
		locReader = cmds.rename( locReader, common.getName( node=locReader, side=side, rigPart='ground', function='read%s' % i, nodeType='loc' ) )
		
		locTop = cmds.spaceLocator()
		cmds.setAttr( cmds.listRelatives( locTop )[0] + '.localScale', .2, .2, .2  )
		locTop = cmds.rename( locTop, common.getName( node=locTop, side=side, rigPart='ground', function='top%s' % i, nodeType='loc' ) )
	
		locReader = cmds.parent( locReader, locBase )[0]
		constRead = cmds.pointConstraint( locTop, locReader, mo = False )
		locBase = cmds.parent( locBase, ctrlGround.control )[0]
		
		locTop = cmds.parent( locTop, grpReadLocs )[0]
	
		cmds.xform( locTop, ws = True, t = cmds.xform( latticeJnt, q = True, ws = True , t = True ) )
		constBase = cmds.pointConstraint( locTop, locBase, mo = False, skip = 'y' )
	
	
		# remap reader output and connect to lattice joint
		remapRead = cmds.shadingNode( 'remapValue', asUtility = True )
		remapRead = cmds.rename( remapRead, common.getName( node=remapRead, side=side, rigPart='ground', function='read%s' % i, nodeType='rmv') )
		cmds.setAttr( remapRead + '.inputMax', -1)
		cmds.setAttr( remapRead + '.outputMax', 1)
		cmds.connectAttr( locReader + '.translateY', remapRead + '.inputValue', f = True )
		cmds.connectAttr( remapRead + '.outValue', latticeJnt + '.translateY', f = True )
		
		if latticeJnt == centerLatticeJnt: # set min max on center remap to enable center drag
			remapRead = cmds.rename( remapRead, common.getName( node=remapRead, side=side, rigPart='ground', function='read_center', nodeType='rmv') )
		
			cmds.setAttr( remapRead + '.inputMin', 0 )
			cmds.setAttr( remapRead + '.outputMin', 0 )
			cmds.setAttr( remapRead + '.inputMax', 1 )
			cmds.setAttr( remapRead + '.outputMax', 1 )
			
			cmds.setAttr( remapRead + '.value[0].value_FloatValue', 0 )	
			cmds.setAttr( remapRead + '.value[0].value_Position', -0.02 )
			cmds.setAttr( remapRead + '.value[0].value_Interp', 1 )
			
			cmds.setAttr( remapRead + '.value[1].value_FloatValue', -1 )	
			cmds.setAttr( remapRead + '.value[1].value_Position', 0.25 )	
			cmds.setAttr( remapRead + '.value[1].value_Interp', 1 )	
					
			cmds.setAttr( remapRead + '.value[2].value_FloatValue', 1 )	
			cmds.setAttr( remapRead + '.value[2].value_Position', -0.25 )	
			cmds.setAttr( remapRead + '.value[2].value_Interp', 1 )			
			
		else: # connect remap output to top joints tz to enhance squishyness
			cmds.connectAttr( remapRead + '.outValue', topLatticeJnts[i-1] + '.translateZ', f = True )
	
	# bind lattice
	clusterLattice = cmds.skinCluster( topLatticeJnts + baseLatticeJnts, ffdLattice ,tsb=True, skinMethod = 0, nw = 1)
	clusterLattice = cmds.rename( clusterLattice, common.getName( node=clusterLattice, side=side, rigPart='foot', function='lattice', nodeType='skinCluster') )
	
	
	# create system and const groups
	grpConst = cmds.group( empty = True, name = side + '_foot_const_grp' )
	common.align( grpConst, ctrlFoot, translate = True, orient = False )
	cmds.parentConstraint( ctrlFoot, grpConst, mo = True )
	
	grpSystem = cmds.group( empty = True, name = side + '_foot_grp' )
	common.align( grpSystem, ctrlFoot, translate = True, orient = False )
	
	# grouping
	grpConst = cmds.parent( grpConst, grpSystem )[0]
	grpJnts = cmds.parent( grpJnts, grpSystem )[0]
	ctrlGrpGround = cmds.parent( ctrlGrpGround, grpSystem )[0]
	grpReadLocs = cmds.parent( grpReadLocs, pivotBall )[0]
	grpLatticeJnts = cmds.parent( grpLatticeJnts, pivotBall )[0]
	pivotRight = cmds.parent( pivotRight, grpConst )[0]
	
	cmds.setAttr( ffdLattice + '.inheritsTransform', 0 )
	ffdLattice, ffdBase = cmds.parent( ffdLattice, ffdBase, pivotBall )
	
	# group ik handle
	cmds.parent( ikHandleLeg, pivotBall )
	for each in cmds.listRelatives( ikHandleLeg ):
		if cmds.nodeType( each ) == 'pointConstraint':
			cmds.delete( each )
	
	# constrain stretch locator
	for each in cmds.listRelatives( stretchLoc ):
		if cmds.nodeType( each ) == 'pointConstraint':
			cmds.delete( each )
	cmds.parentConstraint( pivotBall , stretchLoc, mo = True, weight = 1 )
	
	# constrain low curve jnt grp
	lowCurveGrp = None
	systemGrpLimb = cmds.listRelatives( cmds.listRelatives( stretchLoc, p = True ), p = True )
	for each in cmds.listRelatives( systemGrpLimb ):
		if ( ( side + '_leg_low_curve' ) in each ) and ( cmds.nodeType( each ) == 'transform' ):
			lowCurveGrp = each

	if lowCurveGrp:
		for each in cmds.listRelatives( lowCurveGrp ):
			if cmds.nodeType( each ) == 'parentConstraint':
				cmds.delete( each )
		cmds.parentConstraint( pivotBall, lowCurveGrp, mo = True )
	else: print( 'Could not find leg_low_curve_#_jnt_grp, please constrain manually to ball pivot locator.' )
	
	# hide const / utility grp
	if cleanUp == 1:
		cmds.setAttr( grpConst + '.visibility', 0 )
		cmds.setAttr( grpJnts + '.visibility', 0 )
	
	
	outDict['systemsGrp'] = grpSystem
		
	
	cmds.select( ctrlFoot )
	print('DO NOT FORGET: If the rig is build before there is a skincluster attached to the mesh you will need to reorder the deformers.')
	print('Reverse Foot %s created.' % side)
	return outDict
示例#3
0
def buildFinger( side, name, rootJnt, rootGrp, cleanUp ):
    # Get aim axis - this presumes the rootJnt is NOT an extend joint
    childJnt = cmds.listRelatives( rootJnt, c=1, type='joint' )[0]
    childPos = cmds.getAttr('%s.t' % childJnt)[0]
    childPosAbs = [ math.fabs(value) for value in childPos ]
    twistIndex = childPosAbs.index(max(childPosAbs))
    twistAxis = ['x', 'y', 'z'][ twistIndex ]
    
    # Determine whether the joints have a negative translation along the twist axis
    neg = False
    if childPos[ twistIndex ] < 0.0:
        neg = True
    
    # Duplicate joint chain
    dupJnts = cmds.duplicate( rootJnt, rc=1 )
    print dupJnts
    jnts=[]
    ctrls = []
    index=1
    for d in range(len(dupJnts)):
        if isExtendJnt(dupJnts[d]):
            j = cmds.rename(dupJnts[d], common.getName( node=dupJnts[d], side=side, rigPart='hand', function='%sExtend%s' % ( name, index ), nodeType='jnt'))
            grp = common.insertGroup( node=j )
            grp = cmds.rename( grp, j.replace('jnt', 'grp') )
            # Connect twistAxis translate to ctrl and ctrl rotatePivotTrnaslate back to ctrl.translate
            cmds.connectAttr('%s.t%s' % (ctrls[index-2], twistAxis), '%s.t%s' % ( j, twistAxis ) )
            rotPivTransUC = cmds.createNode('unitConversion')
            rotPivTransUC = cmds.rename(rotPivTransUC, common.getName( node=rotPivTransUC, side=side, rigPart='hand', function='%sNegTrans%s' % ( name, index ), nodeType='conv'))
            cmds.setAttr( '%s.conversionFactor' % rotPivTransUC, -1.0 )
            cmds.connectAttr( '%s.t' % ctrls[index-2], '%s.input' % rotPivTransUC )
            cmds.connectAttr( '%s.output' % rotPivTransUC, '%s.rotatePivotTranslate' % ctrls[index-2] )
        else:
            j = cmds.rename(dupJnts[d], common.getName( node=dupJnts[d], side=side, rigPart='hand', function='%s%s' % ( name, index ), nodeType='jnt'))
            
            if d < (len(dupJnts)-1):
                # Build control
                c = controls.Control( side=side, rigPart="hand", function='%s%s' % ( name, index ), nodeType="ctrl", size=0.15, color=side, aimAxis=twistAxis, flip=neg )
                c.pinCtrl()
                grp = common.insertGroup( node=c.control )
                grp = cmds.rename( grp, c.control.replace('ctrl', 'grp') )
                common.align( node=grp, target=j )
                if d == 0:
                    cmds.parent( grp, rootGrp )
                    cmds.parent( j, rootGrp )
                else:
                    cmds.parent(grp, ctrls[index-2])
                    cmds.pointConstraint(j, grp)
                ctrls.append(c.control)
                
                # Connect joint rotations to ctrl rotations
                cmds.connectAttr('%s.r' % c.control, '%s.r' % j)
                
            index = index + 1
            
        jnts.append( j )
        
        if cleanUp:
            lockAxes = ['t%s' % axis for axis in ['x', 'y', 'z'] if not axis == twistAxis]
            attrList=lockAxes + ['sx', 'sy', 'sz', 'visibility']
            for c in ctrls:
                common.attrCtrl(nodeList=[c], attrList=attrList)
            #common.attrCtrl(nodeList=[ctrls[-1]], attrList='t%s' % twistAxis) 
        
    return {'jnts':jnts, 'ctrls':ctrls}
示例#4
0
def build ( char=None, cleanUp=False ):
    '''
    Builds the specified character
    
    '''
    #Build root structure
    rootSys = root.build()
    
    if not char:
        char = 'defaultChar'

    # import geo
    geoList = geo.importGeo(char)
    for g in geoList:
        cmds.parent( g, rootSys['geoGrp'] )
    
    # Import joints
    joints.importSkel(char)
    
    # Parent joints to root defJnts group
    jnts = cmds.ls(type='joint')
    for j in jnts:
        p = cmds.listRelatives(j, p=1)
        if not p:
            cmds.parent(j, rootSys['defJntsGrp'])

    # Build spine
    spineSys = spine.build( hips='cn_spine_01_defJnt', chest='cn_spine_06_defJnt', head='cn_spine_11_defJnt',numSpineJoints=6, numHeadJoints=6, twistAxis='x', bendAxis='y', cleanUp=cleanUp )
    cmds.parent(spineSys['xformGrp'], rootSys['systemsGrp'])
    spineTarg = cmds.group(empty=True)
    spineTarg = cmds.rename(spineTarg, common.getName(side='cn', rigPart='spine', function='const', nodeType='grp'))
    common.align(spineTarg, spineSys['xformGrp'])
    cmds.parent(spineTarg, rootSys['constGrp'])
    cmds.parentConstraint( spineTarg, spineSys['xformGrp'] )

    # Build limbs
    lf_arm = anom.systems.limb.build( startJoint='lf_shoulder_1_defJnt', middleJoint='lf_arm_1_defJnt', endJoint='lf_hand_defJnt', extraJoint='lf_hand_end_defJnt', side='lf', name='arm', twistJointCount=6 )
    cmds.parent( lf_arm['limbSystem_grp'], rootSys['systemsGrp'] )
    
    rt_arm = anom.systems.limb.build( startJoint='rt_shoulder_1_defJnt', middleJoint='rt_arm_1_defJnt', endJoint='rt_hand_defJnt', extraJoint='rt_hand_end_defJnt', side='rt', name='arm', twistJointCount=6 )
    cmds.parent( rt_arm['limbSystem_grp'], rootSys['systemsGrp'] )
    
    lf_leg = anom.systems.limb.build( startJoint='lf_leg_1_defJnt', middleJoint='lf_knee_1_defJnt', endJoint='lf_foot_defJnt', extraJoint='lf_foot_end_defJnt', side='lf', name='leg', twistJointCount=5, isLeg=True )
    cmds.parent( lf_leg['limbSystem_grp'], rootSys['systemsGrp'] )
    
    rt_leg = anom.systems.limb.build( startJoint='rt_leg_1_defJnt', middleJoint='rt_knee_1_defJnt', endJoint='rt_foot_defJnt', extraJoint='rt_foot_end_defJnt', side='rt', name='leg', twistJointCount=5, isLeg=True )
    cmds.parent( rt_leg['limbSystem_grp'], rootSys['systemsGrp'] )


    
    # Build hands
    lf_hand = anom.systems.hand.build( side='lf', root='lf_hand_root_defJnt',
                                       fingerDict={'thumb':'lf_hand_thumb1_defJnt',
                                                   'index':'lf_hand_index1_defJnt',
                                                   'mid':'lf_hand_mid1_defJnt',
                                                   'pinky':'lf_hand_pinky1_defJnt'},
                                      cleanUp=cleanUp )
    cmds.parent (lf_hand['systemGrp'], rootSys['systemsGrp'])
    cmds.parentConstraint( lf_arm['jointList'][2], lf_hand['systemGrp'], mo=1 ) # Sorry - I know it's horrible but time's running out!! (Bad, bad Duncan... :p)
    
    rt_hand = anom.systems.hand.build( side='rt', root='rt_hand_root_defJnt',
                                       fingerDict={'thumb':'rt_hand_thumb1_defJnt',
                                                   'index':'rt_hand_index1_defJnt',
                                                   'mid':'rt_hand_mid1_defJnt',
                                                   'pinky':'rt_hand_pinky1_defJnt'},
                                      cleanUp=cleanUp )
    cmds.parent (rt_hand['systemGrp'], rootSys['systemsGrp'])
    cmds.parentConstraint( rt_arm['jointList'][2], rt_hand['systemGrp'], mo=1 )
    
    # Build feet
    lf_foot = reverseFoot.build( side = 'lf', jntFoot = lf_leg['jointList'][-1],
                                 ctrlFoot = lf_leg['end_ctrl'],
                                 ikHandleLeg = lf_leg['ikHandle'],
                                 mesh = 'cn_body_render_mesh',
                                 stretchLoc = lf_leg['stretch_positions'][1],
                                 cleanUp = cleanUp
                                 )
    cmds.parent( lf_foot['systemsGrp'], rootSys['systemsGrp'])
    
    rt_foot = reverseFoot.build( side = 'rt',
                                 jntFoot = rt_leg['jointList'][-1],
                                 ctrlFoot = rt_leg['end_ctrl'],
                                 ikHandleLeg = rt_leg['ikHandle'],
                                 mesh = 'cn_body_render_mesh',
                                 stretchLoc = rt_leg['stretch_positions'][1],
                                 cleanUp = cleanUp
                                 )
    cmds.parent( rt_foot['systemsGrp'], rootSys['systemsGrp'])
    
    # Build AutoHips
    lf_autoHip = autoHip.createAutoHip(lf_leg['start_ctrl'], spineSys['hipCtrl'][0], lf_leg['end_ctrl'])
    rt_autoHip = autoHip.createAutoHip(rt_leg['start_ctrl'], spineSys['hipCtrl'][0], rt_leg['end_ctrl'])
    cmds.parent( lf_autoHip['autoHipSystem_grp'], rt_autoHip['autoHipSystem_grp'], rootSys['systemsGrp'])

    # Build Rivets
    lf_shoulder_rivet = rivet.build( targ=lf_arm['start_ctrl'], mesh='cn_body_render_mesh', side='lf', rigPart='arm', cleanUp=cleanUp )
    cmds.parent( lf_shoulder_rivet['follicle'], rootSys['constGrp'] )
    cmds.pointConstraint( lf_shoulder_rivet['constGrp'], cmds.listRelatives(lf_arm['start_ctrl'], p=1)[0] )
    
    rt_shoulder_rivet = rivet.build( targ=rt_arm['start_ctrl'], mesh='cn_body_render_mesh', side='rt', rigPart='arm', cleanUp=cleanUp )
    cmds.parent( rt_shoulder_rivet['follicle'], rootSys['constGrp'] )
    cmds.pointConstraint( rt_shoulder_rivet['constGrp'], cmds.listRelatives(rt_arm['start_ctrl'], p=1)[0] )
    
    lf_eye_rivet = rivet.build( targ='lf_head_eye_defJnt', mesh='cn_body_render_mesh', side='lf', rigPart='eye', cleanUp=cleanUp )
    cmds.parent( lf_eye_rivet['follicle'], rootSys['constGrp'] )
    
    rt_eye_rivet = rivet.build( targ='rt_head_eye_defJnt', mesh='cn_body_render_mesh', side='rt', rigPart='eye', cleanUp=cleanUp )
    cmds.parent( rt_eye_rivet['follicle'], rootSys['constGrp'] )
    
    connectToPuppet()

    # Bind skin
    cmds.select( cmds.listRelatives(rootSys['geoGrp']) )
    anom.core.skinWeights.rebindSkinClusters( char )
    anom.core.skinWeights.setSkinWeights( char )
    cmds.reorderDeformers( 'lf_foot_contact_ffd', 'skincluster_cn_body_render_mesh', 'cn_body_render_mesh' )

    # Make everything follow root the evil way | global scale
    cmds.parentConstraint( rootSys['constGrp'][0], rootSys['systemsGrp'][0], mo=1 )
    cmds.scaleConstraint ( rootSys['constGrp'][0], rootSys['systemsGrp'][0], mo=1 )
    cmds.connectAttr( rootSys['systemsGrp'][0] + '.scale', rootSys['defJntsGrp'][0] + '.scale' )

    invScale = cmds.shadingNode( 'multiplyDivide', asUtility = True, name = 'root_systems_invScale_md' )
    cmds.setAttr( invScale + '.operation', 2 )
    cmds.setAttr( invScale + '.input1', 1, 1, 1, type='double3' )
    cmds.connectAttr( rootSys['systemsGrp'][0] + '.scale', invScale + '.input2' )

    #connect autoHip to inverse world scale
    cmds.connectAttr( invScale + '.output', lf_autoHip['placer'][0] + '.scale' )
    cmds.connectAttr( invScale + '.output', rt_autoHip['placer'][0] + '.scale' )

    #connect follicles to systemGrp scale this fixes errors from the offset at extreme scaling values
    for fol in [ lf_shoulder_rivet['follicle'], rt_shoulder_rivet['follicle'], lf_eye_rivet['follicle'], rt_eye_rivet['follicle'] ]:
        cmds.connectAttr( rootSys['systemsGrp'][0] + '.scale', fol+'.scale' )
        cmds.setAttr( fol+'.v', 0 )

    # finally hide defJnts
    if cleanUp:
        cmds.setAttr( rootSys['offsetCtrl'][0].control +'.jointsVisibility', 0 )

    # create a Layer for the meshes
    cmds.createDisplayLayer( name='MESH_layer', empty=True )
    cmds.editDisplayLayerMembers( 'MESH_layer', rootSys['geoGrp'], noRecurse=True )
    cmds.setAttr( 'MESH_layer.displayType', 2 ) # unselectable

    cmds.select( clear=True )
示例#5
0
def build( joint=None, twistAxis='x', name='' ):
    '''
    Creates a duplicate of the specified joint which will remove the twist.
    Based on Victor Vinyal's nonroll setup
    Duplicates the joint and deletes all children apart from the first child joint ( there must at least a single child joint in the hierarchy )
    Creates an RP IKHandle with the pole vector set to zero
    Parents the pole vector to the original joint
    
    point constrains the nonRoll to the joint
    
    groups the nonRoll outside the hierarchy
    
    Also creates an 'info' locator which is parented to 'nonRoll' and aim constrained to 'nonRollEnd' using 'joint' as its up vector -
         - this is the node from which to read the twist value
    
    '''
        
    if not joint:
        if len(cmds.ls(sl=1, exactType='joint'))==1:
            joint = cmds.ls(sl=1, exactType='joint')[0]
        else:
            return showDialog( 'Argument Error', 'Please provide a joint' )
        
        
    prefix = '%s_%s' % (common.getSide(joint), name)
       
    # main group for nonRoll system
    main_grp = cmds.group( empty=1, n='%s_nonRoll_grp' % prefix )
    common.align( main_grp, joint )
    
    # Duplicate the joints and delete all but first child joint
    copies = cmds.duplicate( joint, name='%s_nonRoll_jnt' % prefix, rc=1 )
    nonRoll = copies[ 0 ]
    nonRollEnd = cmds.listRelatives( nonRoll, c=1, type='joint' )[ 0 ]
    deleteList = [ c for c in copies if not c in [ nonRoll, nonRollEnd ] ]
    if deleteList:
        cmds.delete( deleteList )        
    nonRollEnd = cmds.rename( nonRollEnd, '%s_nonRoll_end' % prefix )
    
    rad = cmds.getAttr( '%s.radius' % nonRoll ) * 1.5
    cmds.setAttr( '%s.radius' % nonRoll, rad )
    cmds.setAttr( '%s.radius' % nonRollEnd, rad )
    
    cmds.parent( nonRoll, main_grp )
    cmds.pointConstraint( joint, nonRoll )
    
    # build ikHandle
    ikHandle = cmds.ikHandle( sj=nonRoll, ee=nonRollEnd, n='%s_nonRoll_ikHandle' % prefix, sol='ikRPsolver' )[ 0 ]
    cmds.setAttr( '%s.poleVectorX' % ikHandle, 0 )
    cmds.setAttr( '%s.poleVectorY' % ikHandle, 0 )
    cmds.setAttr( '%s.poleVectorZ' % ikHandle, 0 )
    
    cmds.parent( ikHandle, joint )
    
    # build info locator
    info = cmds.spaceLocator( n='%s_nonRoll_info' % prefix )[ 0 ]
    common.align( info, joint )
    cmds.parent( info, nonRoll )
    
    if cmds.getAttr( '%s.t%s' % (nonRollEnd, twistAxis)) < 0:
        aimVec = ( -1, 0, 0 )
    else:
        aimVec = ( 1, 0, 0 )
    cmds.aimConstraint( nonRollEnd, info, aimVector=aimVec, wut='objectrotation', wuo=joint )
    
    returnDict = {
                  'main_grp':main_grp,
                  'nonRoll':nonRoll,
                  'nonRollEnd':nonRollEnd,
                  'ikHandle':ikHandle,
                  'info':info
                  }
    
    return returnDict
        
        
示例#6
0
def build(
    hips=None, chest=None, head=None, numSpineJoints=6, numHeadJoints=6, twistAxis="x", bendAxis="y", cleanUp=True
):
    """
    function to create the fk spine with head isolation...
    
    requires 3 objects to be selected or supplied - hips, chest, head
    
    hips: location of the first spine joint - this joint doesn't inherit any chest rotation
    chest: where the chest ctrl is placed. It's rotation is distributed from the second spine joint up to this point
    head: location of the last spine joint

    """

    axisDict = {"x": [1, 0, 0], "y": [0, 1, 0], "z": [0, 0, 1]}

    # Validate that the correct arguments have been supplied
    if not hips or not chest or not head:
        # If hips, chest anf head aren't explicitly supplied, check for a valid selection to use
        sel = cmds.ls(sl=1, type="transform")
        if len(sel) == 3:
            hips, chest, head = sel[0], sel[1], sel[2]
        else:
            return showDialog("Argument Error", "Cannot determine hips, chest and head points")

    # Get positions of spine and head joints
    spineLocs = common.pointsAlongVector(start=hips, end=chest, divisions=numSpineJoints - 1)
    headLocs = common.pointsAlongVector(start=chest, end=head, divisions=numHeadJoints - 1)
    spineLocs.extend(headLocs[1:])

    xformGrp = cmds.group(empty=1)
    xformGrp = cmds.rename(
        xformGrp, common.getName(node=xformGrp, side="cn", rigPart="spine", function="xform", nodeType="grp")
    )
    common.align(node=xformGrp, target=hips)
    aimConst = cmds.aimConstraint(
        chest, xformGrp, aimVector=axisDict[twistAxis], upVector=axisDict[bendAxis], worldUpVector=[1, 0, 0]
    )
    cmds.delete(aimConst)

    # Build control objects
    hipCtrl = controls.Control(
        side="cn", rigPart="spine", function="hips", nodeType="ctrl", size=1.25, color="green", aimAxis="twistAxis"
    )
    hipCtrl.circleCtrl()
    hipGrp = common.insertGroup(node=hipCtrl.control)
    hipGrp = cmds.rename(hipGrp, hipCtrl.control.replace("ctrl", "grp"))
    common.align(node=hipGrp, target=xformGrp)
    cmds.parent(hipGrp, xformGrp)

    chestCtrl = controls.Control(
        side="cn", rigPart="spine", function="chest", nodeType="ctrl", size=1.25, color="green", aimAxis="twistAxis"
    )
    chestCtrl.circleCtrl()
    chestGrp = common.insertGroup(node=chestCtrl.control)
    chestGrp = cmds.rename(chestGrp, chestCtrl.control.replace("ctrl", "grp"))
    common.align(node=chestGrp, orient=False, target=chest)
    common.align(node=chestGrp, translate=False, target=xformGrp)
    cmds.parent(chestGrp, hipCtrl.control)

    headCtrl = controls.Control(
        side="cn", rigPart="spine", function="head", nodeType="ctrl", size=1.25, color="green", aimAxis="twistAxis"
    )
    headCtrl.crownCtrl()
    headGrp = common.insertGroup(node=headCtrl.control)
    headGrp = cmds.rename(headGrp, headCtrl.control.replace("ctrl", "grp"))
    common.align(node=headGrp, target=head, orient=False)
    common.align(node=headGrp, target=xformGrp, translate=False)
    cmds.parent(headGrp, xformGrp)

    # Add attr to head ctrl for isolating head rotation
    cmds.addAttr(headCtrl.control, longName="isolate_rotation", at="double", keyable=True, min=0, max=1)

    # Build driven groups
    spineGrps = []
    for i in range(len(spineLocs)):
        g = cmds.group(empty=1)
        g = cmds.rename(
            g, common.getName(node=g, side="cn", rigPart="spine", function="driven%s" % str(i + 1), nodeType="grp")
        )
        spineGrps.append(g)
        cmds.setAttr("%s.t" % g, spineLocs[i][0], spineLocs[i][1], spineLocs[i][2])
        common.align(node=g, target=xformGrp, translate=False)
        if i > 0:
            cmds.parent(g, spineGrps[i - 1])
        else:
            cmds.parent(g, xformGrp)
            cmds.parentConstraint(hipCtrl.control, g)

    # Build joints
    spineJoints = []
    for i in range(len(spineLocs)):
        cmds.select(spineGrps[i])
        j = cmds.joint()
        common.align(node=j, target=xformGrp, translate=False)
        cmds.setAttr("%s.jointOrient" % j)
        j = cmds.rename(
            j, common.getName(node=j, side="cn", rigPart="spine", function="driven%s" % str(i + 1), nodeType="jnt")
        )
        spineJoints.append(j)
        cmds.setAttr("%s.displayLocalAxis" % j, 1)

    # set rotation orders
    leanAxis = [axis for axis in ["x", "y", "z"] if not axis in [twistAxis, bendAxis]][0]
    rotateOrder = "%s%s%s" % (twistAxis, leanAxis, bendAxis)
    rotateOrderDict = {"xyz": 0, "yzx": 1, "zxy": 2, "xzy": 3, "yxz": 4, "zyx": 5}

    for i in (
        [hipGrp, hipCtrl.control, chestGrp, chestCtrl.control, headGrp, headCtrl.control] + spineGrps + spineJoints
    ):
        cmds.setAttr("%s.rotateOrder" % i, rotateOrderDict[rotateOrder])

    # connect driven groups and joints below chest to chest control rotations
    spineBendUC = cmds.createNode("unitConversion")
    spineBendUC = cmds.rename(
        spineBendUC, common.getName(node=spineBendUC, side="cn", rigPart="spine", function="bend", nodeType="conv")
    )
    cmds.connectAttr("%s.r%s" % (chestCtrl.control, bendAxis), "%s.input" % spineBendUC)
    cmds.setAttr("%s.conversionFactor" % spineBendUC, 1.0 / (numSpineJoints - 1))

    spineLeanUC = cmds.createNode("unitConversion")
    spineLeanUC = cmds.rename(
        spineLeanUC, common.getName(node=spineLeanUC, side="cn", rigPart="spine", function="lean", nodeType="conv")
    )
    cmds.connectAttr("%s.r%s" % (chestCtrl.control, leanAxis), "%s.input" % spineLeanUC)
    cmds.setAttr("%s.conversionFactor" % spineLeanUC, 1.0 / (numSpineJoints - 1))

    for grp in spineGrps[1:numSpineJoints]:
        cmds.connectAttr("%s.output" % spineBendUC, "%s.r%s" % (grp, bendAxis))
        cmds.connectAttr("%s.output" % spineLeanUC, "%s.r%s" % (grp, leanAxis))

    for i in range(numSpineJoints - 1):
        spineTwistUC = cmds.createNode("unitConversion")
        spineTwistUC = cmds.rename(
            spineTwistUC,
            common.getName(node=spineTwistUC, side="cn", rigPart="spine", function="twist%s" % i, nodeType="conv"),
        )
        cmds.connectAttr("%s.r%s" % (chestCtrl.control, twistAxis), "%s.input" % spineTwistUC)
        cmds.setAttr("%s.conversionFactor" % spineTwistUC, 1.0 / (numSpineJoints - 1) * (i + 1))
        cmds.connectAttr("%s.output" % spineTwistUC, "%s.r%s" % (spineJoints[i + 1], twistAxis))

    # connect driven groups above chest to reverse chest control rotations + hips control rotations
    hipRotUC = cmds.createNode("unitConversion")
    hipRotUC = cmds.rename(
        hipRotUC, common.getName(node=hipRotUC, side="cn", rigPart="spine", function="hipRotate", nodeType="conv")
    )
    cmds.setAttr("%s.conversionFactor" % hipRotUC, -1)
    cmds.connectAttr("%s.r" % hipCtrl.control, "%s.input" % hipRotUC)
    hipRotMD = cmds.createNode("multiplyDivide")
    hipRotMD = cmds.rename(
        hipRotMD, common.getName(node=hipRotMD, side="cn", rigPart="spine", function="hipRotate", nodeType="multDiv")
    )
    cmds.connectAttr("%s.output" % hipRotUC, "%s.input1" % hipRotMD)
    cmds.connectAttr("%s.isolate_rotation" % headCtrl.control, "%s.input2X" % hipRotMD)
    cmds.connectAttr("%s.isolate_rotation" % headCtrl.control, "%s.input2Y" % hipRotMD)
    cmds.connectAttr("%s.isolate_rotation" % headCtrl.control, "%s.input2Z" % hipRotMD)

    chestRotUC = cmds.createNode("unitConversion")
    chestRotUC = cmds.rename(
        chestRotUC, common.getName(node=chestRotUC, side="cn", rigPart="spine", function="chestRotate", nodeType="conv")
    )
    cmds.setAttr("%s.conversionFactor" % chestRotUC, -1)
    cmds.connectAttr("%s.r" % chestCtrl.control, "%s.input" % chestRotUC)
    chestRotMD = cmds.createNode("multiplyDivide")
    chestRotMD = cmds.rename(
        chestRotMD,
        common.getName(node=chestRotMD, side="cn", rigPart="spine", function="chestRotate", nodeType="multDiv"),
    )
    cmds.connectAttr("%s.output" % chestRotUC, "%s.input1" % chestRotMD)
    cmds.connectAttr("%s.isolate_rotation" % headCtrl.control, "%s.input2X" % chestRotMD)
    cmds.connectAttr("%s.isolate_rotation" % headCtrl.control, "%s.input2Y" % chestRotMD)
    cmds.connectAttr("%s.isolate_rotation" % headCtrl.control, "%s.input2Z" % chestRotMD)

    headRotUC = cmds.createNode("unitConversion")
    headRotUC = cmds.rename(
        headRotUC, common.getName(node=headRotUC, side="cn", rigPart="spine", function="headRotate", nodeType="conv")
    )
    cmds.setAttr("%s.conversionFactor" % headRotUC, 1)
    cmds.connectAttr("%s.r" % headCtrl.control, "%s.input" % headRotUC)

    # negative hip and chest rotation are added to head rotation. The negative values are piped through a multiplier to blend the head isolation off and on
    spineRotNegPMA = cmds.createNode("plusMinusAverage")
    spineRotNegPMA = cmds.rename(
        spineRotNegPMA,
        common.getName(node=spineRotNegPMA, side="cn", rigPart="spine", function="rotateNeg", nodeType="pma"),
    )
    cmds.connectAttr("%s.output" % hipRotMD, "%s.input3D[0]" % spineRotNegPMA)
    cmds.connectAttr("%s.output" % chestRotMD, "%s.input3D[1]" % spineRotNegPMA)
    cmds.connectAttr("%s.output" % headRotUC, "%s.input3D[2]" % spineRotNegPMA)

    spineBendNegUC = cmds.createNode("unitConversion")
    spineBendNegUC = cmds.rename(
        spineBendNegUC,
        common.getName(node=spineBendNegUC, side="cn", rigPart="spine", function="bendNeg", nodeType="conv"),
    )
    cmds.connectAttr("%s.output3D%s" % (spineRotNegPMA, bendAxis), "%s.input" % spineBendNegUC)
    cmds.setAttr("%s.conversionFactor" % spineBendNegUC, 1.0 / (numHeadJoints - 2))

    spineLeanNegUC = cmds.createNode("unitConversion")
    spineLeanNegUC = cmds.rename(
        spineLeanNegUC,
        common.getName(node=spineLeanNegUC, side="cn", rigPart="spine", function="leanNeg", nodeType="conv"),
    )
    cmds.connectAttr("%s.output3D%s" % (spineRotNegPMA, leanAxis), "%s.input" % spineLeanNegUC)
    cmds.setAttr("%s.conversionFactor" % spineLeanNegUC, 1.0 / (numHeadJoints - 2))

    for grp in spineGrps[numSpineJoints:-1]:
        cmds.connectAttr("%s.output" % spineBendNegUC, "%s.r%s" % (grp, bendAxis))
        cmds.connectAttr("%s.output" % spineLeanNegUC, "%s.r%s" % (grp, leanAxis))

    # Direct output from chest control. If head isolation is off. All twist joints above it recieve 100% of its twisting
    chestTwistDirectUC = cmds.createNode("unitConversion")
    chestTwistDirectUC = cmds.rename(
        chestTwistDirectUC,
        common.getName(node=chestTwistDirectUC, side="cn", rigPart="spine", function="chestTwist", nodeType="conv"),
    )
    cmds.connectAttr("%s.r%s" % (chestCtrl.control, twistAxis), "%s.input" % chestTwistDirectUC)
    cmds.setAttr("%s.conversionFactor" % chestTwistDirectUC, 1.0)

    for i in range(numSpineJoints + 1, len(spineJoints)):
        # gradually negate hip twist from chest up
        hipTwistUC = cmds.createNode("unitConversion")
        hipTwistUC = cmds.rename(
            hipTwistUC,
            common.getName(node=hipTwistUC, side="cn", rigPart="spine", function="hipTwist%s" % i, nodeType="conv"),
        )
        cmds.connectAttr("%s.r%s" % (hipCtrl.control, twistAxis), "%s.input" % hipTwistUC)
        cmds.setAttr("%s.conversionFactor" % hipTwistUC, -1.0 / (numHeadJoints - 2) * (i - numSpineJoints))

        # gradually negate chest twist from chest up
        chestTwistUC = cmds.createNode("unitConversion")
        chestTwistUC = cmds.rename(
            chestTwistUC,
            common.getName(node=chestTwistUC, side="cn", rigPart="spine", function="chestTwist%s" % i, nodeType="conv"),
        )
        cmds.connectAttr("%s.r%s" % (chestCtrl.control, twistAxis), "%s.input" % chestTwistUC)
        cmds.setAttr("%s.conversionFactor" % chestTwistUC, 1.0 / (numHeadJoints - 2) * (len(spineJoints) - (i + 1)))

        # sum hip and chest negation
        spineTwistNegPMA = cmds.createNode("plusMinusAverage")
        spineTwistNegPMA = cmds.rename(
            spineTwistNegPMA,
            common.getName(
                node=spineTwistNegPMA, side="cn", rigPart="spine", function="twistNeg%s" % i, nodeType="pma"
            ),
        )
        cmds.connectAttr("%s.output" % hipTwistUC, "%s.input1D[0]" % spineTwistNegPMA)
        cmds.connectAttr("%s.output" % chestTwistUC, "%s.input1D[1]" % spineTwistNegPMA)

        # Distribute head twist down to chest
        spineTwistUC = cmds.createNode("unitConversion")
        spineTwistUC = cmds.rename(
            spineTwistUC,
            common.getName(node=spineTwistUC, side="cn", rigPart="spine", function="twist%s" % i, nodeType="conv"),
        )
        cmds.connectAttr("%s.r%s" % (headCtrl.control, twistAxis), "%s.input" % spineTwistUC)
        cmds.setAttr("%s.conversionFactor" % spineTwistUC, 1.0 / (numHeadJoints - 2) * (i - numSpineJoints))

        # Blend between hip and chest negation (head isolation on) and full inheritance of chest twist (head isolation off)
        spineTwistNegBC = cmds.createNode("blendColors")
        spineTwistNegBC = cmds.rename(
            spineTwistNegBC,
            common.getName(node=spineTwistNegBC, side="cn", rigPart="spine", function="twistNeg%s" % i, nodeType="bc"),
        )
        cmds.connectAttr("%s.output1D" % spineTwistNegPMA, "%s.color1R" % spineTwistNegBC)
        cmds.connectAttr("%s.output" % chestTwistDirectUC, "%s.color2R" % spineTwistNegBC)
        cmds.connectAttr("%s.isolate_rotation" % headCtrl.control, "%s.blender" % spineTwistNegBC)

        # Sum blended negation twist and head twist
        spineTwistPMA = cmds.createNode("plusMinusAverage")
        spineTwistPMA = cmds.rename(
            spineTwistPMA,
            common.getName(
                node=spineTwistPMA, side="cn", rigPart="spine", function="twistResult%s" % i, nodeType="pma"
            ),
        )
        cmds.connectAttr("%s.outputR" % spineTwistNegBC, "%s.input1D[0]" % spineTwistPMA)
        cmds.connectAttr("%s.output" % spineTwistUC, "%s.input1D[1]" % spineTwistPMA)

        # Force unit conversion to 1
        spineTwistResultUC = cmds.createNode("unitConversion")
        spineTwistResultUC = cmds.rename(
            spineTwistResultUC,
            common.getName(
                node=spineTwistResultUC, side="cn", rigPart="spine", function="twistResult%s" % i, nodeType="conv"
            ),
        )
        cmds.connectAttr("%s.output1D" % spineTwistPMA, "%s.input" % spineTwistResultUC)
        cmds.setAttr("%s.conversionFactor" % spineTwistResultUC, 1.0)

        cmds.connectAttr("%s.output" % spineTwistResultUC, "%s.r%s" % (spineJoints[i - 1], twistAxis))

    # Orient contstrain last head joint to second last head joint
    orientConst = cmds.orientConstraint(spineJoints[-2], spineJoints[-1])
    # Point constrain chest ctrl grp to its spine group
    pointConst = cmds.pointConstraint(spineGrps[numSpineJoints - 1], chestGrp)
    # Point constrain head ctrl grp to last spine group
    pointConst = cmds.pointConstraint(spineGrps[-1], headGrp)
    # Add orient constraint to headCtrl group - weighted between root noXformGrp and last spine grp - weight controlled by isolation attr
    orientConst = cmds.orientConstraint(xformGrp, chestCtrl.control, headGrp)[0]
    isolateRev = cmds.createNode("reverse")
    isolateRev = cmds.rename(
        isolateRev, common.getName(node=isolateRev, side="cn", rigPart="spine", function="headIsolate", nodeType="rev")
    )
    cmds.connectAttr("%s.isolate_rotation" % headCtrl.control, "%s.inputX" % isolateRev)
    cmds.connectAttr("%s.isolate_rotation" % headCtrl.control, "%s.%sW0" % (orientConst, xformGrp), f=1)
    cmds.connectAttr("%s.outputX" % isolateRev, "%s.%sW1" % (orientConst, chestCtrl.control), f=1)
    cmds.setAttr("%s.interpType" % orientConst, 2)

    # Clean up attributes and objects that need to be hidden / locked
    if cleanUp:
        cmds.setAttr("%s.visibility" % spineGrps[0], 0)
        common.attrCtrl(nodeList=[spineGrps[1]], attrList=["visibility"])
        common.attrCtrl(nodeList=[hipCtrl.control], attrList=["sx", "sy", "sz", "visibility"])
        common.attrCtrl(
            nodeList=[chestCtrl.control, headCtrl.control], attrList=["tx", "ty", "tz", "sx", "sy", "sz", "visibility"]
        )

    returnDict = collections.defaultdict(list)
    returnDict["xformGrp"].append(xformGrp)
    returnDict["hipCtrl"].append(hipCtrl.control)
    returnDict["chestCtrl"].append(chestCtrl.control)
    returnDict["headCtrl"].append(headCtrl.control)

    return returnDict
示例#7
0
def build( startJoint=None, middleJoint=None, endJoint=None, extraJoint=None, side=None, name=None, twistJointCount=None, isLeg=False, cleanUp=True ):
    if not startJoint or not middleJoint or not endJoint or not extraJoint or not side or not name or not twistJointCount:
        print 'limb: Error! Must supply all arguments.'
        return

    # first reconstruct the joints for our system
    joint1 = cmds.createNode( 'joint', n=side+"_"+name+'_start_drvJnt' )
    joint2 = cmds.createNode( 'joint', n=side+"_"+name+'_middle_drvJnt' )
    joint3 = cmds.createNode( 'joint', n=side+"_"+name+'_end_jnt' )
    joint4 = cmds.createNode( 'joint', n=side+"_"+name+'_extra_drvJnt' )
    common.align( node=joint1, target=startJoint )
    cmds.setAttr('%s.jointOrient' % joint1, 0,0,0)
    common.align( node=joint2, target=middleJoint )
    cmds.setAttr('%s.jointOrient' % joint2, 0,0,0)
    common.align( node=joint3, target=endJoint )
    cmds.setAttr('%s.jointOrient' % joint3, 0,0,0)
    common.align( node=joint4, target=extraJoint )
    cmds.setAttr('%s.jointOrient' % joint4, 0,0,0)
    cmds.parent( joint4, joint3 )
    cmds.parent( joint3, joint2 )
    cmds.parent( joint2, joint1 )
    if isLeg: cmds.setAttr( joint2+'.preferredAngle', 0, 0, -90 )
    else:     cmds.setAttr( joint2+'.preferredAngle', 0, -90, 0 )
    cmds.makeIdentity( joint1, apply=True )

    # auto color controls using the 'side' argument
    myColor  = 'green'
    if   side == 'lf': myColor = 'blue'
    elif side == 'rt': myColor = 'red'

    # creation of the start control (shoulder or hips offset)
    limbStartCtrl = controls.Control( side=side, rigPart='limb', function=name+'_start', nodeType='ctrl', size=0.4, color=myColor, aimAxis='y' )
    limbStartCtrl.pinCtrl()
    common.align( node=limbStartCtrl.control, target=joint1, orient=False )
    limbStartCtrlGrp = common.insertGroup( node=limbStartCtrl.control )

    # creation of the end control (hand or foot)
    limbEndCtrl = controls.Control( side=side, rigPart='limb', function=name+'_end', nodeType='ctrl', size=0.4 + 0.2*isLeg, color=myColor, aimAxis='x' )
    limbEndCtrl.cubeCtrl()
    common.align( node=limbEndCtrl.control, target=joint3, orient=False )
    limbEndCtrlGrp = common.insertGroup( node=limbEndCtrl.control )


    # entire system group
    systemGrp = cmds.group( empty=True, n=side+'_'+name+'_xform_grp')
    cmds.parent( joint1, systemGrp )

    #
    # NON ROLLs CREATION 
    #

    startNonRoll = nonRoll.build(joint=joint1, name = name+'Start')
    cmds.parent(startNonRoll['main_grp'], systemGrp)
    
    upNonRoll = nonRoll.build(joint=joint2, name = name+'Mid')
    cmds.parent(upNonRoll['main_grp'], joint1)
    
    lowNonRoll = nonRoll.build(joint=joint3, name = name+'End')
    cmds.parent(lowNonRoll['main_grp'], joint2)
    
    
    '''
    if isLeg:
        if   side == 'lf': aimUp  = (0,  1, 0)
        elif side == 'rt': aimUp  = (0, -1, 0)
    cmds.aimConstraint( lowNonRollEnd, lowNonRollLoc, aimVector=aimVec, upVector=aimUp, worldUpType='objectrotation', worldUpVector=aimWorldUp, worldUpObject=joint3 )

    if isLeg:
        common.align( node=lowNonRollLocGrp, target=joint3 )
    else:
        common.align( node=lowNonRollLocGrp, target=joint2 )
        common.align( node=lowNonRollLocGrp, target=joint3, orient=False )

    cmds.setAttr( lowNonRollGrp+'.v', 0 )
    cmds.setAttr( lowTwistIkHandle+'.poleVector', 0, 0, 0 )
    if not isLeg:
        if side == 'rt': cmds.setAttr( lowNonRollLocGrp+'.r', 0, 0, 0 )
    else:
        if side == 'lf': cmds.setAttr( lowNonRollLocGrp+'.r', 90, 0, 0 )
    '''

    # create stretch setup
    ssStartPos = cmds.group( empty=True, n=side+'_'+name+'_stretchStart_loc')
    ssEndPos   = cmds.group( empty=True, n=side+'_'+name+'_stretchEnd_loc')
    common.align( node=ssStartPos, target=joint1, orient=False )
    cmds.pointConstraint( limbEndCtrl.control, ssEndPos )
    ssGrp      = cmds.group( ssStartPos, ssEndPos, n=side+'_'+name+'_stretchLocs_grp')
    cmds.parent( ssGrp, systemGrp )

    # sums the two separate distances to create the stretch
    originalUpLimbTx  = cmds.getAttr( joint2+'.translateX' )
    originalLowLimbTx = cmds.getAttr( joint3+'.translateX' )
    limbSize = common.getDistance( joint1, joint2 ) + common.getDistance( joint2, joint3 )
    dbNode   = cmds.createNode( 'distanceBetween', n=side+'_'+name+'_stretch_distance')
    cmds.connectAttr( ssStartPos+'.t', dbNode+'.point1' )
    cmds.connectAttr( ssEndPos+'.t', dbNode+'.point2' )

    # creates a keyframe-based stretch factor
    ssKeyUp = cmds.createNode( 'animCurveUL', n=side+'_'+name+'_upStretch_key' )
    cmds.connectAttr( dbNode+'.distance', ssKeyUp+'.input' )
    cmds.connectAttr( ssKeyUp+'.output', joint2+'.translateX' )
    ssKeyLow = cmds.createNode( 'animCurveUL', n=side+'_'+name+'_LowStretch_key' )
    cmds.connectAttr( dbNode+'.distance', ssKeyLow+'.input' )
    cmds.connectAttr( ssKeyLow+'.output', joint3+'.translateX' )
    
    # set driven key values
    cmds.setDrivenKeyframe( joint2+'.translateX', currentDriver=dbNode+'.distance', driverValue=limbSize, value=originalUpLimbTx, itt='linear', ott='linear' )
    cmds.setDrivenKeyframe( joint2+'.translateX', currentDriver=dbNode+'.distance', driverValue=limbSize*2, value=originalUpLimbTx*2, itt='linear', ott='spline' )
    cmds.setInfinity( joint2+'.translateX', poi='linear' )
    cmds.setDrivenKeyframe( joint3+'.translateX', currentDriver=dbNode+'.distance', driverValue=limbSize, value=originalLowLimbTx, itt='linear', ott='linear' )
    cmds.setDrivenKeyframe( joint3+'.translateX', currentDriver=dbNode+'.distance', driverValue=limbSize*2, value=originalLowLimbTx*2, itt='linear', ott='spline' )
    cmds.setInfinity( joint3+'.translateX', poi='linear' )

    # create the twistSections
    twistUp = 'z'
    if isLeg: twistUp = 'y'
    doNotInvertUp = False
    if isLeg and side == 'rt': doNotInvertUp = True
    upTwistDict  = twistSection.build( side=side, name='up_'+name, startPos=joint1, endPos=joint2, jointCount=twistJointCount, worldUpVector=twistUp, worldUpObject=startNonRoll['nonRoll'], twistReader=startNonRoll['info'], doNotInvertUp=doNotInvertUp )
    lowTwistDict = twistSection.build( side=side, name='low_'+name, startPos=joint2, endPos=joint3, jointCount=twistJointCount, worldUpVector=twistUp, worldUpObject=upNonRoll['nonRoll'], twistReader=lowNonRoll['info'], doNotInvertUp=doNotInvertUp )

    # create the joints that will skin the twistSection curves
    # to get that nice bendy effect for free
    # start with the shoulder/hips joint
    mdNode   = None
    crvJntA1 = cmds.createNode( 'joint', n=side+'_'+name+'_up_curve_1_jnt')
    crvJntA2 = cmds.createNode( 'joint', n=side+'_'+name+'_up_curve_2_jnt')
    cmds.parent( crvJntA2, crvJntA1 )
    cmds.parent( crvJntA1, systemGrp )
    cmds.addAttr( limbEndCtrl.control, ln='start_tangent', min=0, dv=0.1, k=True )
    if side == 'lf':
        cmds.connectAttr( limbEndCtrl.control+'.start_tangent', crvJntA2+'.translateX' )
    else:
        mdNode = cmds.createNode( 'multiplyDivide', n=side+'_'+name+'_pma' )
        cmds.setAttr( mdNode+'.input2', -1, -1, -1 )
        cmds.connectAttr( limbEndCtrl.control+'.start_tangent', mdNode+'.input1X' )
        cmds.connectAttr( mdNode+'.outputX', crvJntA2+'.translateX' )

    common.align( node=crvJntA1, target=joint1 )

    # elbow/knee joints
    avgGrp   = cmds.group( empty=True, n=side+'_'+name+'_avg_grp' )

    crvJntB1 = cmds.createNode( 'joint', n=side+'_'+name+'_cn_curve_1_avgJnt')
    crvJntB2 = cmds.createNode( 'joint', n=side+'_'+name+'_cn_curve_2_avgJnt')
    cmds.parent( crvJntB2, crvJntB1 )
    cmds.parent( crvJntB1, avgGrp )
    cmds.setAttr( crvJntB1+'.rotateY', 180 ) # flip

    crvJntC1 = cmds.createNode( 'joint', n=side+'_'+name+'_cn_curve_3_avgJnt')
    crvJntC2 = cmds.createNode( 'joint', n=side+'_'+name+'_cn_curve_4_avgJnt')
    cmds.parent( crvJntC2, crvJntC1 )
    cmds.parent( crvJntC1, avgGrp )
    cmds.addAttr( limbEndCtrl.control, ln='bend_tangent', min=0, dv=0.85, k=True )
    if mdNode:
        cmds.connectAttr( limbEndCtrl.control+'.bend_tangent', mdNode+'.input1Y' )
        cmds.connectAttr( mdNode+'.outputY', crvJntB2+'.translateX' )
        cmds.connectAttr( mdNode+'.outputY', crvJntC2+'.translateX' )
    else:
        cmds.connectAttr( limbEndCtrl.control+'.bend_tangent', crvJntB2+'.translateX' )
        cmds.connectAttr( limbEndCtrl.control+'.bend_tangent', crvJntC2+'.translateX' )

    cmds.pointConstraint( joint2, avgGrp )
    cons     = cmds.orientConstraint( joint1, joint2, avgGrp )[0]
    cmds.setAttr( cons+'.interpType', 0 ) # noFlip
    
    # wrist/foot joints
    crvJntD1 = cmds.createNode( 'joint', n=side+'_'+name+'_low_curve_1_jnt')
    crvJntD2 = cmds.createNode( 'joint', n=side+'_'+name+'_low_curve_2_jnt')
    crvJntD1Grp = cmds.group( crvJntD1, n=crvJntD1+'_grp' )
    cmds.parent( crvJntD2, crvJntD1 )
    cmds.parent( crvJntD1Grp, systemGrp )
    cmds.setAttr( crvJntD1+'.rotateY', 180 ) # flip
    common.align( node=crvJntD1Grp, target=joint3 )
    cmds.parentConstraint( limbEndCtrl.control, crvJntD1Grp, mo=True )
    cmds.addAttr( limbEndCtrl.control, ln='end_tangent', min=0, dv=0.2, k=True )
    if mdNode:
        cmds.connectAttr( limbEndCtrl.control+'.end_tangent', mdNode+'.input1Z' )
        cmds.connectAttr( mdNode+'.outputZ', crvJntD2+'.translateX' )
    else:
        cmds.connectAttr( limbEndCtrl.control+'.end_tangent', crvJntD2+'.translateX' )

    # extra jnt follow orientation of endCtrl
    cmds.orientConstraint( limbEndCtrl.control, joint3, mo=True )

    # constrains entire limb to startCtrl
    cmds.orientConstraint( limbStartCtrl.control, startNonRoll['main_grp'], mo=True )

    # skin the twistSection curves to these joints
    skinClsUp  = cmds.skinCluster( crvJntA1, crvJntA2, crvJntB1, crvJntB2, upTwistDict['twist_curve'] )[0]
    cmds.skinPercent( skinClsUp, upTwistDict['twist_curve']+'.cv[0]', transformValue=[(crvJntA1, 1)])
    cmds.skinPercent( skinClsUp, upTwistDict['twist_curve']+'.cv[1]', transformValue=[(crvJntA2, 1)])
    cmds.skinPercent( skinClsUp, upTwistDict['twist_curve']+'.cv[2]', transformValue=[(crvJntB2, 1)])
    cmds.skinPercent( skinClsUp, upTwistDict['twist_curve']+'.cv[3]', transformValue=[(crvJntB1, 1)])

    skinClsLow = cmds.skinCluster( crvJntC1, crvJntC2, crvJntD1, crvJntD2, lowTwistDict['twist_curve'] )[0]
    cmds.skinPercent( skinClsLow, lowTwistDict['twist_curve']+'.cv[0]', transformValue=[(crvJntC1, 1)])
    cmds.skinPercent( skinClsLow, lowTwistDict['twist_curve']+'.cv[1]', transformValue=[(crvJntC2, 1)])
    cmds.skinPercent( skinClsLow, lowTwistDict['twist_curve']+'.cv[2]', transformValue=[(crvJntD2, 1)])
    cmds.skinPercent( skinClsLow, lowTwistDict['twist_curve']+'.cv[3]', transformValue=[(crvJntD1, 1)])

    #
    # IKHANDLE CREATION
    #
    ikHandle, effector = cmds.ikHandle( startJoint=joint1, endEffector=joint3, solver='ikRPsolver', n=side+'_'+name+'_ikHandle' )

    pvCtrl   = controls.Control( side=side, rigPart=name, function='poleVec', nodeType="ctrl", size=0.3, color=myColor, aimAxis="twistAxis" )
    pvCtrl.cubeCtrl()

    # snaps the poleVector control to the middle joint
    common.align( node=pvCtrl.control, target=joint2, orient=False )
    pvCtrlGrp = common.insertGroup( node=pvCtrl.control )
    cmds.poleVectorConstraint( pvCtrl.control, ikHandle )

    cmds.setAttr( pvCtrl.control+'.tz', -5 )
    # invert poleVector position if it's a leg
    if isLeg:
        cmds.setAttr( pvCtrl.control+'.tz', 5 )
        # auto follow foot control
        cmds.pointConstraint(  limbEndCtrl.control, pvCtrlGrp, mo=True )
        cmds.orientConstraint( limbEndCtrl.control, pvCtrlGrp, skip=['x'], mo=True )

    # ikHandle follow endCtrl
    cmds.pointConstraint( limbEndCtrl.control, ikHandle )

    # extra attributes to endCtrl
    cmds.addAttr( limbEndCtrl.control, ln='twist', k=True )
    cmds.connectAttr( limbEndCtrl.control+'.twist', ikHandle+'.twist' )

    ## ik lock
    # add attribute to control
    if ( cmds.attributeQuery( 'lock', node=limbEndCtrl.control, exists=True ) ) == True:
         print( 'Attribute %s already exists on %s.' % ( 'lock', limbEndCtrl ) )
    else:
        cmds.addAttr( limbEndCtrl.control, longName='lock', attributeType='float', keyable=True, min=0.0, max=1.0, dv=0.0 )

    # create distance locs
    startLoc = cmds.spaceLocator( name=side + '_' + name + '_start_loc' )[0]
    middleLoc = cmds.spaceLocator( name=side + '_' + name + '_middle_loc' )[0]
    endLoc = cmds.spaceLocator( name=side + '_' + name + '_end_loc' )[0]
    upDist = cmds.createNode( 'distanceDimShape' )
    lowDist = cmds.createNode( 'distanceDimShape' )

    cmds.connectAttr( cmds.listRelatives(startLoc)[0] + '.worldPosition[0]', upDist + '.startPoint' )
    cmds.connectAttr( cmds.listRelatives(middleLoc)[0] + '.worldPosition[0]', upDist + '.endPoint' )
    cmds.connectAttr( cmds.listRelatives(middleLoc)[0] + '.worldPosition[0]', lowDist + '.startPoint' )
    cmds.connectAttr( cmds.listRelatives(endLoc)[0] + '.worldPosition[0]', lowDist + '.endPoint' )

    cmds.pointConstraint( limbStartCtrl.control, startLoc )
    cmds.pointConstraint( pvCtrl.control, middleLoc )
    cmds.pointConstraint( limbEndCtrl.control, endLoc )

    # create and connect blend utilities
    bl2aLockUp = cmds.shadingNode( 'blendTwoAttr', asUtility=True )
    bl2aLockUp = cmds.rename(bl2aLockUp, common.getName(node=bl2aLockUp, side=side, rigPart=name, function='lock_up', nodeType='bl2a'))
    bl2aLockLow = cmds.shadingNode( 'blendTwoAttr', asUtility=True )
    bl2aLockLow = cmds.rename(bl2aLockLow, common.getName(node=bl2aLockLow, side=side, rigPart=name, function='lock_low', nodeType='bl2a'))

    cmds.connectAttr( ssKeyUp+'.output', bl2aLockUp + '.input[0]' )
    cmds.connectAttr( upDist + '.distance', bl2aLockUp + '.input[1]' )
    cmds.connectAttr( limbEndCtrl.control + '.lock', bl2aLockUp + '.attributesBlender' )
    cmds.connectAttr( bl2aLockUp + '.output', joint2 + '.translateX', force = True )

    cmds.connectAttr( ssKeyLow+'.output', bl2aLockLow + '.input[0]' )
    cmds.connectAttr( lowDist + '.distance', bl2aLockLow + '.input[1]' )
    cmds.connectAttr( limbEndCtrl.control + '.lock', bl2aLockLow + '.attributesBlender' )
    cmds.connectAttr( bl2aLockLow + '.output', joint3 + '.translateX', force = True )

    # get distance nodes transforms and rename
    upDist = cmds.listRelatives( upDist, parent=True )[0]
    upDist = cmds.rename( upDist, common.getName(node=upDist, side=side, rigPart=name, function='up', nodeType='distance') )
    lowDist = cmds.listRelatives( lowDist, parent=True )[0]
    lowDist = cmds.rename( lowDist, common.getName(node=lowDist, side=side, rigPart=name, function='low', nodeType='distance') )

    # negate distance output if side = rt
    if side == 'rt':
        mdivLockRev = cmds.shadingNode( 'multiplyDivide', asUtility=True)
        mdivLockRev = cmds.rename( mdivLockRev, common.getName(node=mdivLockRev, side=side, rigPart=name, function='lock_dist_rev', nodeType='mdiv') )
        cmds.setAttr( mdivLockRev + '.input2', -1, -1, -1 )
        cmds.connectAttr( upDist + '.distance', mdivLockRev + '.input1X', force=True )
        cmds.connectAttr( mdivLockRev + '.outputX', bl2aLockUp + '.input[1]', force=True )
        cmds.connectAttr( lowDist + '.distance', mdivLockRev + '.input1Y', force=True )
        cmds.connectAttr( mdivLockRev + '.outputY', bl2aLockLow + '.input[1]', force=True )

    # group lock utilities
    lockGrp = cmds.group( startLoc, middleLoc, endLoc, upDist, lowDist, name=side + '_' + name + '_lock_grp' )

    #
    # CLEAN UP
    #
    # hide objects
    if cleanUp:
        cmds.setAttr( ikHandle+'.v', 0 )
        cmds.parent( ikHandle, limbEndCtrl.control )
        cmds.setAttr( lowTwistDict['twist_curve']+'.inheritsTransform', 0 )
        cmds.setAttr( upTwistDict['twist_curve']+'.inheritsTransform', 0 )

        for mp in cmds.listRelatives( lowTwistDict['motionPaths_group'] ) + cmds.listRelatives( upTwistDict['motionPaths_group'] ):
            cmds.setAttr( mp+'.inheritsTransform', 0 )

        if isLeg: cmds.setAttr( limbStartCtrl.control+'.v', 0 ) # hides first control of the leg

        # parenting everything under one group
        cmds.parent( limbStartCtrlGrp, limbEndCtrlGrp, pvCtrlGrp, systemGrp ) # ctrls
        hideGrp = cmds.group( joint1, startNonRoll['main_grp'], ssGrp, lockGrp, crvJntA1, crvJntD1Grp, lowTwistDict['twist_curve'], upTwistDict['twist_curve'], avgGrp, lowTwistDict['motionPaths_group'], upTwistDict['motionPaths_group'], upTwistDict['joints_group'], lowTwistDict['joints_group'], name=systemGrp.replace('_grp', '_hide_grp'), parent=systemGrp )
        cmds.setAttr( hideGrp+'.v', 0 )

        cmds.parentConstraint( limbStartCtrl.control, hideGrp, mo=True )

        # lock and hide attributes
        cmds.setAttr( limbStartCtrl.control + '.sx', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( limbStartCtrl.control + '.sy', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( limbStartCtrl.control + '.sz', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( limbStartCtrl.control + '.visibility', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( pvCtrl.control + '.rx', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( pvCtrl.control + '.ry', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( pvCtrl.control + '.rz', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( pvCtrl.control + '.sx', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( pvCtrl.control + '.sy', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( pvCtrl.control + '.sz', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( pvCtrl.control + '.visibility', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( limbEndCtrl.control + '.visibility', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( limbEndCtrl.control + '.sx', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( limbEndCtrl.control + '.sy', lock=True, keyable=False, channelBox=False )
        cmds.setAttr( limbEndCtrl.control + '.sz', lock=True, keyable=False, channelBox=False )


    returnDic = { 'ikHandle'   : ikHandle,
                  'jointList'  : [ joint1, joint2, joint3, joint4 ],
                  'poleVector' : pvCtrl,
                  'poleVector_grp' : pvCtrlGrp,
                  'start_ctrl' : limbStartCtrl.control,
                  'end_ctrl'   : limbEndCtrl.control,
                  'end_ctrl_grp'   : limbEndCtrlGrp,
                  'skinJoints_group' : [ upTwistDict['joints_group'], lowTwistDict['joints_group'] ],
                  'limbSystem_grp'   : systemGrp,
                  'stretch_positions': [ ssStartPos, ssEndPos ]
                }
    return returnDic