Esempio n. 1
0
def addJntsOnSurfIntersection(surf1, surf2, jntsNum):
    '''
    Places jnts along intersection curve between surf1 and surf2
    naming convention based on surf1
    '''
    
    # intersect surfaces
    crvGrp, intNode = mc.intersect(surf1, surf2, fs=True, ch=True, o=True, cos=False)[:2]
    intNode = mc.rename(intNode, surf1+'_ints')
    crvGrp = mc.rename(crvGrp, surf1+'_ints_crv_grp')
    crv = mc.listRelatives(crvGrp, c=True)[0]
    crv = mc.rename(crv, surf1+'_ints_crv')
    
    # rebuild curve to jntNum spans
    rbdCrv, rbdNode = mc.rebuildCurve(crv, ch=True, o=True, rpo=False, spans=jntsNum, rt=0, kr=2, n=crv+'_rbd_crv')
    rbdNode = mc.rename(rbdNode, crv+'_rbd')
    
    # offset curve to control size of eye hole
    offsetCrv, offsetNode = mc.offsetCurve(rbdCrv, ch=True, distance=0, o=True, ugn=0, n=crv+'_offset_crv')
    offsetNode = mc.rename(offsetNode, crv+'_offset')
    
    locs = []
    locName = '_'.join(surf1.split('_')[:2])
    # attach locators to intersection curve
    for locId in range(jntsNum):
        loc = mc.spaceLocator(n=locName+'_loc_%d' % locId)[0]
        rt.attachToMotionPath(offsetCrv, locId, loc, fm=False)
        mc.setAttr(loc+'.localScale', 0.05, 0.05, 0.05)
        locs.append(loc)
        
    # normal constraint to surf1
    for loc in locs:
        mc.normalConstraint(surf2, loc, aim=(1,0,0))
    
    jnts = []
    # add joints under locators
    for loc in locs:
        mc.select(cl=True)
        jnt = mc.joint(n=loc.replace('_loc_','_jnt_'))
        rt.parentSnap(jnt, loc)
        mc.setAttr(jnt+'.jointOrient', 0,0,0)
        jnts.append(jnt)
        
    # groups
    grp = mc.group(crvGrp, offsetCrv, rbdCrv, locs, n=surf1+'_intersect_loc_grp')
    
    # create offset attribute
    mc.addAttr(grp, ln='collideOffset', at='double', dv=0, k=True)
    offsetPlug = cn.create_multDoubleLinear(grp+'.collideOffset', -1)
    mc.connectAttr(offsetPlug, offsetNode+'.distance', f=True)
    
    # connect debug
    rt.connectVisibilityToggle(offsetCrv, grp, 'offsetCrv', False)
    rt.connectVisibilityToggle(rbdCrv, grp, 'rebuildCrv', False)
    rt.connectVisibilityToggle(crvGrp, grp, 'intersectCrv', False)
    rt.connectVisibilityToggle(locs, grp, 'crvLocs', False)
    rt.connectVisibilityToggle(jnts, grp, 'crvJnts', False)
Esempio n. 2
0
def createPtDriverSys(nodeName, attachGeo=None):
    '''
    Create driver system based on vertex selection in viewport
    Returns drvSysGrp, and a list of locators that can be used to drive offset controls
    '''
    
    # get vertex selections
    selVerts = mc.ls(os=True, fl=True)

    # create control placement locators on each vert
    drvLocs = []
    origLocs = []
    popcNodes = []
    ctlNum = len(selVerts)
    for ctlId in range(ctlNum):
        loc = mc.spaceLocator(n=nodeName+'_wireOffset_drvLoc%d'%ctlId)[0]
        targetVert = selVerts[ctlId]
        mc.select(targetVert, loc, r=True)
        meval('doCreatePointOnPolyConstraintArgList 2 {   "0" ,"0" ,"0" ,"1" ,"" ,"1" ,"0" ,"0" ,"0" ,"0" };')
        meval('CBdeleteConnection "%s.rx";' % loc)
        meval('CBdeleteConnection "%s.ry";' % loc)
        meval('CBdeleteConnection "%s.rz";' % loc)
        mc.setAttr(loc+'.r', 0,0,0)
        drvLocs.append(loc)
        
        # if attachGeo is defined, use attachGeo to drive pointOnPolyConstraint
        popcNode = mc.listRelatives(loc, c=True, type='pointOnPolyConstraint')[0]
        if attachGeo:
            # make origLoc to preserve position
            origLoc = mc.group(n=loc.replace('_drvLoc', '_drvLocOrig'), em=True)
            rt.parentSnap(origLoc, loc)
            mc.parent(origLoc, w=True)
            
            # swap input mesh for popcNode -> this will move drvLoc
            mc.connectAttr(attachGeo, popcNode+'.target[0].targetMesh', f=True)
            
            # parent origLoc back under driverLoc, and use origLoc as driverLoc instead
            mc.parent(origLoc, loc)
            origLocs.append(origLoc)
            
        popcNodes.append(popcNode)
    
    drvLocGrp = mc.group(drvLocs, n=nodeName+'_wireOffset_drvLocs_grp')
    drvSysGrp = mc.group(drvLocGrp, n=nodeName+'_wireOffset_drvSys_grp')
    rt.connectVisibilityToggle(drvLocs, drvSysGrp, 'drvLocsVis', False)
    
    mc.addAttr(drvSysGrp, ln='enabled', at='bool', k=True, dv=True)
    for eachPopc in popcNodes:
        rt.connectSDK(drvSysGrp+'.enabled', eachPopc+'.nodeState', {1:0, 0:2})
    
    if attachGeo:
        return drvSysGrp, origLocs
    
    return drvSysGrp, drvLocs
Esempio n. 3
0
def addTangentMPTo(startMP, endMP, direction, default=0.5, reverse=False):
    '''
    add a "tangent" attribute to startLoc, and a tangentLoc to startLoc
    at 0, tangentLoc would be exactly on startLoc
    at 1, tangentLoc would be projected on direction vector onto endLoc's plane
    
    direction (string): 'x', 'y', or 'z'
    reverse (bool): reverse direction
    
    return tangentLoc
    '''
    # add tangentLoc
    tangentLoc = mc.spaceLocator(n=startMP+'_tangent_loc')[0]
    rt.parentSnap(tangentLoc, startMP)
    
    # add planeLoc - moves on plane of endMP, to help calculcate max distance
    planeLoc = mc.spaceLocator(n=endMP+'_plane_loc')[0]
    rt.parentSnap(planeLoc, endMP)
    
    # pointConstraint planeLoc to startMP
    # skip "direction" axis, to maintain sliding on endMP's plane
    mc.pointConstraint(startMP, planeLoc, skip=direction)
    
    # get distance between startMP and planeLoc
    # this is the maximum length for tangent
    maxDistance = cn.create_distanceBetween(startMP, planeLoc)
    
    # add "tangent" attribute to startMP
    mc.addAttr(startMP, ln='tangent', at='double', min=0, max=1, dv=default, k=True)
    
    # multiply tangent value by maxDistance
    tangentDistance = cn.create_multDoubleLinear(startMP+'.tangent', maxDistance)
    
    # reverse the direction if necessary
    if reverse:
        tangentDistance = cn.create_multDoubleLinear(tangentDistance, -1)
    
    # this will be used to drive the tangentLoc
    translatePlug = '%s.t%s' % (tangentLoc, direction)
    mc.connectAttr(tangentDistance, translatePlug, f=True)
    
    # connect locators for debug
    rt.connectVisibilityToggle((planeLoc, tangentLoc), startMP, 'debugTangentLocs', default=False)
    rt.connectVisibilityToggle(tangentLoc, startMP, 'tangentLoc', False)
    
    return tangentLoc
