import maya.cmds as mc import maya.OpenMayaUI as omui import maya.api.OpenMaya as om import math JNT_SIZE = 0.75 D_SPANS = 10 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 JointRibbonGuideUI(object): def __init__(self): self.window = 'JointRibbonGuideUI_uniqueId' self.title = 'Joint Ribbon 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.RibbonParameters() self.ButtonsLayout() self.mainWindow.show() def RibbonParameters(self): widget_selectionWidget = QtWidgets.QWidget() label_directions = QtWidgets.QLabel('Please select a group of edges to fit the ribbon into. This program will create a '+ 'ribbon and place the control joints in a manner that will make the ribbon best match '+ 'group of edges selected. ') 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 widget_NJntWid = QtWidgets.QWidget() cNameWidget, self.rNameTextbox = textParam('Ribbon Name', 'ribbon') wid_Joints, self.rJointsTextBox = numParam('Control Joints', 6) hlayout_nameNumJnts = gui_makeHorizontalLayout([cNameWidget, wid_Joints], margins=[0,0,0,0], ratio=[6,2]) widget_NJntWid.setLayout(hlayout_nameNumJnts) #NUMBER WIDGETS widget_numParams = QtWidgets.QWidget() wid_jOR, self.jORTextBox = numParam('Joints on Ribbon', 8) wid_gcS, self.gcSTextBox = numParam('Guide Curve Spans', 10) wid_jS, self.jSTextBox = numParam('Joint Size', 0.2, 'd') layout_numParams = gui_makeHorizontalLayout([wid_jOR,wid_gcS, wid_jS], margins=[0,0,0,0]) widget_numParams.setLayout(layout_numParams) #curve_spans, curve_jnt_num, jnt_size layoutParameters = gui_makeVerticalLayout([widget_NJntWid, widget_numParams]) 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) 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 processParams(self): #(self, edgelist, ribbon_name, num_ctrl_jnts, num_ribbon_jnts, guide_curve_spans, jnt_size) CurveRibbon(self.EdgeList, self.rNameTextbox.text(), int(self.rJointsTextBox.text()), int(self.jORTextBox.text()), int(self.gcSTextBox.text()), 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) class JointRibbon(): def __init__(self, r_name, num_ctrl_jnts, r_length, num_ribbon_jnts, jnt_size): self.ribbonName = r_name self.numCtrlJnts = num_ctrl_jnts self.len = r_length self.num_ribbon_jnts = num_ribbon_jnts self.jntSize = jnt_size self.makeBindedRibbon() self.makeRibbonJnts() self.groupJointRibbon() def makeBindedRibbon(self): ribbon = mc.nurbsPlane(d=3, u=self.numCtrlJnts-1, v=1, ax=[0,1,0], w=self.len, lr=0.15, n=self.ribbonName+'_nurbsPlane') self.ribbonShape = ribbon[0] #print(ribbon) startL = self.len*-0.5 upUnit = self.len/(self.numCtrlJnts-1) bindGrpList = [] skinSel = [] for i in range(self.numCtrlJnts): ord_num = ("%%0%si" % len(str(self.numCtrlJnts))) % int(i+1) mc.select(cl=True) jnt = mc.joint(p=[0,0,0], n=self.ribbonName+'_bindJnt_'+ord_num, radius=self.jntSize) loc = mc.spaceLocator(p=[0,0,0], n=self.ribbonName+'_bindJnt_loc_'+ord_num) mc.parent(jnt, loc) grp = mc.group(loc, n=self.ribbonName+'_bindJnt_grp_'+ord_num) bindGrpList.append(grp) skinSel.append(jnt) #mc.parent(jnt, grp) mc.setAttr(grp+'.translate', startL+(upUnit*i), 0, 0) self.bndGrp = mc.group(em=True, n=self.ribbonName+'_bindJoints') skinSel.append(self.ribbonShape) mc.skinCluster(skinSel, dr=0.1, mi=1) mc.parent(bindGrpList, self.bndGrp) def makeRibbonJnts(self): #print('hello:'+ str(self.ribbonShape)) ribbonJntGrpList = [] for i in range(self.num_ribbon_jnts): ord_num = ("%%0%si" % len(str(self.numCtrlJnts))) % int(i+1) mc.select(cl=True) jnt = mc.joint(p=[0,0,0], n=self.ribbonName+'_ribbonJnt_'+ord_num, radius=self.jntSize/2) grp = mc.group(jnt, n=self.ribbonName+'_ribbonJnt_offset_'+ord_num) loc = mc.spaceLocator(p=[0,0,0], n=self.ribbonName+'_ribbonJnt_loc_'+ord_num) mc.setAttr(loc[0]+'.scale', 0.2, 0.2, 0.2) #mc.setAttr(grp+'.scale', 5.0, 5.0, 5.0) mc.parent(grp, loc[0]) ribbonJntGrpList.append(loc[0]) point1 = mc.createNode('pointOnSurfaceInfo', n=self.ribbonName+'_pointOnSurfaceInfo_'+ord_num) mc.connectAttr(self.ribbonShape+'Shape.worldSpace[0]', point1+'.inputSurface') mc.setAttr(point1+'.parameterV', 0.5) mc.setAttr(point1+'.parameterU', (i)/(self.num_ribbon_jnts-1)) fourMat1 = mc.createNode ('fourByFourMatrix', n=self.ribbonName+'_fourByFourMatrix_' +ord_num) mc.connectAttr(point1+'.normalizedNormalX', fourMat1+'.in00') mc.connectAttr(point1+'.normalizedNormalY', fourMat1+'.in01') mc.connectAttr(point1+'.normalizedNormalZ', fourMat1+'.in02') mc.connectAttr(point1+'.normalizedTangentVX', fourMat1+'.in10') mc.connectAttr(point1+'.normalizedTangentVY', fourMat1+'.in11') mc.connectAttr(point1+'.normalizedTangentVZ', fourMat1+'.in12') mc.connectAttr(point1+'.normalizedTangentUX', fourMat1+'.in20') mc.connectAttr(point1+'.normalizedTangentUY', fourMat1+'.in21') mc.connectAttr(point1+'.normalizedTangentUZ', fourMat1+'.in22') mc.connectAttr(point1+'.positionX', fourMat1+'.in30') mc.connectAttr(point1+'.positionY', fourMat1+'.in31') mc.connectAttr(point1+'.positionZ', fourMat1+'.in32') decomp1 = mc.createNode ('decomposeMatrix', n=self.ribbonName+'_decomposeMatrix_'+ord_num) mc.connectAttr(fourMat1+'.output', decomp1+'.inputMatrix') mc.connectAttr(decomp1+'.outputTranslate', loc[0]+'.translate') mc.connectAttr(decomp1+'.outputRotate', loc[0]+'.rotate') self.ribbonGrp = mc.group(em=True, n=self.ribbonName+'_ribbonJoints') mc.parent(ribbonJntGrpList, self.ribbonGrp) def getBindJntsGrps(self): grp_lst = mc.listRelatives(self.bndGrp, c=True, f=True) return grp_lst def groupJointRibbon(self): grplist = [self.ribbonShape, self.bndGrp, self.ribbonGrp] grp = mc.group(em=True, n=self.ribbonName+'_GRP') mc.parent(grplist, grp) class CurveRibbon(): def __init__(self, edgelist, ribbon_name, num_ctrl_jnts, num_ribbon_jnts, guide_curve_spans, jnt_size): self.edgeList = edgelist self.ribbonName = ribbon_name self.numCtrlJnts = num_ctrl_jnts self.numRibbonJnts = num_ribbon_jnts self.guideCurveSpans = guide_curve_spans self.jntSize = jnt_size self.makeCurve() self.positionGuideLocs() self.makeJointRibbon() self.deleteGuides() def makeCurve(self): mc.select(self.edgeList) #self.guideCurve = mc.polyToCurve(form=2, degree=3, n=self.ribbonName+'_Curve') curve1 = mc.polyToCurve(form=2, degree=3) self.guideCurve = mc.rebuildCurve(curve1, ch=1, rpo=0, rt=0, end=1, kr=0, kep=1, kt=0, s=self.guideCurveSpans, d=3, tol=0.01, n=self.ribbonName+'_Curve') mc.delete(curve1) mc.select(clear=True) def positionGuideLocs(self): skinJntGrpList = [] self.endPts = [] for i in range(self.numCtrlJnts): ord_num = ("%%0%si" % len(str(self.numCtrlJnts))) % int(i+1) pAC = mc.pointOnCurve(self.guideCurve[0], top=True, pr=float(i*(1/(self.numCtrlJnts-1)))) if i == 0 or i == self.numCtrlJnts-1: self.endPts.append(pAC) ### pNN = mc.pointOnCurve(self.guideCurve[0], top=True, pr=float(i*(1/(self.numCtrlJnts-1))), nn=True) ### pNT = om.MVector(mc.pointOnCurve(self.guideCurve[0], top=True, pr=float(i*(1/(self.numCtrlJnts-1))), nt=True)) '''nPt = [pNT[0],pNT[1],pNT[2]] if i == 3: print('pingurs: '+str(nPt))''' #gRot = om.MQuaternion(om.MVector([1,0,0]), om.MVector(nPt),factor=1.0).asEulerRotation() buffRot_x = om.MQuaternion(math.pi*-0.5, om.MVector([1,0,0])) buffRot_z = om.MQuaternion(math.pi, om.MVector([0,0,1])) gRot = om.MQuaternion(om.MVector([1,0,0]), -1*pNT,factor=1.0) arr = buffRot_x*buffRot_z*gRot angles = [math.degrees(angle) for angle in (arr.asEulerRotation().x, arr.asEulerRotation().y, arr.asEulerRotation().z)] #print('what is pAC:') #print(pAC) #jnt = mc.joint(p=[0,0,0], n=self.ribbonName+'_joint_'+ord_num, radius=JNT_SIZE) spcLoc = mc.spaceLocator(p=[0,0,0], n=self.ribbonName+'_loc_'+ord_num) #mc.parent(jnt, spcLoc) grp = mc.group(spcLoc, n=self.ribbonName+'_grp_'+ord_num) skinJntGrpList.append(grp) mc.setAttr(grp+'.translate', pAC[0], pAC[1], pAC[2]) mc.setAttr(grp+'.rotate', angles[0], angles[1], angles[2]) #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.ribbonName+'_GuideLocs') mc.parent(skinJntGrpList, self.skGrp) def getDist(self, pl1, pl2): p1 = om.MPoint(pl1) p2 = om.MPoint(pl2) return om.MVector(p2-p1).length() def makeJointRibbon(self): dist = self.getDist(self.endPts[0], self.endPts[1]) #print('dist: '+str(dist)) jointRibbon = JointRibbon(self.ribbonName, self.numCtrlJnts, dist, self.numRibbonJnts, self.jntSize) bJG = jointRibbon.getBindJntsGrps() guides = mc.listRelatives(self.skGrp, c=True, f=True) #print('the sheesh: '+str(bJG)) #print('the shoosh: '+str(guides)) for i, jnt in enumerate(bJG): mc.delete(mc.parentConstraint(guides[i], jnt)) mc.select(clear=True) mc.select(bJG) def deleteGuides(self): mc.delete(self.skGrp) mc.delete(self.guideCurve[0]) #edgelist = mc.ls(sl=True) #mc.select(clear=True) #to sample the curve, its better to have the object in mode 3 () #curveRibbon1 = CurveRibbon(edgelist,'ribbon', 6, 8, D_SPANS, JNT_SIZE) #jointRibbon = JointRibbon('ribbon', 6, 10, 8) bjindow = JointRibbonGuideUI() bjindow.create()