import maya.cmds as mc import maya.OpenMayaUI as omui import maya.api.OpenMaya as om import math try: from PySide import QtCore, QtWidgets, QtGui from shiboken import wrapInstance except: from PySide2 import QtCore, QtWidgets, QtGui from shiboken2 import wrapInstance def getMayaWindow(): pointer = omui.MQtUtil.mainWindow() if pointer is not None: return wrapInstance(int(pointer), QtWidgets.QWidget)#int should be long, but for some reason It doesn't work :/ def gui_makeHorizontalLayout(widgets, margins=None, ratio=None): horizLayout = QtWidgets.QHBoxLayout() for i,wid in enumerate(widgets): if ratio: horizLayout.addWidget(wid, ratio[i]) else: horizLayout.addWidget(wid) if margins: horizLayout.setContentsMargins(margins[0],margins[1],margins[2],margins[3]) return horizLayout def gui_makeVerticalLayout(widgets, margins=None, ratio=None): vertLayout = QtWidgets.QVBoxLayout() for i,wid in enumerate(widgets): if ratio: vertLayout.addWidget(wid, ratio[i]) else: vertLayout.addWidget(wid) if margins: vertLayout.setContentsMargins(margins[0],margins[1],margins[2],margins[3]) return vertLayout def textParam(paramName='', initialVar=''): #return the widget and the parambox obj widget_paramWidget = QtWidgets.QWidget() label_paramName = QtWidgets.QLabel(paramName+': ') label_paramName.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) textbox_paramValue = QtWidgets.QLineEdit() textbox_paramValue.setText(initialVar) layout_horz = gui_makeHorizontalLayout([label_paramName, textbox_paramValue], margins=[0,0,0,0], ratio=[2,8]) widget_paramWidget.setLayout(layout_horz) return widget_paramWidget, textbox_paramValue def numParam(paramName='', initialVar=0, d_type = 'i'): widget_intParam = QtWidgets.QWidget() label_intParam = QtWidgets.QLabel(paramName+': ') label_intParam.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) textbox_intParam= QtWidgets.QLineEdit(str(initialVar)) val = QtGui.QIntValidator() if d_type == 'd': val = QtGui.QDoubleValidator() textbox_intParam.setValidator(val) layout_intParam = gui_makeHorizontalLayout([label_intParam, textbox_intParam], margins=[0,0,0,0], ratio=[2,6]) widget_intParam.setLayout(layout_intParam) return widget_intParam, textbox_intParam class BezierCurveJointGuideUI(object): def __init__(self): self.window = 'BezierCurveJointGuideUI_uniqueId' self.title = 'Bezier Cuve Joint Guide Creator' self.size = (1000, 650) self.supportsToolAction = False self.actionName = 'Create and Close' self.applyName = 'Create' self.closeName = 'Close' self.EdgeList = [] def create(self): if mc.window(self.window, exists= True): mc.deleteUI(self.window ,window= True) self.parentWindow = getMayaWindow() self.mainWindow = QtWidgets.QMainWindow(self.parentWindow) self.mainWindow.setObjectName(self.window) self.mainWindow.setWindowTitle(self.title) self.mainWidget = QtWidgets.QWidget() self.mainWindow.setCentralWidget(self.mainWidget) QtWidgets.QStyleFactory.create('Windows') self.mainLayout = QtWidgets.QVBoxLayout(self.mainWidget) self.BezierParameters() self.ButtonsLayout() self.mainWindow.show() def BezierParameters(self): widget_selectionWidget = QtWidgets.QWidget() label_directions = QtWidgets.QLabel('please select a group of edges to model the bezier curve after. After setting the '+ 'parameters, select apply to create or add to create and close. The rotation of the '+ 'guide joints can be set manually or by copying the rotations of another object by '+ 'pressing the SELECT OBJECT button.') label_directions.setWordWrap(True) #top select edges obj widget_SelEdges = QtWidgets.QWidget() widget_SelEdges.setStyleSheet(".QWidget{background-color: rgb(60, 60, 60); " "margin-left: 2px; margin-right: 2px}") self.label_selectionTitle = QtWidgets.QLabel('Selection: ') self.label_selectionTitle.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setBold(True) self.label_selectionTitle.setFont(font) self.button_selEdges = QtWidgets.QPushButton('Select Edges') horizLayout_Top = gui_makeHorizontalLayout([self.label_selectionTitle, self.button_selEdges], ratio=[8,2]) widget_SelEdges.setLayout(horizLayout_Top) self.widget_varsWidget = QtWidgets.QWidget() #NAME WIDGET cNameWidget, self.cNameTextbox = textParam('Curve Name', 'curvename') #susWidget, self.susTextbox = textParam('Sus', 'Sussy') #NUMBER WIDGETS widget_numParams = QtWidgets.QWidget() wid_cSpans, self.cSpansTextBox = numParam('Curve Spans', 3) wid_cJn, self.cJnTextBox = numParam('Curve Joint Number', 7) wid_jS, self.jSTextBox = numParam('Joint Size', 0.2, 'd') layout_numParams = gui_makeHorizontalLayout([wid_cSpans, wid_cJn, wid_jS], margins=[0,0,0,0]) widget_numParams.setLayout(layout_numParams) #curve_spans, curve_jnt_num, jnt_size #ROTATION WIDGET widget_rotAngParam = QtWidgets.QWidget() label_rotation = QtWidgets.QLabel('joints rotation : ') label_rotation.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.textbox_xRot= QtWidgets.QLineEdit(str(0.0000)) self.textbox_xRot.setValidator(QtGui.QDoubleValidator()) self.textbox_yRot= QtWidgets.QLineEdit(str(0.0000)) self.textbox_yRot.setValidator(QtGui.QDoubleValidator()) self.textbox_zRot= QtWidgets.QLineEdit(str(0.0000)) self.textbox_zRot.setValidator(QtGui.QDoubleValidator()) self.button_selObjRot = QtWidgets.QPushButton('Select Object') layout_rotation = gui_makeHorizontalLayout([label_rotation, self.textbox_xRot, self.textbox_yRot, self.textbox_zRot, self.button_selObjRot], margins=[0,0,0,0], ratio=[1,1,1,1,1]) widget_rotAngParam.setLayout(layout_rotation) layoutParameters = gui_makeVerticalLayout([cNameWidget, widget_numParams, widget_rotAngParam]) self.widget_varsWidget.setLayout(layoutParameters) vertLayout_Brezier = gui_makeVerticalLayout([label_directions, QtWidgets.QLabel(''), widget_SelEdges, self.widget_varsWidget]) widget_selectionWidget.setLayout(vertLayout_Brezier) self.mainLayout.addWidget(widget_selectionWidget) self.widget_varsWidget.setEnabled(False) self.button_selEdges.clicked.connect(self.selectEdgesAndEnableInputs) self.button_selObjRot.clicked.connect(self.getSelectedObjectRotation) def ButtonsLayout(self): button_widget = QtWidgets.QWidget() self.button_Add = QtWidgets.QPushButton('Add') self.button_Add.clicked.connect(self.addButtonPressed) self.button_Apply = QtWidgets.QPushButton('Apply') self.button_Apply.clicked.connect(self.applyButtonPressed) self.button_Close = QtWidgets.QPushButton('Close') self.button_Close.clicked.connect(self.closeButtonPressed) button_HorizLayout = gui_makeHorizontalLayout([self.button_Add, self.button_Apply, self.button_Close]) button_HorizLayout.setContentsMargins(0,0,0,0) button_widget.setLayout(button_HorizLayout) self.mainLayout.addWidget(button_widget) self.button_Add.setEnabled(False) self.button_Apply.setEnabled(False) def selectEdgesAndEnableInputs(self): sel = mc.ls(sl=True, type='float3') edgelist = mc.polyListComponentConversion(sel, fe=True, te =True) mc.select(clear=True) if edgelist: #print(edgelist) self.EdgeList = edgelist self.widget_varsWidget.setEnabled(True) self.button_Add.setEnabled(True) self.button_Apply.setEnabled(True) se = [x for i, x in enumerate(edgelist) if i<2] ps = '' for j, e in enumerate(se): if j <1: ps = e else: ps = ps+', '+e prtStr = ps if len(edgelist) <= 2 else ps+' ...' self.label_selectionTitle.setText('Selection: '+prtStr) else: mc.warning('Please select edges.') def getSelectedObjectRotation(self): sel = mc.ls(sl=True) mc.select(clear=True) angles = mc.xform(sel[0],q=1,ws=1,ro=1) print(angles) self.textbox_xRot.setText(str(angles[0])) self.textbox_yRot.setText(str(angles[1])) self.textbox_zRot.setText(str(angles[2])) def processParams(self): angles = [float(self.textbox_xRot.text()), float(self.textbox_yRot.text()), float(self.textbox_zRot.text())] CurveSpline(self.EdgeList, self.cNameTextbox.text(), int(self.cSpansTextBox.text()), int(self.cJnTextBox.text()), angles, float(self.jSTextBox.text())) def addButtonPressed(self): self.processParams() mc.deleteUI(self.window ,window= True) def applyButtonPressed(self): self.processParams() def closeButtonPressed(self): mc.deleteUI(self.window ,window= True) CURVE_NAME = 'R_upper_eyelid' CURVE_SPANS = 3#default=3, for eye creases=6 SKIN_JNT_NUM = 7# there will be 6 spans, for eyecreases=15 #Left:[-26.954, 37.66, -15.088] #Right:[26.954, 142.34, 15.088] ANG = [-26.954, 37.66, -15.088] JNT_SIZE = 0.2 class CurveSpline(): def __init__(self, edgelist, curve_name, spans, sk_jnt_num, jnt_ang, jnt_size): self.curveName = curve_name self.edgeList = edgelist self.grpSkinJntsName = curve_name+'_sk_jnts'#grp_skn_jnts_name self.bindJntName = curve_name#bind_jnt_name self.grpLocJntsName = curve_name+'_loc'#grp_loc_jnts_name self.locJntName = curve_name+'_Bjnt'#loc_jnt_name self.jntAng = jnt_ang self.jntSize = jnt_size self.spanNum = spans self.skJntNum = sk_jnt_num self.avgRot = 0 self.makeCurve() self.makeLocJnts() self.makeSknJnts() self.groupAllComponents() def makeCurve(self): mc.select(self.edgeList) curve1 = mc.polyToCurve(form=2, degree=3) self.rebuiltCurve = mc.rebuildCurve(curve1, ch=1, rpo=0, rt=0, end=1, kr=0, kep=1, kt=0, s=self.spanNum, d=3, tol=0.01, n=self.curveName) mc.delete(curve1) mc.select(clear=True) point = mc.xform(self.rebuiltCurve[0]+str('.cv[1]'), q=True, t=True, ws=True) print(point) #mc.select(self.theCurve[0]+str('.cv[0]')) #degs = mc.getAttr(curve1[0]+'.degree' ) self.locPointsNum = mc.getAttr(self.rebuiltCurve[0]+'.spans')+1 #print(self.theCurve[0]+str('.cv[0]')) def makeLocJnts(self): theCurve = self.rebuiltCurve[0] skinSel = [] locGrpsList = [] pointCount = self.locPointsNum+2 self.avgRot = self.jntAng#self.findAvgOrientation() for i in range(pointCount): ord_num = ("%%0%si" % len(str(self.locPointsNum))) % int(i+1) mc.select(cl=True) jnt = mc.joint(p=[0,0,0], n=self.locJntName+'_'+ord_num) spcLoc = mc.spaceLocator(p=[0,0,0], n=self.locJntName+'_loc_'+ord_num) mc.parent(jnt, spcLoc) off = mc.group(spcLoc, n=self.locJntName+'_offset_'+ord_num) grp = mc.group(off, n=self.locJntName+'_grp_'+ord_num) locGrpsList.append(grp) cvPos = mc.xform(theCurve+str('.cv['+str(i)+']'), q=True, t=True, ws=True) mc.setAttr(grp+'.translate', cvPos[0], cvPos[1], cvPos[2]) mc.setAttr(grp+'.rotate', self.avgRot[0], self.avgRot[1], self.avgRot[2]) skinSel.append(mc.listRelatives(grp, ad=True, type='joint', f=True)[0]) #just find the y rot skinSel.append(theCurve) mc.skinCluster(skinSel, dr=0.1, mi=1) self.locGrp = mc.group(em=True, n=self.grpLocJntsName) mc.parent(locGrpsList, self.locGrp) def findRotation(self, pointNum, currPoint, pointCount, theCurve, spef_axis = '', fromVect=[-1,0,0]): xm = ym = zm = 1 if spef_axis == 'x': xm = 0 elif spef_axis == 'y': ym = 0 elif spef_axis == 'z': zm = 0 cvP = om.MPoint(currPoint[0]*xm, currPoint[1]*ym, currPoint[2]*zm) if pointNum == pointCount-1: np = mc.xform(theCurve+str('.cv['+str(pointNum-1)+']'), q=True, t=True, ws=True) nextpoint = om.MPoint(np[0]*xm,np[1]*ym,np[2]*zm) orVect = om.MVector(nextpoint-cvP).normalize() else: np = mc.xform(theCurve+str('.cv['+str(pointNum+1)+']'), q=True, t=True, ws=True) nextpoint = om.MPoint(np[0]*xm,np[1]*ym,np[2]*zm) orVect = om.MVector(cvP-nextpoint).normalize() gRot = om.MQuaternion(om.MVector(fromVect),orVect,factor=1.0).asEulerRotation() angles = [math.degrees(angle) for angle in (gRot.x, gRot.y, gRot.z)] return angles def makeSknJnts(self): skinJntGrpList = [] for i in range(self.skJntNum): ord_num = ("%%0%si" % len(str(self.skJntNum))) % int(i+1) curveInfo = mc.createNode( 'pointOnCurveInfo', n=self.rebuiltCurve[0]+'_curveInfo_'+ord_num ) mc.connectAttr( self.rebuiltCurve[0]+'.worldSpace[0]', curveInfo+'.inputCurve') mc.setAttr(curveInfo+'.parameter', float(i*(1/(self.skJntNum-1)))) jnt = mc.joint(p=[0,0,0], n=self.bindJntName+'_'+ord_num, radius=self.jntSize) spcLoc = mc.spaceLocator(p=[0,0,0], n=self.bindJntName+'_loc_'+ord_num) mc.parent(jnt, spcLoc) grp = mc.group(spcLoc, n=self.bindJntName+'_grp_'+ord_num) skinJntGrpList.append(grp) mc.connectAttr( curveInfo+'.position', grp+'.translate') #self.makeRotateMath(curveInfo, grp) mc.setAttr(grp+'.rotate', self.avgRot[0], self.avgRot[1], self.avgRot[2]) self.skGrp = mc.group(em=True, n=self.grpSkinJntsName) mc.parent(skinJntGrpList, self.skGrp) def groupAllComponents(self): comp = [self.rebuiltCurve[0], self.locGrp, self.skGrp] self.compedGrp = mc.group(em=True, n=self.curveName+'_GRP') mc.parent(comp, self.compedGrp) '''def makeRotateMath(self, curveInfo, jntGrp): rH = mc.createNode('rotateHelper', n=curveInfo+'_rH') #mc.connectAttr(curveInfo+'.normalizedNormal', rH+'.up') mc.setAttr(rH+'.forward',0,0,1) mc.connectAttr(curveInfo+'.normalizedTangent', rH+'.up') dM = mc.createNode('decomposeMatrix', n=curveInfo+'_dM') mc.connectAttr(rH+'.rotateMatrix', dM+'.inputMatrix') mc.connectAttr(dM+'.outputRotateX', jntGrp+'.rotateX') mc.connectAttr(dM+'.outputRotateZ', jntGrp+'.rotateZ')''' '''def findAvgOrientation(self): theCurve = self.rebuiltCurve[0] pointCount = self.locPointsNum+2 rotX=[] rotY=[] rotZ=[] for i in range(pointCount): cvPos = mc.xform(theCurve+str('.cv['+str(i)+']'), q=True, t=True, ws=True) rot =self.findRotation(i, cvPos, pointCount, theCurve, 'y') rotX.append(rot[0]) rotY.append(rot[1]) rotZ.append(rot[2]) avg = [sum(rotX)/len(rotX), sum(rotY)/len(rotY), sum(rotZ)/len(rotZ)] return avg''' #have the program take two curves and make controls out of them #dont include the locators for open/close lid control... yet bWindow = BezierCurveJointGuideUI() bWindow.create()