Esempio n. 4
0
def createCtlSys(nodeName, drvLocs, size=1, addGrp=1):
    '''
    Adds controls under drvLocs, to be used as offset controls
    size - [float] radius of nurbs sphere control
            if size=1, radius will be 0.2 of distance between first 2 locators
    addGrp - [int] number of offset grps above the control
    Returns ctlSysGrp, and a list of controls
    '''
    # calculate size
    pos1 = pm.dt.Point(mc.xform(drvLocs[0], q=True, ws=True, t=True))
    pos2 = pm.dt.Point(mc.xform(drvLocs[1], q=True, ws=True, t=True))
    vec = pos2 - pos1
    dist = vec.length()
    size = dist * 0.2 * size
    
    # create controls
    ctls = []
    for eachLoc in drvLocs:
        
        grp = eachLoc
        # add offset grps
        for grpId in range(addGrp):
            grp = mc.group(em=True, n=eachLoc.replace('_wireOffset_drvLoc', '_wireOffset_offset%d_grp'%grpId).replace('Orig',''), p=grp)
            mc.xform(grp, os=True, a=True, t=(0,0,0), ro=(0,0,0))
            
        # create control
        ctl = mc.sphere(r=size, n=eachLoc.replace('_wireOffset_drvLoc', '_wireOffset_ctl').replace('Orig',''))[0]
        mc.delete(ctl, ch=True)
        rt.parentSnap(ctl, grp)
        
        # assign color
        # first, break connection to shader
        ctlShape = mc.listRelatives(ctl, c=True, s=True)[0]
        shdConn = mc.listConnections(ctlShape+'.instObjGroups', p=True)[0]
        mc.disconnectAttr(ctlShape+'.instObjGroups', shdConn)
        # assign color override
        mc.setAttr(ctlShape+'.overrideEnabled', 1)
        mc.setAttr(ctlShape+'.overrideColor', 6)
        
        ctls.append(ctl)
    
    ctlSysGrp = mc.group(em=True, n=nodeName+'_wireOffset_ctlSys_grp')
    rt.connectVisibilityToggle(ctls, ctlSysGrp, 'ctlVis', True)
    
    return ctlSysGrp, ctls
Esempio n. 5
0
def addProxyGeoToJnt(jnt, geo, child=None):
    '''
    Parent-snaps geo under jnt
    If child=None, uses first child of jnt
    Distance between jnt & child drives scaleX (assuming X is downAxis)
    '''
    geo = mc.duplicate(geo, n=jnt+'_proxy')[0]
    rt.parentSnap(geo, jnt)
    
    if not child:
        child = mc.listRelatives(jnt, c=True, type='joint')[0]
    
    # get distance between jnt and child
    
    dist = cn.create_distanceBetween(jnt, child)
    finalScale = cn.create_multiplyDivide(dist, jnt+'.scaleX', 2)
    mc.connectAttr(finalScale, geo+'.sx', f=True)
    
    return geo
Esempio n. 6
0
def addProxyGeoToJnt(jnt, geo, child=None):
    """
    Parent-snaps geo under jnt
    If child=None, uses first child of jnt
    Distance between jnt & child drives scaleX (assuming X is downAxis)
    """
    geo = mc.duplicate(geo, n=jnt + "_proxy")[0]
    rt.parentSnap(geo, jnt)

    if not child:
        child = mc.listRelatives(jnt, c=True, type="joint")[0]

    # get distance between jnt and child

    dist = cn.create_distanceBetween(jnt, child)
    finalScale = cn.create_multiplyDivide(dist, jnt + ".scaleX", 2)
    mc.connectAttr(finalScale, geo + ".sx", f=True)

    return geo
Esempio n. 7
0
def getMirroredPos(ctls, parent=None):
    '''
    Mirror a list of ctls, in worldSpace if parent=None
    Assume to use X world axis
    Assume local mirror axis is Z
    
    Returns posList, rotList
    '''
    # get transforms for all ctls in worldSpace first 
    # (before anything gets moved, as one control may move another in FK)
    loc1 = mc.spaceLocator()[0]
    loc2 = mc.spaceLocator()[0]
    mc.parent(loc2, loc1)
    flipGrp = mc.group(n='tempFlipGrp', em=True, w=True)
    
    if parent:
        mc.parent(flipGrp, parent)
        mc.xform(flipGrp, os=True, t=(0,0,0), ro=(0,0,0))
        mc.parent(flipGrp, w=True)
    
    posList = []
    rotList = []
    
    for eachCtl in ctls:
        rt.parentSnap(loc1, eachCtl)
        mc.setAttr(loc1+'.rotateOrder', mc.getAttr(eachCtl+'.rotateOrder'))
        mc.setAttr(loc2+'.rotateOrder', mc.getAttr(eachCtl+'.rotateOrder'))
        mc.setAttr(loc1+'.scaleZ', 1)
        mc.setAttr(flipGrp+'.scaleX', 1)
        mc.parent(loc1, flipGrp)
        mc.setAttr(flipGrp+'.scaleX', -1)
        mc.setAttr(loc1+'.scaleZ', -1)
        posList.append(mc.xform(loc2, q=True, ws=True, t=True))
        rotList.append(mc.xform(loc2, q=True, ws=True, ro=True))
        
    mc.delete(flipGrp)
    
    mc.select(ctls, r=True)
    
    return posList, rotList
Esempio n. 8
0
def addMidMP(startMP, endMP, startAimMP, endAimMP, aimVector, upVector, name):
    '''
    add a midLoc positioned between startMP and endMP
    alignment can be set to separate MPs
    
    return midLoc
    '''
    # add midPosLoc to get mid position
    midPosLoc = mc.spaceLocator(n=name+'_midPosLoc')[0]
    
    # constraint midPosLoc between startMP and endMP to get mid position
    mc.pointConstraint(startMP, endMP, midPosLoc)
    
    # add aimUp/Down locs to get orientation
    aimUpLoc = mc.spaceLocator(n=name+'_aimUpLoc')[0]
    aimDownLoc = mc.spaceLocator(n=name+'_aimDownLoc')[0]
    rt.parentSnap(aimUpLoc, midPosLoc)
    rt.parentSnap(aimDownLoc, midPosLoc)
    
    # aim constraints to startAimMP and endAimMPs
    mc.aimConstraint(startAimMP, aimDownLoc, aim=aimVector, u=upVector, wut='objectrotation', wuo=startAimMP, wu=upVector)
    oppositeAimVector = [-c for c in aimVector]
    mc.aimConstraint(endAimMP, aimUpLoc, aim=oppositeAimVector, u=upVector, wut='objectrotation', wuo=endAimMP, wu=upVector)
    
    # create orientLoc to blend between the two aims
    orientLoc = mc.spaceLocator(n=name+'_orientLoc')[0]
    rt.parentSnap(orientLoc, midPosLoc)
    mc.orientConstraint(startAimMP, endAimMP, orientLoc)
    
    # create midLoc for outputMP
    midLoc = mc.spaceLocator(n=name)[0]
    rt.parentSnap(midLoc, orientLoc)
    
    # connect visibilities for debugging
    rt.connectVisibilityToggle(midPosLoc, midLoc, 'debugMidPosLoc', default=False)
    rt.connectVisibilityToggle((aimUpLoc, aimDownLoc), midLoc, 'debugAimLocs', default=False)
    rt.connectVisibilityToggle(orientLoc, midLoc, 'debugOrientLoc', default=False)
    
    return midLoc
