def cycleTweak(name, edgePair, mirrorAxis, baseMesh, rotMesh, transMesh, setupParent, ctlParent, jntOrg=None, grp=None, iconType="square", size=.025, color=13, ro=datatypes.Vector(1.5708, 0, 1.5708 / 2)): """The command to create a cycle tweak. A cycle tweak is a tweak that cycles to the parent position but doesn't create a cycle of dependency. This type of tweaks are very useful to create facial tweakers. Args: name (string): Name for the cycle tweak edgePair (list): List of edge pair to attach the cycle tweak mirrorAxis (bool): If true, will mirror the x axis behaviour. baseMesh (Mesh): The base mesh for the cycle tweak. rotMesh (Mesh): The mesh that will support the rotation transformations for the cycle tweak transMesh (Mesh): The mesh that will support the translation and scale transformations for the cycle tweak setupParent (dagNode): The parent for the setup objects ctlParent (dagNode): The parent for the control objects jntOrg (None or dagNode, optional): The parent for the joints grp (None or set, optional): The set to add the controls iconType (str, optional): The controls shape size (float, optional): The control size color (int, optional): The control color ro (TYPE, optional): The control shape rotation offset Returns: multi: the tweak control and the list of related joints. """ # rotation sctructure rRivet = rivet.rivet() rBase = rRivet.create( baseMesh, edgePair[0], edgePair[1], setupParent, name + "_rRivet_loc") pos = rBase.getTranslation(space="world") # translation structure tRivetParent = pm.createNode("transform", n=name + "_tRivetBase", p=ctlParent) tRivetParent.setMatrix(datatypes.Matrix(), worldSpace=True) tRivet = rivet.rivet() tBase = tRivet.create(transMesh, edgePair[0], edgePair[1], tRivetParent, name + "_tRivet_loc") # create the control tweakBase = pm.createNode("transform", n=name + "_tweakBase", p=ctlParent) tweakBase.setMatrix(datatypes.Matrix(), worldSpace=True) tweakNpo = pm.createNode("transform", n=name + "_tweakNpo", p=tweakBase) tweakBase.setTranslation(pos, space="world") tweakCtl = icon.create(tweakNpo, name + "_ctl", tweakNpo.getMatrix(worldSpace=True), color, iconType, w=size, d=size, ro=ro) inverseTranslateParent(tweakCtl) pm.pointConstraint(tBase, tweakBase) # rot rotBase = pm.createNode("transform", n=name + "_rotBase", p=setupParent) rotBase.setMatrix(datatypes.Matrix(), worldSpace=True) rotNPO = pm.createNode("transform", n=name + "_rot_npo", p=rotBase) rotJointDriver = pm.createNode("transform", n=name + "_rotJointDriver", p=rotNPO) rotBase.setTranslation(pos, space="world") node.createMulNode([rotNPO.attr("tx"), rotNPO.attr("ty"), rotNPO.attr("tz")], [-1, -1, -1], [rotJointDriver.attr("tx"), rotJointDriver.attr("ty"), rotJointDriver.attr("tz")]) pm.pointConstraint(rBase, rotNPO) pm.connectAttr(tweakCtl.r, rotNPO.r) pm.connectAttr(tweakCtl.s, rotNPO.s) # transform posNPO = pm.createNode("transform", n=name + "_pos_npo", p=setupParent) posJointDriver = pm.createNode("transform", n=name + "_posJointDriver", p=posNPO) posNPO.setTranslation(pos, space="world") pm.connectAttr(tweakCtl.t, posJointDriver.t) # mirror behaviour if mirrorAxis: tweakBase.attr("ry").set(tweakBase.attr("ry").get() + 180) rotBase.attr("ry").set(rotBase.attr("ry").get() + 180) posNPO.attr("ry").set(posNPO.attr("ry").get() + 180) tweakBase.attr("sz").set(-1) rotBase.attr("sz").set(-1) posNPO.attr("sz").set(-1) # create joints rJoint = rigbits.addJnt(rotJointDriver, jntOrg, True, grp) tJoint = rigbits.addJnt(posJointDriver, jntOrg, True, grp) # add to rotation skin # TODO: add checker to see if joint is in the skincluster. rSK = skin.getSkinCluster(rotMesh) pm.skinCluster(rSK, e=True, ai=rJoint, lw=True, wt=0) # add to transform skin # TODO: add checker to see if joint is in the skincluster. tSK = skin.getSkinCluster(transMesh) pm.skinCluster(tSK, e=True, ai=tJoint, lw=True, wt=0) return tweakCtl, [rJoint, tJoint]
def rig(edge_loop="", up_vertex="", low_vertex="", name_prefix="", thickness=0.3, do_skin=True, rigid_loops=5, falloff_loops=8, head_joint=None, jaw_joint=None, parent_node=None, control_name="ctl", upper_lip_ctl=None, lower_lip_ctl=None): ###### # Var ###### FRONT_OFFSET = .02 NB_ROPE = 15 ################## # Helper functions ################## def setName(name, side="C", idx=None): namesList = [name_prefix, side, name] if idx is not None: namesList[1] = side + str(idx) name = "_".join(namesList) return name ############### # Checkers ############## # Loop if edge_loop: try: edge_loop = [pm.PyNode(e) for e in edge_loop.split(",")] except pm.MayaNodeError: pm.displayWarning( "Some of the edges listed in edge loop can not be found") return else: pm.displayWarning("Please set the edge loop first") return # Vertex if up_vertex: try: up_vertex = pm.PyNode(up_vertex) except pm.MayaNodeError: pm.displayWarning("%s can not be found" % up_vertex) return else: pm.displayWarning("Please set the upper lip central vertex") return if low_vertex: try: low_vertex = pm.PyNode(low_vertex) except pm.MayaNodeError: pm.displayWarning("%s can not be found" % low_vertex) return else: pm.displayWarning("Please set the lower lip central vertex") return # skinnign data if do_skin: if not head_joint: pm.displayWarning("Please set the Head Jnt or unCheck Compute " "Topological Autoskin") return else: try: head_joint = pm.PyNode(head_joint) except pm.MayaNodeError: pm.displayWarning( "Head Joint: %s can not be found" % head_joint ) return if not jaw_joint: pm.displayWarning("Please set the Jaw Jnt or unCheck Compute " "Topological Autoskin") return else: try: jaw_joint = pm.PyNode(jaw_joint) except pm.MayaNodeError: pm.displayWarning("Jaw Joint: %s can not be found" % jaw_joint) return # check if the rig already exist in the current scene if pm.ls(setName("root")): pm.displayWarning("The object %s already exist in the scene. Please " "choose another name prefix" % setName("root")) return ##################### # Root creation ##################### lips_root = primitive.addTransform(None, setName("root")) lipsCrv_root = primitive.addTransform(lips_root, setName("crvs")) lipsRope_root = primitive.addTransform(lips_root, setName("rope")) ##################### # Geometry ##################### geo = pm.listRelatives(edge_loop[0], parent=True)[0] ##################### # Groups ##################### try: ctlSet = pm.PyNode("rig_controllers_grp") except pm.MayaNodeError: pm.sets(n="rig_controllers_grp", em=True) ctlSet = pm.PyNode("rig_controllers_grp") try: defset = pm.PyNode("rig_deformers_grp") except pm.MayaNodeError: pm.sets(n="rig_deformers_grp", em=True) defset = pm.PyNode("rig_deformers_grp") ##################### # Curves creation ##################### # get extreme position using the outer loop extr_v = meshNavigation.getExtremeVertexFromLoop(edge_loop) upPos = extr_v[0] lowPos = extr_v[1] inPos = extr_v[2] outPos = extr_v[3] edgeList = extr_v[4] vertexList = extr_v[5] upPos = up_vertex lowPos = low_vertex # upper crv upLip_edgeRange = meshNavigation.edgeRangeInLoopFromMid(edgeList, upPos, inPos, outPos) upCrv = curve.createCuveFromEdges(upLip_edgeRange, setName("upperLip"), parent=lipsCrv_root) # store the closest vertex by curv cv index. To be use fo the auto skining upLip_closestVtxList = [] # offset upper lip Curve cvs = upCrv.getCVs(space="world") for i, cv in enumerate(cvs): closestVtx = meshNavigation.getClosestVertexFromTransform(geo, cv) upLip_closestVtxList.append(closestVtx) if i == 0: # we know the curv starts from right to left offset = [cv[0] - thickness, cv[1], cv[2] - thickness] elif i == len(cvs) - 1: offset = [cv[0] + thickness, cv[1], cv[2] - thickness] else: offset = [cv[0], cv[1] + thickness, cv[2]] upCrv.setCV(i, offset, space='world') # lower crv lowLip_edgeRange = meshNavigation.edgeRangeInLoopFromMid(edgeList, lowPos, inPos, outPos) lowCrv = curve.createCuveFromEdges(lowLip_edgeRange, setName("lowerLip"), parent=lipsCrv_root) lowLip_closestVtxList = [] # offset lower lip Curve cvs = lowCrv.getCVs(space="world") for i, cv in enumerate(cvs): closestVtx = meshNavigation.getClosestVertexFromTransform(geo, cv) lowLip_closestVtxList.append(closestVtx) if i == 0: # we know the curv starts from right to left offset = [cv[0] - thickness, cv[1], cv[2] - thickness] elif i == len(cvs) - 1: offset = [cv[0] + thickness, cv[1], cv[2] - thickness] else: # we populate the closest vertext list here to skipt the first # and latest point offset = [cv[0], cv[1] - thickness, cv[2]] lowCrv.setCV(i, offset, space='world') upCrv_ctl = curve.createCurveFromCurve(upCrv, setName("upCtl_crv"), nbPoints=7, parent=lipsCrv_root) lowCrv_ctl = curve.createCurveFromCurve(lowCrv, setName("lowCtl_crv"), nbPoints=7, parent=lipsCrv_root) upRope = curve.createCurveFromCurve(upCrv, setName("upRope_crv"), nbPoints=NB_ROPE, parent=lipsCrv_root) lowRope = curve.createCurveFromCurve(lowCrv, setName("lowRope_crv"), nbPoints=NB_ROPE, parent=lipsCrv_root) upCrv_upv = curve.createCurveFromCurve(upCrv, setName("upCrv_upv"), nbPoints=7, parent=lipsCrv_root) lowCrv_upv = curve.createCurveFromCurve(lowCrv, setName("lowCrv_upv"), nbPoints=7, parent=lipsCrv_root) upRope_upv = curve.createCurveFromCurve(upCrv, setName("upRope_upv"), nbPoints=NB_ROPE, parent=lipsCrv_root) lowRope_upv = curve.createCurveFromCurve(lowCrv, setName("lowRope_upv"), nbPoints=NB_ROPE, parent=lipsCrv_root) # offset upv curves for crv in [upCrv_upv, lowCrv_upv, upRope_upv, lowRope_upv]: cvs = crv.getCVs(space="world") for i, cv in enumerate(cvs): # we populate the closest vertext list here to skipt the first # and latest point offset = [cv[0], cv[1], cv[2] + FRONT_OFFSET] crv.setCV(i, offset, space='world') rigCrvs = [upCrv, lowCrv, upCrv_ctl, lowCrv_ctl, upRope, lowRope, upCrv_upv, lowCrv_upv, upRope_upv, lowRope_upv] for crv in rigCrvs: crv.attr("visibility").set(False) ################## # Joints ################## lvlType = "transform" # upper joints upperJoints = [] cvs = upCrv.getCVs(space="world") pm.progressWindow(title='Creating Upper Joints', progress=0, max=len(cvs)) for i, cv in enumerate(cvs): pm.progressWindow(e=True, step=1, status='\nCreating Joint for %s' % cv) oTransUpV = pm.PyNode(pm.createNode( lvlType, n=setName("upLipRopeUpv", idx=str(i).zfill(3)), p=lipsRope_root, ss=True)) oTrans = pm.PyNode( pm.createNode(lvlType, n=setName("upLipRope", idx=str(i).zfill(3)), p=lipsRope_root, ss=True)) oParam, oLength = curve.getCurveParamAtPosition(upRope, cv) uLength = curve.findLenghtFromParam(upRope, oParam) u = uLength / oLength applyop.pathCns( oTransUpV, upRope_upv, cnsType=False, u=u, tangent=False) cns = applyop.pathCns( oTrans, upRope, cnsType=False, u=u, tangent=False) cns.setAttr("worldUpType", 1) cns.setAttr("frontAxis", 0) cns.setAttr("upAxis", 1) pm.connectAttr(oTransUpV.attr("worldMatrix[0]"), cns.attr("worldUpMatrix")) # getting joint parent if head_joint and isinstance(head_joint, (str, string_types)): try: j_parent = pm.PyNode(head_joint) except pm.MayaNodeError: j_parent = False elif head_joint and isinstance(head_joint, pm.PyNode): j_parent = head_joint else: j_parent = False jnt = rigbits.addJnt(oTrans, noReplace=True, parent=j_parent) upperJoints.append(jnt) pm.sets(defset, add=jnt) pm.progressWindow(e=True, endProgress=True) # lower joints lowerJoints = [] cvs = lowCrv.getCVs(space="world") pm.progressWindow(title='Creating Lower Joints', progress=0, max=len(cvs)) for i, cv in enumerate(cvs): pm.progressWindow(e=True, step=1, status='\nCreating Joint for %s' % cv) oTransUpV = pm.PyNode(pm.createNode( lvlType, n=setName("lowLipRopeUpv", idx=str(i).zfill(3)), p=lipsRope_root, ss=True)) oTrans = pm.PyNode(pm.createNode( lvlType, n=setName("lowLipRope", idx=str(i).zfill(3)), p=lipsRope_root, ss=True)) oParam, oLength = curve.getCurveParamAtPosition(lowRope, cv) uLength = curve.findLenghtFromParam(lowRope, oParam) u = uLength / oLength applyop.pathCns(oTransUpV, lowRope_upv, cnsType=False, u=u, tangent=False) cns = applyop.pathCns(oTrans, lowRope, cnsType=False, u=u, tangent=False) cns.setAttr("worldUpType", 1) cns.setAttr("frontAxis", 0) cns.setAttr("upAxis", 1) pm.connectAttr(oTransUpV.attr("worldMatrix[0]"), cns.attr("worldUpMatrix")) # getting joint parent if jaw_joint and isinstance(jaw_joint, (str, string_types)): try: j_parent = pm.PyNode(jaw_joint) except pm.MayaNodeError: pass elif jaw_joint and isinstance(jaw_joint, pm.PyNode): j_parent = jaw_joint else: j_parent = False jnt = rigbits.addJnt(oTrans, noReplace=True, parent=j_parent) lowerJoints.append(jnt) pm.sets(defset, add=jnt) pm.progressWindow(e=True, endProgress=True) ################## # Controls ################## # Controls lists upControls = [] upVec = [] upNpo = [] lowControls = [] lowVec = [] lowNpo = [] # controls options axis_list = ["sx", "sy", "sz", "ro"] upCtlOptions = [["corner", "R", "square", 4, .05, axis_list], ["upOuter", "R", "circle", 14, .03, []], ["upInner", "R", "circle", 14, .03, []], ["upper", "C", "square", 4, .05, axis_list], ["upInner", "L", "circle", 14, .03, []], ["upOuter", "L", "circle", 14, .03, []], ["corner", "L", "square", 4, .05, axis_list]] lowCtlOptions = [["lowOuter", "R", "circle", 14, .03, []], ["lowInner", "R", "circle", 14, .03, []], ["lower", "C", "square", 4, .05, axis_list], ["lowInner", "L", "circle", 14, .03, []], ["lowOuter", "L", "circle", 14, .03, []]] params = ["tx", "ty", "tz", "rx", "ry", "rz"] # upper controls cvs = upCrv_ctl.getCVs(space="world") pm.progressWindow(title='Upper controls', progress=0, max=len(cvs)) v0 = transform.getTransformFromPos(cvs[0]) v1 = transform.getTransformFromPos(cvs[-1]) distSize = vector.getDistance(v0, v1) * 3 for i, cv in enumerate(cvs): pm.progressWindow(e=True, step=1, status='\nCreating control for%s' % cv) t = transform.getTransformFromPos(cv) # Get nearest joint for orientation of controls joints = upperJoints + lowerJoints nearest_joint = None nearest_distance = None for joint in joints: distance = vector.getDistance( transform.getTranslation(joint), cv ) if nearest_distance is None or distance < nearest_distance: nearest_distance = distance nearest_joint = joint if nearest_joint: t = transform.setMatrixPosition( transform.getTransform(nearest_joint), cv ) temp = primitive.addTransform( lips_root, setName("temp"), t ) temp.rx.set(0) t = transform.getTransform(temp) pm.delete(temp) oName = upCtlOptions[i][0] oSide = upCtlOptions[i][1] o_icon = upCtlOptions[i][2] color = upCtlOptions[i][3] wd = upCtlOptions[i][4] oPar = upCtlOptions[i][5] npo = primitive.addTransform(lips_root, setName("%s_npo" % oName, oSide), t) upNpo.append(npo) ctl = icon.create(npo, setName("%s_%s" % (oName, control_name), oSide), t, icon=o_icon, w=wd * distSize, d=wd * distSize, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, .07 * distSize), color=color) upControls.append(ctl) name_split = control_name.split("_") if len(name_split) == 2 and name_split[-1] == "ghost": pass else: pm.sets(ctlSet, add=ctl) attribute.addAttribute(ctl, "isCtl", "bool", keyable=False) attribute.setKeyableAttributes(ctl, params + oPar) upv = primitive.addTransform(ctl, setName("%s_upv" % oName, oSide), t) upv.attr("tz").set(FRONT_OFFSET) upVec.append(upv) if oSide == "R": npo.attr("sx").set(-1) pm.progressWindow(e=True, endProgress=True) # lower controls cvs = lowCrv_ctl.getCVs(space="world") pm.progressWindow(title='Lower controls', progress=0, max=len(cvs)) for i, cv in enumerate(cvs[1:-1]): pm.progressWindow(e=True, step=1, status='\nCreating control for%s' % cv) t = transform.getTransformFromPos(cv) # Get nearest joint for orientation of controls joints = upperJoints + lowerJoints nearest_joint = None nearest_distance = None for joint in joints: distance = vector.getDistance( transform.getTranslation(joint), cv ) if nearest_distance is None or distance < nearest_distance: nearest_distance = distance nearest_joint = joint if nearest_joint: t = transform.setMatrixPosition( transform.getTransform(nearest_joint), cv ) temp = primitive.addTransform( lips_root, setName("temp"), t ) temp.rx.set(0) t = transform.getTransform(temp) pm.delete(temp) oName = lowCtlOptions[i][0] oSide = lowCtlOptions[i][1] o_icon = lowCtlOptions[i][2] color = lowCtlOptions[i][3] wd = lowCtlOptions[i][4] oPar = lowCtlOptions[i][5] npo = primitive.addTransform(lips_root, setName("%s_npo" % oName, oSide), t) lowNpo.append(npo) ctl = icon.create(npo, setName("%s_%s" % (oName, control_name), oSide), t, icon=o_icon, w=wd * distSize, d=wd * distSize, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, .07 * distSize), color=color) lowControls.append(ctl) name_split = control_name.split("_") if len(name_split) == 2 and control_name.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=ctl) attribute.addAttribute(ctl, "isCtl", "bool", keyable=False) attribute.setKeyableAttributes(ctl, params + oPar) upv = primitive.addTransform(ctl, setName("%s_upv" % oName, oSide), t) upv.attr("tz").set(FRONT_OFFSET) lowVec.append(upv) if oSide == "R": npo.attr("sx").set(-1) pm.progressWindow(e=True, endProgress=True) # reparentig controls pm.parent(upNpo[1], lowNpo[0], upControls[0]) pm.parent(upNpo[2], upNpo[4], upControls[3]) pm.parent(upNpo[-2], lowNpo[-1], upControls[-1]) pm.parent(lowNpo[1], lowNpo[3], lowControls[2]) # Connecting control crvs with controls applyop.gear_curvecns_op(upCrv_ctl, upControls) applyop.gear_curvecns_op(lowCrv_ctl, [upControls[0]] + lowControls + [upControls[-1]]) applyop.gear_curvecns_op(upCrv_upv, upVec) applyop.gear_curvecns_op(lowCrv_upv, [upVec[0]] + lowVec + [upVec[-1]]) # adding wires pm.wire(upCrv, w=upCrv_ctl, dropoffDistance=[0, 1000]) pm.wire(lowCrv, w=lowCrv_ctl, dropoffDistance=[0, 1000]) pm.wire(upRope, w=upCrv_ctl, dropoffDistance=[0, 1000]) pm.wire(lowRope, w=lowCrv_ctl, dropoffDistance=[0, 1000]) pm.wire(upRope_upv, w=upCrv_upv, dropoffDistance=[0, 1000]) pm.wire(lowRope_upv, w=lowCrv_upv, dropoffDistance=[0, 1000]) # setting constrains # up cns_node = pm.parentConstraint(upControls[0], upControls[3], upControls[1].getParent(), mo=True, skipRotate=["x", "y", "z"]) cns_node.attr(upControls[0].name() + "W0").set(.75) cns_node.attr(upControls[3].name() + "W1").set(.25) cns_node.interpType.set(0) # noFlip cns_node = pm.parentConstraint(upControls[0], upControls[3], upControls[2].getParent(), mo=True, skipRotate=["x", "y", "z"]) cns_node.attr(upControls[0].name() + "W0").set(.25) cns_node.attr(upControls[3].name() + "W1").set(.75) cns_node.interpType.set(0) # noFlip cns_node = pm.parentConstraint(upControls[3], upControls[6], upControls[4].getParent(), mo=True, skipRotate=["x", "y", "z"]) cns_node.attr(upControls[3].name() + "W0").set(.75) cns_node.attr(upControls[6].name() + "W1").set(.25) cns_node.interpType.set(0) # noFlip cns_node = pm.parentConstraint(upControls[3], upControls[6], upControls[5].getParent(), mo=True, skipRotate=["x", "y", "z"]) cns_node.attr(upControls[3].name() + "W0").set(.25) cns_node.attr(upControls[6].name() + "W1").set(.75) cns_node.interpType.set(0) # noFlip # low cns_node = pm.parentConstraint(upControls[0], lowControls[2], lowControls[0].getParent(), mo=True, skipRotate=["x", "y", "z"]) cns_node.attr(upControls[0].name() + "W0").set(.75) cns_node.attr(lowControls[2].name() + "W1").set(.25) cns_node.interpType.set(0) # noFlip cns_node = pm.parentConstraint(upControls[0], lowControls[2], lowControls[1].getParent(), mo=True, skipRotate=["x", "y", "z"]) cns_node.attr(upControls[0].name() + "W0").set(.25) cns_node.attr(lowControls[2].name() + "W1").set(.75) cns_node.interpType.set(0) # noFlip cns_node = pm.parentConstraint(lowControls[2], upControls[6], lowControls[3].getParent(), mo=True, skipRotate=["x", "y", "z"]) cns_node.attr(lowControls[2].name() + "W0").set(.75) cns_node.attr(upControls[6].name() + "W1").set(.25) cns_node.interpType.set(0) # noFlip cns_node = pm.parentConstraint(lowControls[2], upControls[6], lowControls[4].getParent(), mo=True, skipRotate=["x", "y", "z"]) cns_node.attr(lowControls[2].name() + "W0").set(.25) cns_node.attr(upControls[6].name() + "W1").set(.75) cns_node.interpType.set(0) # noFlip ########################################### # Connecting rig ########################################### if parent_node: try: if isinstance(parent_node, string_types): parent_node = pm.PyNode(parent_node) parent_node.addChild(lips_root) except pm.MayaNodeError: pm.displayWarning("The Lips rig can not be parent to: %s. Maybe " "this object doesn't exist." % parent_node) if head_joint and jaw_joint: try: if isinstance(head_joint, string_types): head_joint = pm.PyNode(head_joint) except pm.MayaNodeError: pm.displayWarning("Head Joint or Upper Lip Joint %s. Can not be " "fount in the scene" % head_joint) return try: if isinstance(jaw_joint, string_types): jaw_joint = pm.PyNode(jaw_joint) except pm.MayaNodeError: pm.displayWarning("Jaw Joint or Lower Lip Joint %s. Can not be " "fount in the scene" % jaw_joint) return ref_ctls = [head_joint, jaw_joint] if upper_lip_ctl and lower_lip_ctl: try: if isinstance(upper_lip_ctl, string_types): upper_lip_ctl = pm.PyNode(upper_lip_ctl) except pm.MayaNodeError: pm.displayWarning("Upper Lip Ctl %s. Can not be " "fount in the scene" % upper_lip_ctl) return try: if isinstance(lower_lip_ctl, string_types): lower_lip_ctl = pm.PyNode(lower_lip_ctl) except pm.MayaNodeError: pm.displayWarning("Lower Lip Ctl %s. Can not be " "fount in the scene" % lower_lip_ctl) return ref_ctls = [upper_lip_ctl, lower_lip_ctl] # in order to avoid flips lets create a reference transform # also to avoid flips, set any multi target parentConstraint to noFlip ref_cns_list = [] print (ref_ctls) for cns_ref in ref_ctls: t = transform.getTransformFromPos( cns_ref.getTranslation(space='world')) ref = pm.createNode("transform", n=cns_ref.name() + "_cns", p=cns_ref, ss=True) ref.setMatrix(t, worldSpace=True) ref_cns_list.append(ref) # right corner connection cns_node = pm.parentConstraint(ref_cns_list[0], ref_cns_list[1], upControls[0].getParent(), mo=True) cns_node.interpType.set(0) # noFlip # left corner connection cns_node = pm.parentConstraint(ref_cns_list[0], ref_cns_list[1], upControls[-1].getParent(), mo=True) cns_node.interpType.set(0) # noFlip # up control connection cns_node = pm.parentConstraint(ref_cns_list[0], upControls[3].getParent(), mo=True) # low control connection cns_node = pm.parentConstraint(ref_cns_list[1], lowControls[2].getParent(), mo=True) ########################################### # Auto Skinning ########################################### if do_skin: # eyelid vertex rows totalLoops = rigid_loops + falloff_loops vertexLoopList = meshNavigation.getConcentricVertexLoop(vertexList, totalLoops) vertexRowList = meshNavigation.getVertexRowsFromLoops(vertexLoopList) # we set the first value 100% for the first initial loop skinPercList = [1.0] # we expect to have a regular grid topology for r in range(rigid_loops): for rr in range(2): skinPercList.append(1.0) increment = 1.0 / float(falloff_loops) # we invert to smooth out from 100 to 0 inv = 1.0 - increment for r in range(falloff_loops): for rr in range(2): if inv < 0.0: inv = 0.0 skinPercList.append(inv) inv -= increment # this loop add an extra 0.0 indices to avoid errors for r in range(10): for rr in range(2): skinPercList.append(0.0) # base skin if head_joint: try: head_joint = pm.PyNode(head_joint) except pm.MayaNodeError: pm.displayWarning( "Auto skin aborted can not find %s " % head_joint) return # Check if the object has a skinCluster objName = pm.listRelatives(geo, parent=True)[0] skinCluster = skin.getSkinCluster(objName) if not skinCluster: skinCluster = pm.skinCluster(head_joint, geo, tsb=True, nw=2, n='skinClsEyelid') lipsJoints = upperJoints + lowerJoints closestVtxList = upLip_closestVtxList + lowLip_closestVtxList pm.progressWindow(title='Auto skinning process', progress=0, max=len(lipsJoints)) for i, jnt in enumerate(lipsJoints): pm.progressWindow(e=True, step=1, status='\nSkinning %s' % jnt) skinCluster.addInfluence(jnt, weight=0) v = closestVtxList[i] for row in vertexRowList: if v in row: for i, rv in enumerate(row): # find the deformer with max value for each vertex w = pm.skinPercent(skinCluster, rv, query=True, value=True) transJoint = pm.skinPercent(skinCluster, rv, query=True, t=None) max_value = max(w) max_index = w.index(max_value) perc = skinPercList[i] t_value = [(jnt, perc), (transJoint[max_index], 1.0 - perc)] pm.skinPercent(skinCluster, rv, transformValue=t_value) pm.progressWindow(e=True, endProgress=True)
def eyeRig(eyeMesh, edgeLoop, blinkH, namePrefix, offset, rigidLoops, falloffLoops, headJnt, doSkin, parent=None, ctlName="ctl", sideRange=False, customCorner=False, intCorner=None, extCorner=None, ctlGrp=None, defGrp=None): """Create eyelid and eye rig Args: eyeMesh (TYPE): Description edgeLoop (TYPE): Description blinkH (TYPE): Description namePrefix (TYPE): Description offset (TYPE): Description rigidLoops (TYPE): Description falloffLoops (TYPE): Description headJnt (TYPE): Description doSkin (TYPE): Description parent (None, optional): Description ctlName (str, optional): Description sideRange (bool, optional): Description customCorner (bool, optional): Description intCorner (None, optional): Description extCorner (None, optional): Description ctlGrp (None, optional): Description defGrp (None, optional): Description Returns: TYPE: Description """ # Checkers if edgeLoop: edgeLoopList = [pm.PyNode(e) for e in edgeLoop.split(",")] else: pm.displayWarning("Please set the edge loop first") return if eyeMesh: try: eyeMesh = pm.PyNode(eyeMesh) except pm.MayaNodeError: pm.displayWarning("The object %s can not be found in the " "scene" % (eyeMesh)) return else: pm.displayWarning("Please set the eye mesh first") if doSkin: if not headJnt: pm.displayWarning("Please set the Head Jnt or unCheck " "Compute Topological Autoskin") return # Initial Data bboxCenter = meshNavigation.bboxCenter(eyeMesh) extr_v = meshNavigation.getExtremeVertexFromLoop(edgeLoopList, sideRange) upPos = extr_v[0] lowPos = extr_v[1] inPos = extr_v[2] outPos = extr_v[3] edgeList = extr_v[4] vertexList = extr_v[5] # Detect the side L or R from the x value if inPos.getPosition(space='world')[0] < 0.0: side = "R" inPos = extr_v[3] outPos = extr_v[2] normalPos = outPos npw = normalPos.getPosition(space='world') normalVec = npw - bboxCenter else: side = "L" normalPos = outPos npw = normalPos.getPosition(space='world') normalVec = bboxCenter - npw # Manual Vertex corners if customCorner: if intCorner: try: if side == "R": inPos = pm.PyNode(extCorner) else: inPos = pm.PyNode(intCorner) except pm.MayaNodeError: pm.displayWarning("%s can not be found" % intCorner) return else: pm.displayWarning("Please set the internal eyelid corner") return if extCorner: try: normalPos = pm.PyNode(extCorner) npw = normalPos.getPosition(space='world') if side == "R": outPos = pm.PyNode(intCorner) normalVec = npw - bboxCenter else: outPos = pm.PyNode(extCorner) normalVec = bboxCenter - npw except pm.MayaNodeError: pm.displayWarning("%s can not be found" % extCorner) return else: pm.displayWarning("Please set the external eyelid corner") return # Check if we have prefix: if namePrefix: namePrefix = string.removeInvalidCharacter(namePrefix) else: pm.displayWarning("Prefix is needed") return def setName(name, ind=None): namesList = [namePrefix, side, name] if ind is not None: namesList[1] = side + str(ind) name = "_".join(namesList) return name if pm.ls(setName("root")): pm.displayWarning("The object %s already exist in the scene. Please " "choose another name prefix" % setName("root")) return # Eye root eye_root = primitive.addTransform(None, setName("root")) eyeCrv_root = primitive.addTransform(eye_root, setName("crvs")) # Eyelid Main crvs try: upEyelid = meshNavigation.edgeRangeInLoopFromMid( edgeList, upPos, inPos, outPos) upCrv = curve.createCurveFromOrderedEdges(upEyelid, inPos, setName("upperEyelid"), parent=eyeCrv_root) upCrv_ctl = curve.createCurveFromOrderedEdges(upEyelid, inPos, setName("upCtl_crv"), parent=eyeCrv_root) pm.rebuildCurve(upCrv_ctl, s=2, rt=0, rpo=True, ch=False) lowEyelid = meshNavigation.edgeRangeInLoopFromMid( edgeList, lowPos, inPos, outPos) lowCrv = curve.createCurveFromOrderedEdges(lowEyelid, inPos, setName("lowerEyelid"), parent=eyeCrv_root) lowCrv_ctl = curve.createCurveFromOrderedEdges(lowEyelid, inPos, setName("lowCtl_crv"), parent=eyeCrv_root) pm.rebuildCurve(lowCrv_ctl, s=2, rt=0, rpo=True, ch=False) except UnboundLocalError: if customCorner: pm.displayWarning("This error is maybe caused because the custom " "Corner vertex is not part of the edge loop") pm.displayError(traceback.format_exc()) return upBlink = curve.createCurveFromCurve(upCrv, setName("upblink_crv"), nbPoints=30, parent=eyeCrv_root) lowBlink = curve.createCurveFromCurve(lowCrv, setName("lowBlink_crv"), nbPoints=30, parent=eyeCrv_root) upTarget = curve.createCurveFromCurve(upCrv, setName("upblink_target"), nbPoints=30, parent=eyeCrv_root) lowTarget = curve.createCurveFromCurve(lowCrv, setName("lowBlink_target"), nbPoints=30, parent=eyeCrv_root) midTarget = curve.createCurveFromCurve(lowCrv, setName("midBlink_target"), nbPoints=30, parent=eyeCrv_root) rigCrvs = [ upCrv, lowCrv, upCrv_ctl, lowCrv_ctl, upBlink, lowBlink, upTarget, lowTarget, midTarget ] for crv in rigCrvs: crv.attr("visibility").set(False) # localBBOX localBBox = eyeMesh.getBoundingBox(invisible=True, space='world') wRadius = abs((localBBox[0][0] - localBBox[1][0])) dRadius = abs((localBBox[0][1] - localBBox[1][1]) / 1.7) # Groups if not ctlGrp: ctlGrp = "rig_controllers_grp" try: ctlSet = pm.PyNode(ctlGrp) except pm.MayaNodeError: pm.sets(n=ctlGrp, em=True) ctlSet = pm.PyNode(ctlGrp) if not defGrp: defGrp = "rig_deformers_grp" try: defset = pm.PyNode(defGrp) except pm.MayaNodeError: pm.sets(n=defGrp, em=True) defset = pm.PyNode(defGrp) # Calculate center looking at averagePosition = ( (upPos.getPosition(space='world') + lowPos.getPosition(space='world') + inPos.getPosition(space='world') + outPos.getPosition(space='world')) / 4) if side == "R": negate = False offset = offset over_offset = dRadius else: negate = False over_offset = dRadius if side == "R" and sideRange or side == "R" and customCorner: axis = "z-x" # axis = "zx" else: axis = "z-x" t = transform.getTransformLookingAt(bboxCenter, averagePosition, normalVec, axis=axis, negate=negate) over_npo = primitive.addTransform(eye_root, setName("center_lookatRoot"), t) over_ctl = icon.create(over_npo, setName("over_%s" % ctlName), t, icon="square", w=wRadius, d=dRadius, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, over_offset), color=4) node.add_controller_tag(over_ctl) attribute.add_mirror_config_channels(over_ctl) attribute.setKeyableAttributes( over_ctl, params=["tx", "ty", "tz", "ro", "rx", "ry", "rz", "sx", "sy", "sz"]) if side == "R": over_npo.attr("rx").set(over_npo.attr("rx").get() * -1) over_npo.attr("ry").set(over_npo.attr("ry").get() + 180) over_npo.attr("sz").set(-1) if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=over_ctl) center_lookat = primitive.addTransform(over_ctl, setName("center_lookat"), t) # Tracking # Eye aim control t_arrow = transform.getTransformLookingAt(bboxCenter, averagePosition, upPos.getPosition(space='world'), axis="zy", negate=False) radius = abs((localBBox[0][0] - localBBox[1][0]) / 1.7) arrow_npo = primitive.addTransform(eye_root, setName("aim_npo"), t_arrow) arrow_ctl = icon.create(arrow_npo, setName("aim_%s" % ctlName), t_arrow, icon="arrow", w=1, po=datatypes.Vector(0, 0, radius), color=4) if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=arrow_ctl) attribute.setKeyableAttributes(arrow_ctl, params=["rx", "ry", "rz"]) # tracking custom trigger if side == "R": tt = t_arrow else: tt = t aimTrigger_root = primitive.addTransform(center_lookat, setName("aimTrigger_root"), tt) aimTrigger_lvl = primitive.addTransform(aimTrigger_root, setName("aimTrigger_lvl"), tt) aimTrigger_lvl.attr("tz").set(1.0) aimTrigger_ref = primitive.addTransform(aimTrigger_lvl, setName("aimTrigger_ref"), tt) aimTrigger_ref.attr("tz").set(0.0) # connect trigger with arrow_ctl pm.parentConstraint(arrow_ctl, aimTrigger_ref, mo=True) # Controls lists upControls = [] trackLvl = [] # upper eyelid controls upperCtlNames = ["inCorner", "upInMid", "upMid", "upOutMid", "outCorner"] cvs = upCrv_ctl.getCVs(space="world") if side == "R" and not sideRange: # if side == "R": cvs = [cv for cv in reversed(cvs)] for i, cv in enumerate(cvs): if utils.is_odd(i): color = 14 wd = .5 icon_shape = "circle" params = ["tx", "ty", "tz"] else: color = 4 wd = .7 icon_shape = "square" params = [ "tx", "ty", "tz", "ro", "rx", "ry", "rz", "sx", "sy", "sz" ] t = transform.setMatrixPosition(t, cvs[i]) npo = primitive.addTransform(center_lookat, setName("%s_npo" % upperCtlNames[i]), t) npoBase = npo if i == 2: # we add an extra level to input the tracking ofset values npo = primitive.addTransform(npo, setName("%s_trk" % upperCtlNames[i]), t) trackLvl.append(npo) ctl = icon.create(npo, setName("%s_%s" % (upperCtlNames[i], ctlName)), t, icon=icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, offset), color=color) attribute.add_mirror_config_channels(ctl) node.add_controller_tag(ctl, over_ctl) upControls.append(ctl) if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=ctl) attribute.setKeyableAttributes(ctl, params) if side == "R": npoBase.attr("ry").set(180) npoBase.attr("sz").set(-1) # adding parent average contrains to odd controls for i, ctl in enumerate(upControls): if utils.is_odd(i): cns_node = pm.parentConstraint(upControls[i - 1], upControls[i + 1], ctl.getParent(), mo=True) # Make the constraint "noFlip" cns_node.interpType.set(0) # lower eyelid controls lowControls = [upControls[0]] lowerCtlNames = [ "inCorner", "lowInMid", "lowMid", "lowOutMid", "outCorner" ] cvs = lowCrv_ctl.getCVs(space="world") if side == "R" and not sideRange: cvs = [cv for cv in reversed(cvs)] for i, cv in enumerate(cvs): # we skip the first and last point since is already in the uper eyelid if i in [0, 4]: continue if utils.is_odd(i): color = 14 wd = .5 icon_shape = "circle" params = ["tx", "ty", "tz"] else: color = 4 wd = .7 icon_shape = "square" params = [ "tx", "ty", "tz", "ro", "rx", "ry", "rz", "sx", "sy", "sz" ] t = transform.setMatrixPosition(t, cvs[i]) npo = primitive.addTransform(center_lookat, setName("%s_npo" % lowerCtlNames[i]), t) npoBase = npo if i == 2: # we add an extra level to input the tracking ofset values npo = primitive.addTransform(npo, setName("%s_trk" % lowerCtlNames[i]), t) trackLvl.append(npo) ctl = icon.create(npo, setName("%s_%s" % (lowerCtlNames[i], ctlName)), t, icon=icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, offset), color=color) attribute.add_mirror_config_channels(ctl) lowControls.append(ctl) if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=ctl) attribute.setKeyableAttributes(ctl, params) # mirror behaviout on R side controls if side == "R": npoBase.attr("ry").set(180) npoBase.attr("sz").set(-1) for lctl in reversed(lowControls[1:]): node.add_controller_tag(lctl, over_ctl) lowControls.append(upControls[-1]) # adding parent average contrains to odd controls for i, ctl in enumerate(lowControls): if utils.is_odd(i): cns_node = pm.parentConstraint(lowControls[i - 1], lowControls[i + 1], ctl.getParent(), mo=True) # Make the constraint "noFlip" cns_node.interpType.set(0) # Connecting control crvs with controls applyop.gear_curvecns_op(upCrv_ctl, upControls) applyop.gear_curvecns_op(lowCrv_ctl, lowControls) # adding wires w1 = pm.wire(upCrv, w=upBlink)[0] w2 = pm.wire(lowCrv, w=lowBlink)[0] w3 = pm.wire(upTarget, w=upCrv_ctl)[0] w4 = pm.wire(lowTarget, w=lowCrv_ctl)[0] # adding blendshapes bs_upBlink = pm.blendShape(upTarget, midTarget, upBlink, n="blendShapeUpBlink") bs_lowBlink = pm.blendShape(lowTarget, midTarget, lowBlink, n="blendShapeLowBlink") bs_mid = pm.blendShape(lowTarget, upTarget, midTarget, n="blendShapeLowBlink") # setting blendshape reverse connections rev_node = pm.createNode("reverse") pm.connectAttr(bs_upBlink[0].attr(midTarget.name()), rev_node + ".inputX") pm.connectAttr(rev_node + ".outputX", bs_upBlink[0].attr(upTarget.name())) rev_node = pm.createNode("reverse") pm.connectAttr(bs_lowBlink[0].attr(midTarget.name()), rev_node + ".inputX") pm.connectAttr(rev_node + ".outputX", bs_lowBlink[0].attr(lowTarget.name())) rev_node = pm.createNode("reverse") pm.connectAttr(bs_mid[0].attr(upTarget.name()), rev_node + ".inputX") pm.connectAttr(rev_node + ".outputX", bs_mid[0].attr(lowTarget.name())) # setting default values bs_mid[0].attr(upTarget.name()).set(blinkH) # joints root jnt_root = primitive.addTransformFromPos(eye_root, setName("joints"), pos=bboxCenter) # head joint if headJnt: try: headJnt = pm.PyNode(headJnt) jnt_base = headJnt except pm.MayaNodeError: pm.displayWarning("Aborted can not find %s " % headJnt) return else: # Eye root jnt_base = jnt_root eyeTargets_root = primitive.addTransform(eye_root, setName("targets")) eyeCenter_jnt = rigbits.addJnt(arrow_ctl, jnt_base, grp=defset, jntName=setName("center_jnt")) # Upper Eyelid joints ################################################## cvs = upCrv.getCVs(space="world") upCrv_info = node.createCurveInfoNode(upCrv) # aim constrain targets and joints upperEyelid_aimTargets = [] upperEyelid_jnt = [] upperEyelid_jntRoot = [] for i, cv in enumerate(cvs): # aim targets trn = primitive.addTransformFromPos(eyeTargets_root, setName("upEyelid_aimTarget", i), pos=cv) upperEyelid_aimTargets.append(trn) # connecting positions with crv pm.connectAttr(upCrv_info + ".controlPoints[%s]" % str(i), trn.attr("translate")) # joints jntRoot = primitive.addJointFromPos(jnt_root, setName("upEyelid_jnt_base", i), pos=bboxCenter) jntRoot.attr("radius").set(.08) jntRoot.attr("visibility").set(False) upperEyelid_jntRoot.append(jntRoot) applyop.aimCns(jntRoot, trn, axis="zy", wupObject=jnt_root) jnt_ref = primitive.addJointFromPos(jntRoot, setName("upEyelid_jnt_ref", i), pos=cv) jnt_ref.attr("radius").set(.08) jnt_ref.attr("visibility").set(False) jnt = rigbits.addJnt(jnt_ref, jnt_base, grp=defset, jntName=setName("upEyelid_jnt", i)) upperEyelid_jnt.append(jnt) # Lower Eyelid joints ################################################## cvs = lowCrv.getCVs(space="world") lowCrv_info = node.createCurveInfoNode(lowCrv) # aim constrain targets and joints lowerEyelid_aimTargets = [] lowerEyelid_jnt = [] lowerEyelid_jntRoot = [] for i, cv in enumerate(cvs): if i in [0, len(cvs) - 1]: continue # aim targets trn = primitive.addTransformFromPos(eyeTargets_root, setName("lowEyelid_aimTarget", i), pos=cv) lowerEyelid_aimTargets.append(trn) # connecting positions with crv pm.connectAttr(lowCrv_info + ".controlPoints[%s]" % str(i), trn.attr("translate")) # joints jntRoot = primitive.addJointFromPos(jnt_root, setName("lowEyelid_base", i), pos=bboxCenter) jntRoot.attr("radius").set(.08) jntRoot.attr("visibility").set(False) lowerEyelid_jntRoot.append(jntRoot) applyop.aimCns(jntRoot, trn, axis="zy", wupObject=jnt_root) jnt_ref = primitive.addJointFromPos(jntRoot, setName("lowEyelid_jnt_ref", i), pos=cv) jnt_ref.attr("radius").set(.08) jnt_ref.attr("visibility").set(False) jnt = rigbits.addJnt(jnt_ref, jnt_base, grp=defset, jntName=setName("lowEyelid_jnt", i)) lowerEyelid_jnt.append(jnt) # Channels # Adding and connecting attributes for the blink up_ctl = upControls[2] blink_att = attribute.addAttribute(over_ctl, "blink", "float", 0, minValue=0, maxValue=1) blinkMult_att = attribute.addAttribute(over_ctl, "blinkMult", "float", 1, minValue=1, maxValue=2) midBlinkH_att = attribute.addAttribute(over_ctl, "blinkHeight", "float", blinkH, minValue=0, maxValue=1) mult_node = node.createMulNode(blink_att, blinkMult_att) pm.connectAttr(mult_node + ".outputX", bs_upBlink[0].attr(midTarget.name())) pm.connectAttr(mult_node + ".outputX", bs_lowBlink[0].attr(midTarget.name())) pm.connectAttr(midBlinkH_att, bs_mid[0].attr(upTarget.name())) low_ctl = lowControls[2] # Adding channels for eye tracking upVTracking_att = attribute.addAttribute(up_ctl, "vTracking", "float", .02, minValue=0, maxValue=1, keyable=False, channelBox=True) upHTracking_att = attribute.addAttribute(up_ctl, "hTracking", "float", .01, minValue=0, maxValue=1, keyable=False, channelBox=True) lowVTracking_att = attribute.addAttribute(low_ctl, "vTracking", "float", .01, minValue=0, maxValue=1, keyable=False, channelBox=True) lowHTracking_att = attribute.addAttribute(low_ctl, "hTracking", "float", .01, minValue=0, maxValue=1, keyable=False, channelBox=True) mult_node = node.createMulNode(upVTracking_att, aimTrigger_ref.attr("ty")) pm.connectAttr(mult_node + ".outputX", trackLvl[0].attr("ty")) mult_node = node.createMulNode(upHTracking_att, aimTrigger_ref.attr("tx")) pm.connectAttr(mult_node + ".outputX", trackLvl[0].attr("tx")) mult_node = node.createMulNode(lowVTracking_att, aimTrigger_ref.attr("ty")) pm.connectAttr(mult_node + ".outputX", trackLvl[1].attr("ty")) mult_node = node.createMulNode(lowHTracking_att, aimTrigger_ref.attr("tx")) pm.connectAttr(mult_node + ".outputX", trackLvl[1].attr("tx")) # Tension on blink node.createReverseNode(blink_att, w1.scale[0]) node.createReverseNode(blink_att, w3.scale[0]) node.createReverseNode(blink_att, w2.scale[0]) node.createReverseNode(blink_att, w4.scale[0]) ########################################### # Reparenting ########################################### if parent: try: if isinstance(parent, basestring): parent = pm.PyNode(parent) parent.addChild(eye_root) except pm.MayaNodeError: pm.displayWarning("The eye rig can not be parent to: %s. Maybe " "this object doesn't exist." % parent) ########################################### # Auto Skinning ########################################### if doSkin: # eyelid vertex rows totalLoops = rigidLoops + falloffLoops vertexLoopList = meshNavigation.getConcentricVertexLoop( vertexList, totalLoops) vertexRowList = meshNavigation.getVertexRowsFromLoops(vertexLoopList) # we set the first value 100% for the first initial loop skinPercList = [1.0] # we expect to have a regular grid topology for r in range(rigidLoops): for rr in range(2): skinPercList.append(1.0) increment = 1.0 / float(falloffLoops) # we invert to smooth out from 100 to 0 inv = 1.0 - increment for r in range(falloffLoops): for rr in range(2): if inv < 0.0: inv = 0.0 skinPercList.append(inv) inv -= increment # this loop add an extra 0.0 indices to avoid errors for r in range(10): for rr in range(2): skinPercList.append(0.0) # base skin geo = pm.listRelatives(edgeLoopList[0], parent=True)[0] # Check if the object has a skinCluster objName = pm.listRelatives(geo, parent=True)[0] skinCluster = skin.getSkinCluster(objName) if not skinCluster: skinCluster = pm.skinCluster(headJnt, geo, tsb=True, nw=2, n='skinClsEyelid') eyelidJoints = upperEyelid_jnt + lowerEyelid_jnt pm.progressWindow(title='Auto skinning process', progress=0, max=len(eyelidJoints)) firstBoundary = False for jnt in eyelidJoints: pm.progressWindow(e=True, step=1, status='\nSkinning %s' % jnt) skinCluster.addInfluence(jnt, weight=0) v = meshNavigation.getClosestVertexFromTransform(geo, jnt) for row in vertexRowList: if v in row: it = 0 # iterator inc = 1 # increment for i, rv in enumerate(row): try: perc = skinPercList[it] t_val = [(jnt, perc), (headJnt, 1.0 - perc)] pm.skinPercent(skinCluster, rv, transformValue=t_val) if rv.isOnBoundary(): # we need to compare with the first boundary # to check if the row have inverted direction # and offset the value if not firstBoundary: firstBoundary = True firstBoundaryValue = it else: if it < firstBoundaryValue: it -= 1 elif it > firstBoundaryValue: it += 1 inc = 2 except IndexError: continue it = it + inc pm.progressWindow(e=True, endProgress=True) # Eye Mesh skinning skinCluster = skin.getSkinCluster(eyeMesh) if not skinCluster: skinCluster = pm.skinCluster(eyeCenter_jnt, eyeMesh, tsb=True, nw=1, n='skinClsEye')
def rope(DEF_nb=10, ropeName="rope", keepRatio=False, lvlType="transform", oSel=None): """Create rope rig based in 2 parallel curves. Args: DEF_nb (int): Number of deformer joints. ropeName (str): Name for the rope rig. keepRatio (bool): If True, the deformers will keep the length position when the curve is stretched. """ if oSel and len(oSel) == 2 and isinstance(oSel, list): oCrv = oSel[0] if isinstance(oCrv, str): oCrv = pm.PyNode(oCrv) oCrvUpV = oSel[1] if isinstance(oCrvUpV, str): oCrvUpV = pm.PyNode(oCrvUpV) else: if len(pm.selected()) != 2: print("You need to select 2 nurbsCurve") return oCrv = pm.selected()[0] oCrvUpV = pm.selected()[1] if (oCrv.getShape().type() != "nurbsCurve" or oCrvUpV.getShape().type() != "nurbsCurve"): print("One of the selected objects is not of type: 'nurbsCurve'") print(oCrv.getShape().type()) print(oCrvUpV.getShape().type()) return if keepRatio: arclen_node = pm.arclen(oCrv, ch=True) alAttr = pm.getAttr(arclen_node + ".arcLength") muldiv_node = pm.createNode("multiplyDivide") pm.connectAttr(arclen_node + ".arcLength", muldiv_node + ".input1X") pm.setAttr(muldiv_node + ".input2X", alAttr) pm.setAttr(muldiv_node + ".operation", 2) pm.addAttr(oCrv, ln="length_ratio", k=True, w=True) pm.connectAttr(muldiv_node + ".outputX", oCrv + ".length_ratio") root = pm.PyNode(pm.createNode(lvlType, n=ropeName + "_root", ss=True)) step = 1.000 / (DEF_nb - 1) i = 0.000 shds = [] for x in range(DEF_nb): oTransUpV = pm.PyNode( pm.createNode(lvlType, n=ropeName + str(x).zfill(3) + "_upv", p=root, ss=True)) oTrans = pm.PyNode( pm.createNode(lvlType, n=ropeName + str(x).zfill(3) + "_lvl", p=root, ss=True)) cnsUpv = applyop.pathCns(oTransUpV, oCrvUpV, cnsType=False, u=i, tangent=False) cns = applyop.pathCns(oTrans, oCrv, cnsType=False, u=i, tangent=False) if keepRatio: muldiv_node2 = pm.createNode("multiplyDivide") condition_node = pm.createNode("condition") pm.setAttr(muldiv_node2 + ".operation", 2) pm.setAttr(muldiv_node2 + ".input1X", i) pm.connectAttr(oCrv + ".length_ratio", muldiv_node2 + ".input2X") pm.connectAttr(muldiv_node2 + ".outputX", condition_node + ".colorIfFalseR") pm.connectAttr(muldiv_node2 + ".outputX", condition_node + ".secondTerm") pm.connectAttr(muldiv_node2 + ".input1X", condition_node + ".colorIfTrueR") pm.connectAttr(muldiv_node2 + ".input1X", condition_node + ".firstTerm") pm.setAttr(condition_node + ".operation", 4) pm.connectAttr(condition_node + ".outColorR", cnsUpv + ".uValue") pm.connectAttr(condition_node + ".outColorR", cns + ".uValue") cns.setAttr("worldUpType", 1) cns.setAttr("frontAxis", 0) cns.setAttr("upAxis", 1) pm.connectAttr(oTransUpV.attr("worldMatrix[0]"), cns.attr("worldUpMatrix")) shd = rigbits.addJnt(oTrans) shds.append(shd[0]) i += step return shds
def rig( eyeMesh=None, edgeLoop="", blinkH=20, namePrefix="eye", offset=0.05, rigidLoops=2, falloffLoops=4, headJnt=None, doSkin=True, parent_node=None, ctlName="ctl", sideRange=False, customCorner=False, intCorner=None, extCorner=None, ctlSet=None, defSet=None, upperVTrack=0.02, upperHTrack=0.01, lowerVTrack=0.02, lowerHTrack=0.01, aim_controller="", deformers_group="", everyNVertex=1, ): """Create eyelid and eye rig Args: eyeMesh (TYPE): Description edgeLoop (TYPE): Description blinkH (TYPE): Description namePrefix (TYPE): Description offset (TYPE): Description rigidLoops (TYPE): Description falloffLoops (TYPE): Description headJnt (TYPE): Description doSkin (TYPE): Description parent_node (None, optional): Description ctlName (str, optional): Description sideRange (bool, optional): Description customCorner (bool, optional): Description intCorner (None, optional): Description extCorner (None, optional): Description ctlSet (None, optional): Description defSet (None, optional): Description upperVTrack (None, optional): Description upperHTrack (None, optional): Description lowerVTrack (None, optional): Description lowerHTrack (None, optional): Description aim_controller (None, optional): Description deformers_group (None, optional): Description everyNVertex (int, optional): Will create a joint every N vertex No Longer Returned: TYPE: Description """ ########################################## # INITIAL SETUP ########################################## up_axis = pm.upAxis(q=True, axis=True) if up_axis == "z": z_up = True else: z_up = False # getters edgeLoopList = get_edge_loop(edgeLoop) eyeMesh = get_eye_mesh(eyeMesh) # checkers if not edgeLoopList or not eyeMesh: return if doSkin: if not headJnt: pm.displayWarning("Please set the Head Jnt or unCheck " "Compute Topological Autoskin") return # Convert data blinkH = blinkH / 100.0 # Initial Data bboxCenter = meshNavigation.bboxCenter(eyeMesh) extr_v = meshNavigation.getExtremeVertexFromLoop(edgeLoopList, sideRange, z_up) upPos = extr_v[0] lowPos = extr_v[1] inPos = extr_v[2] outPos = extr_v[3] edgeList = extr_v[4] vertexList = extr_v[5] # Detect the side L or R from the x value if inPos.getPosition(space="world")[0] < 0.0: side = "R" inPos = extr_v[3] outPos = extr_v[2] normalPos = outPos npw = normalPos.getPosition(space="world") normalVec = npw - bboxCenter else: side = "L" normalPos = outPos npw = normalPos.getPosition(space="world") normalVec = bboxCenter - npw # Manual Vertex corners if customCorner: if intCorner: try: if side == "R": inPos = pm.PyNode(extCorner) else: inPos = pm.PyNode(intCorner) except pm.MayaNodeError: pm.displayWarning("%s can not be found" % intCorner) return else: pm.displayWarning("Please set the internal eyelid corner") return if extCorner: try: normalPos = pm.PyNode(extCorner) npw = normalPos.getPosition(space="world") if side == "R": outPos = pm.PyNode(intCorner) normalVec = npw - bboxCenter else: outPos = pm.PyNode(extCorner) normalVec = bboxCenter - npw except pm.MayaNodeError: pm.displayWarning("%s can not be found" % extCorner) return else: pm.displayWarning("Please set the external eyelid corner") return # Check if we have prefix: if namePrefix: namePrefix = string.removeInvalidCharacter(namePrefix) else: pm.displayWarning("Prefix is needed") return def setName(name, ind=None): namesList = [namePrefix, side, name] if ind is not None: namesList[1] = side + str(ind) name = "_".join(namesList) return name if pm.ls(setName("root")): pm.displayWarning("The object %s already exist in the scene. Please " "choose another name prefix" % setName("root")) return ########################################## # CREATE OBJECTS ########################################## # Eye root eye_root = primitive.addTransform(None, setName("root")) eyeCrv_root = primitive.addTransform(eye_root, setName("crvs")) # Eyelid Main crvs try: upEyelid_edge = meshNavigation.edgeRangeInLoopFromMid( edgeList, upPos, inPos, outPos) up_crv = curve.createCurveFromOrderedEdges(upEyelid_edge, inPos, setName("upperEyelid"), parent=eyeCrv_root) upCtl_crv = curve.createCurveFromOrderedEdges(upEyelid_edge, inPos, setName("upCtl_crv"), parent=eyeCrv_root) pm.rebuildCurve(upCtl_crv, s=2, rt=0, rpo=True, ch=False) lowEyelid_edge = meshNavigation.edgeRangeInLoopFromMid( edgeList, lowPos, inPos, outPos) low_crv = curve.createCurveFromOrderedEdges(lowEyelid_edge, inPos, setName("lowerEyelid"), parent=eyeCrv_root) lowCtl_crv = curve.createCurveFromOrderedEdges(lowEyelid_edge, inPos, setName("lowCtl_crv"), parent=eyeCrv_root) pm.rebuildCurve(lowCtl_crv, s=2, rt=0, rpo=True, ch=False) except UnboundLocalError: if customCorner: pm.displayWarning("This error is maybe caused because the custom " "Corner vertex is not part of the edge loop") pm.displayError(traceback.format_exc()) return # blendshape curves. All crv have 30 point to allow blendshape connect upDriver_crv = curve.createCurveFromCurve(up_crv, setName("upDriver_crv"), nbPoints=30, parent=eyeCrv_root) upDriver_crv.attr("lineWidth").set(5) lowDriver_crv = curve.createCurveFromCurve(low_crv, setName("lowDriver_crv"), nbPoints=30, parent=eyeCrv_root) lowDriver_crv.attr("lineWidth").set(5) upRest_target_crv = curve.createCurveFromCurve( up_crv, setName("upRest_target_crv"), nbPoints=30, parent=eyeCrv_root) lowRest_target_crv = curve.createCurveFromCurve( low_crv, setName("lowRest_target_crv"), nbPoints=30, parent=eyeCrv_root) upProfile_target_crv = curve.createCurveFromCurve( up_crv, setName("upProfile_target_crv"), nbPoints=30, parent=eyeCrv_root, ) lowProfile_target_crv = curve.createCurveFromCurve( low_crv, setName("lowProfile_target_crv"), nbPoints=30, parent=eyeCrv_root, ) # mid driver midUpDriver_crv = curve.createCurveFromCurve(up_crv, setName("midUpDriver_crv"), nbPoints=30, parent=eyeCrv_root) midLowDriver_crv = curve.createCurveFromCurve(low_crv, setName("midLowDriver_crv"), nbPoints=30, parent=eyeCrv_root) # curve that define the close point of the eyelid closeTarget_crv = curve.createCurveFromCurve(up_crv, setName("closeTarget_crv"), nbPoints=30, parent=eyeCrv_root) eyeCrv_root.attr("visibility").set(False) # localBBOX localBBox = eyeMesh.getBoundingBox(invisible=True, space="world") wRadius = abs((localBBox[0][0] - localBBox[1][0])) dRadius = abs((localBBox[0][1] - localBBox[1][1]) / 1.7) # Groups if not ctlSet: ctlSet = "rig_controllers_grp" try: ctlSet = pm.PyNode(ctlSet) except pm.MayaNodeError: pm.sets(n=ctlSet, em=True) ctlSet = pm.PyNode(ctlSet) if not defSet: defSet = "rig_deformers_grp" try: defset = pm.PyNode(defSet) except pm.MayaNodeError: pm.sets(n=defSet, em=True) defset = pm.PyNode(defSet) # Calculate center looking at averagePosition = (upPos.getPosition(space="world") + lowPos.getPosition( space="world") + inPos.getPosition(space="world") + outPos.getPosition(space="world")) / 4 if z_up: axis = "zx" else: axis = "z-x" t = transform.getTransformLookingAt(bboxCenter, averagePosition, normalVec, axis=axis, negate=False) over_npo = primitive.addTransform(eye_root, setName("center_lookatRoot"), t) center_lookat = primitive.addTransform(over_npo, setName("center_lookat"), t) if side == "R": over_npo.attr("rx").set(over_npo.attr("rx").get() * -1) over_npo.attr("ry").set(over_npo.attr("ry").get() + 180) over_npo.attr("sz").set(-1) t = transform.getTransform(over_npo) # Tracking # Eye aim control t_arrow = transform.getTransformLookingAt( bboxCenter, averagePosition, upPos.getPosition(space="world"), axis="zy", negate=False, ) radius = abs((localBBox[0][0] - localBBox[1][0]) / 1.7) arrow_ctl = None arrow_npo = None if aim_controller: arrow_ctl = pm.PyNode(aim_controller) else: arrow_npo = primitive.addTransform(eye_root, setName("aim_npo"), t_arrow) arrow_ctl = icon.create( arrow_npo, setName("aim_%s" % ctlName), t_arrow, icon="arrow", w=1, po=datatypes.Vector(0, 0, radius), color=4, ) if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=arrow_ctl) attribute.setKeyableAttributes(arrow_ctl, params=["rx", "ry", "rz"]) attribute.addAttribute(arrow_ctl, "isCtl", "bool", keyable=False) # tracking custom trigger if side == "R": tt = t_arrow else: tt = t aimTrigger_root = primitive.addTransform(center_lookat, setName("aimTrigger_root"), tt) # For some unknown reason the right side gets scewed rotation values mgear.core.transform.resetTransform(aimTrigger_root) aimTrigger_lvl = primitive.addTransform(aimTrigger_root, setName("aimTrigger_lvl"), tt) # For some unknown reason the right side gets scewed rotation values mgear.core.transform.resetTransform(aimTrigger_lvl) aimTrigger_lvl.attr("tz").set(1.0) aimTrigger_ref = primitive.addTransform(aimTrigger_lvl, setName("aimTrigger_ref"), tt) # For some unknown reason the right side gets scewed rotation values mgear.core.transform.resetTransform(aimTrigger_ref) aimTrigger_ref.attr("tz").set(0.0) # connect trigger with arrow_ctl pm.parentConstraint(arrow_ctl, aimTrigger_ref, mo=True) # Blink driver controls if z_up: trigger_axis = "tz" ro_up = [0, 1.57079633 * 2, 1.57079633] ro_low = [0, 0, 1.57079633] po = [0, offset * -1, 0] low_pos = 2 # Z else: trigger_axis = "ty" ro_up = (1.57079633, 1.57079633, 0) ro_low = [1.57079633, 1.57079633, 1.57079633 * 2] po = [0, 0, offset] low_pos = 1 # Y # upper ctl p = upRest_target_crv.getCVs(space="world")[15] ut = transform.setMatrixPosition(datatypes.Matrix(), p) npo = primitive.addTransform(over_npo, setName("upBlink_npo"), ut) up_ctl = icon.create( npo, setName("upBlink_ctl"), ut, icon="arrow", w=2.5, d=2.5, ro=datatypes.Vector(ro_up[0], ro_up[1], ro_up[2]), po=datatypes.Vector(po[0], po[1], po[2]), color=4, ) attribute.setKeyableAttributes(up_ctl, [trigger_axis]) pm.sets(ctlSet, add=up_ctl) # use translation of the object to drive the blink blink_driver = primitive.addTransform(up_ctl, setName("blink_drv"), ut) # lowe ctl p_low = lowRest_target_crv.getCVs(space="world")[15] p[low_pos] = p_low[low_pos] lt = transform.setMatrixPosition(ut, p) npo = primitive.addTransform(over_npo, setName("lowBlink_npo"), lt) low_ctl = icon.create( npo, setName("lowBlink_ctl"), lt, icon="arrow", w=1.5, d=1.5, ro=datatypes.Vector(ro_low[0], ro_low[1], ro_low[2]), po=datatypes.Vector(po[0], po[1], po[2]), color=4, ) attribute.setKeyableAttributes(low_ctl, [trigger_axis]) pm.sets(ctlSet, add=low_ctl) # Controls lists upControls = [] trackLvl = [] track_corner_lvl = [] corner_ctl = [] ghost_ctl = [] # upper eyelid controls upperCtlNames = ["inCorner", "upInMid", "upMid", "upOutMid", "outCorner"] cvs = upCtl_crv.getCVs(space="world") if side == "R" and not sideRange: # if side == "R": cvs = [cv for cv in reversed(cvs)] # offset = offset * -1 for i, cv in enumerate(cvs): if utils.is_odd(i): color = 14 wd = 0.3 icon_shape = "circle" params = ["tx", "ty", "tz"] else: color = 4 wd = 0.6 icon_shape = "circle" params = [ "tx", "ty", "tz", "ro", "rx", "ry", "rz", "sx", "sy", "sz", ] t = transform.setMatrixPosition(t, cvs[i]) npo = primitive.addTransform(center_lookat, setName("%s_npo" % upperCtlNames[i]), t) npoBase = npo # track for corners and mid point level if i in [0, 2, 4]: # we add an extra level to input the tracking ofset values npo = primitive.addTransform(npo, setName("%s_trk" % upperCtlNames[i]), t) if i == 2: trackLvl.append(npo) else: track_corner_lvl.append(npo) if i in [1, 2, 3]: ctl = primitive.addTransform(npo, setName("%s_loc" % upperCtlNames[i]), t) # ghost controls if i == 2: gt = transform.setMatrixPosition( t, transform.getPositionFromMatrix(ut)) else: gt = t npo_g = primitive.addTransform( up_ctl, setName("%sCtl_npo" % upperCtlNames[i]), gt) ctl_g = icon.create( npo_g, setName("%s_%s" % (upperCtlNames[i], ctlName)), gt, icon=icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, offset), color=color, ) # define the ctl_param to recive the ctl configuration ctl_param = ctl_g ghost_ctl.append(ctl_g) # connect local SRT rigbits.connectLocalTransform([ctl_g, ctl]) else: ctl = icon.create( npo, setName("%s_%s" % (upperCtlNames[i], ctlName)), t, icon=icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, offset), color=color, ) # define the ctl_param to recive the ctl configuration ctl_param = ctl attribute.addAttribute(ctl_param, "isCtl", "bool", keyable=False) attribute.add_mirror_config_channels(ctl_param) node.add_controller_tag(ctl_param, over_npo) if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=ctl_param) attribute.setKeyableAttributes(ctl_param, params) upControls.append(ctl) # add corner ctls to corner ctl list for tracking if i in [0, 4]: corner_ctl.append(ctl) # if side == "R": # npoBase.attr("ry").set(180) # npoBase.attr("sz").set(-1) # adding parent constraints to odd controls for i, ctl in enumerate(upControls): if utils.is_odd(i): cns_node = pm.parentConstraint(upControls[i - 1], upControls[i + 1], ctl.getParent(), mo=True) # Make the constraint "noFlip" cns_node.interpType.set(0) # adding parent constraint ghost controls cns_node = pm.parentConstraint(ghost_ctl[1], upControls[0], ghost_ctl[0].getParent(), mo=True) cns_node.interpType.set(0) cns_node = pm.parentConstraint(ghost_ctl[1], upControls[-1], ghost_ctl[2].getParent(), mo=True) cns_node.interpType.set(0) # lower eyelid controls lowControls = [upControls[0]] lowerCtlNames = [ "inCorner", "lowInMid", "lowMid", "lowOutMid", "outCorner", ] cvs = lowCtl_crv.getCVs(space="world") if side == "R" and not sideRange: cvs = [cv for cv in reversed(cvs)] for i, cv in enumerate(cvs): # we skip the first and last point since is already in the uper eyelid if i in [0, 4]: continue if utils.is_odd(i): color = 14 wd = 0.3 icon_shape = "circle" params = ["tx", "ty", "tz"] else: color = 4 wd = 0.6 icon_shape = "circle" params = [ "tx", "ty", "tz", "ro", "rx", "ry", "rz", "sx", "sy", "sz", ] t = transform.setMatrixPosition(t, cvs[i]) npo = primitive.addTransform(center_lookat, setName("%s_npo" % lowerCtlNames[i]), t) npoBase = npo if i in [1, 2, 3]: if i == 2: # we add an extra level to input the tracking ofset values npo = primitive.addTransform( npo, setName("%s_trk" % lowerCtlNames[i]), t) trackLvl.append(npo) ctl = primitive.addTransform(npo, setName("%s_loc" % lowerCtlNames[i]), t) # ghost controls if i == 2: gt = transform.setMatrixPosition( t, transform.getPositionFromMatrix(lt)) else: gt = t # ghost controls npo_g = primitive.addTransform( low_ctl, setName("%sCtl_npo" % lowerCtlNames[i]), gt) ctl_g = icon.create( npo_g, setName("%s_%s" % (lowerCtlNames[i], ctlName)), gt, icon=icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, offset), color=color, ) # define the ctl_param to recive the ctl configuration ctl_param = ctl_g ghost_ctl.append(ctl_g) # connect local SRT rigbits.connectLocalTransform([ctl_g, ctl]) else: ctl = icon.create( npo, setName("%s_%s" % (lowerCtlNames[i], ctlName)), t, icon=icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, offset), color=color, ) # define the ctl_param to recive the ctl configuration ctl_param = ctl attribute.addAttribute(ctl_param, "isCtl", "bool", keyable=False) attribute.add_mirror_config_channels(ctl_param) lowControls.append(ctl) if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=ctl_param) attribute.setKeyableAttributes(ctl_param, params) # mirror behaviout on R side controls # if side == "R": # npoBase.attr("ry").set(180) # npoBase.attr("sz").set(-1) for lctl in reversed(lowControls[1:]): node.add_controller_tag(lctl, over_npo) lowControls.append(upControls[-1]) # adding parent constraints to odd controls for i, ctl in enumerate(lowControls): if utils.is_odd(i): cns_node = pm.parentConstraint( lowControls[i - 1], lowControls[i + 1], ctl.getParent(), mo=True, ) # Make the constraint "noFlip" cns_node.interpType.set(0) # adding parent constraint ghost controls cns_node = pm.parentConstraint(ghost_ctl[4], upControls[0], ghost_ctl[3].getParent(), mo=True) cns_node.interpType.set(0) cns_node = pm.parentConstraint(ghost_ctl[4], upControls[-1], ghost_ctl[5].getParent(), mo=True) cns_node.interpType.set(0) ########################################## # OPERATORS ########################################## # Connecting control crvs with controls applyop.gear_curvecns_op(upCtl_crv, upControls) applyop.gear_curvecns_op(lowCtl_crv, lowControls) # adding wires w1 = pm.wire(up_crv, w=upDriver_crv)[0] w2 = pm.wire(low_crv, w=lowDriver_crv)[0] w3 = pm.wire(upProfile_target_crv, w=upCtl_crv)[0] w4 = pm.wire(lowProfile_target_crv, w=lowCtl_crv)[0] if z_up: trigger_axis = "tz" else: trigger_axis = "ty" # connect blink driver pm.pointConstraint(low_ctl, blink_driver, mo=False) rest_val = blink_driver.attr(trigger_axis).get() up_div_node = node.createDivNode(up_ctl.attr(trigger_axis), rest_val) low_div_node = node.createDivNode(low_ctl.attr(trigger_axis), rest_val * -1) # contact driver minus_node = node.createPlusMinusAverage1D( [rest_val, blink_driver.attr(trigger_axis)], operation=2) contact_div_node = node.createDivNode(minus_node.output1D, rest_val) # wire tension for w in [w1, w2, w3, w4]: w.dropoffDistance[0].set(100) # TODO: what is the best solution? # trigger using proximity # remap_node = pm.createNode("remapValue") # contact_div_node.outputX >> remap_node.inputValue # remap_node.value[0].value_Interp.set(2) # remap_node.inputMin.set(0.995) # reverse_node = node.createReverseNode(remap_node.outColorR) # for w in [w1, w2]: # reverse_node.outputX >> w.scale[0] # trigger at starting movement for up and low # up remap_node = pm.createNode("remapValue") up_ctl.attr(trigger_axis) >> remap_node.inputValue remap_node.value[0].value_Interp.set(2) remap_node.inputMax.set(rest_val / 8) reverse_node = node.createReverseNode(remap_node.outColorR) reverse_node.outputX >> w1.scale[0] # low remap_node = pm.createNode("remapValue") low_ctl.attr(trigger_axis) >> remap_node.inputValue remap_node.value[0].value_Interp.set(2) remap_node.inputMin.set((rest_val / 8) * -1) remap_node.outColorR >> w2.scale[0] # mid position drivers blendshapes bs_midUpDrive = pm.blendShape( lowRest_target_crv, upProfile_target_crv, midUpDriver_crv, n="midUpDriver_blendShape", ) bs_midLowDrive = pm.blendShape( upRest_target_crv, lowProfile_target_crv, midLowDriver_crv, n="midlowDriver_blendShape", ) bs_closeTarget = pm.blendShape( midUpDriver_crv, midLowDriver_crv, closeTarget_crv, n="closeTarget_blendShape", ) pm.connectAttr( up_div_node.outputX, bs_midUpDrive[0].attr(lowRest_target_crv.name()), ) pm.connectAttr( low_div_node.outputX, bs_midLowDrive[0].attr(upRest_target_crv.name()), ) pm.setAttr(bs_closeTarget[0].attr(midUpDriver_crv.name()), 0.5) pm.setAttr(bs_closeTarget[0].attr(midLowDriver_crv.name()), 0.5) # Main crv drivers bs_upBlink = pm.blendShape( lowRest_target_crv, closeTarget_crv, upProfile_target_crv, upDriver_crv, n="upBlink_blendShape", ) bs_lowBlink = pm.blendShape( upRest_target_crv, closeTarget_crv, lowProfile_target_crv, lowDriver_crv, n="lowBlink_blendShape", ) # blink contact connections cond_node_up = node.createConditionNode(contact_div_node.outputX, 1, 3, 0, up_div_node.outputX) pm.connectAttr( cond_node_up.outColorR, bs_upBlink[0].attr(lowRest_target_crv.name()), ) cond_node_low = node.createConditionNode(contact_div_node.outputX, 1, 3, 0, low_div_node.outputX) pm.connectAttr( cond_node_low.outColorR, bs_lowBlink[0].attr(upRest_target_crv.name()), ) cond_node_close = node.createConditionNode(contact_div_node.outputX, 1, 2, 1, 0) cond_node_close.colorIfFalseR.set(0) pm.connectAttr( cond_node_close.outColorR, bs_upBlink[0].attr(closeTarget_crv.name()), ) pm.connectAttr( cond_node_close.outColorR, bs_lowBlink[0].attr(closeTarget_crv.name()), ) pm.setAttr(bs_upBlink[0].attr(upProfile_target_crv.name()), 1) pm.setAttr(bs_lowBlink[0].attr(lowProfile_target_crv.name()), 1) # joints root jnt_root = primitive.addTransformFromPos(eye_root, setName("joints"), pos=bboxCenter) if deformers_group: deformers_group = pm.PyNode(deformers_group) pm.parentConstraint(eye_root, jnt_root, mo=True) pm.scaleConstraint(eye_root, jnt_root, mo=True) deformers_group.addChild(jnt_root) # head joint if headJnt: try: headJnt = pm.PyNode(headJnt) jnt_base = headJnt except pm.MayaNodeError: pm.displayWarning("Aborted can not find %s " % headJnt) return else: # Eye root jnt_base = jnt_root eyeTargets_root = primitive.addTransform(eye_root, setName("targets")) eyeCenter_jnt = rigbits.addJnt(arrow_ctl, jnt_base, grp=defset, jntName=setName("center_jnt")) # Upper Eyelid joints ################################################## cvs = up_crv.getCVs(space="world") upCrv_info = node.createCurveInfoNode(up_crv) # aim constrain targets and joints upperEyelid_aimTargets = [] upperEyelid_jnt = [] upperEyelid_jntRoot = [] if z_up: axis = "zy" wupVector = [0, 0, 1] else: axis = "-yz" wupVector = [0, 1, 0] for i, cv in enumerate(cvs): if i % everyNVertex: continue # aim targets trn = primitive.addTransformFromPos(eyeTargets_root, setName("upEyelid_aimTarget", i), pos=cv) upperEyelid_aimTargets.append(trn) # connecting positions with crv pm.connectAttr(upCrv_info + ".controlPoints[%s]" % str(i), trn.attr("translate")) # joints jntRoot = primitive.addJointFromPos(jnt_root, setName("upEyelid_jnt_base", i), pos=bboxCenter) jntRoot.attr("radius").set(0.08) jntRoot.attr("visibility").set(False) upperEyelid_jntRoot.append(jntRoot) applyop.aimCns(jntRoot, trn, axis=axis, wupObject=jnt_root, wupVector=wupVector) jnt_ref = primitive.addJointFromPos(jntRoot, setName("upEyelid_jnt_ref", i), pos=cv) jnt_ref.attr("radius").set(0.08) jnt_ref.attr("visibility").set(False) jnt = rigbits.addJnt(jnt_ref, jnt_base, grp=defset, jntName=setName("upEyelid_jnt", i)) upperEyelid_jnt.append(jnt) # Lower Eyelid joints ################################################## cvs = low_crv.getCVs(space="world") lowCrv_info = node.createCurveInfoNode(low_crv) # aim constrain targets and joints lowerEyelid_aimTargets = [] lowerEyelid_jnt = [] lowerEyelid_jntRoot = [] for i, cv in enumerate(cvs): if i in [0, len(cvs) - 1]: continue if i % everyNVertex: continue # aim targets trn = primitive.addTransformFromPos(eyeTargets_root, setName("lowEyelid_aimTarget", i), pos=cv) lowerEyelid_aimTargets.append(trn) # connecting positions with crv pm.connectAttr(lowCrv_info + ".controlPoints[%s]" % str(i), trn.attr("translate")) # joints jntRoot = primitive.addJointFromPos(jnt_root, setName("lowEyelid_base", i), pos=bboxCenter) jntRoot.attr("radius").set(0.08) jntRoot.attr("visibility").set(False) lowerEyelid_jntRoot.append(jntRoot) applyop.aimCns(jntRoot, trn, axis=axis, wupObject=jnt_root, wupVector=wupVector) jnt_ref = primitive.addJointFromPos(jntRoot, setName("lowEyelid_jnt_ref", i), pos=cv) jnt_ref.attr("radius").set(0.08) jnt_ref.attr("visibility").set(False) jnt = rigbits.addJnt(jnt_ref, jnt_base, grp=defset, jntName=setName("lowEyelid_jnt", i)) lowerEyelid_jnt.append(jnt) # Adding channels for eye tracking upVTracking_att = attribute.addAttribute(up_ctl, "vTracking", "float", upperVTrack, minValue=0) upHTracking_att = attribute.addAttribute(up_ctl, "hTracking", "float", upperHTrack, minValue=0) lowVTracking_att = attribute.addAttribute(low_ctl, "vTracking", "float", lowerVTrack, minValue=0) lowHTracking_att = attribute.addAttribute(low_ctl, "hTracking", "float", lowerHTrack, minValue=0) # vertical tracking connect up_mult_node = node.createMulNode(upVTracking_att, aimTrigger_ref.attr("ty")) low_mult_node = node.createMulNode(lowVTracking_att, aimTrigger_ref.attr("ty")) # remap to use the low or the up eyelid as driver contact base on # the eyetrack trigger direction uT_remap_node = pm.createNode("remapValue") aimTrigger_ref.attr("ty") >> uT_remap_node.inputValue uT_remap_node.inputMax.set(0.1) uT_remap_node.inputMin.set(-0.1) up_mult_node.outputX >> uT_remap_node.outputMax low_mult_node.outputX >> uT_remap_node.outputMin # up u_remap_node = pm.createNode("remapValue") contact_div_node.outputX >> u_remap_node.inputValue u_remap_node.value[0].value_Interp.set(2) u_remap_node.inputMin.set(0.9) up_mult_node.outputX >> u_remap_node.outputMin uT_remap_node.outColorR >> u_remap_node.outputMax # low l_remap_node = pm.createNode("remapValue") contact_div_node.outputX >> l_remap_node.inputValue l_remap_node.value[0].value_Interp.set(2) l_remap_node.inputMin.set(0.9) low_mult_node.outputX >> l_remap_node.outputMin uT_remap_node.outColorR >> l_remap_node.outputMax # up connect and turn down to low when contact pm.connectAttr(u_remap_node.outColorR, trackLvl[0].attr("ty")) pm.connectAttr(l_remap_node.outColorR, trackLvl[1].attr("ty")) # horizontal tracking connect mult_node = node.createMulNode(upHTracking_att, aimTrigger_ref.attr("tx")) # Correct right side horizontal tracking # if side == "R": # mult_node = node.createMulNode(mult_node.attr("outputX"), -1) pm.connectAttr(mult_node + ".outputX", trackLvl[0].attr("tx")) mult_node = node.createMulNode(lowHTracking_att, aimTrigger_ref.attr("tx")) # Correct right side horizontal tracking # if side == "R": # mult_node = node.createMulNode(mult_node.attr("outputX"), -1) pm.connectAttr(mult_node + ".outputX", trackLvl[1].attr("tx")) # adding channels for corner tracking # track_corner_lvl for i, ctl in enumerate(corner_ctl): VTracking_att = attribute.addAttribute(ctl, "vTracking", "float", 0.1, minValue=0) if z_up: mult_node = node.createMulNode(VTracking_att, up_ctl.tz) mult_node2 = node.createMulNode(VTracking_att, low_ctl.tz) plus_node = node.createPlusMinusAverage1D( [mult_node.outputX, mult_node2.outputX]) mult_node3 = node.createMulNode(plus_node.output1D, -1) pm.connectAttr(mult_node3.outputX, track_corner_lvl[i].attr("ty")) else: mult_node = node.createMulNode(VTracking_att, up_ctl.ty) mult_node2 = node.createMulNode(VTracking_att, low_ctl.ty) plus_node = node.createPlusMinusAverage1D( [mult_node.outputX, mult_node2.outputX]) pm.connectAttr(plus_node.output1D, track_corner_lvl[i].attr("ty")) ########################################### # Reparenting ########################################### if parent_node: try: if isinstance(parent_node, string_types): parent_node = pm.PyNode(parent_node) parent_node.addChild(eye_root) except pm.MayaNodeError: pm.displayWarning("The eye rig can not be parent to: %s. Maybe " "this object doesn't exist." % parent_node) ########################################### # Auto Skinning ########################################### if doSkin: # eyelid vertex rows totalLoops = rigidLoops + falloffLoops vertexLoopList = meshNavigation.getConcentricVertexLoop( vertexList, totalLoops) vertexRowList = meshNavigation.getVertexRowsFromLoops(vertexLoopList) # we set the first value 100% for the first initial loop skinPercList = [1.0] # we expect to have a regular grid topology for r in range(rigidLoops): for rr in range(2): skinPercList.append(1.0) increment = 1.0 / float(falloffLoops) # we invert to smooth out from 100 to 0 inv = 1.0 - increment for r in range(falloffLoops): for rr in range(2): if inv < 0.0: inv = 0.0 skinPercList.append(inv) inv -= increment # this loop add an extra 0.0 indices to avoid errors for r in range(10): for rr in range(2): skinPercList.append(0.0) # base skin geo = pm.listRelatives(edgeLoopList[0], parent=True)[0] # Check if the object has a skinCluster objName = pm.listRelatives(geo, parent=True)[0] skinCluster = skin.getSkinCluster(objName) if not skinCluster: skinCluster = pm.skinCluster(headJnt, geo, tsb=True, nw=2, n="skinClsEyelid") eyelidJoints = upperEyelid_jnt + lowerEyelid_jnt pm.progressWindow(title="Auto skinning process", progress=0, max=len(eyelidJoints)) firstBoundary = False for jnt in eyelidJoints: pm.progressWindow(e=True, step=1, status="\nSkinning %s" % jnt) skinCluster.addInfluence(jnt, weight=0) v = meshNavigation.getClosestVertexFromTransform(geo, jnt) for row in vertexRowList: if v in row: it = 0 # iterator inc = 1 # increment for i, rv in enumerate(row): try: perc = skinPercList[it] t_val = [(jnt, perc), (headJnt, 1.0 - perc)] pm.skinPercent(skinCluster, rv, transformValue=t_val) if rv.isOnBoundary(): # we need to compare with the first boundary # to check if the row have inverted direction # and offset the value if not firstBoundary: firstBoundary = True firstBoundaryValue = it else: if it < firstBoundaryValue: it -= 1 elif it > firstBoundaryValue: it += 1 inc = 2 except IndexError: continue it = it + inc pm.progressWindow(e=True, endProgress=True) # Eye Mesh skinning skinCluster = skin.getSkinCluster(eyeMesh) if not skinCluster: skinCluster = pm.skinCluster(eyeCenter_jnt, eyeMesh, tsb=True, nw=1, n="skinClsEye")