def addOffsetRig(): #=============================================================================== # SETUP LOCATORS FOR ALIGN AND AIM #=============================================================================== # add offsetAimLocs to blockAimCrv globalGrp = 'CT_teethOffset_mod_0' mc.group(n=globalGrp, em=True) name = 'CT_teethOffset' targetCrv = 'CT_teethBlockAim_crv_0' locs = [] for locId in range(12): loc = mc.spaceLocator(n=name + '_aim_loc_%d' % locId)[0] mc.setAttr(loc + '.localScale', 0.1, 0.1, 0.1) rt.attachToMotionPath(targetCrv, (float(locId) + 0.5) / 12, loc, True) locs.append(loc) mc.group(locs, n='CT_teethOffset_aim_loc_grp', p=globalGrp) rt.connectVisibilityToggle(locs, globalGrp, 'aimLocs', False) # add offsetAlignLocs to blockDrvCrv targetCrv = 'CT_teethBlockDrv_crv_0' locs = [] for locId in range(12): loc = mc.spaceLocator(n=name + '_align_loc_%d' % locId)[0] mc.setAttr(loc + '.localScale', 0.1, 0.1, 0.1) rt.alignOnMotionPath(targetCrv, (float(locId) + 0.5) / 12, loc, name + '_aim_loc_%d.matrix' % locId, True, ua=1, inverseUp=True) locs.append(loc) mc.group(locs, n='CT_teethOffset_align_loc_grp', p=globalGrp) rt.connectVisibilityToggle(locs, globalGrp, 'alignLocs', False)
def addOffsetRig(): #=============================================================================== # SETUP LOCATORS FOR ALIGN AND AIM #=============================================================================== # add offsetAimLocs to blockAimCrv globalGrp = 'CT_teethOffset_mod_0' mc.group(n=globalGrp, em=True) name = 'CT_teethOffset' targetCrv = 'CT_teethBlockAim_crv_0' locs=[] for locId in range(12): loc = mc.spaceLocator(n=name+'_aim_loc_%d'%locId)[0] mc.setAttr(loc+'.localScale', 0.1,0.1,0.1) rt.attachToMotionPath(targetCrv, (float(locId)+0.5)/12, loc, True) locs.append(loc) mc.group(locs, n='CT_teethOffset_aim_loc_grp', p=globalGrp) rt.connectVisibilityToggle(locs, globalGrp, 'aimLocs', False) # add offsetAlignLocs to blockDrvCrv targetCrv = 'CT_teethBlockDrv_crv_0' locs=[] for locId in range(12): loc = mc.spaceLocator(n=name+'_align_loc_%d'%locId)[0] mc.setAttr(loc+'.localScale', 0.1,0.1,0.1) rt.alignOnMotionPath(targetCrv, (float(locId)+0.5)/12, loc, name+'_aim_loc_%d.matrix'%locId, True, ua=1, inverseUp=True) locs.append(loc) mc.group(locs, n='CT_teethOffset_align_loc_grp', p=globalGrp) rt.connectVisibilityToggle(locs, globalGrp, 'alignLocs', False)
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)
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
def globalRig(): """ Make global teeth rig """ # teeth aim locs globalGrp = 'CT_teethBlockGlobal_mod_0' locNum = 7 name = 'CT_teethBlock' targetCrv = 'CT_teethBlockAim_crv_0' locs = [] for locId in range(locNum): loc = mc.spaceLocator(n=name + '_aim_loc_%d' % locId)[0] mc.setAttr(loc + '.localScale', 0.1, 0.1, 0.1) rt.attachToMotionPath(targetCrv, float(locId) / (locNum - 1), loc, True) locs.append(loc) mc.group(locs, n='CT_teethBlock_aim_loc_grp') rt.connectVisibilityToggle(locs, globalGrp, 'aimLocs', False) # teeth bind jnt for each aimLoc targetCrv = 'CT_teethBlockDrv_crv_0' jnts = [] for jntId in range(locNum): mc.select(cl=True) jnt = mc.joint(n=name + '_bnd_jnt_%d' % jntId) mc.setAttr(jnt + '.radius', 0.5) rt.alignOnMotionPath(targetCrv, float(jntId) / (locNum - 1), jnt, name + '_aim_loc_%d.matrix' % jntId, True, ua=1, inverseUp=True) jnts.append(jnt) mc.group(jnts, n='CT_teethBlock_bnd_jnts_grp') rt.connectVisibilityToggle(jnts, globalGrp, 'bndJnts', False) jnts = mc.ls(sl=True) import utils.wrappers.abRiggingTools as abRT # add bnd jnts for bite # for each bndjnt, make another jnt below it for bndJnt in jnts: mc.select(cl=True) biteJnt = mc.joint(n=bndJnt + '_bite') mc.setAttr(biteJnt + '.radius', 0.5) abRT.snapToPosition(bndJnt, biteJnt) mc.xform(biteJnt, t=(0, -1, 0), r=True) mc.parentConstraint(bndJnt, biteJnt, mo=True) mc.parent(biteJnt, 'CT_teethBlock_bnd_jnts_grp')
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
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
def globalRig(): """ Make global teeth rig """ # teeth aim locs globalGrp = 'CT_teethBlockGlobal_mod_0' locNum = 7 name = 'CT_teethBlock' targetCrv = 'CT_teethBlockAim_crv_0' locs=[] for locId in range(locNum): loc = mc.spaceLocator(n=name+'_aim_loc_%d'%locId)[0] mc.setAttr(loc+'.localScale', 0.1,0.1,0.1) rt.attachToMotionPath(targetCrv, float(locId)/(locNum-1), loc, True) locs.append(loc) mc.group(locs, n='CT_teethBlock_aim_loc_grp') rt.connectVisibilityToggle(locs, globalGrp, 'aimLocs', False) # teeth bind jnt for each aimLoc targetCrv = 'CT_teethBlockDrv_crv_0' jnts=[] for jntId in range(locNum): mc.select(cl=True) jnt = mc.joint(n=name+'_bnd_jnt_%d'%jntId) mc.setAttr(jnt+'.radius', 0.5) rt.alignOnMotionPath(targetCrv, float(jntId)/(locNum-1), jnt, name+'_aim_loc_%d.matrix'%jntId, True, ua=1, inverseUp=True) jnts.append(jnt) mc.group(jnts, n='CT_teethBlock_bnd_jnts_grp') rt.connectVisibilityToggle(jnts, globalGrp, 'bndJnts', False) jnts = mc.ls(sl=True) import utils.wrappers.abRiggingTools as abRT # add bnd jnts for bite # for each bndjnt, make another jnt below it for bndJnt in jnts: mc.select(cl=True) biteJnt = mc.joint(n=bndJnt+'_bite') mc.setAttr(biteJnt+'.radius', 0.5) abRT.snapToPosition(bndJnt, biteJnt) mc.xform(biteJnt, t=(0,-1,0), r=True) mc.parentConstraint(bndJnt, biteJnt, mo=True) mc.parent(biteJnt, 'CT_teethBlock_bnd_jnts_grp')
def createDfmSys(nodeName, drvLocs, ctls, geo): ''' drvLocs are used to drive baseCrv ctls are used to drive wireCrv wire deforms geo return dfmSysGrp ''' # create baseCrv baseCrv = rt.makeCrvThroughObjs(drvLocs, nodeName+'_wireOffset_baseCrv', True, 3) # create wireCrv wireCrv = rt.makeCrvThroughObjs(ctls, nodeName+'_wireOffset_wireCrv', True, 3) # create wireDfm wireDfm, wireCrv = mc.wire(geo, wire=wireCrv, n=nodeName+'_wireOffset_wireDfm', dds=(0,5)) wireBaseUnwanted = wireCrv+'BaseWire' # replace base mc.connectAttr(baseCrv+'.worldSpace[0]', wireDfm+'.baseWire[0]', f=True) mc.delete(wireBaseUnwanted) # create dfmSysGrp dfmSysGrp = mc.group(baseCrv, wireCrv, n=nodeName+'_wireOffset_dfmSys_grp') rt.connectVisibilityToggle(wireCrv, dfmSysGrp, 'wireCrvVis', False) rt.connectVisibilityToggle(baseCrv, dfmSysGrp, 'baseCrvVis', False) mc.addAttr(dfmSysGrp, ln='envelope', at='double', k=True, dv=1) mc.addAttr(dfmSysGrp, ln='dropoff', at='double', k=True, dv=5) mc.addAttr(dfmSysGrp, ln='rotation', at='double', k=True, dv=0) mc.connectAttr(dfmSysGrp+'.envelope', wireDfm+'.envelope', f=True) mc.connectAttr(dfmSysGrp+'.dropoff', wireDfm+'.dds[0]', f=True) mc.connectAttr(dfmSysGrp+'.rotation', wireDfm+'.rotation', f=True) mc.addAttr(dfmSysGrp, ln='enabled', at='bool', k=True, dv=True) rt.connectSDK(dfmSysGrp+'.enabled', wireDfm+'.nodeState', {1:0, 0:2}) return dfmSysGrp
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
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
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
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)
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
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
def createSplineMPs(MPs, newMPsNum, name, twistOffset): ''' creates spline along mps adds new MPs along spline twistOffset - vector to offset twistCrv returns [(drvCrv, twistCrv, oldDrvCrv), MPJnts] ''' numOfMPs = len(MPs) # create twist curve for aiming locs twistCrv = mc.curve(p=[(pt,pt,pt) for pt in range(numOfMPs)]) twistCrv = mc.rename(twistCrv, name+'_twist_crv') # get point from MPs to drive curve CVs for mp in MPs: pmm = mc.createNode('pointMatrixMult', n=mp+'_pmm_0') mc.connectAttr(mp+'.worldMatrix', pmm+'.inMatrix', f=True) mc.setAttr(pmm+'.inPoint', *twistOffset) mc.connectAttr(pmm+'.output', twistCrv+'.cp[%d]' % MPs.index(mp)) # create driver curve for locs drvCrv = mc.curve(p=[(pt,pt,pt) for pt in range(numOfMPs)]) drvCrv = mc.rename(drvCrv, name+'_drv_crv') # get point from MPs to drive curve CVs for mp in MPs: pmm = mc.createNode('pointMatrixMult', n=mp+'_pmm_0') mc.connectAttr(mp+'.worldMatrix', pmm+'.inMatrix', f=True) mc.connectAttr(pmm+'.output', drvCrv+'.cp[%d]' % MPs.index(mp)) # make driver curve uniform oldDrvCrv = drvCrv drvCrv = rt.makeUniformCrv(drvCrv, newMPsNum, name+'_uniform_crv') # add aimLocs to twistCurve aimLocs = [] for locId in range(newMPsNum): loc = mc.spaceLocator(n=name+'_aimLoc_%d' % locId)[0] rt.attachToMotionPath(twistCrv, float(locId)/(newMPsNum-1), loc, 1) mc.setAttr(loc+'.localScale', 0.1,0.1,0.1) aimLocs.append(loc) aimLocsGrp = mc.group(aimLocs, n='CT_%s_aimLocs_grp'%name) MPJnts = [] for jntId in range(newMPsNum): jnt = mc.joint(n=name+'_MPJnt_%d' % jntId) rt.alignOnMotionPath(drvCrv, float(jntId)/(newMPsNum-1), jnt, aimLocs[jntId]+'.worldMatrix', 1) MPJnts.append(jnt) MPJntsGrp = mc.group(MPJnts, n='CT_%s_MPJnts_grp'%name) # connect viz for debug rt.connectVisibilityToggle(oldDrvCrv, MPs[0], 'drvCrv', default=False) rt.connectVisibilityToggle((aimLocsGrp, twistCrv), MPs[0], 'twistLocs', default=False) rt.connectVisibilityToggle((MPJntsGrp, drvCrv), MPs[0], 'drvMPs', default=False) mc.group(oldDrvCrv, twistCrv, drvCrv, aimLocsGrp, MPJntsGrp, n=name+'_splineMPs_grp') return (drvCrv, twistCrv, oldDrvCrv), MPJnts
def makeDynamicWire(jnts, geo, name, ctl): ''' ''' # make curve through joints startCrv = rt.makeCrvThroughObjs(jnts, name+'_startCrv', True, 2) # make curve dynamic mc.select(startCrv, r=True) meval('makeCurvesDynamic 2 { "0", "0", "1", "1", "0"};') # get a handle on new (badly named) nodes hairSys = mc.ls(sl=True)[0] foll = mc.listConnections(hairSys+'.outputHair[0]', d=True)[0] dynCrv = mc.listConnections(foll+'.outCurve', d=True)[0] # rename nodes properly hairSys = mc.rename(hairSys, name+'_hairSysShape') foll = mc.rename(foll, name+'_foll') dynCrv = mc.rename(dynCrv, name+'_dynCrv') # rename group and transform nodes as well hairSysTransform = mc.listRelatives(hairSys, p=True)[0] hairSysTransform = mc.rename(hairSysTransform, name+'_hairSys') startGrp = mc.listRelatives(foll, p=True)[0] startGrp = mc.rename(startGrp, name+'_hairSysFollicles') outputGrp = mc.listRelatives(dynCrv, p=True)[0] outputGrp = mc.rename(outputGrp, name+'_hairSysOutputCrvs') # since we now have start curve and end curve, we can make the wire deformer wireDfm, wireCrv = mc.wire(geo, wire=dynCrv, n=name+'_dyn_wireDfm', dds=(0,50)) wireBaseUnwanted = wireCrv+'BaseWire' # replace base mc.connectAttr(startCrv+'.worldSpace[0]', wireDfm+'.baseWire[0]', f=True) mc.delete(wireBaseUnwanted) # group nodes nicely masterGrp = mc.group(hairSysTransform, startGrp, outputGrp, n=name+'_hairSysMaster_grp') rt.connectVisibilityToggle([startGrp, outputGrp], masterGrp, 'curvesVis', False) rt.connectVisibilityToggle(hairSysTransform, masterGrp, 'hairSysVis', False) # attributes on ctl mc.addAttr(ctl, ln='tDynamics', nn='DYNAMICS', at='enum', en='-----', k=True) #mc.setAttr(ctl+'.tDynamics', l=True) mc.addAttr(ctl, ln='enabled', at='bool', dv=True, k=True) mc.addAttr(ctl, ln='weight', at='double', min=0, max=1, dv=1, k=True) mc.connectAttr(ctl+'.weight', wireDfm+'.envelope', f=True) rt.connectSDK(ctl+'.enabled', hairSys+'.simulationMethod', {0:0, 1:3}) # expose follicle attributes to ctl mc.addAttr(ctl, ln='pointLock', at='enum', en='No Attach:Base:Tip:Both Ends', k=True, dv=1) mc.connectAttr(ctl+'.pointLock', foll+'.pointLock', f=True) # expose hairSystem attributes to ctl mc.addAttr(ctl, ln='startCurveAttract', at='double', min=0, max=1, dv=0.05, k=True) mc.addAttr(ctl, ln='mass', at='double', min=0, dv=1, k=True) mc.addAttr(ctl, ln='drag', at='double', min=0, dv=0.05, k=True) mc.addAttr(ctl, ln='damp', at='double', min=0, dv=0, k=True) mc.connectAttr(ctl+'.startCurveAttract', hairSys+'.startCurveAttract', f=True) mc.connectAttr(ctl+'.mass', hairSys+'.mass', f=True) mc.connectAttr(ctl+'.drag', hairSys+'.drag', f=True) mc.connectAttr(ctl+'.damp', hairSys+'.damp', f=True)
def makeDynamicWire(jnts, geo, name, ctl): ''' ''' # make curve through joints startCrv = rt.makeCrvThroughObjs(jnts, name + '_startCrv', True, 2) # make curve dynamic mc.select(startCrv, r=True) meval('makeCurvesDynamic 2 { "0", "0", "1", "1", "0"};') # get a handle on new (badly named) nodes hairSys = mc.ls(sl=True)[0] foll = mc.listConnections(hairSys + '.outputHair[0]', d=True)[0] dynCrv = mc.listConnections(foll + '.outCurve', d=True)[0] # rename nodes properly hairSys = mc.rename(hairSys, name + '_hairSysShape') foll = mc.rename(foll, name + '_foll') dynCrv = mc.rename(dynCrv, name + '_dynCrv') # rename group and transform nodes as well hairSysTransform = mc.listRelatives(hairSys, p=True)[0] hairSysTransform = mc.rename(hairSysTransform, name + '_hairSys') startGrp = mc.listRelatives(foll, p=True)[0] startGrp = mc.rename(startGrp, name + '_hairSysFollicles') outputGrp = mc.listRelatives(dynCrv, p=True)[0] outputGrp = mc.rename(outputGrp, name + '_hairSysOutputCrvs') # since we now have start curve and end curve, we can make the wire deformer wireDfm, wireCrv = mc.wire(geo, wire=dynCrv, n=name + '_dyn_wireDfm', dds=(0, 50)) wireBaseUnwanted = wireCrv + 'BaseWire' # replace base mc.connectAttr(startCrv + '.worldSpace[0]', wireDfm + '.baseWire[0]', f=True) mc.delete(wireBaseUnwanted) # group nodes nicely masterGrp = mc.group(hairSysTransform, startGrp, outputGrp, n=name + '_hairSysMaster_grp') rt.connectVisibilityToggle([startGrp, outputGrp], masterGrp, 'curvesVis', False) rt.connectVisibilityToggle(hairSysTransform, masterGrp, 'hairSysVis', False) # attributes on ctl mc.addAttr(ctl, ln='tDynamics', nn='DYNAMICS', at='enum', en='-----', k=True) #mc.setAttr(ctl+'.tDynamics', l=True) mc.addAttr(ctl, ln='enabled', at='bool', dv=True, k=True) mc.addAttr(ctl, ln='weight', at='double', min=0, max=1, dv=1, k=True) mc.connectAttr(ctl + '.weight', wireDfm + '.envelope', f=True) rt.connectSDK(ctl + '.enabled', hairSys + '.simulationMethod', { 0: 0, 1: 3 }) # expose follicle attributes to ctl mc.addAttr(ctl, ln='pointLock', at='enum', en='No Attach:Base:Tip:Both Ends', k=True, dv=1) mc.connectAttr(ctl + '.pointLock', foll + '.pointLock', f=True) # expose hairSystem attributes to ctl mc.addAttr(ctl, ln='startCurveAttract', at='double', min=0, max=1, dv=0.05, k=True) mc.addAttr(ctl, ln='mass', at='double', min=0, dv=1, k=True) mc.addAttr(ctl, ln='drag', at='double', min=0, dv=0.05, k=True) mc.addAttr(ctl, ln='damp', at='double', min=0, dv=0, k=True) mc.connectAttr(ctl + '.startCurveAttract', hairSys + '.startCurveAttract', f=True) mc.connectAttr(ctl + '.mass', hairSys + '.mass', f=True) mc.connectAttr(ctl + '.drag', hairSys + '.drag', f=True) mc.connectAttr(ctl + '.damp', hairSys + '.damp', f=True)
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