Esempio n. 9
0
def placePivotsForReverseRoll(baseJnt, bendPos=(0,0,0), leftPos=(0,0,0), rightPos=(0,0,0)):
    '''
    creates bendPivot, leftPivot, rightPivot
    to be adjusted manually
    '''
    bendPivot = mc.spaceLocator(n=baseJnt+'_bendPivot_loc')[0]
    rt.parentSnap(bendPivot, baseJnt)
    mc.setAttr(bendPivot+'.t', *bendPos)
    
    leftPivot = mc.spaceLocator(n=baseJnt+'_leftPivot_loc')[0]
    rt.parentSnap(leftPivot, baseJnt)
    mc.setAttr(leftPivot+'.t', *leftPos)
    
    rightPivot = mc.spaceLocator(n=baseJnt+'_rightPivot_loc')[0]
    rt.parentSnap(rightPivot, baseJnt)
    mc.setAttr(rightPivot+'.t', *rightPos)
    
    return bendPivot, leftPivot, rightPivot
Esempio n. 10
0
def addIndividualOffsetRig(toothId, selVerts):
    """
    returns top group node
    """
    name = 'CT_toothOffset%d_' % toothId
    #===========================================================================
    # ADD OFFSET CONTROLS
    #===========================================================================

    # add offset controlA
    ctlA = rt.ctlCurve(name + 'ctlA',
                       'circle',
                       0,
                       snap='CT_teethOffset_align_loc_%d' % toothId,
                       size=1,
                       ctlOffsets=['bend'])
    mc.aimConstraint('CT_teethOffset_aim_loc_%d' % toothId,
                     ctlA.home,
                     aim=(1, 0, 0),
                     u=(0, 0, 1),
                     wut='objectRotation',
                     wuo='CT_teethOffset_align_loc_%d' % toothId,
                     wu=(0, 0, 1))
    mc.pointConstraint('CT_teethOffset_align_loc_%d' % toothId, ctlA.home)

    # add second offset control as child to first
    ctlB = rt.ctlCurve(name + 'ctlB',
                       'circle',
                       0,
                       snap=ctlA.crv,
                       size=1,
                       ctlOffsets=['bend'])
    mc.parent(ctlB.home, ctlA.crv)
    mc.setAttr(ctlB.home + '.tx', 0.5)

    # local rig's bend should update CtlB
    mc.connectAttr('CT_teethLocal%d_bndB.ry' % (toothId + 1),
                   ctlB.grp['bend'] + '.ry',
                   f=True)

    #===============================================================================
    # Connections from offsetCtls to localRig
    #===============================================================================
    # since scale should always be local
    mc.connectAttr(ctlA.crv + '.sy',
                   'CT_teethLocal%d_bndA.sy' % (toothId + 1),
                   f=True)
    mc.connectAttr(ctlA.crv + '.sz',
                   'CT_teethLocal%d_bndA.sz' % (toothId + 1),
                   f=True)

    mc.connectAttr(ctlB.crv + '.sy',
                   'CT_teethLocal%d_bndB.sy' % (toothId + 1),
                   f=True)
    mc.connectAttr(ctlB.crv + '.sz',
                   'CT_teethLocal%d_bndB.sz' % (toothId + 1),
                   f=True)

    # twisting could also be local
    mc.connectAttr(ctlA.crv + '.rx',
                   'CT_teethLocal%d_bndA.rx' % (toothId + 1),
                   f=True)
    mc.connectAttr(ctlB.crv + '.rx',
                   'CT_teethLocal%d_bndB.rx' % (toothId + 1),
                   f=True)

    # slide tooth along path
    mc.addAttr(ctlA.crv, ln='slide', at='double', k=True)
    mc.connectAttr(ctlA.crv + '.slide',
                   'CT_teethLocal%d_bndA.ty' % (toothId + 1),
                   f=True)

    #===============================================================================
    # Complete CV point placement for curves
    #===============================================================================
    # endLoc under ctlB
    endLoc = mc.spaceLocator(n=name + 'ctlB_endLoc')[0]
    rt.parentSnap(endLoc, ctlB.crv)
    mc.setAttr(endLoc + '.tx', 0.5)
    mc.setAttr(endLoc + '.localScale', 0.1, 0.1, 0.1)

    #===============================================================================
    # second hierarchy for base curve
    #===============================================================================
    ctlABase = mc.group(em=True, n=name + 'ctlABase_grp')
    mc.aimConstraint('CT_teethOffset_aim_loc_%d' % toothId,
                     ctlABase,
                     aim=(1, 0, 0),
                     u=(0, 0, 1),
                     wut='objectRotation',
                     wuo='CT_teethOffset_align_loc_%d' % toothId,
                     wu=(0, 0, 1))
    mc.pointConstraint('CT_teethOffset_align_loc_%d' % toothId, ctlABase)
    ctlBBase = mc.group(em=True, n=name + 'ctlBBase_grp')
    rt.parentSnap(ctlBBase, ctlABase)
    mc.setAttr(ctlBBase + '.tx', 0.5)
    mc.connectAttr('CT_teethLocal%d_bndB.ry' % (toothId + 1),
                   ctlBBase + '.ry',
                   f=True)
    endBaseLoc = mc.spaceLocator(n=name + 'ctlB_endBaseLoc')[0]
    rt.parentSnap(endBaseLoc, ctlBBase)
    mc.setAttr(endBaseLoc + '.tx', 0.5)
    mc.setAttr(endBaseLoc + '.localScale', 0.1, 0.1, 0.1)

    # connect scale for base transforms
    mc.connectAttr(ctlA.crv + '.sy', ctlABase + '.sy', f=True)
    mc.connectAttr(ctlA.crv + '.sz', ctlABase + '.sz', f=True)
    mc.connectAttr(ctlB.crv + '.sy', ctlBBase + '.sy', f=True)
    mc.connectAttr(ctlB.crv + '.sz', ctlBBase + '.sz', f=True)

    #===============================================================================
    # Make wire deformer
    #===============================================================================
    # base curve
    baseCrv = rt.makeCrvThroughObjs([ctlABase, ctlBBase, endBaseLoc],
                                    name + 'baseCrv', True, 2)
    # wire curve
    wireCrv = rt.makeCrvThroughObjs([ctlA.crv, ctlB.crv, endLoc],
                                    name + 'wireCrv', True, 2)
    # make wire
    wireDfm, wireCrv = mc.wire(selVerts,
                               wire=wireCrv,
                               n=name + 'wire_dfm',
                               dds=(0, 50))
    wireBaseUnwanted = wireCrv + 'BaseWire'
    # replace base
    mc.connectAttr(baseCrv + '.worldSpace[0]',
                   wireDfm + '.baseWire[0]',
                   f=True)
    mc.delete(wireBaseUnwanted)

    #===========================================================================
    # GROUPS & DEBUG UTILS
    #===========================================================================
    dfmGrp = mc.group(baseCrv, wireCrv, n=name + 'dfg_0')
    ctlGrp = mc.group(ctlA.home, ctlABase, n=name + 'ctg_0')
    retGrp = mc.group(dfmGrp, ctlGrp, n=name + 'mod_0')

    rt.connectVisibilityToggle([endLoc, endBaseLoc], retGrp, 'endLocs', False)
    rt.connectVisibilityToggle(dfmGrp, retGrp, 'wires', False)

    return retGrp
