Created
March 7, 2026 22:52
-
-
Save StevenGoehrig/2bf1c8a94311bf0b9008bc37413d656b to your computer and use it in GitHub Desktop.
Updated mGear Lips Rigger
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """Rigbits lips rigger tool""" | |
| import json | |
| from functools import partial | |
| from six import string_types | |
| import mgear.core.pyqt as gqt | |
| import pymel.core as pm | |
| from maya.app.general.mayaMixin import MayaQWidgetDockableMixin | |
| from mgear.vendor.Qt import QtCore, QtWidgets | |
| from pymel.core import datatypes | |
| from mgear import rigbits | |
| from mgear.core import meshNavigation, curve, applyop, primitive, icon | |
| from mgear.core import transform, attribute, skin, vector | |
| from . import lib | |
| ########################################################## | |
| # Lips rig constructor | |
| ########################################################## | |
| 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 | |
| # skinning 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 skinning | |
| 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 curve 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=17, #CHANGES HERE | |
| parent=lipsCrv_root) | |
| lowCrv_ctl = curve.createCurveFromCurve(lowCrv, | |
| setName("lowCtl_crv"), | |
| nbPoints=17, #CHANGES HERE | |
| 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=17, #CHANGES HERE | |
| parent=lipsCrv_root) | |
| lowCrv_upv = curve.createCurveFromCurve(lowCrv, | |
| setName("lowCrv_upv"), | |
| nbPoints=17, #CHANGES HERE | |
| 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 vertex list here to skip 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") | |
| print('cvs: ' + str(len(cvs))) | |
| 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) | |
| #CHANGES BEGIN | |
| #Establish name symmetry | |
| if i < (len(cvs)-1)/2: | |
| jointSuffix = "_R_" + str(i).zfill(3) | |
| elif i > (len(cvs)-1)/2: | |
| jointSuffix = "_L_" + str(len(cvs) - 1 - i).zfill(3) | |
| else: | |
| jointSuffix = "_C" | |
| #CHANGES END | |
| oTransUpV = pm.PyNode(pm.createNode( | |
| lvlType, | |
| n=setName("upLipRopeUpv" + jointSuffix), | |
| p=lipsRope_root, | |
| ss=True)) | |
| oTrans = pm.PyNode( | |
| pm.createNode(lvlType, | |
| n=setName("upLipRope" + jointSuffix), | |
| p=lipsRope_root, ss=True)) | |
| oParam, oLength = curve.getCurveParamAtPosition(upRope, cv) | |
| uLength = curve.findLenghtFromParam(upRope, oParam) | |
| u = uLength / oLength * (NB_ROPE - 3) #CHANGES HERE | |
| cnsUpv = applyop.pathCns( | |
| oTransUpV, upRope_upv, cnsType=True, u=u, tangent=False) #CHANGES HERE | |
| cns = applyop.pathCns( | |
| oTrans, upRope, cnsType=True, u=u, tangent=False) #CHANGES HERE | |
| 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) | |
| #CHANGES BEGIN | |
| #Establish name symmetry | |
| if i < (len(cvs)-1)/2: | |
| jointSuffix = "_R_" + str(i).zfill(3) | |
| elif i > (len(cvs)-1)/2: | |
| jointSuffix = "_L_" + str(len(cvs) - 1 - i).zfill(3) | |
| else: | |
| jointSuffix = "_C" | |
| #CHANGES END | |
| oTransUpV = pm.PyNode(pm.createNode( | |
| lvlType, | |
| n=setName("lowLipRopeUpv" + jointSuffix), | |
| p=lipsRope_root, | |
| ss=True)) | |
| oTrans = pm.PyNode(pm.createNode( | |
| lvlType, | |
| n=setName("lowLipRope" + jointSuffix), | |
| p=lipsRope_root, | |
| ss=True)) | |
| oParam, oLength = curve.getCurveParamAtPosition(lowRope, cv) | |
| uLength = curve.findLenghtFromParam(lowRope, oParam) | |
| u = uLength / oLength * (NB_ROPE - 3) #CHANGES HERE | |
| cnsUpv = applyop.pathCns(oTransUpV, | |
| lowRope_upv, | |
| cnsType=True, #CHANGES HERE | |
| u=u, | |
| tangent=False) | |
| cns = applyop.pathCns(oTrans, | |
| lowRope, | |
| cnsType=True, #CHANGES HERE | |
| 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", "circle", 14, .03, []], #CHANGES HERE | |
| ["upOuter", "R", "circle", 14, .03, []], | |
| ["upInner", "R", "circle", 14, .03, []], | |
| ["upper", "C", "circle", 14, .03, []], #CHANGES HERE | |
| ["upInner", "L", "circle", 14, .03, []], | |
| ["upOuter", "L", "circle", 14, .03, []], | |
| ["corner", "L", "circle", 14, .03, []]] #CHANGES HERE | |
| lowCtlOptions = [["lowOuter", "R", "circle", 14, .03, []], | |
| ["lowInner", "R", "circle", 14, .03, []], | |
| ["lower", "C", "circle", 14, .03, []], #CHANGES HERE | |
| ["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 | |
| ctlIndexes = [0, 2, 5, 8, 11, 14, 16] #CHANGES HERE | |
| for i, cv in enumerate(cvs): | |
| if i in ctlIndexes: #CHANGES HERE | |
| 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[round((i+1)/3)][0] #CHANGES BEGIN | |
| oSide = upCtlOptions[round((i+1)/3)][1] | |
| o_icon = upCtlOptions[round((i+1)/3)][2] | |
| color = upCtlOptions[round((i+1)/3)][3] | |
| wd = upCtlOptions[round((i+1)/3)][4] | |
| oPar = upCtlOptions[round((i+1)/3)][5] #CHANGES END | |
| 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)) | |
| ctlIndexes = [2, 5, 8, 11, 14] #CHANGES HERE | |
| for i, cv in enumerate(cvs): | |
| if i in ctlIndexes: #CHANGES HERE | |
| 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[round((i+1)/3-1)][0] #CHANGES BEGIN | |
| oSide = lowCtlOptions[round((i+1)/3-1)][1] | |
| o_icon = lowCtlOptions[round((i+1)/3-1)][2] | |
| color = lowCtlOptions[round((i+1)/3-1)][3] | |
| wd = lowCtlOptions[round((i+1)/3-1)][4] | |
| oPar = lowCtlOptions[round((i+1)/3-1)][5] #CHANGES END | |
| 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) | |
| #CHANGES BEGIN | |
| # Create Mouth controls | |
| mouthPositions = [pm.PyNode('lips_R_corner_npo'), | |
| pm.PyNode('lips_L_corner_npo'), | |
| pm.PyNode('lips_C_upper_npo'), | |
| pm.PyNode('lips_C_lower_npo')] | |
| mouthCtls = [] | |
| for mPos in mouthPositions: | |
| pm.select(cl=True) | |
| newNPO = pm.createNode('transform', n=mPos.name().replace('_npo', 'Mouth_npo'), p=lips_root) | |
| pm.matchTransform(newNPO, mPos) | |
| pm.select(newNPO) | |
| newCtl = rigbits.createCTL(child=True) | |
| pm.rename(newCtl, mPos.name().replace('_npo', 'Mouth_ctl')) | |
| if mouthPositions.index(mPos) == 2: | |
| pm.xform(newCtl, ro=(90, 0, 0), t=(0, 0.5, 1)) | |
| elif mouthPositions.index(mPos) == 3: | |
| pm.xform(newCtl, ro=(90, 0, 0), t=(0, -0.5, 1)) | |
| else: | |
| pm.xform(newCtl, ro=(90, 0, 0), t=(0, 0, 1)) | |
| pm.makeIdentity(apply=True) | |
| mouthCtls.append(pm.listRelatives(newNPO)[0]) | |
| #CHANGES END | |
| # reparenting controls | |
| pm.parent(upNpo[1], lowNpo[0], upNpo[0], mouthCtls[0]) # CHANGES BEGIN | |
| pm.parent(upNpo[2], upNpo[4], upNpo[3], mouthCtls[2]) | |
| pm.parent(upNpo[-2], lowNpo[-1], upNpo[-1], mouthCtls[1]) | |
| pm.parent(lowNpo[1], lowNpo[3], lowNpo[2], mouthCtls[3]) #CHANGES END | |
| # Connecting control crvs with controls | |
| #Up lip controls | |
| for i, item in enumerate(upControls): #CHANGES BEGIN | |
| node = pm.createNode("decomposeMatrix") | |
| pm.connectAttr(item + ".worldMatrix[0]", node + ".inputMatrix") | |
| if i == 0: | |
| pm.connectAttr( | |
| node + ".outputTranslate", upCrv_ctl + ".controlPoints[%s]" % i | |
| ) | |
| elif i == 6: | |
| pm.connectAttr( | |
| node + ".outputTranslate", upCrv_ctl + ".controlPoints[%s]" % 16 | |
| ) | |
| else: | |
| pm.connectAttr( | |
| node + ".outputTranslate", upCrv_ctl + ".controlPoints[%s]" % (i * 3) | |
| ) | |
| pm.connectAttr( | |
| node + ".outputTranslate", upCrv_ctl + ".controlPoints[%s]" % (i * 3 - 1) | |
| ) | |
| pm.connectAttr( | |
| node + ".outputTranslate", upCrv_ctl + ".controlPoints[%s]" % (i * 3 - 2) | |
| ) | |
| #Low lip controls | |
| for i, item in enumerate([upControls[0]] + lowControls + [upControls[-1]]): | |
| node = pm.createNode("decomposeMatrix") | |
| pm.connectAttr(item + ".worldMatrix[0]", node + ".inputMatrix") | |
| if i == 0: | |
| pm.connectAttr( | |
| node + ".outputTranslate", lowCrv_ctl + ".controlPoints[%s]" % i | |
| ) | |
| elif i == 6: | |
| pm.connectAttr( | |
| node + ".outputTranslate", lowCrv_ctl + ".controlPoints[%s]" % 16 | |
| ) | |
| else: | |
| pm.connectAttr( | |
| node + ".outputTranslate", lowCrv_ctl + ".controlPoints[%s]" % (i * 3) | |
| ) | |
| pm.connectAttr( | |
| node + ".outputTranslate", lowCrv_ctl + ".controlPoints[%s]" % (i * 3 - 1) | |
| ) | |
| pm.connectAttr( | |
| node + ".outputTranslate", lowCrv_ctl + ".controlPoints[%s]" % (i * 3 - 2) | |
| ) | |
| #Up lip up vectors | |
| for i, item in enumerate(upVec): | |
| node = pm.createNode("decomposeMatrix") | |
| pm.connectAttr(item + ".worldMatrix[0]", node + ".inputMatrix") | |
| if i == 0: | |
| pm.connectAttr( | |
| node + ".outputTranslate", upCrv_upv + ".controlPoints[%s]" % i | |
| ) | |
| elif i == 6: | |
| pm.connectAttr( | |
| node + ".outputTranslate", upCrv_upv + ".controlPoints[%s]" % 16 | |
| ) | |
| else: | |
| pm.connectAttr( | |
| node + ".outputTranslate", upCrv_upv + ".controlPoints[%s]" % (i * 3) | |
| ) | |
| pm.connectAttr( | |
| node + ".outputTranslate", upCrv_upv + ".controlPoints[%s]" % (i * 3 - 1) | |
| ) | |
| pm.connectAttr( | |
| node + ".outputTranslate", upCrv_upv + ".controlPoints[%s]" % (i * 3 - 2) | |
| ) | |
| #Low lip up vectors | |
| for i, item in enumerate([upVec[0]] + lowVec + [upVec[-1]]): | |
| node = pm.createNode("decomposeMatrix") | |
| pm.connectAttr(item + ".worldMatrix[0]", node + ".inputMatrix") | |
| if i == 0: | |
| pm.connectAttr( | |
| node + ".outputTranslate", lowCrv_upv + ".controlPoints[%s]" % i | |
| ) | |
| elif i == 6: | |
| pm.connectAttr( | |
| node + ".outputTranslate", lowCrv_upv + ".controlPoints[%s]" % 16 | |
| ) | |
| else: | |
| pm.connectAttr( | |
| node + ".outputTranslate", lowCrv_upv + ".controlPoints[%s]" % (i * 3) | |
| ) | |
| pm.connectAttr( | |
| node + ".outputTranslate", lowCrv_upv + ".controlPoints[%s]" % (i * 3 - 1) | |
| ) | |
| pm.connectAttr( | |
| node + ".outputTranslate", lowCrv_upv + ".controlPoints[%s]" % (i * 3 - 2) | |
| ) | |
| #CHANGES END | |
| # 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 constraints | |
| # up | |
| cns_node = pm.parentConstraint(mouthCtls[0], | |
| mouthCtls[2], | |
| upControls[1].getParent(), | |
| mo=True, | |
| skipRotate=["x", "y", "z"]) | |
| cns_node.attr(mouthCtls[0].name() + "W0").set(.75) | |
| cns_node.attr(mouthCtls[2].name() + "W1").set(.25) | |
| cns_node.interpType.set(0) # noFlip | |
| cns_node = pm.parentConstraint(mouthCtls[0], | |
| mouthCtls[2], | |
| upControls[2].getParent(), | |
| mo=True, | |
| skipRotate=["x", "y", "z"]) | |
| cns_node.attr(mouthCtls[0].name() + "W0").set(.25) | |
| cns_node.attr(mouthCtls[2].name() + "W1").set(.75) | |
| cns_node.interpType.set(0) # noFlip | |
| cns_node = pm.parentConstraint(mouthCtls[2], | |
| mouthCtls[1], | |
| upControls[4].getParent(), | |
| mo=True, | |
| skipRotate=["x", "y", "z"]) | |
| cns_node.attr(mouthCtls[2].name() + "W0").set(.75) | |
| cns_node.attr(mouthCtls[1].name() + "W1").set(.25) | |
| cns_node.interpType.set(0) # noFlip | |
| cns_node = pm.parentConstraint(mouthCtls[2], | |
| mouthCtls[1], | |
| upControls[5].getParent(), | |
| mo=True, | |
| skipRotate=["x", "y", "z"]) | |
| cns_node.attr(mouthCtls[2].name() + "W0").set(.25) | |
| cns_node.attr(mouthCtls[1].name() + "W1").set(.75) | |
| cns_node.interpType.set(0) # noFlip | |
| # low | |
| cns_node = pm.parentConstraint(mouthCtls[0], | |
| mouthCtls[3], | |
| lowControls[0].getParent(), | |
| mo=True, | |
| skipRotate=["x", "y", "z"]) | |
| cns_node.attr(mouthCtls[0].name() + "W0").set(.75) | |
| cns_node.attr(mouthCtls[3].name() + "W1").set(.25) | |
| cns_node.interpType.set(0) # noFlip | |
| cns_node = pm.parentConstraint(mouthCtls[0], | |
| mouthCtls[3], | |
| lowControls[1].getParent(), | |
| mo=True, | |
| skipRotate=["x", "y", "z"]) | |
| cns_node.attr(mouthCtls[0].name() + "W0").set(.25) | |
| cns_node.attr(mouthCtls[3].name() + "W1").set(.75) | |
| cns_node.interpType.set(0) # noFlip | |
| cns_node = pm.parentConstraint(mouthCtls[3], | |
| mouthCtls[1], | |
| lowControls[3].getParent(), | |
| mo=True, | |
| skipRotate=["x", "y", "z"]) | |
| cns_node.attr(mouthCtls[3].name() + "W0").set(.75) | |
| cns_node.attr(mouthCtls[1].name() + "W1").set(.25) | |
| cns_node.interpType.set(0) # noFlip | |
| cns_node = pm.parentConstraint(mouthCtls[3], | |
| mouthCtls[1], | |
| lowControls[4].getParent(), | |
| mo=True, | |
| skipRotate=["x", "y", "z"]) | |
| cns_node.attr(mouthCtls[3].name() + "W0").set(.25) | |
| cns_node.attr(mouthCtls[1].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], | |
| mouthCtls[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], | |
| mouthCtls[1].getParent(), | |
| mo=True) | |
| cns_node.interpType.set(0) # noFlip | |
| # up control connection | |
| cns_node = pm.parentConstraint(ref_cns_list[0], | |
| mouthCtls[2].getParent(), | |
| mo=True) | |
| # low control connection | |
| cns_node = pm.parentConstraint(ref_cns_list[1], | |
| mouthCtls[3].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) | |
| ########################################################## | |
| # Lips Rig UI | |
| ########################################################## | |
| class ui(MayaQWidgetDockableMixin, QtWidgets.QDialog): | |
| valueChanged = QtCore.Signal(int) | |
| def __init__(self, parent=None): | |
| super(ui, self).__init__(parent) | |
| self.filter = "Lips Rigger Configuration .lips (*.lips)" | |
| self.create() | |
| def create(self): | |
| self.setWindowTitle("Lips Rigger") | |
| self.setWindowFlags(QtCore.Qt.Window) | |
| self.setAttribute(QtCore.Qt.WA_DeleteOnClose, 1) | |
| self.create_controls() | |
| self.create_layout() | |
| self.create_connections() | |
| def create_controls(self): | |
| # Geometry input controls | |
| self.geometryInput_group = QtWidgets.QGroupBox("Geometry Input") | |
| self.edgedge_loop_label = QtWidgets.QLabel("Edge Loop:") | |
| self.edge_loop = QtWidgets.QLineEdit() | |
| self.edge_loop_button = QtWidgets.QPushButton("<<") | |
| self.up_vertex_label = QtWidgets.QLabel("Upper Vertex:") | |
| self.up_vertex = QtWidgets.QLineEdit() | |
| self.up_vertex_button = QtWidgets.QPushButton("<<") | |
| self.low_vertex_label = QtWidgets.QLabel("Lower Vertex:") | |
| self.low_vertex = QtWidgets.QLineEdit() | |
| self.low_vertex_button = QtWidgets.QPushButton("<<") | |
| # Name prefix | |
| self.prefix_group = QtWidgets.QGroupBox("Name Prefix") | |
| self.name_prefix = QtWidgets.QLineEdit() | |
| self.name_prefix.setText("lips") | |
| # control extension | |
| self.control_group = QtWidgets.QGroupBox("Control Name Extension") | |
| self.control_name = QtWidgets.QLineEdit() | |
| self.control_name.setText("ctl") | |
| # joints | |
| self.joints_group = QtWidgets.QGroupBox("Joints") | |
| self.head_joint_label = QtWidgets.QLabel("Head or Upper Lip Joint:") | |
| self.head_joint = QtWidgets.QLineEdit() | |
| self.head_joint_button = QtWidgets.QPushButton("<<") | |
| self.jaw_joint_label = QtWidgets.QLabel("Jaw or Lower Lip Joint:") | |
| self.jaw_joint = QtWidgets.QLineEdit() | |
| self.jaw_joint_button = QtWidgets.QPushButton("<<") | |
| # Lips Controls | |
| self.control_ref_group = QtWidgets.QGroupBox( | |
| "Lips Base Controls (Optional. Required for Shifter Game tools)") | |
| self.upper_lip_ctl_label = QtWidgets.QLabel("Upper Lip Control:") | |
| self.upper_lip_ctl = QtWidgets.QLineEdit() | |
| self.upper_lip_ctl_button = QtWidgets.QPushButton("<<") | |
| self.lower_lip_ctl_label = QtWidgets.QLabel("Lower Lip Control:") | |
| self.lower_lip_ctl = QtWidgets.QLineEdit() | |
| self.lower_lip_ctl_button = QtWidgets.QPushButton("<<") | |
| # Topological Autoskin | |
| self.topoSkin_group = QtWidgets.QGroupBox("Skin") | |
| self.rigid_loops_label = QtWidgets.QLabel("Rigid Loops:") | |
| self.rigid_loops = QtWidgets.QSpinBox() | |
| self.rigid_loops.setRange(0, 30) | |
| self.rigid_loops.setSingleStep(1) | |
| self.rigid_loops.setValue(5) | |
| self.falloff_loops_label = QtWidgets.QLabel("Falloff Loops:") | |
| self.falloff_loops = QtWidgets.QSpinBox() | |
| self.falloff_loops.setRange(0, 30) | |
| self.falloff_loops.setSingleStep(1) | |
| self.falloff_loops.setValue(8) | |
| self.do_skin = QtWidgets.QCheckBox( | |
| 'Compute Topological Autoskin') | |
| self.do_skin.setChecked(True) | |
| # Options | |
| self.options_group = QtWidgets.QGroupBox("Options") | |
| self.thickness_label = QtWidgets.QLabel("Lips Thickness:") | |
| self.thickness = QtWidgets.QDoubleSpinBox() | |
| self.thickness.setRange(0, 10) | |
| self.thickness.setSingleStep(.01) | |
| self.thickness.setValue(.03) | |
| self.parent_label = QtWidgets.QLabel("Static Rig Parent:") | |
| self.parent_node = QtWidgets.QLineEdit() | |
| self.parent_button = QtWidgets.QPushButton("<<") | |
| # Build button | |
| self.build_button = QtWidgets.QPushButton("Build Lips Rig") | |
| self.import_button = QtWidgets.QPushButton("Import Config from json") | |
| self.export_button = QtWidgets.QPushButton("Export Config to json") | |
| def create_layout(self): | |
| # Edge Loop Layout | |
| edgedge_loop_layout = QtWidgets.QHBoxLayout() | |
| edgedge_loop_layout.setContentsMargins(1, 1, 1, 1) | |
| edgedge_loop_layout.addWidget(self.edgedge_loop_label) | |
| edgedge_loop_layout.addWidget(self.edge_loop) | |
| edgedge_loop_layout.addWidget(self.edge_loop_button) | |
| # Outer Edge Loop Layout | |
| up_vertex_layout = QtWidgets.QHBoxLayout() | |
| up_vertex_layout.setContentsMargins(1, 1, 1, 1) | |
| up_vertex_layout.addWidget(self.up_vertex_label) | |
| up_vertex_layout.addWidget(self.up_vertex) | |
| up_vertex_layout.addWidget(self.up_vertex_button) | |
| # inner Edge Loop Layout | |
| low_vertex_layout = QtWidgets.QHBoxLayout() | |
| low_vertex_layout.setContentsMargins(1, 1, 1, 1) | |
| low_vertex_layout.addWidget(self.low_vertex_label) | |
| low_vertex_layout.addWidget(self.low_vertex) | |
| low_vertex_layout.addWidget(self.low_vertex_button) | |
| # Geometry Input Layout | |
| geometryInput_layout = QtWidgets.QVBoxLayout() | |
| geometryInput_layout.setContentsMargins(6, 1, 6, 2) | |
| geometryInput_layout.addLayout(edgedge_loop_layout) | |
| geometryInput_layout.addLayout(up_vertex_layout) | |
| geometryInput_layout.addLayout(low_vertex_layout) | |
| self.geometryInput_group.setLayout(geometryInput_layout) | |
| # joints Layout | |
| head_joint_layout = QtWidgets.QHBoxLayout() | |
| head_joint_layout.addWidget(self.head_joint_label) | |
| head_joint_layout.addWidget(self.head_joint) | |
| head_joint_layout.addWidget(self.head_joint_button) | |
| jaw_joint_layout = QtWidgets.QHBoxLayout() | |
| jaw_joint_layout.addWidget(self.jaw_joint_label) | |
| jaw_joint_layout.addWidget(self.jaw_joint) | |
| jaw_joint_layout.addWidget(self.jaw_joint_button) | |
| joints_layout = QtWidgets.QVBoxLayout() | |
| joints_layout.setContentsMargins(6, 4, 6, 4) | |
| joints_layout.addLayout(head_joint_layout) | |
| joints_layout.addLayout(jaw_joint_layout) | |
| self.joints_group.setLayout(joints_layout) | |
| # control Layout | |
| upper_lip_ctl_layout = QtWidgets.QHBoxLayout() | |
| upper_lip_ctl_layout.addWidget(self.upper_lip_ctl_label) | |
| upper_lip_ctl_layout.addWidget(self.upper_lip_ctl) | |
| upper_lip_ctl_layout.addWidget(self.upper_lip_ctl_button) | |
| lower_lip_ctl_layout = QtWidgets.QHBoxLayout() | |
| lower_lip_ctl_layout.addWidget(self.lower_lip_ctl_label) | |
| lower_lip_ctl_layout.addWidget(self.lower_lip_ctl) | |
| lower_lip_ctl_layout.addWidget(self.lower_lip_ctl_button) | |
| control_ref_layout = QtWidgets.QVBoxLayout() | |
| control_ref_layout.setContentsMargins(6, 4, 6, 4) | |
| control_ref_layout.addLayout(upper_lip_ctl_layout) | |
| control_ref_layout.addLayout(lower_lip_ctl_layout) | |
| self.control_ref_group.setLayout(control_ref_layout) | |
| # topological autoskin Layout | |
| skinLoops_layout = QtWidgets.QGridLayout() | |
| skinLoops_layout.addWidget(self.rigid_loops_label, 0, 0) | |
| skinLoops_layout.addWidget(self.falloff_loops_label, 0, 1) | |
| skinLoops_layout.addWidget(self.rigid_loops, 1, 0) | |
| skinLoops_layout.addWidget(self.falloff_loops, 1, 1) | |
| topoSkin_layout = QtWidgets.QVBoxLayout() | |
| topoSkin_layout.setContentsMargins(6, 4, 6, 4) | |
| topoSkin_layout.addWidget(self.do_skin, | |
| alignment=QtCore.Qt.Alignment()) | |
| topoSkin_layout.addLayout(skinLoops_layout) | |
| topoSkin_layout.addLayout(head_joint_layout) | |
| topoSkin_layout.addLayout(jaw_joint_layout) | |
| self.topoSkin_group.setLayout(topoSkin_layout) | |
| # Options Layout | |
| lipThickness_layout = QtWidgets.QHBoxLayout() | |
| lipThickness_layout.addWidget(self.thickness_label) | |
| lipThickness_layout.addWidget(self.thickness) | |
| parent_layout = QtWidgets.QHBoxLayout() | |
| parent_layout.addWidget(self.parent_label) | |
| parent_layout.addWidget(self.parent_node) | |
| parent_layout.addWidget(self.parent_button) | |
| options_layout = QtWidgets.QVBoxLayout() | |
| options_layout.setContentsMargins(6, 1, 6, 2) | |
| options_layout.addLayout(lipThickness_layout) | |
| # options_layout.addLayout(offset_layout) | |
| options_layout.addLayout(parent_layout) | |
| self.options_group.setLayout(options_layout) | |
| # Name prefix | |
| name_prefix_layout = QtWidgets.QHBoxLayout() | |
| name_prefix_layout.setContentsMargins(1, 1, 1, 1) | |
| name_prefix_layout.addWidget(self.name_prefix) | |
| self.prefix_group.setLayout(name_prefix_layout) | |
| # Control Name Extension | |
| controlExtension_layout = QtWidgets.QHBoxLayout() | |
| controlExtension_layout.setContentsMargins(1, 1, 1, 1) | |
| controlExtension_layout.addWidget(self.control_name) | |
| self.control_group.setLayout(controlExtension_layout) | |
| # Main Layout | |
| main_layout = QtWidgets.QVBoxLayout() | |
| main_layout.setContentsMargins(6, 6, 6, 6) | |
| main_layout.addWidget(self.prefix_group) | |
| main_layout.addWidget(self.control_group) | |
| main_layout.addWidget(self.geometryInput_group) | |
| main_layout.addWidget(self.options_group) | |
| main_layout.addWidget(self.joints_group) | |
| main_layout.addWidget(self.control_ref_group) | |
| main_layout.addWidget(self.topoSkin_group) | |
| main_layout.addWidget(self.build_button) | |
| main_layout.addWidget(self.import_button) | |
| main_layout.addWidget(self.export_button) | |
| self.setLayout(main_layout) | |
| def create_connections(self): | |
| self.edge_loop_button.clicked.connect( | |
| partial(self.populate_edge_loop, self.edge_loop) | |
| ) | |
| self.up_vertex_button.clicked.connect( | |
| partial(self.populate_element, self.up_vertex, "vertex") | |
| ) | |
| self.low_vertex_button.clicked.connect( | |
| partial(self.populate_element, self.low_vertex, "vertex") | |
| ) | |
| self.parent_button.clicked.connect( | |
| partial(self.populate_element, self.parent_node) | |
| ) | |
| self.head_joint_button.clicked.connect( | |
| partial(self.populate_element, self.head_joint, "joint") | |
| ) | |
| self.jaw_joint_button.clicked.connect( | |
| partial(self.populate_element, self.jaw_joint, "joint") | |
| ) | |
| self.upper_lip_ctl_button.clicked.connect( | |
| partial(self.populate_element, self.upper_lip_ctl) | |
| ) | |
| self.lower_lip_ctl_button.clicked.connect( | |
| partial(self.populate_element, self.lower_lip_ctl) | |
| ) | |
| self.build_button.clicked.connect(self.build_rig) | |
| self.import_button.clicked.connect(self.import_settings) | |
| self.export_button.clicked.connect(self.export_settings) | |
| # SLOTS ########################################################## | |
| # TODO: create a checker to ensure that the vertex selected are part of | |
| # the main edgelopp | |
| def populate_element(self, lEdit, oType="transform"): | |
| if oType == "joint": | |
| oTypeInst = pm.nodetypes.Joint | |
| elif oType == "vertex": | |
| oTypeInst = pm.MeshVertex | |
| else: | |
| oTypeInst = pm.nodetypes.Transform | |
| oSel = pm.selected() | |
| if oSel: | |
| if isinstance(oSel[0], oTypeInst): | |
| lEdit.setText(oSel[0].name()) | |
| else: | |
| pm.displayWarning( | |
| "The selected element is not a valid %s" % oType) | |
| else: | |
| pm.displayWarning("Please select first one %s." % oType) | |
| def populate_edge_loop(self, lineEdit): | |
| lineEdit.setText(lib.get_edge_loop_from_selection()) | |
| def build_rig(self): | |
| rig(**lib.get_settings_from_widget(self)) | |
| def export_settings(self): | |
| data_string = json.dumps( | |
| lib.get_settings_from_widget(self), indent=4, sort_keys=True | |
| ) | |
| file_path = lib.get_file_path(self.filter, "save") | |
| if not file_path: | |
| return | |
| with open(file_path, "w") as f: | |
| f.write(data_string) | |
| def import_settings(self): | |
| file_path = lib.get_file_path(self.filter, "open") | |
| if not file_path: | |
| return | |
| lib.import_settings_from_file(file_path, self) | |
| # Build from json file. | |
| def rig_from_file(path): | |
| rig(**json.load(open(path))) | |
| def show(*args): | |
| gqt.showDialog(ui) | |
| if __name__ == "__main__": | |
| show() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment