def addCnsCurve(parent, name, centers, degree=1, m=datatypes.Matrix()): """Create a curve attached to given centers. One point per center Arguments: parent (dagNode): Parent object. name (str): Name centers (list of dagNode): Object that will drive the curve. degree (int): 1 for linear curve, 3 for Cubic. Returns: dagNode: The newly created curve. """ # rebuild list to avoid input list modification centers = centers[:] if degree == 3: if len(centers) == 2: centers.insert(0, centers[0]) centers.append(centers[-1]) elif len(centers) == 3: centers.append(centers[-1]) points = [datatypes.Vector() for center in centers] node = addCurve(parent, name, points, False, degree, m=m) applyop.gear_curvecns_op(node, centers) return node
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 _addCurveControllers(self, t, crv, ctlNames, inCtl=None, outCtl=None): cvs = crv.getCVs(space="world") if self.negate: # cvs = [cv for cv in reversed(cvs)] pass ctls = [] for i, cv in enumerate(cvs): if inCtl is not None and i == 0: ctls.append(inCtl) continue if outCtl is not None and (i == len(cvs) - 1): ctls.append(outCtl) continue if utils.is_odd(i): color = 14 wd = .5 offset = self.offset * 1.1 icon_shape = "circle" params = ["tx", "ty", "tz"] else: color = 4 wd = .7 offset = self.offset * 1.3 icon_shape = "square" params = ["tx", "ty", "tz", "ro", "rx", "ry", "rz", "sx", "sy", "sz"] t = setMatrixPosition(t, cvs[i]) npo = addTransform(self.center_lookat, self.getName("%s_npo" % ctlNames[i]), t) npoBase = npo if i == 2: # we add an extra level to input the tracking ofset values npo = addTransform(npo, self.getName("%s_trk" % ctlNames[i]), t) self.trackLvl.append(npo) ctl = self.addCtl(npo, "%s_%s" % (ctlNames[i], self.ctlName), t, color, icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=offset ) self.addToSubGroup(self.over_ctl, self.primaryControllersGroupName) ymt_util.setKeyableAttributesDontLockVisibility(ctl, params) ctls.append(ctl) # adding parent average contrains to odd controls for i, ctl in enumerate(ctls): if utils.is_odd(i): s = ctls[i - 1] d = ctls[i + 1] pm.parentConstraint(s, d, ctl.getParent(), mo=True) applyop.gear_curvecns_op(crv, ctls) return ctls
def addControllers(self): 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, []]] self.upNpos, self.upCtls, self.upUpvs = self._addControls( self.upCrv_ctl, upCtlOptions, False) self.lowNpos, self.lowCtls, self.lowUpvs = self._addControls( self.lowCrv_ctl, lowCtlOptions, True) self.lips_R_Corner_ctl = self.upCtls[0] self.lips_R_upOuter_ctl = self.upCtls[1] self.lips_R_upInner_ctl = self.upCtls[2] self.lips_C_upper_ctl = self.upCtls[3] self.lips_L_upInner_ctl = self.upCtls[4] self.lips_L_upOuter_ctl = self.upCtls[5] self.lips_L_Corner_ctl = self.upCtls[6] self.lips_R_lowOuter_ctl = self.lowCtls[0] self.lips_R_lowInner_ctl = self.lowCtls[1] self.lips_C_lower_ctl = self.lowCtls[2] self.lips_L_lowInner_ctl = self.lowCtls[3] self.lips_L_lowOuter_ctl = self.lowCtls[4] self.lips_R_Corner_npo = self.upNpos[0] self.lips_R_upOuter_npo = self.upNpos[1] self.lips_R_upInner_npo = self.upNpos[2] self.lips_C_upper_npo = self.upNpos[3] self.lips_L_upInner_npo = self.upNpos[4] self.lips_L_upOuter_npo = self.upNpos[5] self.lips_L_Corner_npo = self.upNpos[6] self.lips_R_lowOuter_npo = self.lowNpos[0] self.lips_R_lowInner_npo = self.lowNpos[1] self.lips_C_lower_npo = self.lowNpos[2] self.lips_L_lowInner_npo = self.lowNpos[3] self.lips_L_lowOuter_npo = self.lowNpos[4] upvec = self.upUpvs + self.lowUpvs pm.parent(self.lips_R_upOuter_npo, self.lips_R_lowOuter_npo, self.lips_R_Corner_ctl) pm.parent(self.lips_R_upInner_npo, self.lips_L_upInner_npo, self.lips_C_upper_ctl) pm.parent(self.lips_L_upOuter_npo, self.lips_L_lowOuter_npo, self.lips_L_Corner_ctl) pm.parent(self.lips_R_lowInner_npo, self.lips_L_lowInner_npo, self.lips_C_lower_ctl) # Connecting control crvs with controls applyop.gear_curvecns_op(self.upCrv_ctl, self.upCtls) applyop.gear_curvecns_op(self.lowCrv_ctl, [self.upCtls[0]] + self.lowCtls + [self.upCtls[-1]]) applyop.gear_curvecns_op(self.upCrv_upv, upvec) applyop.gear_curvecns_op(self.lowCrv_upv, [upvec[0]] + self.lowUpvs + [upvec[-1]]) # adding wires pm.wire(self.upCrv, w=self.upCrv_ctl, dropoffDistance=[0, 1000]) pm.wire(self.lowCrv, w=self.lowCrv_ctl, dropoffDistance=[0, 1000]) pm.wire(self.upRope, w=self.upCrv_ctl, dropoffDistance=[0, 1000]) pm.wire(self.lowRope, w=self.lowCrv_ctl, dropoffDistance=[0, 1000]) pm.wire(self.upRope_upv, w=self.upCrv_upv, dropoffDistance=[0, 1000]) pm.wire(self.lowRope_upv, w=self.lowCrv_upv, dropoffDistance=[0, 1000]) return
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # 1 bone chain Upv ref ============================== self.ikHandleUpvRef = primitive.addIkHandle( self.root, self.getName("ikHandleLegChainUpvRef"), self.legChainUpvRef, "ikSCsolver") pm.pointConstraint(self.ik_ctl, self.ikHandleUpvRef) pm.parentConstraint(self.legChainUpvRef[0], self.ik_ctl, self.upv_cns, mo=True) # Visibilities ------------------------------------- # shape.dispGeometry # fk fkvis_node = node.createReverseNode(self.blend_att) for shp in self.fk0_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk1_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk2_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) # ik for shp in self.upv_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ikcns_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ik_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.line_ref.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) # IK Solver ----------------------------------------- out = [self.bone0, self.bone1, self.ctrn_loc, self.eff_loc] o_node = applyop.gear_ikfk2bone_op(out, self.root_ctl, self.ik_ref, self.upv_ctl, self.fk_ctl[0], self.fk_ctl[1], self.fk_ref, self.length0, self.length1, self.negate) pm.connectAttr(self.blend_att, o_node + ".blend") if self.negate: mulVal = -1 else: mulVal = 1 node.createMulNode(self.roll_att, mulVal, o_node + ".roll") # pm.connectAttr(self.roll_att, o_node+".roll") pm.connectAttr(self.scale_att, o_node + ".scaleA") pm.connectAttr(self.scale_att, o_node + ".scaleB") pm.connectAttr(self.maxstretch_att, o_node + ".maxstretch") pm.connectAttr(self.slide_att, o_node + ".slide") pm.connectAttr(self.softness_att, o_node + ".softness") pm.connectAttr(self.reverse_att, o_node + ".reverse") # Twist references --------------------------------- o_node = applyop.gear_mulmatrix_op( self.eff_loc.attr("worldMatrix"), self.root.attr("worldInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputTranslate", self.tws2_npo.attr("translate")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputRotate", self.tws2_npo.attr("rotate")) # spline IK for twist jnts self.ikhUpLegTwist, self.uplegTwistCrv = applyop.splineIK( self.getName("uplegTwist"), self.uplegTwistChain, parent=self.root, cParent=self.bone0) self.ikhLowLegTwist, self.lowlegTwistCrv = applyop.splineIK( self.getName("lowlegTwist"), self.lowlegTwistChain, parent=self.root, cParent=self.bone1) # references self.ikhUpLegRef, self.tmpCrv = applyop.splineIK( self.getName("uplegRollRef"), self.uplegRollRef, parent=self.root, cParent=self.bone0) self.ikhLowLegRef, self.tmpCrv = applyop.splineIK( self.getName("lowlegRollRef"), self.lowlegRollRef, parent=self.root, cParent=self.eff_loc) self.ikhAuxTwist, self.tmpCrv = applyop.splineIK( self.getName("auxTwist"), self.auxTwistChain, parent=self.root, cParent=self.eff_loc) # setting connexions for ikhUpLegTwist self.ikhUpLegTwist.attr("dTwistControlEnable").set(True) self.ikhUpLegTwist.attr("dWorldUpType").set(4) self.ikhUpLegTwist.attr("dWorldUpAxis").set(3) self.ikhUpLegTwist.attr("dWorldUpVectorZ").set(1.0) self.ikhUpLegTwist.attr("dWorldUpVectorY").set(0.0) self.ikhUpLegTwist.attr("dWorldUpVectorEndZ").set(1.0) self.ikhUpLegTwist.attr("dWorldUpVectorEndY").set(0.0) pm.connectAttr(self.uplegRollRef[0].attr("worldMatrix[0]"), self.ikhUpLegTwist.attr("dWorldUpMatrix")) pm.connectAttr(self.bone0.attr("worldMatrix[0]"), self.ikhUpLegTwist.attr("dWorldUpMatrixEnd")) # setting connexions for ikhAuxTwist self.ikhAuxTwist.attr("dTwistControlEnable").set(True) self.ikhAuxTwist.attr("dWorldUpType").set(4) self.ikhAuxTwist.attr("dWorldUpAxis").set(3) self.ikhAuxTwist.attr("dWorldUpVectorZ").set(1.0) self.ikhAuxTwist.attr("dWorldUpVectorY").set(0.0) self.ikhAuxTwist.attr("dWorldUpVectorEndZ").set(1.0) self.ikhAuxTwist.attr("dWorldUpVectorEndY").set(0.0) pm.connectAttr(self.lowlegRollRef[0].attr("worldMatrix[0]"), self.ikhAuxTwist.attr("dWorldUpMatrix")) pm.connectAttr(self.tws_ref.attr("worldMatrix[0]"), self.ikhAuxTwist.attr("dWorldUpMatrixEnd")) pm.connectAttr(self.auxTwistChain[1].attr("rx"), self.ikhLowLegTwist.attr("twist")) pm.parentConstraint(self.bone1, self.aux_npo, maintainOffset=True) # scale arm length for twist chain (not the squash and stretch) arclen_node = pm.arclen(self.uplegTwistCrv, ch=True) alAttrUpLeg = arclen_node.attr("arcLength") muldiv_nodeArm = pm.createNode("multiplyDivide") pm.connectAttr(arclen_node.attr("arcLength"), muldiv_nodeArm.attr("input1X")) muldiv_nodeArm.attr("input2X").set(alAttrUpLeg.get()) muldiv_nodeArm.attr("operation").set(2) for jnt in self.uplegTwistChain: pm.connectAttr(muldiv_nodeArm.attr("outputX"), jnt.attr("sx")) # scale forearm length for twist chain (not the squash and stretch) arclen_node = pm.arclen(self.lowlegTwistCrv, ch=True) alAttrLowLeg = arclen_node.attr("arcLength") muldiv_nodeLowLeg = pm.createNode("multiplyDivide") pm.connectAttr(arclen_node.attr("arcLength"), muldiv_nodeLowLeg.attr("input1X")) muldiv_nodeLowLeg.attr("input2X").set(alAttrLowLeg.get()) muldiv_nodeLowLeg.attr("operation").set(2) for jnt in self.lowlegTwistChain: pm.connectAttr(muldiv_nodeLowLeg.attr("outputX"), jnt.attr("sx")) # scale compensation for the first twist join dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(self.root.attr("worldMatrix[0]"), dm_node.attr("inputMatrix")) pm.connectAttr(dm_node.attr("outputScale"), self.uplegTwistChain[0].attr("inverseScale")) pm.connectAttr(dm_node.attr("outputScale"), self.lowlegTwistChain[0].attr("inverseScale")) # tangent controls muldiv_node = pm.createNode("multiplyDivide") muldiv_node.attr("input2X").set(-1) pm.connectAttr(self.tws1A_npo.attr("rz"), muldiv_node.attr("input1X")) muldiv_nodeBias = pm.createNode("multiplyDivide") pm.connectAttr(muldiv_node.attr("outputX"), muldiv_nodeBias.attr("input1X")) pm.connectAttr(self.roundness_att, muldiv_nodeBias.attr("input2X")) pm.connectAttr(muldiv_nodeBias.attr("outputX"), self.tws1A_loc.attr("rz")) if self.negate: axis = "xz" else: axis = "-xz" applyop.aimCns(self.tws1A_npo, self.tws0_loc, axis=axis, wupType=2, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) applyop.aimCns(self.lowlegTangentB_loc, self.lowlegTangentA_npo, axis=axis, wupType=2, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) pm.pointConstraint(self.eff_loc, self.lowlegTangentB_loc) muldiv_node = pm.createNode("multiplyDivide") muldiv_node.attr("input2X").set(-1) pm.connectAttr(self.tws1B_npo.attr("rz"), muldiv_node.attr("input1X")) muldiv_nodeBias = pm.createNode("multiplyDivide") pm.connectAttr(muldiv_node.attr("outputX"), muldiv_nodeBias.attr("input1X")) pm.connectAttr(self.roundness_att, muldiv_nodeBias.attr("input2X")) pm.connectAttr(muldiv_nodeBias.attr("outputX"), self.tws1B_loc.attr("rz")) if self.negate: axis = "-xz" else: axis = "xz" applyop.aimCns(self.tws1B_npo, self.tws2_loc, axis=axis, wupType=2, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) applyop.aimCns(self.uplegTangentA_loc, self.uplegTangentB_npo, axis=axis, wupType=2, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) # Volume ------------------------------------------- distA_node = node.createDistNode(self.tws0_loc, self.tws1_loc) distB_node = node.createDistNode(self.tws1_loc, self.tws2_loc) add_node = node.createAddNode(distA_node + ".distance", distB_node + ".distance") div_node = node.createDivNode(add_node + ".output", self.root_ctl.attr("sx")) # comp scaling issue dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(self.root.attr("worldMatrix"), dm_node + ".inputMatrix") div_node2 = node.createDivNode(div_node + ".outputX", dm_node + ".outputScaleX") self.volDriver_att = div_node2 + ".outputX" # connecting tangent scaele compensation after volume to # avoid duplicate some nodes distA_node = node.createDistNode(self.tws0_loc, self.mid_ctl) distB_node = node.createDistNode(self.mid_ctl, self.tws2_loc) div_nodeUpLeg = node.createDivNode(distA_node + ".distance", dm_node.attr("outputScaleX")) div_node2 = node.createDivNode(div_nodeUpLeg + ".outputX", distA_node.attr("distance").get()) pm.connectAttr(div_node2.attr("outputX"), self.tws1A_loc.attr("sx")) pm.connectAttr(div_node2.attr("outputX"), self.uplegTangentA_loc.attr("sx")) div_nodeLowLeg = node.createDivNode(distB_node + ".distance", dm_node.attr("outputScaleX")) div_node2 = node.createDivNode(div_nodeLowLeg + ".outputX", distB_node.attr("distance").get()) pm.connectAttr(div_node2.attr("outputX"), self.tws1B_loc.attr("sx")) pm.connectAttr(div_node2.attr("outputX"), self.lowlegTangentB_loc.attr("sx")) # conection curve cnts = [ self.uplegTangentA_loc, self.uplegTangentA_ctl, self.uplegTangentB_ctl, self.kneeTangent_ctl ] applyop.gear_curvecns_op(self.uplegTwistCrv, cnts) cnts = [ self.kneeTangent_ctl, self.lowlegTangentA_ctl, self.lowlegTangentB_ctl, self.lowlegTangentB_loc ] applyop.gear_curvecns_op(self.lowlegTwistCrv, cnts) # Tangent controls vis for shp in self.uplegTangentA_ctl.getShapes(): pm.connectAttr(self.tangentVis_att, shp.attr("visibility")) for shp in self.uplegTangentB_ctl.getShapes(): pm.connectAttr(self.tangentVis_att, shp.attr("visibility")) for shp in self.lowlegTangentA_ctl.getShapes(): pm.connectAttr(self.tangentVis_att, shp.attr("visibility")) for shp in self.lowlegTangentB_ctl.getShapes(): pm.connectAttr(self.tangentVis_att, shp.attr("visibility")) for shp in self.kneeTangent_ctl.getShapes(): pm.connectAttr(self.tangentVis_att, shp.attr("visibility")) # Divisions ---------------------------------------- # at 0 or 1 the division will follow exactly the rotation of the # controler.. and we wont have this nice tangent + roll for i, div_cns in enumerate(self.div_cns): if i < (self.settings["div0"] + 2): mulmat_node = applyop.gear_mulmatrix_op( self.uplegTwistChain[i] + ".worldMatrix", div_cns + ".parentInverseMatrix") lastUpLegDiv = div_cns else: o_node = self.lowlegTwistChain[i - (self.settings["div0"] + 2)] mulmat_node = applyop.gear_mulmatrix_op( o_node + ".worldMatrix", div_cns + ".parentInverseMatrix") lastLowLegDiv = div_cns dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") pm.connectAttr(dm_node + ".outputTranslate", div_cns + ".t") pm.connectAttr(dm_node + ".outputRotate", div_cns + ".r") # Squash n Stretch o_node = applyop.gear_squashstretch2_op( div_cns, None, pm.getAttr(self.volDriver_att), "x") pm.connectAttr(self.volume_att, o_node + ".blend") pm.connectAttr(self.volDriver_att, o_node + ".driver") pm.connectAttr(self.st_att[i], o_node + ".stretch") pm.connectAttr(self.sq_att[i], o_node + ".squash") # force translation for last loc arm and foreamr applyop.gear_mulmatrix_op(self.kneeTangent_ctl.worldMatrix, lastUpLegDiv.parentInverseMatrix, lastUpLegDiv, "t") applyop.gear_mulmatrix_op(self.tws2_loc.worldMatrix, lastLowLegDiv.parentInverseMatrix, lastLowLegDiv, "t") # NOTE: next line fix the issue on meters. # This is special case becasuse the IK solver from mGear use the # scale as lenght and we have shear # TODO: check for a more clean and elegant solution instead of # re-match the world matrix again transform.matchWorldTransform(self.fk_ctl[0], self.match_fk0_off) transform.matchWorldTransform(self.fk_ctl[1], self.match_fk1_off) transform.matchWorldTransform(self.fk_ctl[0], self.match_fk0) transform.matchWorldTransform(self.fk_ctl[1], self.match_fk1) # match IK/FK ref pm.parentConstraint(self.bone0, self.match_fk0_off, mo=True) pm.parentConstraint(self.bone1, self.match_fk1_off, mo=True) return
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 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")