Esempio n. 11
0
def createCrvDriverSys(nodeName, ctlNum, form=0, attachGeo=None):
    '''
    Create driver system based on edge loop selection in viewport
    nodeName [string]
    ctlNum [int] - number of controls to add along curve
    form - [int] 0 = open, 1 = periodic
    Returns drvSysGrp, and a list of locators that can be used to drive offset controls
    '''
    
    # select edge loop in UI
    drvCrv, p2cNode = mc.polyToCurve(form=form, degree=1, n=nodeName+'_wireOffset_crv')
    p2cNode = mc.rename(p2cNode, nodeName+'_wireOffset_p2c')
    crvSpans = mc.getAttr(drvCrv+'.spans')

    
    # create control placement locators on drvCrv
    drvLocs = []
    for ctlId in range(ctlNum):
        loc = mc.spaceLocator(n=nodeName+'_wireOffset_drvLoc%d'%ctlId)[0]
        param = float(ctlId) / ctlNum * crvSpans
        rt.attachToMotionPath(drvCrv, param, loc, False)
        drvLocs.append(loc)
        
    # if curve is open, we will create an extra ctl, where param = crvSpans 
    if mc.getAttr(drvCrv+'.form') != 2:
        loc = mc.spaceLocator(n=nodeName+'_wireOffset_drvLoc%d'%ctlNum)[0]
        param = crvSpans
        rt.attachToMotionPath(drvCrv, param, loc, False)
        drvLocs.append(loc)
        
    
    drvLocGrp = mc.group(drvLocs, n=nodeName+'_wireOffset_drvLocs_grp')
    drvSysGrp = mc.group(drvCrv, drvLocGrp, n=nodeName+'_wireOffset_drvSys_grp')
    
    rt.connectVisibilityToggle(drvLocs, drvSysGrp, 'drvLocsVis', False)
    rt.connectVisibilityToggle(drvCrv, drvSysGrp, 'drvCrvVis', False)
    
    mc.addAttr(drvSysGrp, ln='enabled', at='bool', k=True, dv=True)
    rt.connectSDK(drvSysGrp+'.enabled', p2cNode+'.nodeState', {1:0, 0:2})
    
        
    # if attachGeo is defined, use attachGeo to drive polyToCurve
    if attachGeo:
        # make an origLoc for each driverLoc to preserve orig positions
        origLocs = []
        for eachLoc in drvLocs:
            origLoc = mc.group(n=eachLoc.replace('_drvLoc', '_drvLocOrig'), em=True)
            rt.parentSnap(origLoc, eachLoc)
            mc.parent(origLoc, w=True)
            origLocs.append(origLoc)
            
        # switch the input mesh for polyToCurve -> this will move drvLocs
        mc.connectAttr(attachGeo, p2cNode+'.inputPolymesh', f=True)
        
        # parent orig loc back under driver loc, preserving transforms
        for drv, orig in zip(drvLocs, origLocs):
            mc.parent(orig, drv)
            
        return drvSysGrp, origLocs
    
    return drvSysGrp, drvLocs
Esempio n. 12
0
def addReverseRoll(jnts, bendPivot, leftPivot, rightPivot):
    '''
    add reverse roll to hand or foot setups
    
    jnts - 
    [base,
    [digitBase, digitEnd],
    ...
    ]
    
    digitBase joints will be parentConstrained to the new "stableDigitJoints" that sticks with the IK handles
    (so you should pass in an offset grp above the actual joint)
    
    ***
    ASSUME ONE SPLIT JOINT BETWEEN BASE AND DIGIT 
    TO BE CUSTOMIZED
    ***
    
    returns rollGrp, baseJnt
    
    EXAMPLE USE ON HAND:
    rollGrp (TRS, and attributes Bend & Side) should be driven by Ik/FKHand
    rollLocs rotations are driven by attributes on the Hand
    baseStableJnt is a child of the rollLocs, and therefore rotate with pivots at the rollLocs
    baseStableJnt drives the child of Ik/FkHand
    
    '''
    
    #===========================================================================
    # BUILD DRIVER JOINT CHAIN
    #===========================================================================
    
    baseJnt = jnts[0]
    digitJnts = jnts[1:]
    basePos = mc.xform(baseJnt, q=True, t=True, ws=True)
    
    # base joint
    mc.select(cl=True)
    baseStableJnt = mc.joint(n=baseJnt+'_stable')
    rt.parentSnap(baseStableJnt, baseJnt)
    mc.setAttr(baseStableJnt+'.jointOrient', 0,0,0)
    mc.parent(baseStableJnt, w=True)
    
    ikHs = []
    
    # digit joints
    for base, tip in digitJnts:
        
        #=======================================================================
        # MAKE JOINTS
        #=======================================================================
        
        mc.select(cl=True)
        
        # split joint
        digitPos = mc.xform(base, q=True, t=True, ws=True)
        # get midPoint between base to digitBase
        midPoint = [(b + d)/2 for b, d in zip(basePos, digitPos)]
        splitJnt = mc.joint(p=midPoint, n=base+'_mid')
        
        # digit base jnt
        digitBaseJnt = mc.joint(p=digitPos, n=base+'_stable')
        
        # digit end jnt
        tipPos = mc.xform(tip, q=True, t=True, ws=True)
        digitEndJnt = mc.joint(p=tipPos, n=base+'_stableTip')
        
        # orient joint chain
        mc.joint(splitJnt, oj='xyz', ch=True, sao='yup', e=True)
        mc.setAttr(digitEndJnt+'.jointOrient', 0,0,0)
        
        mc.parent(splitJnt, baseStableJnt)
        
        #=======================================================================
        # MAKE IKHANDLE
        #=======================================================================
        
        ikH = mc.ikHandle(solver='ikSCsolver', n=base+'_ikH', sj=digitBaseJnt, ee=digitEndJnt)[0]
        ikHs.append(ikH)
        
        #=======================================================================
        # PARENT CONSTRAINT original joints to stable joints
        #=======================================================================
        mc.parentConstraint(digitBaseJnt, base, mo=True)
    
    ikHdlGrp = mc.group(ikHs, n=baseJnt+'reverseRoll_ikHdl_grp')
    
    # parent baseStableJnt under locators to make multiple pivots
    
    rollGrp = abRT.groupFreeze(baseStableJnt)
    mc.parent(bendPivot, rollGrp)
    mc.parent(leftPivot, bendPivot)
    mc.parent(rightPivot, leftPivot)
    mc.parent(baseStableJnt, rightPivot)
    mc.parent(ikHdlGrp, rollGrp)
    
    # hide locators for debugging
    rt.connectVisibilityToggle([bendPivot, leftPivot, rightPivot], rollGrp, 'debugPivotLocs', False)
    
    # add attributes for controlling bend and side-to-side
    mc.addAttr(rollGrp, ln='bend', at='double', min=-10, max=10, dv=0, k=True)
    mc.addAttr(rollGrp, ln='side', at='double', min=-10, max=10, dv=0, k=True)
    
    rt.connectSDK(rollGrp+'.bend', bendPivot+'.rz', {-10:90, 10:-90})
    rt.connectSDK(rollGrp+'.side', leftPivot+'.rx', {0:0, 10:-90})
    rt.connectSDK(rollGrp+'.side', rightPivot+'.rx', {0:0, -10:90})
            
    return rollGrp, baseStableJnt
Esempio n. 13
0
def addJntsOnSurfIntersection(surf1, surf2, jntsNum):
    '''
    Places jnts along intersection curve between surf1 and surf2
    naming convention based on surf1
    '''

    # intersect surfaces
    crvGrp, intNode = mc.intersect(surf1,
                                   surf2,
                                   fs=True,
                                   ch=True,
                                   o=True,
                                   cos=False)[:2]
    intNode = mc.rename(intNode, surf1 + '_ints')
    crvGrp = mc.rename(crvGrp, surf1 + '_ints_crv_grp')
    crv = mc.listRelatives(crvGrp, c=True)[0]
    crv = mc.rename(crv, surf1 + '_ints_crv')

    # rebuild curve to jntNum spans
    rbdCrv, rbdNode = mc.rebuildCurve(crv,
                                      ch=True,
                                      o=True,
                                      rpo=False,
                                      spans=jntsNum,
                                      rt=0,
                                      kr=2,
                                      n=crv + '_rbd_crv')
    rbdNode = mc.rename(rbdNode, crv + '_rbd')

    # offset curve to control size of eye hole
    offsetCrv, offsetNode = mc.offsetCurve(rbdCrv,
                                           ch=True,
                                           distance=0,
                                           o=True,
                                           ugn=0,
                                           n=crv + '_offset_crv')
    offsetNode = mc.rename(offsetNode, crv + '_offset')

    locs = []
    locName = '_'.join(surf1.split('_')[:2])
    # attach locators to intersection curve
    for locId in range(jntsNum):
        loc = mc.spaceLocator(n=locName + '_loc_%d' % locId)[0]
        rt.attachToMotionPath(offsetCrv, locId, loc, fm=False)
        mc.setAttr(loc + '.localScale', 0.05, 0.05, 0.05)
        locs.append(loc)

    # normal constraint to surf1
    for loc in locs:
        mc.normalConstraint(surf2, loc, aim=(1, 0, 0))

    jnts = []
    # add joints under locators
    for loc in locs:
        mc.select(cl=True)
        jnt = mc.joint(n=loc.replace('_loc_', '_jnt_'))
        rt.parentSnap(jnt, loc)
        mc.setAttr(jnt + '.jointOrient', 0, 0, 0)
        jnts.append(jnt)

    # groups
    grp = mc.group(crvGrp,
                   offsetCrv,
                   rbdCrv,
                   locs,
                   n=surf1 + '_intersect_loc_grp')

    # create offset attribute
    mc.addAttr(grp, ln='collideOffset', at='double', dv=0, k=True)
    offsetPlug = cn.create_multDoubleLinear(grp + '.collideOffset', -1)
    mc.connectAttr(offsetPlug, offsetNode + '.distance', f=True)

    # connect debug
    rt.connectVisibilityToggle(offsetCrv, grp, 'offsetCrv', False)
    rt.connectVisibilityToggle(rbdCrv, grp, 'rebuildCrv', False)
    rt.connectVisibilityToggle(crvGrp, grp, 'intersectCrv', False)
    rt.connectVisibilityToggle(locs, grp, 'crvLocs', False)
    rt.connectVisibilityToggle(jnts, grp, 'crvJnts', False)
Esempio n. 14
0
def rigEyes():
    # eyeBall - eyeLids intersections
    surf1 = 'LT_eyeBallIntersect_srf_0'
    surf2 = 'CT_eyeBallHeadIntersecter_srf_0'
    jntsNum = 20
    addJntsOnSurfIntersection(surf1, surf2, jntsNum)

    # eyeBall pop controls
    baseTangentMP = ms.addTangentMPTo('LT_eyeBase_mPt',
                                      'LT_eyeTip_mPt',
                                      'z',
                                      default=0.2,
                                      reverse=False)
    tipTangentMP = ms.addTangentMPTo('LT_eyeTip_mPt',
                                     'LT_eyeBase_mPt',
                                     'z',
                                     default=0.2,
                                     reverse=True)
    midMP = ms.addMidMP(baseTangentMP, tipTangentMP, 'LT_eyeBase_mPt',
                        'LT_eyeTip_mPt', (0, 0, 1), (0, 1, 0), 'LT_mid_mPt')
    crv = ms.createSplineMPs(('LT_eyeBase_mPt', baseTangentMP, midMP,
                              tipTangentMP, 'LT_eyeTip_mPt'), 8, 'LT_eyeSpine',
                             (0, 3, 0))

    baseTangentMP = ms.addTangentMPTo('RT_eyeBase_mPt',
                                      'RT_eyeTip_mPt',
                                      'z',
                                      default=0.2,
                                      reverse=False)
    tipTangentMP = ms.addTangentMPTo('RT_eyeTip_mPt',
                                     'RT_eyeBase_mPt',
                                     'z',
                                     default=0.2,
                                     reverse=True)
    midMP = ms.addMidMP(baseTangentMP, tipTangentMP, 'RT_eyeBase_mPt',
                        'RT_eyeTip_mPt', (0, 0, 1), (0, 1, 0), 'RT_mid_mPt')
    crv = ms.createSplineMPs(('RT_eyeBase_mPt', baseTangentMP, midMP,
                              tipTangentMP, 'RT_eyeTip_mPt'), 8, 'RT_eyeSpine',
                             (0, 3, 0))

    #===========================================================================
    # add IK offset ctrls to eyeball
    #===========================================================================
    lfMps = mc.ls(sl=True)
    ctls = []

    # create left controls
    for ctlId in range(0, len(lfMps)):
        ctl = cs.ctlCurve(lfMps[ctlId].replace('_MPJnt_', '_ctl_'),
                          'circle',
                          0,
                          size=6,
                          snap=lfMps[ctlId])
        ctl.setSpaces([lfMps[ctlId]], ['Eye'])
        ctls.append(ctl)

    rtMps = mc.ls(sl=True)
    ctls = []

    # create right controls
    for ctlId in range(0, len(rtMps)):
        ctl = cs.ctlCurve(rtMps[ctlId].replace('_MPJnt_', '_ctl_'),
                          'circle',
                          0,
                          size=6,
                          snap=rtMps[ctlId])
        ctl.setSpaces([rtMps[ctlId]], ['Eye'])
        ctls.append(ctl)

    #===========================================================================
    # Add stretchy volume for eyeBall spine
    #===========================================================================

    stretchAmts = {
        'LT_eyeSpine_ctl_0_space': 10,
        'LT_eyeSpine_ctl_1_space': 9,
        'LT_eyeSpine_ctl_2_space': 8,
        'LT_eyeSpine_ctl_3_space': 5,
        'LT_eyeSpine_ctl_4_space': 3,
        'LT_eyeSpine_ctl_5_space': 1.25,
        'LT_eyeSpine_ctl_6_space': 0,
        'LT_eyeSpine_ctl_7_space': -1
    }

    ms.addVolume('LT_eyeSpine_uniform_crv_crv', stretchAmts)

    stretchAmts = {
        'RT_eyeSpine_ctl_0_space': 10,
        'RT_eyeSpine_ctl_1_space': 9,
        'RT_eyeSpine_ctl_2_space': 8,
        'RT_eyeSpine_ctl_3_space': 5,
        'RT_eyeSpine_ctl_4_space': 3,
        'RT_eyeSpine_ctl_5_space': 1.25,
        'RT_eyeSpine_ctl_6_space': 0,
        'RT_eyeSpine_ctl_7_space': -1
    }

    ms.addVolume('RT_eyeSpine_uniform_crv_crv', stretchAmts)
    #===========================================================================
    # Add control lattice to eyeBall nurbs
    #===========================================================================

    # Create lattice - hard coded to 8 ctls in Z
    eyeSphere = 'LT_eyeBallIntersect_srf_0'
    prefix = 'LT_eyeBallIntersect_'
    ffd, lat, latBase = mc.lattice(eyeSphere,
                                   n=prefix + 'ffd',
                                   oc=True,
                                   dv=(4, 4, 8))
    grp = abRT.groupFreeze(lat)
    rt.transferAttrValues(lat + '.s', grp + '.s', False)
    mc.setAttr(lat + '.s', 1, 1, 1)
    mc.parent(latBase, grp)

    # Create lattice - hard coded to 8 ctls in Z
    eyeSphere = 'RT_eyeBallIntersect_srf_0'
    prefix = 'RT_eyeBallIntersect_'
    ffd, lat, latBase = mc.lattice(eyeSphere,
                                   n=prefix + 'ffd',
                                   oc=True,
                                   dv=(4, 4, 8))
    grp = abRT.groupFreeze(lat)
    rt.transferAttrValues(lat + '.s', grp + '.s', False)
    mc.setAttr(lat + '.s', 1, 1, 1)
    mc.parent(latBase, grp)

    # DO THIS FOR LEFT AND RIGHT SIDES

    # Create joints under each ctl
    ctls = mc.ls(os=True)
    jnts = []
    for eachCtl in ctls:
        mc.select(cl=True)
        jnt = mc.joint(n=eachCtl.replace('_ctl', '_jnt'))
        rt.parentSnap(jnt, eachCtl)
        jnts.append(jnt)
        mc.setAttr(jnt + '.radius', 3)
        mc.setAttr(jnt + '.jointOrient', 0, 0, 0)

    # Weight joints to lattice
    skn = mc.skinCluster(jnts, lat, name=lat + '_skn')[0]
    for jnt in jnts:
        i = jnts.index(jnt)
        mc.skinPercent(skn, lat + '.pt[*][*][%d]' % i, tv=((jnt, 1)))
Esempio n. 15
0
def addIndividualOffsetRig(toothId, selVerts):
    """
    returns top group node
    """
    name = 'CT_toothOffset%d_'%toothId
    #===========================================================================
    # ADD OFFSET CONTROLS
    #===========================================================================
    
    # add offset controlA
    ctlA = rt.ctlCurve(name+'ctlA', 'circle', 0, snap='CT_teethOffset_align_loc_%d'%toothId, size=1, ctlOffsets=['bend'])
    mc.aimConstraint('CT_teethOffset_aim_loc_%d'%toothId, ctlA.home, aim=(1,0,0), u=(0,0,1), wut='objectRotation', wuo='CT_teethOffset_align_loc_%d'%toothId, wu=(0,0,1))
    mc.pointConstraint('CT_teethOffset_align_loc_%d'%toothId, ctlA.home)
    
    # add second offset control as child to first
    ctlB = rt.ctlCurve(name+'ctlB', 'circle', 0, snap=ctlA.crv, size=1, ctlOffsets=['bend'])
    mc.parent(ctlB.home, ctlA.crv)
    mc.setAttr(ctlB.home+'.tx', 0.5)
    
    # local rig's bend should update CtlB
    mc.connectAttr('CT_teethLocal%d_bndB.ry'%(toothId+1), ctlB.grp['bend']+'.ry', f=True)
    
    #===============================================================================
    # Connections from offsetCtls to localRig
    #===============================================================================
    # since scale should always be local
    mc.connectAttr(ctlA.crv+'.sy', 'CT_teethLocal%d_bndA.sy'%(toothId+1), f=True)
    mc.connectAttr(ctlA.crv+'.sz', 'CT_teethLocal%d_bndA.sz'%(toothId+1), f=True)
    
    mc.connectAttr(ctlB.crv+'.sy', 'CT_teethLocal%d_bndB.sy'%(toothId+1), f=True)
    mc.connectAttr(ctlB.crv+'.sz', 'CT_teethLocal%d_bndB.sz'%(toothId+1), f=True)
    
    # twisting could also be local
    mc.connectAttr(ctlA.crv+'.rx', 'CT_teethLocal%d_bndA.rx'%(toothId+1), f=True)
    mc.connectAttr(ctlB.crv+'.rx', 'CT_teethLocal%d_bndB.rx'%(toothId+1), f=True)
    
    # slide tooth along path
    mc.addAttr(ctlA.crv, ln='slide', at='double', k=True)
    mc.connectAttr(ctlA.crv+'.slide', 'CT_teethLocal%d_bndA.ty'%(toothId+1), f=True)
    
    #===============================================================================
    # Complete CV point placement for curves
    #===============================================================================
    # endLoc under ctlB
    endLoc = mc.spaceLocator(n=name+'ctlB_endLoc')[0]
    rt.parentSnap(endLoc, ctlB.crv)
    mc.setAttr(endLoc+'.tx', 0.5)
    mc.setAttr(endLoc+'.localScale', 0.1,0.1,0.1)
    
    #===============================================================================
    # second hierarchy for base curve
    #===============================================================================
    ctlABase = mc.group(em=True, n=name+'ctlABase_grp')
    mc.aimConstraint('CT_teethOffset_aim_loc_%d'%toothId, ctlABase, aim=(1,0,0), u=(0,0,1), wut='objectRotation', wuo='CT_teethOffset_align_loc_%d'%toothId, wu=(0,0,1))
    mc.pointConstraint('CT_teethOffset_align_loc_%d'%toothId, ctlABase)
    ctlBBase = mc.group(em=True, n=name+'ctlBBase_grp')
    rt.parentSnap(ctlBBase, ctlABase)
    mc.setAttr(ctlBBase+'.tx', 0.5)
    mc.connectAttr('CT_teethLocal%d_bndB.ry'%(toothId+1), ctlBBase+'.ry', f=True)
    endBaseLoc = mc.spaceLocator(n=name+'ctlB_endBaseLoc')[0]
    rt.parentSnap(endBaseLoc, ctlBBase)
    mc.setAttr(endBaseLoc+'.tx', 0.5)
    mc.setAttr(endBaseLoc+'.localScale', 0.1,0.1,0.1)
    
    # connect scale for base transforms
    mc.connectAttr(ctlA.crv+'.sy', ctlABase+'.sy', f=True)
    mc.connectAttr(ctlA.crv+'.sz', ctlABase+'.sz', f=True)
    mc.connectAttr(ctlB.crv+'.sy', ctlBBase+'.sy', f=True)
    mc.connectAttr(ctlB.crv+'.sz', ctlBBase+'.sz', f=True)

    #===============================================================================
    # Make wire deformer
    #===============================================================================
    # base curve
    baseCrv = rt.makeCrvThroughObjs([ctlABase, ctlBBase, endBaseLoc], name+'baseCrv', True, 2)
    # wire curve
    wireCrv = rt.makeCrvThroughObjs([ctlA.crv, ctlB.crv, endLoc], name+'wireCrv', True, 2)
    # make wire
    wireDfm, wireCrv = mc.wire(selVerts, wire=wireCrv, n=name+'wire_dfm', dds=(0,50))
    wireBaseUnwanted = wireCrv+'BaseWire'
    # replace base
    mc.connectAttr(baseCrv+'.worldSpace[0]', wireDfm+'.baseWire[0]', f=True)
    mc.delete(wireBaseUnwanted)
    
    #===========================================================================
    # GROUPS & DEBUG UTILS
    #===========================================================================
    dfmGrp = mc.group(baseCrv, wireCrv, n=name+'dfg_0')
    ctlGrp = mc.group(ctlA.home, ctlABase, n=name+'ctg_0')
    retGrp= mc.group(dfmGrp, ctlGrp, n=name+'mod_0')
    
    rt.connectVisibilityToggle([endLoc, endBaseLoc], retGrp, 'endLocs', False)
    rt.connectVisibilityToggle(dfmGrp, retGrp, 'wires', False)
    
    return retGrp
Esempio n. 16
0
def addOffsetPt(pt, aims, upObj, name):
    '''
    '''
    # make master grp
    masterGrp = mc.group(n=name+'_offsetPt_grp', em=True)
    mc.addAttr(masterGrp, ln='rollDistance', at='double', k=True, dv=2)
    
    #===========================================================================
    # BASE
    #===========================================================================
    # create base locator (drive base surface)
    baseLoc = mc.spaceLocator(n=name+'_baseLoc')[0]
    
    # point constraint to pt
    mc.pointConstraint(pt, baseLoc)
    
    # aim constraint to aim(s), using upObj as up object
    orientLocs = [] # should have maximum of 2 items
    for eachAim in aims:
        oriLoc = mc.spaceLocator(n=name+'_oriLoc#')[0]
        mc.pointConstraint(pt, oriLoc)
        if aims.index(eachAim) == 1:
            aimVec = (-1,0,0) # for the second aim constraint, use -X to aim opposite direction
        else:
            aimVec = (1,0,0)
        mc.aimConstraint(eachAim, oriLoc, aim=aimVec, u=(0,1,0), wuo=upObj, wut='object')
        orientLocs.append(oriLoc)
        
    mc.orientConstraint(orientLocs, baseLoc)
    
    # create base bnd jnt, parent under base loc
    baseBndJnt = mc.joint(n=name+'_base_bndJnt')
    rt.parentSnap(baseBndJnt, baseLoc)
    
    #===========================================================================
    # OFFSET
    #===========================================================================
    # create offset locator (drive offset surface), parent snap to base loc
    offsetLoc = mc.spaceLocator(n=name+'_offsetLoc')[0]
    rt.parentSnap(offsetLoc, baseLoc)
    
    # create acs locator (for automation), parent snap to offset loc
    offsetAcsLoc = mc.spaceLocator(n=name+'_offset_acsLoc')[0]
    rt.parentSnap(offsetAcsLoc, offsetLoc)
    
    #===========================================================================
    # ROLL
    #===========================================================================
    # create roll loc, parent snap to base loc, translate Z by masterGrp.rollDistance
    rollLoc = mc.spaceLocator(n=name+'_rollLoc')[0]
    rt.parentSnap(rollLoc, baseLoc)
    mc.connectAttr(masterGrp+'.rollDistance', rollLoc+'.tz', f=True)
    
    # create offset roll loc
    offsetRollLoc = mc.spaceLocator(n=name+'_roll_offsetLoc')[0]
    rt.parentSnap(offsetRollLoc, rollLoc)
    
    # create acs roll loc (for automation), parent snap to roll offset loc
    acsRollLoc = mc.spaceLocator(n=name+'_roll_acsLoc')[0]
    rt.parentSnap(acsRollLoc, offsetRollLoc)
    
    #===========================================================================
    # calculate Offset transform in Roll space
    #===========================================================================
    # offset-acs-loc.worldMatrix X roll-loc.inverseWorldMatrix
    mm = mc.createNode('multMatrix', n=name+'_calcRollSpace_mm')
    mc.connectAttr(offsetAcsLoc+'.worldMatrix[0]', mm+'.matrixIn[0]', f=True)
    mc.connectAttr(rollLoc+'.worldInverseMatrix[0]', mm+'.matrixIn[1]', f=True)
    
    dm = mc.createNode('decomposeMatrix', n=name+'_calcRollSpace_dm')
    mc.connectAttr(mm+'.matrixSum', dm+'.inputMatrix', f=True)

    #===========================================================================
    # offset bnd jnt
    #===========================================================================
    # use calculated position in roll-space, but parent under acs-roll-loc, to inherit offsets
    # make a locked loc first (to receive transforms)
    lockedLoc = mc.spaceLocator(n=name+'_readTransforms_loc')[0]
    rt.parentSnap(lockedLoc, acsRollLoc)
    
    mc.connectAttr(dm+'.outputTranslate', lockedLoc+'.t', f=True)
    mc.connectAttr(dm+'.outputRotate', lockedLoc+'.r', f=True)
    mc.connectAttr(dm+'.outputScale', lockedLoc+'.s', f=True)
    
    # user-controllable locator
    ctl = mc.spaceLocator(n=name+'_ctl')[0]
    rt.parentSnap(ctl, lockedLoc)
    
    # bnd jnt for offset surface
    offsetBndJnt = mc.joint(n=name+'_offset_bndJnt')
    
    #===========================================================================
    # HIDING
    #===========================================================================
    mc.addAttr(masterGrp, ln='debugVis', at='bool', k=True)
    mc.setAttr(masterGrp+'.debugVis', l=True)
    rt.connectVisibilityToggle(offsetBndJnt, masterGrp, 'offsetJnt', False)
    rt.connectVisibilityToggle(baseBndJnt, masterGrp, 'baseJnt', False)
    rt.connectVisibilityToggle(ctl, masterGrp, 'ctl', True)
    rt.connectVisibilityToggle(offsetRollLoc, masterGrp, 'rollLoc', True)
    rt.connectVisibilityToggle(orientLocs, masterGrp, 'orientLocs', False)
    rt.connectVisibilityToggle([baseLoc, oriLoc, offsetLoc, offsetAcsLoc, rollLoc, acsRollLoc, lockedLoc], masterGrp, 'locs', False)
    
    mc.parent(baseLoc, orientLocs, masterGrp)
    mc.select(masterGrp, r=True)
    
    return masterGrp
Esempio n. 17
0
def rigEyes():
    # eyeBall - eyeLids intersections
    surf1 = 'LT_eyeBallIntersect_srf_0'
    surf2 = 'CT_eyeBallHeadIntersecter_srf_0'
    jntsNum = 20
    addJntsOnSurfIntersection(surf1, surf2, jntsNum)
    
    # eyeBall pop controls
    baseTangentMP = ms.addTangentMPTo('LT_eyeBase_mPt', 'LT_eyeTip_mPt', 'z', default=0.2, reverse=False)
    tipTangentMP = ms.addTangentMPTo('LT_eyeTip_mPt', 'LT_eyeBase_mPt', 'z', default=0.2, reverse=True)
    midMP = ms.addMidMP(baseTangentMP, tipTangentMP, 'LT_eyeBase_mPt', 'LT_eyeTip_mPt', (0,0,1), (0,1,0), 'LT_mid_mPt')
    crv = ms.createSplineMPs(('LT_eyeBase_mPt', baseTangentMP, midMP, tipTangentMP, 'LT_eyeTip_mPt'), 8, 'LT_eyeSpine', (0,3,0))
    
    baseTangentMP = ms.addTangentMPTo('RT_eyeBase_mPt', 'RT_eyeTip_mPt', 'z', default=0.2, reverse=False)
    tipTangentMP = ms.addTangentMPTo('RT_eyeTip_mPt', 'RT_eyeBase_mPt', 'z', default=0.2, reverse=True)
    midMP = ms.addMidMP(baseTangentMP, tipTangentMP, 'RT_eyeBase_mPt', 'RT_eyeTip_mPt', (0,0,1), (0,1,0), 'RT_mid_mPt')
    crv = ms.createSplineMPs(('RT_eyeBase_mPt', baseTangentMP, midMP, tipTangentMP, 'RT_eyeTip_mPt'), 8, 'RT_eyeSpine', (0,3,0))
    
    #===========================================================================
    # add IK offset ctrls to eyeball
    #===========================================================================
    lfMps = mc.ls(sl=True)
    ctls = []
    
    # create left controls
    for ctlId in range(0,len(lfMps)):
        ctl = cs.ctlCurve(lfMps[ctlId].replace('_MPJnt_', '_ctl_'), 'circle', 0, size=6, snap=lfMps[ctlId])
        ctl.setSpaces([lfMps[ctlId]], ['Eye'])
        ctls.append(ctl)
        
    rtMps = mc.ls(sl=True)
    ctls = []
    
    # create right controls
    for ctlId in range(0,len(rtMps)):
        ctl = cs.ctlCurve(rtMps[ctlId].replace('_MPJnt_', '_ctl_'), 'circle', 0, size=6, snap=rtMps[ctlId])
        ctl.setSpaces([rtMps[ctlId]], ['Eye'])
        ctls.append(ctl)
        
    #===========================================================================
    # Add stretchy volume for eyeBall spine
    #===========================================================================
    
    stretchAmts = {'LT_eyeSpine_ctl_0_space':10,
                'LT_eyeSpine_ctl_1_space':9,
                'LT_eyeSpine_ctl_2_space':8,
                'LT_eyeSpine_ctl_3_space':5,
                'LT_eyeSpine_ctl_4_space':3,
                'LT_eyeSpine_ctl_5_space':1.25,
                'LT_eyeSpine_ctl_6_space':0,
                'LT_eyeSpine_ctl_7_space':-1}
    
    ms.addVolume('LT_eyeSpine_uniform_crv_crv', stretchAmts)
    
    stretchAmts = {'RT_eyeSpine_ctl_0_space':10,
                'RT_eyeSpine_ctl_1_space':9,
                'RT_eyeSpine_ctl_2_space':8,
                'RT_eyeSpine_ctl_3_space':5,
                'RT_eyeSpine_ctl_4_space':3,
                'RT_eyeSpine_ctl_5_space':1.25,
                'RT_eyeSpine_ctl_6_space':0,
                'RT_eyeSpine_ctl_7_space':-1}
    
    ms.addVolume('RT_eyeSpine_uniform_crv_crv', stretchAmts)
    #===========================================================================
    # Add control lattice to eyeBall nurbs
    #===========================================================================
    
    # Create lattice - hard coded to 8 ctls in Z
    eyeSphere = 'LT_eyeBallIntersect_srf_0'
    prefix = 'LT_eyeBallIntersect_'
    ffd, lat, latBase = mc.lattice(eyeSphere, n=prefix+'ffd', oc=True, dv=(4,4,8))
    grp = abRT.groupFreeze(lat)
    rt.transferAttrValues(lat+'.s', grp+'.s', False)
    mc.setAttr(lat+'.s',1,1,1)
    mc.parent(latBase, grp)
    
    # Create lattice - hard coded to 8 ctls in Z
    eyeSphere = 'RT_eyeBallIntersect_srf_0'
    prefix = 'RT_eyeBallIntersect_'
    ffd, lat, latBase = mc.lattice(eyeSphere, n=prefix+'ffd', oc=True, dv=(4,4,8))
    grp = abRT.groupFreeze(lat)
    rt.transferAttrValues(lat+'.s', grp+'.s', False)
    mc.setAttr(lat+'.s',1,1,1)
    mc.parent(latBase, grp)
    
    # DO THIS FOR LEFT AND RIGHT SIDES
    
    # Create joints under each ctl
    ctls = mc.ls(os=True)
    jnts = []
    for eachCtl in ctls:
        mc.select(cl=True)
        jnt = mc.joint(n=eachCtl.replace('_ctl', '_jnt'))
        rt.parentSnap(jnt, eachCtl)
        jnts.append(jnt)
        mc.setAttr(jnt+'.radius', 3)
        mc.setAttr(jnt+'.jointOrient', 0,0,0)
        
    # Weight joints to lattice
    skn = mc.skinCluster(jnts, lat, name=lat+'_skn')[0]
    for jnt in jnts:
        i = jnts.index(jnt)
        mc.skinPercent(skn, lat+'.pt[*][*][%d]'%i, tv=((jnt, 1)))