Updated
165
Scripts/Modeling/Edit/AlignEdge.py
Normal file
@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import maya.cmds as mc
|
||||
import maya.mel as mel
|
||||
import maya.OpenMaya as om
|
||||
import maya.OpenMayaUI as omui
|
||||
import maya.api.OpenMaya as OpenMaya
|
||||
from maya.OpenMaya import MGlobal
|
||||
import math
|
||||
|
||||
def run():
|
||||
mesh=mc.ls(sl=1,fl=1)
|
||||
if len(mesh) == 1:
|
||||
checkLongName = mc.ls(mesh[0],l=1)
|
||||
parentNode = checkLongName[0].split('|')
|
||||
if len(parentNode) > 2:
|
||||
outParent = ''
|
||||
outParent = '|'.join(parentNode[1:-1])
|
||||
mc.parent(mesh[0],w=1)
|
||||
cleanList = ('sampleCurv*','sampleMes*','rotationPlan*')
|
||||
for c in cleanList:
|
||||
if mc.objExists(c):
|
||||
mc.delete(c)
|
||||
gface, gHitp,cEdge,cEdgePos = getClosestEdge()
|
||||
mc.select(cEdge)
|
||||
checkCVList=mc.ls( mc.polyListComponentConversion (cEdge,fe=True,tv=True),flatten=True)
|
||||
mx,my,mz = mc.pointPosition(checkCVList[0],w=1)
|
||||
mc.polyPlane(w=1, h=1, sx=1, sy=1, ax=(0,1,0), cuv=2, ch=0, n='rotationPlane')
|
||||
mc.polyCreateFacet( p=[(mx, my, mz),(cEdgePos[0], cEdgePos[1], cEdgePos[2]),(gHitp[0], gHitp[1], gHitp[2])] )
|
||||
mc.rename('sampleMesh')
|
||||
mc.select("rotationPlane.vtx[0:2]", "sampleMesh.vtx[0:2]")
|
||||
CMD = 'snap3PointsTo3Points(0);'
|
||||
mel.eval(CMD)
|
||||
mc.parent(mesh[0],'rotationPlane')
|
||||
axes = ["X", "Y", "Z"]
|
||||
for a in axes:
|
||||
val = mc.getAttr( mesh[0] + ".rotate" + a)
|
||||
valTmp = ''
|
||||
if val > 0:
|
||||
valTmp = val + 45
|
||||
else:
|
||||
valTmp = val - 45
|
||||
valNew = int (valTmp/90)
|
||||
mc.setAttr(( mesh[0] + ".rotate" + a), (valNew*90))
|
||||
|
||||
mc.move(gHitp[0], gHitp[1], gHitp[2], mesh[0], rpr=True,wd=True)
|
||||
mc.select(mesh[0])
|
||||
mc.parent(w=1)
|
||||
if len(parentNode) > 2:
|
||||
mc.parent(mesh[0],outParent)
|
||||
for c in cleanList:
|
||||
if mc.objExists(c):
|
||||
mc.delete(c)
|
||||
|
||||
def getClosestEdge():
|
||||
mayaMesh = mc.ls(sl=1,fl=1)
|
||||
gFace = ''
|
||||
gHitP = ''
|
||||
gFace,gHitP = getClosestMeshHit(mayaMesh[0])
|
||||
listF2E=mc.ls( mc.polyListComponentConversion (gFace,ff=True,te=True),flatten=True)
|
||||
cEdge = ''
|
||||
smallestDist = 1000000
|
||||
cEdgePos = []
|
||||
for l in listF2E:
|
||||
mc.select(l)
|
||||
mc.polyToCurve(form=2, degree=1, conformToSmoothMeshPreview=1)
|
||||
sampleCurve = mc.ls(sl=1)
|
||||
selectionList = om.MSelectionList()
|
||||
selectionList.add(sampleCurve[0])
|
||||
dagPath = om.MDagPath()
|
||||
selectionList.getDagPath(0, dagPath)
|
||||
omCurveOut = om.MFnNurbsCurve(dagPath)
|
||||
pointInSpace = om.MPoint(gHitP[0],gHitP[1],gHitP[2])
|
||||
closestPoint = om.MPoint()
|
||||
closestPoint = omCurveOut.closestPoint(pointInSpace)
|
||||
getDist = math.sqrt( ((closestPoint[0] - gHitP[0])**2) + ((closestPoint[1]- gHitP[1])**2) + ((closestPoint[2] - gHitP[2])**2))
|
||||
if getDist < smallestDist:
|
||||
smallestDist = getDist
|
||||
cEdge = l
|
||||
cEdgePos = [closestPoint[0],closestPoint[1],closestPoint[2]]
|
||||
mc.delete(sampleCurve)
|
||||
mc.select(cEdge)
|
||||
return(gFace,gHitP,cEdge,cEdgePos)
|
||||
|
||||
def getClosestMeshHit(mayaMesh):
|
||||
myShape = mc.listRelatives(mayaMesh, shapes=True,f=True)
|
||||
checkList = screenVisPoly()
|
||||
checkList.remove(myShape[0])
|
||||
meshPos = mc.xform(mayaMesh,q=1, ws=1, a=1, piv=1)
|
||||
posXXX = [meshPos[0],meshPos[1],meshPos[2]]
|
||||
shortDistanceCheck = 10000
|
||||
resultFace = []
|
||||
resultCV =[]
|
||||
resultHitPoint = []
|
||||
for c in checkList:
|
||||
transNode = mc.listRelatives(c, p=True)
|
||||
getFaceDist,getFace,getHitPoint = getClosestPointOnFace(transNode[0],posXXX)
|
||||
#print (getCV, getFaceDist, getFace)
|
||||
if getFaceDist < shortDistanceCheck:
|
||||
shortDistanceCheck = getFaceDist
|
||||
resultFace = getFace
|
||||
resultHitPoint = getHitPoint
|
||||
return (resultFace,resultHitPoint)
|
||||
|
||||
def getClosestPointOnFace(mayaMesh,pos=[0,0,0]):
|
||||
mVector = OpenMaya.MVector(pos)#using MVector type to represent position
|
||||
selectionList = OpenMaya.MSelectionList()
|
||||
selectionList.add(mayaMesh)
|
||||
dPath= selectionList.getDagPath(0)
|
||||
mMesh=OpenMaya.MFnMesh(dPath)
|
||||
ID = mMesh.getClosestPoint(OpenMaya.MPoint(mVector),space=OpenMaya.MSpace.kWorld)[1] #getting closest face ID
|
||||
closestPoint= mMesh.getClosestPoint(OpenMaya.MPoint(mVector),space=OpenMaya.MSpace.kWorld)[0]
|
||||
cpx = closestPoint[0]
|
||||
cpy = closestPoint[1]
|
||||
cpz = closestPoint[2]
|
||||
hitPointPosition = [cpx,cpy,cpz]
|
||||
hitFaceName = (mayaMesh+'.f['+str(ID)+']')
|
||||
getFaceDist = math.sqrt( ((pos[0] - cpx)**2) + ((pos[1]- cpy)**2) + ((pos[2] - cpz)**2))
|
||||
return (getFaceDist, hitFaceName,hitPointPosition)
|
||||
|
||||
def screenVisPoly():
|
||||
commonList= []
|
||||
view = omui.M3dView.active3dView()
|
||||
om.MGlobal.selectFromScreen(0, 0, view.portWidth(), view.portHeight(), om.MGlobal.kReplaceList)
|
||||
objects = om.MSelectionList()
|
||||
sel = om.MSelectionList()
|
||||
om.MGlobal.getActiveSelectionList(objects)
|
||||
om.MGlobal.setActiveSelectionList(sel, om.MGlobal.kReplaceList)
|
||||
fromScreen = []
|
||||
objects.getSelectionStrings(fromScreen)
|
||||
shapesOnScreen = mc.listRelatives(fromScreen, shapes=True,f=True)
|
||||
meshList = mc.ls(type='mesh',l=True)#only polygon
|
||||
if len(meshList)>0 and shapesOnScreen is not None:
|
||||
commonList = list(set(meshList) & set(shapesOnScreen))
|
||||
return commonList
|
||||
else:
|
||||
commonList = []
|
||||
return commonList
|
||||
|
||||
def getPolyFaceCenter(faceName):
|
||||
meshFaceName = faceName.split('.')[0]
|
||||
findVtx = mc.polyInfo(faceName, fv=1)
|
||||
getNumber = []
|
||||
checkNumber = ((findVtx[0].split(':')[1]).split('\n')[0]).split(' ')
|
||||
for c in checkNumber:
|
||||
findNumber = ''.join([n for n in c.split('|')[-1] if n.isdigit()])
|
||||
if findNumber:
|
||||
getNumber.append(findNumber)
|
||||
centerX = 0
|
||||
centerY = 0
|
||||
centerZ = 0
|
||||
for g in getNumber:
|
||||
x,y,z = mc.pointPosition((meshFaceName + '.vtx['+g + ']'),w=1)
|
||||
centerX = centerX + x
|
||||
centerY = centerY + y
|
||||
centerZ = centerZ + z
|
||||
|
||||
centerX = centerX/len(getNumber)
|
||||
centerY = centerY/len(getNumber)
|
||||
centerZ = centerZ/len(getNumber)
|
||||
return centerX,centerY,centerZ
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
382
Scripts/Modeling/Edit/ArcDeformer.py
Normal file
@ -0,0 +1,382 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import maya.cmds as cmds # type: ignore
|
||||
import maya.mel as mel # type: ignore
|
||||
import math
|
||||
|
||||
# set currentArcCurve and storeEdge as global variables
|
||||
global currentArcCurve
|
||||
global storeEdge
|
||||
currentArcCurve = ""
|
||||
storeEdge = []
|
||||
|
||||
def run():
|
||||
if cmds.window("arcDeformerUI", exists = True):
|
||||
cmds.deleteUI("arcDeformerUI")
|
||||
arcDeformerUI = cmds.window("arcDeformerUI",title = "Arc Deformer", w=320)
|
||||
cmds.frameLayout(labelVisible= False)
|
||||
cmds.rowColumnLayout(nc=4 ,cw=[(1,5),(2,60),(3,20),(4,180)])
|
||||
cmds.text(l ='')
|
||||
cmds.text(l ='Curve Type')
|
||||
cmds.text(l ='')
|
||||
cmds.radioButtonGrp('curveType', nrb=2, sl=1, labelArray2=['Bezier', 'Nurbs'], cw = [(1,100),(2,100)],cc=lambda x: controlNumberSwitch())
|
||||
cmds.setParent( '..' )
|
||||
cmds.rowColumnLayout(nc=10 ,cw=[(1,10),(2,60),(3,20),(4,50),(5,10),(6,50),(7,10),(8,50),(9,10),(10,95)])
|
||||
cmds.text(l ='')
|
||||
cmds.text(l ='Options')
|
||||
cmds.text(l ='')
|
||||
cmds.checkBox('makeArc', label= "Arc" ,v = 1, cc= lambda x: makeArcSwitch())
|
||||
cmds.text(l ='')
|
||||
cmds.checkBox('snapCurve', label= "Snap" ,v = 1, cc= lambda x: disableEvenSpaceCheckBox())
|
||||
cmds.text(l ='')
|
||||
cmds.checkBox('evenSpace', label= "Even" ,v = 1)
|
||||
cmds.text(l ='')
|
||||
cmds.checkBox('cleanCurve', label= "Keep Curve" ,v = 1)
|
||||
cmds.setParent( '..' )
|
||||
cmds.intSliderGrp('CPSlider', cw3=[80, 30, 180], label = 'Control Point ', field= 1, min= 2, max= 10, fmx = 500, v = 3 )
|
||||
cmds.floatSliderGrp('dropOffSlider' , label = 'DropOff', v = 0.01, cw3=[80, 30, 180], field=1 ,pre = 2, min= 0.01, max= 10)
|
||||
cmds.rowColumnLayout(nc=4 ,cw=[(1,120),(2,80),(3,10),(4,80)])
|
||||
cmds.text(l ='')
|
||||
cmds.button( l= 'Run', c = lambda x: arcEdgeLoop())
|
||||
cmds.text(l ='')
|
||||
cmds.button( l= 'Done', c = lambda x: arcDone())
|
||||
cmds.text(l ='')
|
||||
cmds.setParent( '..' )
|
||||
cmds.showWindow(arcDeformerUI)
|
||||
|
||||
def arcDone():
|
||||
global storeEdge
|
||||
global currentArcCurve
|
||||
if cmds.objExists('arcCurve*'):
|
||||
arcCurveList = cmds.ls( "arcCurve*", transforms =1 )
|
||||
a = arcCurveList[0]
|
||||
for a in arcCurveList:
|
||||
if 'BaseWire' not in a:
|
||||
shapeNode = cmds.listRelatives(a, fullPath=True )
|
||||
hist = cmds.listConnections(cmds.listConnections(shapeNode[0],sh=1, d=1 ) ,d=1 ,sh=1)
|
||||
cmds.delete(hist,ch=1)
|
||||
cmds.delete('arcCurve*')
|
||||
if len(currentArcCurve)>0:
|
||||
if cmds.objExists(currentArcCurve):
|
||||
shapeNode = cmds.listRelatives(currentArcCurve, fullPath=True )
|
||||
hist = cmds.listConnections(cmds.listConnections(shapeNode[0],sh=1, d=1 ) ,d=1 ,sh=1)
|
||||
cmds.delete(hist,ch=1)
|
||||
if cmds.objExists(currentArcCurve):
|
||||
cmds.select(currentArcCurve)
|
||||
cmds.select(storeEdge,add=1)
|
||||
if cmds.objExists(currentArcCurve + 'BaseWire'):
|
||||
cmds.delete(currentArcCurve + 'BaseWire')
|
||||
|
||||
def arcEdgeLoop():
|
||||
global storeEdge
|
||||
global currentArcCurve
|
||||
currentDropOff = cmds.floatSliderGrp('dropOffSlider' ,q=1,v=1)
|
||||
snapCheck = cmds.checkBox('snapCurve',q = 1 ,v = 1)
|
||||
goEven = cmds.checkBox('evenSpace', q=1 ,v = 1)
|
||||
conP = cmds.intSliderGrp('CPSlider',q=1 , v = True )
|
||||
curveT = cmds.radioButtonGrp('curveType', q=1, sl=1)
|
||||
goArc = cmds.checkBox('makeArc', q=1 ,v = 1)
|
||||
cClean = cmds.checkBox('cleanCurve', q=1 ,v = 1)
|
||||
selEdge = cmds.filterExpand(expand=True ,sm=32)
|
||||
selCurve = cmds.filterExpand(expand=True ,sm=9)
|
||||
if selCurve:
|
||||
if len(selEdge)>0 and len(selCurve)== 1:
|
||||
storeEdge = selEdge
|
||||
cmds.select(selCurve,d=1)
|
||||
selMeshForDeformer = cmds.ls(sl=1,o=1)
|
||||
getCircleState,listVtx = vtxLoopOrderCheck()
|
||||
newCurve = cmds.duplicate(selCurve[0], rr=1)
|
||||
cmds.rename(newCurve[0],'newsnapCurve')
|
||||
currentArcCurve = 'newsnapCurve'
|
||||
cmds.rebuildCurve(currentArcCurve,ch=1, rpo=1, rt=0, end=1, kr=0, kcp=0, kep=1, kt=0, s = 100, d=1, tol=0.01)
|
||||
#check tip order
|
||||
curveTip = cmds.pointOnCurve(currentArcCurve , pr = 0, p=1)
|
||||
tipA = cmds.pointPosition(listVtx[0],w=1)
|
||||
tipB = cmds.pointPosition(listVtx[-1],w=1)
|
||||
distA = math.sqrt( ((tipA[0] - curveTip[0])**2) + ((tipA[1] - curveTip[1])**2) + ((tipA[2] - curveTip[2])**2) )
|
||||
distB = math.sqrt( ((tipB[0] - curveTip[0])**2) + ((tipB[1] - curveTip[1])**2) + ((tipB[2] - curveTip[2])**2) )
|
||||
if distA > distB:
|
||||
listVtx.reverse()
|
||||
#snap to curve
|
||||
if goEven == 1:
|
||||
for q in range(len(selEdge)+1):
|
||||
if q == 0:
|
||||
pp = cmds.pointOnCurve(currentArcCurve , pr = 0, p=1)
|
||||
cmds.move( pp[0], pp[1], pp[2],listVtx[q] , a =True, ws=True)
|
||||
else:
|
||||
pp = cmds.pointOnCurve(currentArcCurve , pr = (1.0/len(selEdge)*q), p=1)
|
||||
cmds.move( pp[0], pp[1], pp[2],listVtx[q] , a =True, ws=True)
|
||||
else:
|
||||
sum = 0
|
||||
totalEdgeLoopLength = 0
|
||||
Llist = []
|
||||
uList = []
|
||||
pList = []
|
||||
for i in range(len(listVtx)-1):
|
||||
pA = cmds.pointPosition(listVtx[i], w =1)
|
||||
pB = cmds.pointPosition(listVtx[i+1], w =1)
|
||||
checkDistance = math.sqrt( ((pA[0] - pB[0])**2) + ((pA[1] - pB[1])**2) + ((pA[2] - pB[2])**2) )
|
||||
Llist.append(checkDistance)
|
||||
totalEdgeLoopLength = totalEdgeLoopLength + checkDistance
|
||||
|
||||
for j in Llist:
|
||||
sum = sum + j
|
||||
uList.append(sum)
|
||||
for k in uList:
|
||||
p = k / totalEdgeLoopLength
|
||||
pList.append(p)
|
||||
|
||||
for q in range(len(selEdge)+1):
|
||||
if q == 0:
|
||||
pp = cmds.pointOnCurve(currentArcCurve , pr = 0, p=1)
|
||||
cmds.move( pp[0], pp[1], pp[2],listVtx[q] , a =True, ws=True)
|
||||
else:
|
||||
pp = cmds.pointOnCurve(currentArcCurve , pr = pList[q-1], p=1)
|
||||
cmds.move( pp[0], pp[1], pp[2],listVtx[q] , a =True, ws=True)
|
||||
cmds.delete('newsnapCurve')
|
||||
deformerNames = cmds.wire(selMeshForDeformer, gw=0, en = 1, ce = 0, li= 0, dds = [(0,1)], dt=1, w = selCurve[0])
|
||||
cmds.connectControl("dropOffSlider", (deformerNames[0]+".dropoffDistance[0]"))
|
||||
if snapCheck == 0:
|
||||
cmds.setAttr((deformerNames[0] + '.dropoffDistance[0]'),1)
|
||||
else:
|
||||
cmds.setAttr((deformerNames[0] + '.dropoffDistance[0]'),currentDropOff)
|
||||
currentArcCurve = selCurve[0]
|
||||
cmds.select(selCurve[0])
|
||||
else:
|
||||
if selEdge:
|
||||
storeEdge = selEdge
|
||||
if cClean == 0:
|
||||
if cmds.objExists('arcCurve*'):
|
||||
arcDone()
|
||||
selMeshForDeformer = cmds.ls(sl=1,o=1)
|
||||
getCircleState,listVtx = vtxLoopOrderCheck()
|
||||
deformerNames = []
|
||||
#make nurbs curve
|
||||
if getCircleState == 0: #Arc
|
||||
if goArc == 1:
|
||||
midP = int(len(listVtx)/2)
|
||||
cmds.move(0.01, 0, 0,selEdge[midP],r=1, cs=1 ,ls=1, wd =1)
|
||||
p1 = cmds.pointPosition(listVtx[0], w =1)
|
||||
p2 = cmds.pointPosition(listVtx[midP], w =1)
|
||||
p3 = cmds.pointPosition(listVtx[-1], w =1)
|
||||
newNode = cmds.createNode('makeThreePointCircularArc')
|
||||
cmds.setAttr((newNode + '.pt1'), p1[0], p1[1] , p1[2])
|
||||
cmds.setAttr((newNode + '.pt2'), p2[0], p2[1] , p2[2])
|
||||
cmds.setAttr((newNode + '.pt3'), p3[0], p3[1] , p3[2])
|
||||
cmds.setAttr((newNode + '.d'), 3)
|
||||
cmds.setAttr((newNode + '.s'), len(listVtx))
|
||||
newCurve = cmds.createNode('nurbsCurve')
|
||||
cmds.connectAttr((newNode+'.oc'), (newCurve+'.cr'))
|
||||
cmds.delete(ch=1)
|
||||
transformNode = cmds.listRelatives(newCurve, fullPath=True , parent=True )
|
||||
cmds.select(transformNode)
|
||||
cmds.rename(transformNode,'arcCurve0')
|
||||
getNewNode = cmds.ls(sl=1)
|
||||
currentArcCurve = getNewNode[0]
|
||||
numberP = 0
|
||||
if curveT == 2:#nubs curve
|
||||
numberP = int(conP) - 3
|
||||
if numberP < 1:
|
||||
numberP = 1
|
||||
else:
|
||||
numberP = int(conP) -1
|
||||
cmds.rebuildCurve(currentArcCurve,ch=1, rpo=1, rt=0, end=1, kr=0, kcp=0, kep=1, kt=0, s= numberP, d=3, tol=0.01)
|
||||
else:
|
||||
p1 = cmds.pointPosition(listVtx[0], w =1)
|
||||
cmds.curve(d= 1, p=p1)
|
||||
cmds.rename('arcCurve0')
|
||||
getNewNode = cmds.ls(sl=1)
|
||||
currentArcCurve = getNewNode[0]
|
||||
for l in range(1,len(listVtx)):
|
||||
p2 = cmds.pointPosition(listVtx[l], w =1)
|
||||
cmds.curve(currentArcCurve, a= 1, d= 1, p=p2)
|
||||
numberP = int(conP) -1
|
||||
cmds.rebuildCurve(currentArcCurve,ch=1, rpo=1, rt=0, end=1, kr=0, kcp=0, kep=1, kt=0, s= numberP, d=1, tol=0.01)
|
||||
else: #circle
|
||||
p1 = cmds.pointPosition(listVtx[0], w =1)
|
||||
cmds.curve(d= 1, p=p1)
|
||||
cmds.rename('arcCurve0')
|
||||
getNewNode = cmds.ls(sl=1)
|
||||
currentArcCurve = getNewNode[0]
|
||||
for l in range(1,len(listVtx)):
|
||||
p2 = cmds.pointPosition(listVtx[l], w =1)
|
||||
cmds.curve(currentArcCurve, a= 1, d= 1, p=p2)
|
||||
cmds.curve(currentArcCurve, a= 1, d= 1, p=p1)
|
||||
cmds.closeCurve(currentArcCurve,ch=0, ps=2, rpo=1, bb= 0.5, bki=0, p=0.1)
|
||||
conP = cmds.intSliderGrp('CPSlider',q=1 , v = True )
|
||||
numberP = int(conP)
|
||||
if numberP < 4:
|
||||
numberP = 4
|
||||
cmds.intSliderGrp('CPSlider',e=1 , v = 4 )
|
||||
cmds.rebuildCurve(currentArcCurve,ch=1, rpo=1, rt=0, end=1, kr=0, kcp=0, kep=1, kt=0, s = numberP, d=3, tol=0.01)
|
||||
###########################################################################
|
||||
cmds.delete(currentArcCurve ,ch=1)
|
||||
totalEdgeLoopLength = 0;
|
||||
sum = 0
|
||||
Llist = []
|
||||
uList = []
|
||||
pList = []
|
||||
#cmds.select(selEdge)
|
||||
for i in range(len(listVtx)-1):
|
||||
pA = cmds.pointPosition(listVtx[i], w =1)
|
||||
pB = cmds.pointPosition(listVtx[i+1], w =1)
|
||||
checkDistance = math.sqrt( ((pA[0] - pB[0])**2) + ((pA[1] - pB[1])**2) + ((pA[2] - pB[2])**2) )
|
||||
Llist.append(checkDistance)
|
||||
totalEdgeLoopLength = totalEdgeLoopLength + checkDistance
|
||||
if goEven == 1:
|
||||
avg = totalEdgeLoopLength / (len(selEdge))
|
||||
for j in range(len(selEdge)):
|
||||
sum = ((j+1)*avg)
|
||||
uList.append(sum)
|
||||
else:
|
||||
for j in Llist:
|
||||
sum = sum + j
|
||||
uList.append(sum)
|
||||
for k in uList:
|
||||
p = k / totalEdgeLoopLength
|
||||
pList.append(p)
|
||||
#snap to curve
|
||||
if snapCheck == 1:
|
||||
for q in range(len(pList)):
|
||||
if q+1 == len(listVtx):
|
||||
pp = cmds.pointOnCurve(currentArcCurve, pr = 0, p=1)
|
||||
cmds.move( pp[0], pp[1], pp[2],listVtx[0] , a =True, ws=True)
|
||||
else:
|
||||
pp = cmds.pointOnCurve(currentArcCurve , pr = pList[q], p=1)
|
||||
cmds.move( pp[0], pp[1], pp[2],listVtx[q+1] , a =True, ws=True)
|
||||
#convert to Bezier Curve
|
||||
cmds.delete(currentArcCurve ,ch=1)
|
||||
cmds.select(currentArcCurve)
|
||||
if curveT == 1:
|
||||
cmds.nurbsCurveToBezier()
|
||||
if getCircleState == 1: #circle need to fix bug
|
||||
cmds.closeCurve(currentArcCurve,ch=0, ps=2, rpo=1, bb= 0.5, bki=0, p=0.1)
|
||||
cmds.closeCurve(currentArcCurve,ch=0, ps=2, rpo=1, bb= 0.5, bki=0, p=0.1)
|
||||
#wireWrap
|
||||
deformerNames = cmds.wire( selMeshForDeformer, gw=0, en = 1, ce = 0, li= 0, dds = [(0,1)], dt=1, w = currentArcCurve)
|
||||
#select controllers
|
||||
if getCircleState == 0:
|
||||
cmds.setToolTo('moveSuperContext')
|
||||
degree = cmds.getAttr(currentArcCurve + '.degree')
|
||||
spans = cmds.getAttr(currentArcCurve + '.spans')
|
||||
numberCVs = degree + spans
|
||||
collect = []
|
||||
for x in range(int(numberCVs/3)-1):
|
||||
g = currentArcCurve + '.cv[' + str((x+1)*3) + ']'
|
||||
collect.append(g)
|
||||
cmds.select(collect ,r=1)
|
||||
|
||||
else:
|
||||
cmds.select(currentArcCurve + '.cv[*]')
|
||||
cmd = 'doMenuNURBComponentSelection("' + currentArcCurve + '", "controlVertex");'
|
||||
mel.eval(cmd)
|
||||
cmds.connectControl("dropOffSlider", (deformerNames[0]+".dropoffDistance[0]"))
|
||||
if snapCheck == 0:
|
||||
cmds.setAttr((deformerNames[0] + '.dropoffDistance[0]'),1)
|
||||
else:
|
||||
cmds.setAttr((deformerNames[0] + '.dropoffDistance[0]'),currentDropOff)
|
||||
#add to viewport even in isolate mode
|
||||
for x in range(1,5):
|
||||
cmds.isolateSelect(('modelPanel' + str(x)), ado= currentArcCurve )
|
||||
|
||||
def makeArcSwitch():# min point for Nurbs are 4 point
|
||||
goArc = cmds.checkBox('makeArc', q=1 ,v = 1)
|
||||
curveT = cmds.radioButtonGrp('curveType', q=1, sl=1)
|
||||
if goArc == 0:
|
||||
cmds.intSliderGrp('CPSlider', e=1, min= 4, v = 4 , fmx = 500)
|
||||
else:
|
||||
if curveT == 1:
|
||||
cmds.intSliderGrp('CPSlider', e=1, min= 2, v = 3, fmx = 500)
|
||||
else:
|
||||
cmds.intSliderGrp('CPSlider', e=1, min= 4, v = 4, fmx = 500)
|
||||
|
||||
def disableEvenSpaceCheckBox():
|
||||
snapCheck = cmds.checkBox('snapCurve',q = 1 ,v = 1)
|
||||
if snapCheck == 0 :
|
||||
cmds.checkBox('evenSpace', e=1 ,en=0)
|
||||
else:
|
||||
cmds.checkBox('evenSpace', e=1 ,en=1)
|
||||
|
||||
def controlNumberSwitch():# min point for Nurbs are 4 point
|
||||
curveT = cmds.radioButtonGrp('curveType', q=1, sl=1)
|
||||
getCurrentV = cmds.intSliderGrp('CPSlider', q=1 ,v = 1 )
|
||||
if curveT == 2:
|
||||
cmds.intSliderGrp('CPSlider', e=1, min= 4 )
|
||||
if getCurrentV < 4:
|
||||
cmds.intSliderGrp('CPSlider', e=1, v= 4 )
|
||||
else:
|
||||
cmds.intSliderGrp('CPSlider', e=1, min= 2 )
|
||||
|
||||
def vtxLoopOrderCheck():
|
||||
selEdges = cmds.ls(sl=1,fl=1)
|
||||
shapeNode = cmds.listRelatives(selEdges[0], fullPath=True , parent=True )
|
||||
transformNode = cmds.listRelatives(shapeNode[0], fullPath=True , parent=True )
|
||||
edgeNumberList = []
|
||||
for a in selEdges:
|
||||
checkNumber = ((a.split('.')[1]).split('\n')[0]).split(' ')
|
||||
for c in checkNumber:
|
||||
findNumber = ''.join([n for n in c.split('|')[-1] if n.isdigit()])
|
||||
if findNumber:
|
||||
edgeNumberList.append(findNumber)
|
||||
getNumber = []
|
||||
for s in selEdges:
|
||||
evlist = cmds.polyInfo(s,ev=True)
|
||||
checkNumber = ((evlist[0].split(':')[1]).split('\n')[0]).split(' ')
|
||||
for c in checkNumber:
|
||||
findNumber = ''.join([n for n in c.split('|')[-1] if n.isdigit()])
|
||||
if findNumber:
|
||||
getNumber.append(findNumber)
|
||||
dup = set([x for x in getNumber if getNumber.count(x) > 1])
|
||||
getHeadTail = list(set(getNumber) - dup)
|
||||
checkCircleState = 0
|
||||
if not getHeadTail: #close curve
|
||||
checkCircleState = 1
|
||||
getHeadTail.append(getNumber[0])
|
||||
vftOrder = []
|
||||
vftOrder.append(getHeadTail[0])
|
||||
count = 0
|
||||
while len(dup)> 0 and count < 1000:
|
||||
checkVtx = transformNode[0]+'.vtx['+ vftOrder[-1] + ']'
|
||||
velist = cmds.polyInfo(checkVtx,ve=True)
|
||||
getNumber = []
|
||||
checkNumber = ((velist[0].split(':')[1]).split('\n')[0]).split(' ')
|
||||
for c in checkNumber:
|
||||
findNumber = ''.join([n for n in c.split('|')[-1] if n.isdigit()])
|
||||
if findNumber:
|
||||
getNumber.append(findNumber)
|
||||
findNextEdge = []
|
||||
for g in getNumber:
|
||||
if g in edgeNumberList:
|
||||
findNextEdge = g
|
||||
edgeNumberList.remove(findNextEdge)
|
||||
checkVtx = transformNode[0]+'.e['+ findNextEdge + ']'
|
||||
findVtx = cmds.polyInfo(checkVtx,ev=True)
|
||||
getNumber = []
|
||||
checkNumber = ((findVtx[0].split(':')[1]).split('\n')[0]).split(' ')
|
||||
for c in checkNumber:
|
||||
findNumber = ''.join([n for n in c.split('|')[-1] if n.isdigit()])
|
||||
if findNumber:
|
||||
getNumber.append(findNumber)
|
||||
gotNextVtx = []
|
||||
for g in getNumber:
|
||||
if g in dup:
|
||||
gotNextVtx = g
|
||||
dup.remove(gotNextVtx)
|
||||
vftOrder.append(gotNextVtx)
|
||||
count += 1
|
||||
if checkCircleState == 0:
|
||||
vftOrder.append(getHeadTail[1])
|
||||
else:#close curve remove connected vtx
|
||||
if vftOrder[0] == vftOrder[1]:
|
||||
vftOrder = vftOrder[1:]
|
||||
elif vftOrder[0] == vftOrder[-1]:
|
||||
vftOrder = vftOrder[0:-1]
|
||||
finalList = []
|
||||
for v in vftOrder:
|
||||
finalList.append(transformNode[0]+'.vtx['+ v + ']' )
|
||||
|
||||
return checkCircleState, finalList
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
239
Scripts/Modeling/Edit/AutoSnap.py
Normal file
@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import maya.OpenMaya as om
|
||||
import maya.OpenMayaAnim as oma
|
||||
import collections
|
||||
|
||||
util = om.MScriptUtil()
|
||||
util.createFromInt(0)
|
||||
|
||||
dummy = {}
|
||||
dummy['mob'] = om.MObject()
|
||||
dummy['intPtr'] = util.asIntPtr()
|
||||
dummy['omp'] = om.MPoint()
|
||||
|
||||
class MDagPath(om.MDagPath):
|
||||
def __init__(self, node=None):
|
||||
super(MDagPath, self).__init__()
|
||||
if node is not None:
|
||||
sel = om.MSelectionList()
|
||||
sel.add(node)
|
||||
sel.getDagPath(0, self, dummy['mob'])
|
||||
|
||||
def __str__(self):
|
||||
return self.fullPathName()
|
||||
|
||||
class MeshData(object):
|
||||
def __init__(self, mfnMesh):
|
||||
self.mesh = mfnMesh
|
||||
self.regist()
|
||||
|
||||
def minmax(self, minA, maxA, pos):
|
||||
for i in range(3):
|
||||
if minA[i] > pos[i]:
|
||||
minA[i] = pos[i]
|
||||
if maxA[i] <= pos[i]:
|
||||
maxA[i] = pos[i]
|
||||
|
||||
def createBlock(self, minA, maxA):
|
||||
functions = []
|
||||
split = 8
|
||||
for i in range(3):
|
||||
reach = (maxA[i] - minA[i]) / split
|
||||
code = "lambda x: "
|
||||
for j in range(split - 1):
|
||||
characters = {'min': minA[i], 'reach': reach, 'i': j}
|
||||
if j == 0:
|
||||
code += "(((%(min)s - 10000000 <= x < (%(min)s + %(reach)s * (%(i)s+2)))) * (%(i)s+2) or " % characters
|
||||
elif j == split - 2:
|
||||
code += "(((%(min)s + %(reach)s * (%(i)s+1)) <= x < (%(min)s + 100000000))) * (%(i)s+2) or 0) - 1" % characters
|
||||
else:
|
||||
code += "(((%(min)s + %(reach)s * (%(i)s+1)) <= x < (%(min)s + %(reach)s * (%(i)s+2)))) * (%(i)s+2) or " % characters
|
||||
functions.append(eval(code))
|
||||
getBlockKeyFunc = lambda x, y, z: (functions[0](x), functions[1](y), functions[2](z))
|
||||
|
||||
indexDict = collections.defaultdict(list)
|
||||
for x in range(1, split):
|
||||
reachX = (maxA[0] - minA[0]) / split
|
||||
for y in range(1, split):
|
||||
reachY = (maxA[1] - minA[1]) / split
|
||||
for z in range(1, split):
|
||||
reachZ = (maxA[2] - minA[2]) / split
|
||||
# Use a tuple of coordinates instead of MPoint
|
||||
p = (reachX * x + minA[0], reachY * y + minA[1], reachZ * z + minA[2])
|
||||
a = indexDict[p]
|
||||
for xx in range(2):
|
||||
for yy in range(2):
|
||||
for zz in range(2):
|
||||
a.append((x - xx, y - yy, z - zz))
|
||||
return getBlockKeyFunc, indexDict
|
||||
|
||||
def getPoints(self):
|
||||
posA = om.MPointArray()
|
||||
self.mesh.getPoints(posA, om.MSpace.kWorld)
|
||||
return posA
|
||||
|
||||
def regist(self):
|
||||
posA = self.getPoints()
|
||||
length = posA.length()
|
||||
self.posA = posA
|
||||
|
||||
minXYZ = [100000, 100000, 100000]
|
||||
maxXYZ = [-100000, -100000, -100000]
|
||||
for i in range(length):
|
||||
pos = posA[i]
|
||||
self.minmax(minXYZ, maxXYZ, pos)
|
||||
|
||||
getBlockKeyFunc, indexDict = self.createBlock(minXYZ, maxXYZ)
|
||||
pointsDict = collections.defaultdict(list)
|
||||
for i in range(length):
|
||||
pos = posA[i]
|
||||
key = getBlockKeyFunc(pos[0], pos[1], pos[2])
|
||||
pointsDict[key].append((pos, i))
|
||||
|
||||
for points, keys in indexDict.items():
|
||||
if not any(pointsDict[k] for k in keys):
|
||||
indexDict[points] = False
|
||||
|
||||
self.point_boxIndex = indexDict
|
||||
self.boxIndex_points = pointsDict
|
||||
|
||||
def getColosetVertexId(self, point):
|
||||
minLength = 10000000000
|
||||
colosetBox = None
|
||||
|
||||
# Convert back to MPoint for distance calculation
|
||||
for k, v in self.point_boxIndex.items():
|
||||
if v:
|
||||
k_point = om.MPoint(k[0], k[1], k[2]) # Convert tuple back to MPoint
|
||||
length = k_point.distanceTo(point)
|
||||
if length < minLength:
|
||||
minLength = length
|
||||
colosetBox = k
|
||||
|
||||
if colosetBox is None:
|
||||
return -1
|
||||
|
||||
minLength = 10000000000
|
||||
result = -1
|
||||
|
||||
for k in self.point_boxIndex[colosetBox]:
|
||||
points = self.boxIndex_points[k]
|
||||
|
||||
for _point, idx in points:
|
||||
length = _point.distanceTo(point)
|
||||
if length < minLength:
|
||||
minLength = length
|
||||
result = idx
|
||||
|
||||
return result
|
||||
|
||||
class LatticeData(MeshData):
|
||||
def __init__(self, mfnLattice):
|
||||
super(LatticeData, self).__init__(mfnLattice)
|
||||
|
||||
def getPoints(self):
|
||||
mfnLattice = self.mesh
|
||||
divisions = cmds.lattice(mfnLattice.fullPathName(), q=True, divisions=True)
|
||||
points = om.MPointArray()
|
||||
for u in range(divisions[2]):
|
||||
for t in range(divisions[1]):
|
||||
for s in range(divisions[0]):
|
||||
trans = cmds.pointPosition(mfnLattice.fullPathName() + '.pt[%s][%s][%s]' % (s, t, u), world=True)
|
||||
points.append(om.MPoint(trans[0], trans[1], trans[2]))
|
||||
|
||||
return points
|
||||
|
||||
def progress(**kwargs):
|
||||
if cmds.about(b=True):
|
||||
return False
|
||||
if kwargs.get('maxValue', False) is not False and kwargs['maxValue'] == 0:
|
||||
return False
|
||||
cmds.progressWindow(**kwargs)
|
||||
|
||||
def isLttice(targetMesh):
|
||||
try:
|
||||
oma.MFnLattice(MDagPath(targetMesh))
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def applySnapOnClosetVertex(targetMesh, moveVertexList):
|
||||
if isLttice(targetMesh):
|
||||
target = oma.MFnLattice(MDagPath(targetMesh))
|
||||
tgtMesh = LatticeData(target)
|
||||
else:
|
||||
target = om.MFnMesh(MDagPath(targetMesh))
|
||||
tgtMesh = MeshData(target)
|
||||
tgtPoints = tgtMesh.getPoints()
|
||||
|
||||
moveNode = moveVertexList[0].split('.')[0]
|
||||
if isLttice(moveNode):
|
||||
compName = 'pt'
|
||||
movTarget = oma.MFnLattice(MDagPath(moveNode))
|
||||
movMesh = LatticeData(movTarget)
|
||||
else:
|
||||
compName = 'vtx'
|
||||
movTarget = om.MFnMesh(MDagPath(moveNode))
|
||||
movMesh = MeshData(movTarget)
|
||||
movPoints = movMesh.getPoints()
|
||||
|
||||
length = movPoints.length()
|
||||
|
||||
vtx = cmds.ls(moveVertexList, fl=True)
|
||||
if compName == 'pt':
|
||||
divisions = cmds.lattice(moveNode, divisions=True, q=True)
|
||||
vertexIndex = [
|
||||
(int(v.split('[')[-3].split(']')[0]),
|
||||
int(v.split('[')[-2].split(']')[0]),
|
||||
int(v.split('[')[-1].split(']')[0]))
|
||||
for v in vtx
|
||||
]
|
||||
vertexIndex = tuple((s + divisions[0] * t + (divisions[0] * divisions[1]) * u) for s, t, u in vertexIndex)
|
||||
else:
|
||||
vertexIndex = tuple(int(v.split('[')[-1].split(']')[0]) for v in vtx)
|
||||
|
||||
progress(title='rsSnapOnClosestVertex', progress=0, maxValue=length, status='', isInterruptable=True)
|
||||
for i in range(length):
|
||||
if i in vertexIndex:
|
||||
if i % 50 == 0:
|
||||
progress(e=True, progress=i, status='{}/{}'.format(i, length))
|
||||
num = tgtMesh.getColosetVertexId(movPoints[i])
|
||||
point = tgtPoints[num]
|
||||
cmds.move(point.x, point.y, point.z, '{}.{}[{}]'.format(moveNode, compName, i), a=True, ws=True)
|
||||
progress(endProgress=1)
|
||||
|
||||
def cmd():
|
||||
try:
|
||||
companyName = cmds.displayString('rsCompany', q=True, value=True)
|
||||
if companyName == 'square-enix':
|
||||
pass
|
||||
else:
|
||||
mel.eval('RsDccTpc ("rsSnapOnClosestVertex", "2015/10", "kimutoru@rstool", "cmd", "功能描述","");')
|
||||
except Exception as e:
|
||||
print("Error: {}".format(e))
|
||||
|
||||
cmds.undoInfo(ock=True)
|
||||
sel = cmds.ls(sl=True)
|
||||
if len(sel) > 1:
|
||||
if isLttice(sel[0].split('.')[0]):
|
||||
vtx = cmds.ls('*.pt[*][*][*]', sl=True, fl=True)
|
||||
if not vtx:
|
||||
moveNode = sel[0].split('.')[0]
|
||||
vtx = cmds.ls(moveNode + '.pt[*]')
|
||||
else:
|
||||
vtx = cmds.ls('*.vtx[*]', sl=True, fl=True)
|
||||
if not vtx:
|
||||
moveNode = sel[0].split('.')[0]
|
||||
vtx = cmds.ls(moveNode + '.vtx[*]')
|
||||
|
||||
applySnapOnClosetVertex(sel[-1], vtx)
|
||||
else:
|
||||
cmds.error('[rsSnapOnClosestVertex] 请至少选择两个对象,一个是目标对象,另一个是要移动的顶点列表。')
|
||||
cmds.undoInfo(cck=True)
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# cmd()
|
445
Scripts/Modeling/Edit/CreasePlus/CreasePlusBase.py
Normal file
@ -0,0 +1,445 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
_____ _____ ______ _____ ______
|
||||
/ ____| __ \| ____| /\ / ____| ____|_
|
||||
| | | |__) | |__ / \ | (___ | |__ _| |_
|
||||
| | | _ /| __| / /\ \ \___ \| __|_ _|
|
||||
| |____| | \ \| |____ / ____ \ ____) | |____|_|
|
||||
\_____|_| \_\______/_/ \_\_____/|______|
|
||||
|
||||
"""
|
||||
|
||||
import maya.cmds as cmds
|
||||
import maya.api.OpenMaya as om
|
||||
|
||||
# uses python maya 2
|
||||
|
||||
maya_useNewAPI = True
|
||||
|
||||
class MayaVerObj():
|
||||
def __init__(self):
|
||||
self.num = None
|
||||
self.extnum = 0
|
||||
|
||||
def cPwhatmayaversion():
|
||||
verstring = cmds.about(v=True)
|
||||
|
||||
splited = verstring.split()
|
||||
num = None
|
||||
extnum = 0
|
||||
|
||||
for r in splited:
|
||||
if "20" in r:
|
||||
num = int(r)
|
||||
break
|
||||
|
||||
i = -1
|
||||
for r in splited:
|
||||
i += 1
|
||||
if r.lower() == "extension" or r.lower() == "ext":
|
||||
j = i+1
|
||||
for j in range(len(splited)):
|
||||
if splited[j].isdigit():
|
||||
extnum = int(splited[j])
|
||||
break
|
||||
break
|
||||
|
||||
if not num:
|
||||
raise Exception("can't get maya version")
|
||||
|
||||
mayavobj = MayaVerObj()
|
||||
mayavobj.num = num
|
||||
mayavobj.extnum = extnum
|
||||
return mayavobj
|
||||
|
||||
global_creasePlus_mayaver = cPwhatmayaversion()
|
||||
|
||||
def getmayaver():
|
||||
global global_creasePlus_mayaver
|
||||
return global_creasePlus_mayaver
|
||||
|
||||
class CpMsg:
|
||||
|
||||
kNoSel = 'nothing is selected'
|
||||
kNoSelCurve = 'no curve(s) selected'
|
||||
kNoSelMesh = 'no mesh(es) selected'
|
||||
kNoSelEdge = 'no edge(s) selected'
|
||||
kNoSelVert = 'no vertice(s) selected'
|
||||
kNoSelFace = 'no face(s) selected'
|
||||
|
||||
kSelOneMesh = 'select one mesh'
|
||||
kSelMesh = 'mesh(es) must be selected'
|
||||
kSelEdge = 'edge(s) must be selected'
|
||||
kSelFace = 'face(s) must be selected'
|
||||
kSelVert = 'vertice(s) must be selected'
|
||||
|
||||
kSelEdgeOrOneMesh = 'select edge(s) or a mesh'
|
||||
|
||||
kSelCurveCv = 'curve cv(s) must be selected'
|
||||
kWorksForOne = 'works just for one entity'
|
||||
|
||||
kNoHardEdges = 'no hard edge(s) were found'
|
||||
|
||||
kSelLeastTwoMesh = 'select at least two meshes'
|
||||
kSelLeastTwoCurve = 'select at least two curves'
|
||||
|
||||
kInvalidFuncArgs = 'function called with invalid arguments'
|
||||
|
||||
kRequModelView = 'you are required to be in a modeling view/pan'
|
||||
kWrongSel = 'wrong selection'
|
||||
|
||||
kcPnodePluginNotLoaded = \
|
||||
'creasePlus nodes plugin must be loaded to use this command'
|
||||
|
||||
# obj assumed to be a parent
|
||||
def cPgotoChild(obj, mfntyp):
|
||||
dagn = om.MFnDagNode(obj)
|
||||
for i in range(dagn.childCount()):
|
||||
cur = dagn.child(i)
|
||||
if cur.hasFn(mfntyp):
|
||||
return cur
|
||||
return om.MObject.kNullObj
|
||||
|
||||
def cPshapeDagPath(obj):
|
||||
dagn = om.MFnDagNode(obj)
|
||||
return dagn.getPath()
|
||||
|
||||
def cPhardEdgesStrings(shape):
|
||||
|
||||
sel = om.MSelectionList()
|
||||
sel.add(shape)
|
||||
|
||||
dagp = sel.getDagPath(0)
|
||||
|
||||
edgeIter = om.MItMeshEdge(dagp)
|
||||
selStrings = []
|
||||
shapeString = dagp.partialPathName()
|
||||
while not edgeIter.isDone():
|
||||
if edgeIter.isSmooth == False:
|
||||
selStrings.append(shapeString + '.e['
|
||||
+ str(edgeIter.index()) + ']')
|
||||
|
||||
edgeIter.next()
|
||||
|
||||
return selStrings
|
||||
|
||||
def cPgetShapeStringsFromSel(mfntyp):
|
||||
|
||||
sel = om.MGlobal.getActiveSelectionList()
|
||||
selIt = om.MItSelectionList(sel)
|
||||
|
||||
selStrings = []
|
||||
dagFn = om.MFnDagNode()
|
||||
while not selIt.isDone():
|
||||
|
||||
if selIt.itemType() != selIt.kDagSelectionItem:
|
||||
selIt.next()
|
||||
continue
|
||||
|
||||
obj = selIt.getDependNode()
|
||||
|
||||
if not obj.hasFn(mfntyp):
|
||||
|
||||
obj = cPgotoChild(obj, mfntyp)
|
||||
|
||||
if not obj.hasFn(mfntyp):
|
||||
selIt.next()
|
||||
continue
|
||||
|
||||
dagFn.setObject(obj)
|
||||
selStrings.append(dagFn.partialPathName())
|
||||
selIt.next()
|
||||
|
||||
return selStrings
|
||||
|
||||
def cPcameraDominantPlane():
|
||||
|
||||
activePan = cmds.getPanel(wf=True)
|
||||
|
||||
if cmds.getPanel(to=activePan) != 'modelPanel':
|
||||
cmds.error(CpMsg.kRequModelView)
|
||||
|
||||
camPos = cmds.camera(cmds.modelEditor(activePan, q=True, cam=True),
|
||||
q=True, p=True)
|
||||
camTarget = cmds.camera(cmds.modelEditor(activePan, q=True, cam=True),
|
||||
q=True, wci=True)
|
||||
|
||||
camDir = om.MVector(abs(camTarget[0] - camPos[0]), abs(camTarget[1]
|
||||
- camPos[1]), abs(camTarget[2] - camPos[2]))
|
||||
|
||||
maxv = 0
|
||||
ddir = 'x'
|
||||
if maxv < camDir.x:
|
||||
maxv = camDir.x
|
||||
ddir = 'x'
|
||||
if maxv < camDir.y:
|
||||
maxv = camDir.y
|
||||
ddir = 'y'
|
||||
if maxv < camDir.z:
|
||||
maxv = camDir.z
|
||||
ddir = 'z'
|
||||
|
||||
return ddir
|
||||
|
||||
# cmds.ls(sl=True)
|
||||
# get shape + v, e, f comps in a tuple
|
||||
|
||||
def cPgetShapeAndCoStrings(mselit):
|
||||
|
||||
shape = None
|
||||
v = None
|
||||
e = None
|
||||
f = None
|
||||
|
||||
if mselit.itemType() != mselit.kDagSelectionItem:
|
||||
return (shape, v, e, f)
|
||||
|
||||
hasComp = mselit.hasComponents()
|
||||
|
||||
if hasComp == True:
|
||||
|
||||
comp = mselit.getComponent()
|
||||
|
||||
if comp[0].node().hasFn(om.MFn.kMesh):
|
||||
shape = comp[0]
|
||||
|
||||
if comp[1].hasFn(om.MFn.kMeshPolygonComponent):
|
||||
f = comp[1]
|
||||
elif comp[1].hasFn(om.MFn.kMeshEdgeComponent):
|
||||
|
||||
e = comp[1]
|
||||
elif comp[1].hasFn(om.MFn.kMeshVertComponent):
|
||||
|
||||
v = comp[1]
|
||||
else:
|
||||
|
||||
obj = mselit.getDependNode()
|
||||
|
||||
if not obj.hasFn(om.MFn.kMesh):
|
||||
obj = cPgotoChild(obj, om.MFn.kMesh)
|
||||
|
||||
if obj.hasFn(om.MFn.kMesh):
|
||||
shape = cPshapeDagPath(obj)
|
||||
|
||||
return (shape, v, e, f)
|
||||
|
||||
def cPfaceToHardEdgeStrings(dagp, faceComps):
|
||||
|
||||
edgeIds = set()
|
||||
|
||||
faceIt = om.MItMeshPolygon(dagp, faceComps)
|
||||
meshFn = om.MFnMesh(dagp.node())
|
||||
|
||||
while not faceIt.isDone():
|
||||
|
||||
fEdges = faceIt.getEdges()
|
||||
|
||||
for idx in fEdges:
|
||||
if not meshFn.isEdgeSmooth(idx):
|
||||
edgeIds.add(idx)
|
||||
|
||||
faceIt.next(None) # maya api bug
|
||||
|
||||
shapeString = dagp.partialPathName()
|
||||
return [shapeString + '.e[' + str(idx) + ']' for idx in edgeIds]
|
||||
|
||||
def cPedgeToStrings(dagp, edgeComps):
|
||||
|
||||
edgeStrings = []
|
||||
edgeIt = om.MItMeshEdge(dagp, edgeComps)
|
||||
shapeString = dagp.partialPathName()
|
||||
while not edgeIt.isDone():
|
||||
|
||||
edgeStrings.append(shapeString + '.e[' + str(edgeIt.index())
|
||||
+ ']')
|
||||
edgeIt.next()
|
||||
|
||||
return edgeStrings
|
||||
|
||||
################################################################ CONTEXTS #################################################################
|
||||
|
||||
# draw curve ctx
|
||||
try:
|
||||
global_cPcurveCtxStr
|
||||
except:
|
||||
global_cPcurveCtxStr = cmds.curveCVCtx('cPcurveCtx', degree=1)
|
||||
|
||||
#
|
||||
|
||||
# booleanop context
|
||||
try:
|
||||
global_cPboolOpCtxStr
|
||||
except:
|
||||
global_cPboolOpCtxStr = cmds.dragAttrContext('cPboolOpCtx')
|
||||
|
||||
global_cPboolOpAttrCnt = 3
|
||||
global_cPboolOpAttrIter = 0
|
||||
|
||||
def cPboolOpIterSetVal(val):
|
||||
global global_cPboolOpAttrIter
|
||||
global_cPboolOpAttrIter = val
|
||||
return global_cPboolOpAttrIter % global_cPboolOpAttrCnt
|
||||
|
||||
def cPboolOpIterVal():
|
||||
global global_cPboolOpAttrIter
|
||||
return global_cPboolOpAttrIter % global_cPboolOpAttrCnt
|
||||
|
||||
def cPboolOpIterIncVal():
|
||||
global global_cPboolOpAttrIter
|
||||
global_cPboolOpAttrIter += 1
|
||||
return global_cPboolOpAttrIter % global_cPboolOpAttrCnt
|
||||
|
||||
#
|
||||
|
||||
# mirror context
|
||||
try:
|
||||
global_cPmirrorCtxStr
|
||||
except:
|
||||
global_cPmirrorCtxStr = cmds.dragAttrContext('cPmirrorCtx')
|
||||
|
||||
global_cPmirrorAttrCnt = None
|
||||
if getmayaver().num > 2016:
|
||||
global_cPmirrorAttrCnt = 1
|
||||
else:
|
||||
global_cPmirrorAttrCnt = 3
|
||||
|
||||
global_cPmirrorAttrIter = 0
|
||||
|
||||
def cPmirrorIterSetVal(val):
|
||||
global global_cPmirrorAttrIter
|
||||
global_cPmirrorAttrIter = val
|
||||
return global_cPmirrorAttrIter % global_cPmirrorAttrCnt
|
||||
|
||||
def cPmirrorIterVal():
|
||||
global global_cPmirrorAttrIter
|
||||
return global_cPmirrorAttrIter % global_cPmirrorAttrCnt
|
||||
|
||||
def cPmirrorIterIncVal():
|
||||
global global_cPmirrorAttrIter
|
||||
global_cPmirrorAttrIter += 1
|
||||
return global_cPmirrorAttrIter % global_cPmirrorAttrCnt
|
||||
|
||||
#
|
||||
|
||||
# hbevel context
|
||||
try:
|
||||
global_hBevelCtxStr
|
||||
except:
|
||||
global_hBevelCtxStr = cmds.dragAttrContext('hBevelCtx')
|
||||
|
||||
global_hBevelAttrCnt = 2
|
||||
global_hBevelAttrIter = 0
|
||||
|
||||
def cPhBevelIterSetVal(val):
|
||||
global global_hBevelAttrIter
|
||||
global_hBevelAttrIter = val
|
||||
return global_hBevelAttrIter % global_hBevelAttrCnt
|
||||
|
||||
def cPhBevelIterVal():
|
||||
global global_hBevelAttrIter
|
||||
return global_hBevelAttrIter % global_hBevelAttrCnt
|
||||
|
||||
def cPhBevelIterIncVal():
|
||||
global global_hBevelAttrIter
|
||||
global_hBevelAttrIter += 1
|
||||
return global_hBevelAttrIter % global_hBevelAttrCnt
|
||||
|
||||
#
|
||||
|
||||
# curvebevel context
|
||||
try:
|
||||
global_cPcurveBevelCtxStr
|
||||
except:
|
||||
global_cPcurveBevelCtxStr = cmds.dragAttrContext('cPcurveBvlCtx')
|
||||
|
||||
global_cPcurveBevelAttrCnt = 2
|
||||
global_cPcurveBevelAttrIter = 0
|
||||
|
||||
def cPcurveBevelIterSetVal(val):
|
||||
global global_cPcurveBevelAttrIter
|
||||
global_cPcurveBevelAttrIter = val
|
||||
return global_cPcurveBevelAttrIter % global_cPcurveBevelAttrCnt
|
||||
|
||||
def cPcurveBevelIterVal():
|
||||
global global_cPcurveBevelAttrIter
|
||||
return global_cPcurveBevelAttrIter % global_cPcurveBevelAttrCnt
|
||||
|
||||
def cPcurveBevelIterIncVal():
|
||||
global global_cPcurveBevelAttrIter
|
||||
global_cPcurveBevelAttrIter += 1
|
||||
return global_cPcurveBevelAttrIter % global_cPcurveBevelAttrCnt
|
||||
|
||||
#
|
||||
|
||||
# crease tool context
|
||||
try:
|
||||
global_cPcreaseCtxStr
|
||||
except:
|
||||
global_cPcreaseCtxStr = cmds.polyCreaseCtx('cPcreaseCtx', es=True, r=True)
|
||||
|
||||
#
|
||||
|
||||
# physical crease context
|
||||
try:
|
||||
global_pCreaseCtxStr
|
||||
except:
|
||||
global_pCreaseCtxStr = cmds.dragAttrContext('pCreaseCtx')
|
||||
|
||||
global_pCreaseAttrCnt = 2
|
||||
global_pCreaseAttrIter = 0
|
||||
|
||||
def cPpCreaseIterSetVal(val):
|
||||
global global_pCreaseAttrIter
|
||||
global_pCreaseAttrIter = val
|
||||
return global_pCreaseAttrIter % global_pCreaseAttrCnt
|
||||
|
||||
def cPpCreaseIterVal():
|
||||
global global_pCreaseAttrIter
|
||||
return global_pCreaseAttrIter % global_pCreaseAttrCnt
|
||||
|
||||
def cPpCreaseIterIncVal():
|
||||
global global_pCreaseAttrIter
|
||||
global_pCreaseAttrIter += 1
|
||||
return global_pCreaseAttrIter % global_pCreaseAttrCnt
|
||||
|
||||
#
|
||||
def cPcontextUndo():
|
||||
if cmds.currentCtx() == global_cPboolOpCtxStr:
|
||||
# cmds.setToolTo('moveSuperContext')
|
||||
cmds.dragAttrContext(global_cPboolOpCtxStr, e=True, reset=True)
|
||||
cmds.setToolTo(global_cPboolOpCtxStr)
|
||||
elif cmds.currentCtx() == global_hBevelCtxStr:
|
||||
|
||||
# cmds.setToolTo('moveSuperContext')
|
||||
cmds.dragAttrContext(global_hBevelCtxStr, e=True, reset=True)
|
||||
cmds.setToolTo(global_hBevelCtxStr)
|
||||
elif cmds.currentCtx() == global_cPmirrorCtxStr:
|
||||
|
||||
# cmds.setToolTo('moveSuperContext')
|
||||
cmds.dragAttrContext(global_cPmirrorCtxStr, e=True, reset=True)
|
||||
cmds.setToolTo(global_cPmirrorCtxStr)
|
||||
elif cmds.currentCtx() == global_cPcurveBevelCtxStr:
|
||||
|
||||
# cmds.setToolTo('moveSuperContext')
|
||||
cmds.dragAttrContext(global_cPcurveBevelCtxStr, e=True, reset=True)
|
||||
cmds.setToolTo(global_cPcurveBevelCtxStr)
|
||||
elif cmds.currentCtx() == global_pCreaseCtxStr:
|
||||
|
||||
# cmds.setToolTo('moveSuperContext')
|
||||
cmds.dragAttrContext(global_pCreaseCtxStr, e=True, reset=True)
|
||||
cmds.setToolTo(global_pCreaseCtxStr)
|
||||
|
||||
try:
|
||||
global_creasePlusCtxUndoJob
|
||||
except:
|
||||
global_creasePlusCtxUndoJob = cmds.scriptJob(event=['Undo', cPcontextUndo])
|
||||
|
||||
############################################################################################################
|
||||
|
||||
def main():
|
||||
return None
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
2049
Scripts/Modeling/Edit/CreasePlus/CreasePlusCore.py
Normal file
BIN
Scripts/Modeling/Edit/CreasePlus/CreasePlusEULA.pdf
Normal file
5
Scripts/Modeling/Edit/CreasePlus/CreasePlusExcept.py
Normal file
@ -0,0 +1,5 @@
|
||||
import maya.api.OpenMaya as om
|
||||
|
||||
def cPexcept(excepstr=""):
|
||||
om.MGlobal.displayError(excepstr)
|
||||
return Exception(excepstr)
|
1061
Scripts/Modeling/Edit/CreasePlus/CreasePlusMain.py
Normal file
497
Scripts/Modeling/Edit/CreasePlus/CreasePlusNodes.py
Normal file
@ -0,0 +1,497 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
_____ _____ ______ _____ ______
|
||||
/ ____| __ \| ____| /\ / ____| ____|_
|
||||
| | | |__) | |__ / \ | (___ | |__ _| |_
|
||||
| | | _ /| __| / /\ \ \___ \| __|_ _|
|
||||
| |____| | \ \| |____ / ____ \ ____) | |____|_|
|
||||
\_____|_| \_\______/_/ \_\_____/|______|
|
||||
|
||||
"""
|
||||
|
||||
# import maya.cmds as mc
|
||||
import maya.api.OpenMaya as om
|
||||
# import copy
|
||||
|
||||
# uses python maya 2
|
||||
|
||||
maya_useNewAPI = True
|
||||
|
||||
#################
|
||||
|
||||
def cPhardEdgeIds(mesh):
|
||||
edgeIter = om.MItMeshEdge(mesh)
|
||||
ids = []
|
||||
while not edgeIter.isDone():
|
||||
if not edgeIter.isSmooth:
|
||||
ids.append(edgeIter.index())
|
||||
edgeIter.next()
|
||||
return ids
|
||||
|
||||
def cPcompToIds(compdata, cptyp):
|
||||
compDataFn = om.MFnComponentListData(compdata)
|
||||
# compDataFn
|
||||
ids = []
|
||||
for i in range(compDataFn.length()):
|
||||
curcomp = compDataFn.get(i)
|
||||
if curcomp.hasFn(cptyp):
|
||||
sic = om.MFnSingleIndexedComponent(curcomp)
|
||||
for j in range(sic.elementCount):
|
||||
curIdx = sic.element(j)
|
||||
ids.append(curIdx)
|
||||
return ids
|
||||
|
||||
def cPidsToComp(ids, cptyp):
|
||||
sic = om.MFnSingleIndexedComponent()
|
||||
sic.create(cptyp) # om.MFn.kMeshEdgeComponent
|
||||
|
||||
sic.addElements(ids)
|
||||
|
||||
compData = om.MFnComponentListData()
|
||||
compObj = compData.create()
|
||||
compData.add(sic.object())
|
||||
return (sic.object(), compObj)
|
||||
|
||||
def cPtransformMeshPoints(mesh, mat):
|
||||
meshFn = om.MFnMesh(mesh)
|
||||
pts = meshFn.getPoints()
|
||||
for pt in pts:
|
||||
pt *= mat
|
||||
|
||||
meshFn.setPoints(pts)
|
||||
|
||||
"""
|
||||
input: mesh
|
||||
output: componentlist
|
||||
"""
|
||||
|
||||
MayaNodeT = om.MPxNode
|
||||
|
||||
class CpCurveBevel(MayaNodeT):
|
||||
|
||||
kNodeName = "creasePlusCurveBevel"
|
||||
kNodeId = om.MTypeId(0x1154)
|
||||
|
||||
aCvs = None
|
||||
aOffset = None
|
||||
aSeg = None
|
||||
aOffsetFrac = None
|
||||
aInputCurve = None
|
||||
aOutput = None
|
||||
|
||||
def __init__(self):
|
||||
super(CpCurveBevel, self).__init__()
|
||||
|
||||
@classmethod
|
||||
def creator(cls):
|
||||
return cls()
|
||||
|
||||
@classmethod
|
||||
def initialize(cls):
|
||||
|
||||
nAttr = om.MFnNumericAttribute()
|
||||
tAttr = om.MFnTypedAttribute()
|
||||
|
||||
cls.aCvs = tAttr.create("cvComponentList", "cvs",
|
||||
om.MFnComponentListData.kComponentList)
|
||||
|
||||
cls.aOffset = nAttr.create("offset", "off", om.MFnNumericData.kFloat)
|
||||
nAttr.setMin(0)
|
||||
nAttr.default = 0.5
|
||||
nAttr.keyable = True
|
||||
cls.aSeg = nAttr.create("segments", "seg", om.MFnNumericData.kInt)
|
||||
nAttr.setMin(1)
|
||||
nAttr.default = 1
|
||||
nAttr.keyable = True
|
||||
|
||||
cls.aOffsetFrac = nAttr.create("offsetAsFraction", "oaf",
|
||||
om.MFnNumericData.kBoolean)
|
||||
nAttr.default = False
|
||||
nAttr.keyable = True
|
||||
|
||||
cls.aInputCurve = tAttr.create("inCurve", "inc",
|
||||
om.MFnData.kNurbsCurve)
|
||||
cls.aOutput = tAttr.create("outCurve", "out", om.MFnData.kNurbsCurve)
|
||||
tAttr.storable = False
|
||||
tAttr.writable = False
|
||||
|
||||
MayaNodeT.addAttribute(cls.aCvs)
|
||||
MayaNodeT.addAttribute(cls.aOffset)
|
||||
MayaNodeT.addAttribute(cls.aSeg)
|
||||
MayaNodeT.addAttribute(cls.aOffsetFrac)
|
||||
MayaNodeT.addAttribute(cls.aInputCurve)
|
||||
MayaNodeT.addAttribute(cls.aOutput)
|
||||
|
||||
MayaNodeT.attributeAffects(cls.aCvs, cls.aOutput)
|
||||
MayaNodeT.attributeAffects(cls.aOffset, cls.aOutput)
|
||||
MayaNodeT.attributeAffects(cls.aSeg, cls.aOutput)
|
||||
MayaNodeT.attributeAffects(cls.aOffsetFrac, cls.aOutput)
|
||||
MayaNodeT.attributeAffects(cls.aInputCurve, cls.aOutput)
|
||||
|
||||
def setOutputToCopy(self, data):
|
||||
|
||||
inCurveHandle = data.inputValue(CpCurveBevel.aInputCurve)
|
||||
outHandle = data.outputValue(CpCurveBevel.aOutput)
|
||||
outHandle.copy(inCurveHandle)
|
||||
outHandle.setClean()
|
||||
|
||||
def compute(self, plug, data):
|
||||
|
||||
if plug != CpCurveBevel.aOutput:
|
||||
return None
|
||||
|
||||
inCurveHandle = data.inputValue(CpCurveBevel.aInputCurve)
|
||||
|
||||
inCurve = inCurveHandle.asNurbsCurveTransformed()
|
||||
|
||||
inCvsHandle = data.inputValue(CpCurveBevel.aCvs)
|
||||
compData = inCvsHandle.data()
|
||||
ids = cPcompToIds(compData, om.MFn.kCurveCVComponent)
|
||||
#
|
||||
inOffset = data.inputValue(CpCurveBevel.aOffset).asFloat()
|
||||
inSeg = data.inputValue(CpCurveBevel.aSeg).asInt()
|
||||
inFrac = data.inputValue(CpCurveBevel.aOffsetFrac).asBool()
|
||||
|
||||
if len(ids) == 0 or inOffset == 0:
|
||||
self.setOutputToCopy(data)
|
||||
return
|
||||
|
||||
curveFn = om.MFnNurbsCurve(inCurve)
|
||||
curveDegree = curveFn.degree
|
||||
curveForm = curveFn.form
|
||||
# curveKnots = curveFn.knots()
|
||||
curvePts = curveFn.cvPositions() # to be modified
|
||||
numcv = len(curvePts) # to be modified
|
||||
|
||||
bevelParam = 1 / float(inSeg)
|
||||
bevelPts = []
|
||||
|
||||
ids.sort()
|
||||
|
||||
for cvIdx in ids:
|
||||
|
||||
mpos = om.MVector(curvePts[cvIdx])
|
||||
|
||||
if curveForm == curveFn.kPeriodic:
|
||||
lpos = om.MVector(curvePts[(cvIdx + numcv -
|
||||
(1 + curveDegree)) %
|
||||
(numcv - curveDegree)])
|
||||
rpos = om.MVector(curvePts[(cvIdx + 1) %
|
||||
(numcv - curveDegree)])
|
||||
else:
|
||||
lpos = om.MVector(curvePts[(cvIdx + numcv - 1) % numcv])
|
||||
rpos = om.MVector(curvePts[(cvIdx + 1) % numcv])
|
||||
|
||||
lvec = lpos - mpos
|
||||
rvec = rpos - mpos
|
||||
|
||||
lanchor = None
|
||||
ranchor = None
|
||||
if inFrac:
|
||||
lanchor = mpos + (inOffset * lvec)
|
||||
ranchor = mpos + (inOffset * rvec)
|
||||
else:
|
||||
lanchor = mpos + (inOffset * lvec.normal())
|
||||
ranchor = mpos + (inOffset * rvec.normal())
|
||||
|
||||
bevelPts.append(lanchor)
|
||||
t = 1 * bevelParam
|
||||
for i in range(inSeg - 1):
|
||||
# (1-t)^2A+2t(1-t)B+t^2C
|
||||
res = (1 - t)**2 * lanchor + (2 * t * (1 - t) * mpos) + (
|
||||
t**2 * ranchor)
|
||||
bevelPts.append(res)
|
||||
t += bevelParam
|
||||
bevelPts.append(ranchor)
|
||||
|
||||
#
|
||||
i = len(bevelPts) - (inSeg + 1)
|
||||
ids.sort(reverse=True)
|
||||
for cvIdx in ids:
|
||||
curvePts[cvIdx] = bevelPts[i]
|
||||
curvePts[cvIdx + 1:cvIdx + 1] = bevelPts[i + 1:i + inSeg + 1]
|
||||
i -= (inSeg + 1)
|
||||
|
||||
numcv = len(curvePts)
|
||||
if curveForm == curveFn.kPeriodic:
|
||||
curvePts[-curveDegree:] = curvePts[:curveDegree]
|
||||
|
||||
if curveDegree == 1:
|
||||
knots = [float(i) for i in range(numcv)]
|
||||
|
||||
else:
|
||||
knots = [None] * (numcv - curveDegree + (2 * curveDegree) - 1)
|
||||
|
||||
if curveForm == curveFn.kPeriodic:
|
||||
knots[:curveDegree] = [
|
||||
float(i) for i in reversed(range(0, -curveDegree, -1))
|
||||
]
|
||||
knots[curveDegree:] = [
|
||||
float(i) for i in range(1,
|
||||
len(knots) - curveDegree + 1)
|
||||
]
|
||||
|
||||
else:
|
||||
knots[:curveDegree] = [float(0)] * curveDegree
|
||||
knots[-curveDegree:] = [float(numcv - curveDegree)
|
||||
] * curveDegree
|
||||
knots[curveDegree:-curveDegree] = [
|
||||
float(i)
|
||||
for i in range(1,
|
||||
len(knots) - (2 * curveDegree) + 1)
|
||||
]
|
||||
|
||||
curveDataFn = om.MFnNurbsCurveData()
|
||||
curveDataFn.create()
|
||||
|
||||
knots = om.MDoubleArray(knots)
|
||||
curveFn.create(
|
||||
curvePts,
|
||||
knots,
|
||||
curveDegree,
|
||||
curveForm,
|
||||
False,
|
||||
False,
|
||||
parent=curveDataFn.object())
|
||||
|
||||
out = data.outputValue(CpCurveBevel.aOutput)
|
||||
out.setMObject(curveDataFn.object())
|
||||
out.setClean()
|
||||
|
||||
class CpCurveToPoly(MayaNodeT):
|
||||
|
||||
kNodeName = "creasePlusCurveToPoly"
|
||||
kNodeId = om.MTypeId(0x12547)
|
||||
|
||||
aCount = None
|
||||
aRevNorm = None
|
||||
aControlPts = None
|
||||
aInputCurve = None
|
||||
aOutput = None
|
||||
|
||||
def __init__(self):
|
||||
super(CpCurveToPoly, self).__init__()
|
||||
|
||||
@classmethod
|
||||
def creator(cls):
|
||||
return cls()
|
||||
|
||||
@classmethod
|
||||
def initialize(cls):
|
||||
|
||||
nAttr = om.MFnNumericAttribute()
|
||||
tAttr = om.MFnTypedAttribute()
|
||||
|
||||
cls.aRevNorm = nAttr.create("reverse", "rev",
|
||||
om.MFnNumericData.kBoolean)
|
||||
nAttr.default = False
|
||||
nAttr.keyable = True
|
||||
|
||||
cls.aCount = nAttr.create("count", "cnt", om.MFnNumericData.kInt)
|
||||
nAttr.setMin(3)
|
||||
nAttr.default = 12
|
||||
nAttr.keyable = True
|
||||
|
||||
cls.aControlPts = nAttr.create("controlPts", "cv",
|
||||
om.MFnNumericData.kBoolean)
|
||||
nAttr.default = True
|
||||
nAttr.keyable = True
|
||||
|
||||
cls.aInputCurve = tAttr.create("inCurve", "inc",
|
||||
om.MFnData.kNurbsCurve)
|
||||
cls.aOutput = tAttr.create("outPoly", "out", om.MFnData.kMesh)
|
||||
tAttr.storable = False
|
||||
tAttr.writable = False
|
||||
|
||||
MayaNodeT.addAttribute(cls.aRevNorm)
|
||||
MayaNodeT.addAttribute(cls.aCount)
|
||||
MayaNodeT.addAttribute(cls.aControlPts)
|
||||
MayaNodeT.addAttribute(cls.aInputCurve)
|
||||
MayaNodeT.addAttribute(cls.aOutput)
|
||||
|
||||
MayaNodeT.attributeAffects(cls.aRevNorm, cls.aOutput)
|
||||
MayaNodeT.attributeAffects(cls.aCount, cls.aOutput)
|
||||
MayaNodeT.attributeAffects(cls.aControlPts, cls.aOutput)
|
||||
MayaNodeT.attributeAffects(cls.aInputCurve, cls.aOutput)
|
||||
|
||||
def setOutputToNull(self, data):
|
||||
out = data.outputValue(CpCurveToPoly.aOutput)
|
||||
out.setMObject(om.MObject.kNullObj)
|
||||
out.setClean()
|
||||
|
||||
def compute(self, plug, data):
|
||||
|
||||
if plug != CpCurveToPoly.aOutput:
|
||||
return None
|
||||
|
||||
reverseNormal = data.inputValue(CpCurveToPoly.aRevNorm).asBool()
|
||||
inCurveHandle = data.inputValue(CpCurveToPoly.aInputCurve)
|
||||
|
||||
inCurve = inCurveHandle.asNurbsCurveTransformed()
|
||||
|
||||
inCount = data.inputValue(CpCurveToPoly.aCount).asInt() #
|
||||
useCvs = data.inputValue(CpCurveToPoly.aControlPts).asBool()
|
||||
|
||||
curveFn = om.MFnNurbsCurve(inCurve)
|
||||
curveForm = curveFn.form
|
||||
curveDegree = curveFn.degree
|
||||
polyPts = None
|
||||
|
||||
meshDataFn = om.MFnMeshData()
|
||||
meshDataFn.create()
|
||||
|
||||
if useCvs:
|
||||
if curveForm == curveFn.kPeriodic:
|
||||
polyPts = curveFn.cvPositions()[:-curveDegree]
|
||||
else:
|
||||
polyPts = curveFn.cvPositions()
|
||||
else:
|
||||
polyPts = []
|
||||
domain = curveFn.knotDomain
|
||||
param = domain[1] / float(inCount)
|
||||
t = 0.0
|
||||
for i in range(inCount):
|
||||
polyPts.append(curveFn.getPointAtParam(t))
|
||||
t += param
|
||||
|
||||
if reverseNormal:
|
||||
polyPts = [pt for pt in reversed(polyPts)]
|
||||
# create(vertices, polygonCounts, polygonConnects, uValues=None, vValues=None, parent=kNullObj) -> MObject
|
||||
meshFn = om.MFnMesh()
|
||||
meshFn.create(
|
||||
polyPts, [len(polyPts)], [i for i in range(len(polyPts))],
|
||||
parent=meshDataFn.object())
|
||||
|
||||
out = data.outputValue(CpCurveToPoly.aOutput)
|
||||
out.setMObject(meshDataFn.object())
|
||||
out.setClean()
|
||||
|
||||
class CpHeIds(MayaNodeT):
|
||||
|
||||
kNodeName = "creasePlusBevelHe"
|
||||
kNodeId = om.MTypeId(0x1157)
|
||||
|
||||
aForceComp = None
|
||||
aInputMesh = None
|
||||
aIds = None
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super(CpHeIds, self).__init__()
|
||||
|
||||
self.numVertices = 0
|
||||
self.numPolygons = 0
|
||||
self.numNormals = 0
|
||||
self.dummycompute = False
|
||||
|
||||
@classmethod
|
||||
def creator(cls):
|
||||
return cls()
|
||||
|
||||
@classmethod
|
||||
def initialize(cls):
|
||||
tAttr = om.MFnTypedAttribute()
|
||||
nAttr = om.MFnNumericAttribute()
|
||||
|
||||
cls.aForceComp = nAttr.create("forceCompute", "fc",
|
||||
om.MFnNumericData.kBoolean, 0)
|
||||
nAttr.default = False
|
||||
nAttr.keyable = True
|
||||
|
||||
cls.aInputMesh = tAttr.create("inMesh", "i", om.MFnData.kMesh)
|
||||
|
||||
cls.aIds = tAttr.create("componentList", "cl",
|
||||
om.MFnComponentListData.kComponentList)
|
||||
tAttr.storable = False
|
||||
tAttr.writable = False
|
||||
|
||||
MayaNodeT.addAttribute(cls.aForceComp)
|
||||
MayaNodeT.addAttribute(cls.aInputMesh)
|
||||
MayaNodeT.addAttribute(cls.aIds)
|
||||
|
||||
def attrToPlug(self, attr):
|
||||
return om.MPlug(self.thisMObject(), attr)
|
||||
|
||||
def setDependentsDirty(self, plug, affect):
|
||||
|
||||
if plug == CpHeIds.aForceComp:
|
||||
if self.dummycompute == False:
|
||||
self.dummycompute = True
|
||||
affect.append(self.attrToPlug(CpHeIds.aIds))
|
||||
else:
|
||||
self.dummycompute = False
|
||||
elif plug == CpHeIds.aInputMesh:
|
||||
affect.append(self.attrToPlug(CpHeIds.aIds))
|
||||
|
||||
def compute(self, plug, data):
|
||||
if plug != CpHeIds.aIds:
|
||||
return None
|
||||
|
||||
doingit = False
|
||||
|
||||
data.inputValue(CpHeIds.aForceComp)
|
||||
inMeshHandle = data.inputValue(CpHeIds.aInputMesh)
|
||||
inmesh = inMeshHandle.asMesh()
|
||||
meshFn = om.MFnMesh(inmesh)
|
||||
|
||||
if self.dummycompute == True:
|
||||
doingit = True
|
||||
elif (self.numVertices != meshFn.numVertices
|
||||
or self.numPolygons != meshFn.numPolygons
|
||||
or self.numNormals != meshFn.numNormals):
|
||||
doingit = True
|
||||
|
||||
if doingit == True:
|
||||
heIds = cPhardEdgeIds(inmesh)
|
||||
compObj = cPidsToComp(heIds, om.MFn.kMeshEdgeComponent)[1]
|
||||
|
||||
outIdsHandle = data.outputValue(CpHeIds.aIds)
|
||||
outIdsHandle.setMObject(compObj)
|
||||
outIdsHandle.setClean()
|
||||
|
||||
if self.dummycompute == True:
|
||||
forceCompplug = self.attrToPlug(CpHeIds.aForceComp)
|
||||
forceCompplug.setBool(False)
|
||||
|
||||
def initializePlugin(obj):
|
||||
|
||||
mplugin = om.MFnPlugin(obj, "Baidhir Hidair", "1.0")
|
||||
|
||||
nodeName = None
|
||||
|
||||
try:
|
||||
nodeName = CpHeIds.kNodeName
|
||||
mplugin.registerNode(CpHeIds.kNodeName, CpHeIds.kNodeId,
|
||||
CpHeIds.creator, CpHeIds.initialize)
|
||||
|
||||
nodeName = CpCurveBevel.kNodeName
|
||||
mplugin.registerNode(CpCurveBevel.kNodeName, CpCurveBevel.kNodeId,
|
||||
CpCurveBevel.creator, CpCurveBevel.initialize)
|
||||
|
||||
nodeName = CpCurveToPoly.kNodeName
|
||||
mplugin.registerNode(CpCurveToPoly.kNodeName, CpCurveToPoly.kNodeId,
|
||||
CpCurveToPoly.creator, CpCurveToPoly.initialize)
|
||||
|
||||
except:
|
||||
raise Exception('failed to register node: ' + nodeName)
|
||||
|
||||
def uninitializePlugin(obj):
|
||||
|
||||
mplugin = om.MFnPlugin(obj)
|
||||
|
||||
nodeName = None
|
||||
|
||||
try:
|
||||
nodeName = CpHeIds.kNodeName
|
||||
mplugin.deregisterNode(CpHeIds.kNodeId)
|
||||
|
||||
nodeName = CpCurveBevel.kNodeName
|
||||
mplugin.deregisterNode(CpCurveBevel.kNodeId)
|
||||
|
||||
nodeName = CpCurveToPoly.kNodeName
|
||||
mplugin.deregisterNode(CpCurveToPoly.kNodeId)
|
||||
|
||||
except:
|
||||
raise Exception('failed to deregister node: ' + nodeName)
|
BIN
Scripts/Modeling/Edit/CreasePlus/CreasePlus_def_sheet.pdf
Normal file
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_bevel_ico.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_bevellive_ico.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_bool_ico.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_close_ico.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_crease_ico.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curveattach_ico.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curvebevel_ico.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curveclose_ico.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curvedraw_ico.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curveint_ico.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curvemult_ico.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curvepoly_ico.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_displayhe_ico.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_eye_ico.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_hardedge_ico.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_menu_ico.png
Normal file
After Width: | Height: | Size: 676 B |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_meshslicer_ico.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_mirror_ico.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_next_ico.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_panelbool_ico.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_physcrease_ico.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_shapeshifter_ico.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_smoothangle_ico.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_subd_ico.png
Normal file
After Width: | Height: | Size: 502 B |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_uv_ico.png
Normal file
After Width: | Height: | Size: 769 B |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_weighttool_ico.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_zbrush_ico.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
17
Scripts/Modeling/Edit/CreasePlus/MayaUndoRun.py
Normal file
@ -0,0 +1,17 @@
|
||||
from functools import wraps
|
||||
import maya.cmds as cmds
|
||||
|
||||
def mayaUndoRun(func):
|
||||
""" Puts the wrapped `func` into a single Maya Undo action, then
|
||||
undoes it when the function enters the finally: block """
|
||||
@wraps(func)
|
||||
def _undofunc(*args, **kwargs):
|
||||
try:
|
||||
# start an undo chunk
|
||||
cmds.undoInfo(openChunk=True)
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
# after calling the func, end the undo chunk
|
||||
cmds.undoInfo(closeChunk=True)
|
||||
|
||||
return _undofunc
|
5
Scripts/Modeling/Edit/CreasePlus/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Empty Init file
|
||||
from . import *
|
2
Scripts/Modeling/Edit/CreasePlus/delpyc.bat
Normal file
@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
del *.pyc
|
40
Scripts/Modeling/Edit/CreasePlus/readit.txt
Normal file
@ -0,0 +1,40 @@
|
||||
_____ _____ ______ _____ ______
|
||||
/ ____| __ \| ____| /\ / ____| ____|_
|
||||
| | | |__) | |__ / \ | (___ | |__ _| |_
|
||||
| | | _ /| __| / /\ \ \___ \| __|_ _|
|
||||
| |____| | \ \| |____ / ____ \ ____) | |____|_|
|
||||
\_____|_| \_\______/_/ \_\_____/|______|
|
||||
|
||||
This version of CreasePlus is rewritten entirely in python cmds(maya) and API's.
|
||||
thus there is no longer support for MayaLT. And the run-in lang is Python.
|
||||
|
||||
install :
|
||||
extract, then just place icon and main folders in documents/maya/(maya_version)/scripts/
|
||||
restart maya if opened.
|
||||
|
||||
#
|
||||
# attach it as python script / runtime command to hotkey:
|
||||
import maya.cmds as cmds
|
||||
from CreasePlus import CreasePlusMain
|
||||
CreasePlusMain.start()
|
||||
|
||||
if not cmds.pluginInfo("CreasePlusNodes", q=True, loaded=True):
|
||||
cmds.loadPlugin("CreasePlusNodes.py")
|
||||
|
||||
|
||||
# attach it as python script / runtime command to hotkey, for attribute iteration in context (optional):
|
||||
from CreasePlus import CreasePlusMain
|
||||
CreasePlusMain.crepcore.creasePlusLastCtx()
|
||||
|
||||
# attach it as python script / runtime command to hotkey, for edge soft/hard toggle (optional):
|
||||
from CreasePlus import CreasePlusMain
|
||||
CreasePlusMain.crepcore.creasePlusToggleEdgeSmooth()
|
||||
|
||||
# attach it as python script / runtime command to hotkey, for edge makeUV (optional):
|
||||
from CreasePlus import CreasePlusMain
|
||||
CreasePlusMain.crepcore.creasePlusMakeUv()
|
||||
|
||||
|
||||
# commands / defs for bindings / scripts can be found in def_sheet file
|
||||
|
||||
# thank you
|
BIN
Scripts/Modeling/Edit/EdgeSensei.jpg
Normal file
After Width: | Height: | Size: 272 KiB |
3350
Scripts/Modeling/Edit/EdgeSensei.py
Normal file
219
Scripts/Modeling/Edit/EvenEdgeLoop.py
Normal file
@ -0,0 +1,219 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import maya.cmds as mc # type: ignore
|
||||
import maya.mel as mel # type: ignore
|
||||
import math
|
||||
import re
|
||||
|
||||
def evenEdgeLoopDoitRun(smoothType):
|
||||
sel=mc.ls(sl=1,fl=1)
|
||||
if sel:
|
||||
shape = mc.listRelatives(sel,p=1 )
|
||||
mc.displaySmoothness(divisionsU=0, divisionsV=0, pointsWire=4, pointsShaded=1, polygonObject=1)
|
||||
sortEdgeLoopGrp = getEdgeRingGroup(0,'')
|
||||
for s in sortEdgeLoopGrp:
|
||||
mc.select(s)
|
||||
evenEdgeLoopDoit(smoothType)
|
||||
mc.select(sel)
|
||||
cmd = 'doMenuComponentSelection("' + shape[0] +'", "edge");'
|
||||
mel.eval(cmd)
|
||||
mc.select(sel)
|
||||
|
||||
def run():
|
||||
if mc.window("evenEdgeLoopDoitUI", exists = True):
|
||||
mc.deleteUI("evenEdgeLoopDoitUI")
|
||||
|
||||
evenEdgeLoopDoitUI = mc.window("Even Edge Loop", w = 230, s = 1 ,mxb = False,mnb = False)
|
||||
mc.columnLayout(adj=1)
|
||||
mc.rowColumnLayout(nc= 5 ,cw=[(1,75),(2,10),(3,75),(4,10),(5,75)])
|
||||
mc.button( l= "Average", c = lambda x: evenEdgeLoopDoitRun("even") )
|
||||
mc.text(l='')
|
||||
mc.button( l= "Arc", c = lambda x: evenEdgeLoopDoitRun("2P") )
|
||||
mc.text(l='')
|
||||
mc.button( l= "Straighten ", c = lambda x: evenEdgeLoopDoitRun("straighten") )
|
||||
mc.showWindow(evenEdgeLoopDoitUI)
|
||||
|
||||
def evenEdgeLoopDoit(smoothType):
|
||||
if mc.objExists('tempEvenCurve'):
|
||||
mc.delete('tempEvenCurve')
|
||||
sel =mc.ls(sl=1,fl=1)
|
||||
|
||||
getCircleState,listVtx = vtxLoopOrderCheck()
|
||||
mc.polyToCurve(form=2, degree=1, conformToSmoothMeshPreview=1)
|
||||
mc.rename('tempEvenCurve')
|
||||
curveCVs =mc.ls('tempEvenCurve.cv[*]',fl=1)
|
||||
posCurve = mc.xform(curveCVs[0], a=1,ws=1, q=1, t=1)
|
||||
posEdge = mc.xform(listVtx[0], a=1,ws=1, q=1, t=1)
|
||||
if posCurve == posEdge:
|
||||
pass
|
||||
else:
|
||||
listVtx = listVtx[::-1]
|
||||
if len(curveCVs)>2:
|
||||
if smoothType == '2P':
|
||||
if len(curveCVs)>3:
|
||||
mc.delete(curveCVs[1:-1])
|
||||
mc.rebuildCurve('tempEvenCurve',ch=0, rpo=1, rt=0, end=1, kr=0, kcp=0, kep=1, kt=0, s = 2 , d=2, tol=0)
|
||||
midA = len(listVtx)/3
|
||||
midB = len(listVtx)/3*2
|
||||
midA = int(midA)
|
||||
midB = int(midB)
|
||||
posA = mc.xform(listVtx[midA], q=1, ws=1, t=1)
|
||||
posB = mc.xform(listVtx[midB], q=1, ws=1, t=1)
|
||||
mc.xform('tempEvenCurve.cv[1]', a=1, ws=1, t = (posA[0],posA[1],posA[2]) )
|
||||
mc.xform('tempEvenCurve.cv[2]', a=1, ws=1, t = (posB[0],posB[1],posB[2]) )
|
||||
mc.rebuildCurve('tempEvenCurve',ch=0, rpo=1, rt=0, end=1, kr=0, kcp=0, kep=1, kt=0, s = (len(listVtx)-1) , d=1, tol=0)
|
||||
curveCVs =mc.ls('tempEvenCurve.cv[*]',fl=1)
|
||||
elif smoothType == 'straighten ':
|
||||
mc.delete(curveCVs[1:-1])
|
||||
newNumber = (len(listVtx)-1)
|
||||
mc.rebuildCurve('tempEvenCurve',ch=0, rpo=1, rt=0, end=1, kr=0, kcp=0, kep=1, kt=0, s = newNumber , d=1, tol=0)
|
||||
if newNumber == 2:
|
||||
mc.delete('tempEvenCurve.cv[1]','tempEvenCurve.cv[3]')
|
||||
curveCVs =mc.ls('tempEvenCurve.cv[*]',fl=1)
|
||||
else:
|
||||
mc.rebuildCurve('tempEvenCurve',ch=1, rpo=1, rt=0, end=1, kr=0, kcp=0, kep=1, kt=0, s = 0 , d=1, tol=0)
|
||||
if len(curveCVs)< 4:
|
||||
mc.delete( 'tempEvenCurve.cv[1]', 'tempEvenCurve.cv[3]')
|
||||
curveCVs =mc.ls('tempEvenCurve.cv[*]',fl=1)
|
||||
posCurve = mc.xform(curveCVs[0], a=1,ws=1, q=1, t=1)
|
||||
posEdge = mc.xform(listVtx[0], a=1,ws=1, q=1, t=1)
|
||||
posEdge[0] = round(posEdge[0],3)
|
||||
posEdge[1] = round(posEdge[1],3)
|
||||
posEdge[2] = round(posEdge[2],3)
|
||||
posCurve[0] = round(posCurve[0],3)
|
||||
posCurve[1] = round(posCurve[1],3)
|
||||
posCurve[2] = round(posCurve[2],3)
|
||||
for i in range(len(curveCVs)):
|
||||
pos = mc.xform(curveCVs[i], a=1,ws=1, q=1, t=1)
|
||||
mc.xform(listVtx[i], a=1, ws=1, t = (pos[0],pos[1],pos[2]) )
|
||||
mc.delete('tempEvenCurve')
|
||||
|
||||
def getEdgeRingGroup(listSort,listInput):
|
||||
selEdges = mc.ls(sl=1,fl=1)
|
||||
trans = selEdges[0].split(".")[0]
|
||||
e2vInfos = mc.polyInfo(selEdges, ev=True)
|
||||
e2vDict = {}
|
||||
fEdges = []
|
||||
for info in e2vInfos:
|
||||
evList = [ int(i) for i in re.findall('\\d+', info) ]
|
||||
e2vDict.update(dict([(evList[0], evList[1:])]))
|
||||
while True:
|
||||
try:
|
||||
startEdge, startVtxs = e2vDict.popitem()
|
||||
except:
|
||||
break
|
||||
edgesGrp = [startEdge]
|
||||
num = 0
|
||||
for vtx in startVtxs:
|
||||
curVtx = vtx
|
||||
while True:
|
||||
|
||||
nextEdges = []
|
||||
for k in e2vDict:
|
||||
if curVtx in e2vDict[k]:
|
||||
nextEdges.append(k)
|
||||
if nextEdges:
|
||||
if len(nextEdges) == 1:
|
||||
if num == 0:
|
||||
edgesGrp.append(nextEdges[0])
|
||||
else:
|
||||
edgesGrp.insert(0, nextEdges[0])
|
||||
nextVtxs = e2vDict[nextEdges[0]]
|
||||
curVtx = [ vtx for vtx in nextVtxs if vtx != curVtx ][0]
|
||||
e2vDict.pop(nextEdges[0])
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
num += 1
|
||||
fEdges.append(edgesGrp)
|
||||
retEdges =[]
|
||||
for f in fEdges:
|
||||
f= list(map(lambda x: (trans +".e["+ str(x) +"]"), f))
|
||||
retEdges.append(f)
|
||||
if listSort == 1:
|
||||
sortEdgeLoopOrder=[]
|
||||
getCircleState,listVtx = vtxLoopOrderCheck(listInput)
|
||||
for l in listVtx:
|
||||
for r in retEdges:
|
||||
checkCvList = mc.ls(mc.polyListComponentConversion( r,fe=True, tv=True), fl=True,l=True)
|
||||
if l in checkCvList:
|
||||
sortEdgeLoopOrder.append(r)
|
||||
return sortEdgeLoopOrder
|
||||
else:
|
||||
return retEdges
|
||||
|
||||
def vtxLoopOrderCheck():
|
||||
selEdges = mc.ls(sl=1,fl=1)
|
||||
shapeNode = mc.listRelatives(selEdges[0], fullPath=True , parent=True )
|
||||
transformNode = mc.listRelatives(shapeNode[0], fullPath=True , parent=True )
|
||||
edgeNumberList = []
|
||||
for a in selEdges:
|
||||
checkNumber = ((a.split('.')[1]).split('\n')[0]).split(' ')
|
||||
for c in checkNumber:
|
||||
findNumber = ''.join([n for n in c.split('|')[-1] if n.isdigit()])
|
||||
if findNumber:
|
||||
edgeNumberList.append(findNumber)
|
||||
getNumber = []
|
||||
for s in selEdges:
|
||||
evlist = mc.polyInfo(s,ev=True)
|
||||
checkNumber = ((evlist[0].split(':')[1]).split('\n')[0]).split(' ')
|
||||
for c in checkNumber:
|
||||
findNumber = ''.join([n for n in c.split('|')[-1] if n.isdigit()])
|
||||
if findNumber:
|
||||
getNumber.append(findNumber)
|
||||
dup = set([x for x in getNumber if getNumber.count(x) > 1])
|
||||
getHeadTail = list(set(getNumber) - dup)
|
||||
checkCircleState = 0
|
||||
if not getHeadTail: #close curve
|
||||
checkCircleState = 1
|
||||
getHeadTail.append(getNumber[0])
|
||||
vftOrder = []
|
||||
vftOrder.append(getHeadTail[0])
|
||||
count = 0
|
||||
while len(dup)> 0 and count < 1000:
|
||||
checkVtx = transformNode[0]+'.vtx['+ vftOrder[-1] + ']'
|
||||
velist = mc.polyInfo(checkVtx,ve=True)
|
||||
getNumber = []
|
||||
checkNumber = ((velist[0].split(':')[1]).split('\n')[0]).split(' ')
|
||||
for c in checkNumber:
|
||||
findNumber = ''.join([n for n in c.split('|')[-1] if n.isdigit()])
|
||||
if findNumber:
|
||||
getNumber.append(findNumber)
|
||||
findNextEdge = []
|
||||
for g in getNumber:
|
||||
if g in edgeNumberList:
|
||||
findNextEdge = g
|
||||
edgeNumberList.remove(findNextEdge)
|
||||
checkVtx = transformNode[0]+'.e['+ findNextEdge + ']'
|
||||
findVtx = mc.polyInfo(checkVtx,ev=True)
|
||||
getNumber = []
|
||||
checkNumber = ((findVtx[0].split(':')[1]).split('\n')[0]).split(' ')
|
||||
for c in checkNumber:
|
||||
findNumber = ''.join([n for n in c.split('|')[-1] if n.isdigit()])
|
||||
if findNumber:
|
||||
getNumber.append(findNumber)
|
||||
gotNextVtx = []
|
||||
for g in getNumber:
|
||||
if g in dup:
|
||||
gotNextVtx = g
|
||||
dup.remove(gotNextVtx)
|
||||
vftOrder.append(gotNextVtx)
|
||||
count += 1
|
||||
if checkCircleState == 0:
|
||||
vftOrder.append(getHeadTail[1])
|
||||
else:#close curve remove connected vtx
|
||||
if vftOrder[0] == vftOrder[1]:
|
||||
vftOrder = vftOrder[1:]
|
||||
elif vftOrder[0] == vftOrder[-1]:
|
||||
vftOrder = vftOrder[0:-1]
|
||||
finalList = []
|
||||
for v in vftOrder:
|
||||
finalList.append(transformNode[0]+'.vtx['+ v + ']' )
|
||||
|
||||
return checkCircleState, finalList
|
||||
#edge
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
BIN
Scripts/Modeling/Edit/InstantDrag.jpg
Normal file
After Width: | Height: | Size: 237 KiB |
471
Scripts/Modeling/Edit/InstantDrag.py
Normal file
@ -0,0 +1,471 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import maya.cmds as mc
|
||||
import maya.mel as mel
|
||||
import maya.OpenMaya as om
|
||||
import maya.OpenMayaUI as omui
|
||||
from maya.OpenMaya import MGlobal
|
||||
import math
|
||||
import maya.api.OpenMaya as oma
|
||||
import re
|
||||
|
||||
def run():
|
||||
cleanList = ('instPicker','instRot')
|
||||
for c in cleanList:
|
||||
if mc.objExists(c):
|
||||
mc.delete(c)
|
||||
global ctx
|
||||
ctx = 'Click2dTo3dCtx'
|
||||
global storeHitFace
|
||||
storeHitFace =''
|
||||
if mc.draggerContext(ctx, exists=True):
|
||||
mc.deleteUI(ctx)
|
||||
mc.draggerContext(ctx, pressCommand = instDragPick, rc = instDragClean, dragCommand = instDragMove, name=ctx, cursor='crossHair',undoMode='step')
|
||||
mc.setToolTo(ctx)
|
||||
|
||||
def instDragPick():
|
||||
preSelect = mc.ls(sl=1,fl=1,l=1)
|
||||
global ctx
|
||||
global screenX,screenY
|
||||
global checkScreenMeshList
|
||||
global storeCameraPosition
|
||||
global storeMeshNode
|
||||
global parentDir
|
||||
global targetMeshName
|
||||
global instDul
|
||||
global storeHitFace
|
||||
global cameraFarClip
|
||||
global storeRotCount
|
||||
global lockCount
|
||||
global edgeAlignRecord
|
||||
edgeAlignRecord = 0
|
||||
storeRotCount = 0
|
||||
vpX, vpY, _ = mc.draggerContext(ctx, query=True, anchorPoint=True)
|
||||
screenX = vpX
|
||||
screenY = vpY
|
||||
pos = om.MPoint()
|
||||
dir = om.MVector()
|
||||
omui.M3dView().active3dView().viewToWorld(int(vpX), int(vpY), pos, dir)
|
||||
pos2 = om.MFloatPoint(pos.x, pos.y, pos.z)
|
||||
view = omui.M3dView.active3dView()
|
||||
cam = om.MDagPath()
|
||||
view.getCamera(cam)
|
||||
camPath = cam.fullPathName()
|
||||
cameraTrans = mc.listRelatives(camPath,type='transform',p=True)
|
||||
cameraFarClip = mc.getAttr(cameraTrans[0]+'.farClipPlane')
|
||||
storeCameraPosition = mc.xform(cameraTrans,q=1,ws=1,rp=1)
|
||||
##########################################################
|
||||
storeMeshNode=[]
|
||||
instDul = 0
|
||||
checkHit = 0
|
||||
finalMesh = []
|
||||
shortDistance = cameraFarClip
|
||||
distanceBetween = cameraFarClip
|
||||
hitpoint = om.MFloatPoint()
|
||||
hitFace = om.MScriptUtil()
|
||||
hitFace.createFromInt(0)
|
||||
hitFacePtr = hitFace.asIntPtr()
|
||||
##########################################################
|
||||
checkScreenMeshList = screenVisPoly()
|
||||
for mesh in checkScreenMeshList:
|
||||
selectionList = om.MSelectionList()
|
||||
selectionList.add(mesh)
|
||||
dagPath = om.MDagPath()
|
||||
selectionList.getDagPath(0, dagPath)
|
||||
fnMesh = om.MFnMesh(dagPath)
|
||||
intersection = fnMesh.closestIntersection(
|
||||
om.MFloatPoint(pos2),
|
||||
om.MFloatVector(dir),
|
||||
None,
|
||||
None,
|
||||
False,
|
||||
om.MSpace.kWorld,
|
||||
cameraFarClip,
|
||||
False,
|
||||
None,
|
||||
hitpoint,
|
||||
None,
|
||||
hitFacePtr,
|
||||
None,
|
||||
None,
|
||||
None)
|
||||
if intersection:
|
||||
x = hitpoint.x
|
||||
y = hitpoint.y
|
||||
z = hitpoint.z
|
||||
distanceBetween = math.sqrt( ((float(storeCameraPosition[0]) - x)**2) + ((float(storeCameraPosition[1]) - y)**2) + ((float(storeCameraPosition[2]) - z)**2))
|
||||
if distanceBetween < shortDistance:
|
||||
shortDistance = distanceBetween
|
||||
finalMesh = mesh
|
||||
if preSelect:
|
||||
checkShape = mc.listRelatives(preSelect, shapes=True, fullPath=True)
|
||||
finalMesh = checkShape
|
||||
|
||||
if len(finalMesh) > 0:
|
||||
#preSelect = mc.ls(sl=1,fl=1,l=1)
|
||||
#checkShape = mc.listRelatives(preSelect, shapes=True, fullPath=True)
|
||||
#finalMesh = checkShape
|
||||
storeMeshNode=mc.listRelatives(finalMesh,type='transform',p=True,f=True)
|
||||
shapeNode = mc.listRelatives(storeMeshNode[0], fullPath=True,ad=True )
|
||||
parentDir = '|'.join(storeMeshNode[0].split('|')[0:-1])
|
||||
targetMeshName = storeMeshNode[0].split('|')[-1]
|
||||
#move pivot to bbox base
|
||||
rotSave = mc.getAttr(targetMeshName + '.rotate')
|
||||
posSave = mc.xform(targetMeshName, q=True, ws=True, piv=True)[:3]
|
||||
mc.setAttr(targetMeshName + '.rotate',0,0,0)
|
||||
bbox = mc.exactWorldBoundingBox(targetMeshName)
|
||||
base_position = bbox[1]
|
||||
mc.move(posSave[0], base_position, posSave[2] , (targetMeshName+ '.scalePivot'), (targetMeshName + '.rotatePivot'), ws=True)
|
||||
|
||||
##########################################################
|
||||
mc.group(empty=1,n ='instPicker')
|
||||
mc.duplicate('instPicker')
|
||||
mc.rename('instRot')
|
||||
mc.parent('instRot','instPicker')
|
||||
mc.select('instPicker',storeMeshNode[0])
|
||||
mc.matchTransform(pos=1,rot=1)
|
||||
mc.parent(storeMeshNode[0],'instRot')
|
||||
mc.select('instPicker|instRot|'+targetMeshName)
|
||||
mc.setAttr('instPicker.rotate',rotSave[0][0],rotSave[0][1],rotSave[0][2])
|
||||
mc.makeIdentity(apply=True, t=1, r=1, s=1, n=0)
|
||||
mc.delete(constructionHistory=True)
|
||||
##########################################################
|
||||
for s in shapeNode:
|
||||
if s in checkScreenMeshList:
|
||||
checkScreenMeshList.remove(s)
|
||||
currentRoteY = mc.getAttr('instRot.rotateY')
|
||||
lockCount = int(currentRoteY / 15)*15
|
||||
|
||||
def instDragClean():
|
||||
global parentDir
|
||||
global targetMeshName
|
||||
global instDul
|
||||
global edgeAlignRecord
|
||||
edgeAlignRecord = 0
|
||||
if mc.objExists('instPicker'):
|
||||
if len(parentDir) == 0:
|
||||
mc.select('instPicker|instRot|'+targetMeshName)
|
||||
mc.parent(w=1)
|
||||
else:
|
||||
mc.parent(('|instPicker|instRot|'+targetMeshName),(parentDir))
|
||||
|
||||
cleanList = ('instPicker','instRot')
|
||||
for c in cleanList:
|
||||
if mc.objExists(c):
|
||||
mc.delete(c)
|
||||
instDul = 0
|
||||
mc.select(cl=1)
|
||||
|
||||
def instDragMove():
|
||||
global screenX
|
||||
global storeMeshNode
|
||||
global storeHitFace
|
||||
global cameraFarClip
|
||||
global storeRotCount
|
||||
global storeRotCount
|
||||
global lockCount
|
||||
global edgeAlignRecord
|
||||
if storeMeshNode:
|
||||
if mc.objExists('instPicker'):
|
||||
global ctx
|
||||
global screenX,screenY
|
||||
global storeCameraPosition
|
||||
global checkScreenMeshList
|
||||
global parentDir
|
||||
global targetMeshName
|
||||
global instDul
|
||||
|
||||
modifiers = mc.getModifiers()
|
||||
vpX, vpY, _ = mc.draggerContext(ctx, query=True, dragPoint=True)
|
||||
|
||||
if (modifiers == 4):
|
||||
#press Ctrl ----> rotate
|
||||
if screenX > vpX:
|
||||
lockCount = lockCount - 2
|
||||
else:
|
||||
lockCount = lockCount + 2
|
||||
screenX = vpX
|
||||
if lockCount < -360:
|
||||
lockCount = -360
|
||||
elif lockCount > 360:
|
||||
lockCount = 360
|
||||
|
||||
getX = int(lockCount / 15)*15
|
||||
|
||||
if storeRotCount != getX:
|
||||
storeRotCount = getX
|
||||
mc.setAttr('instRot.rotateY',storeRotCount)
|
||||
mc.refresh(cv=True,f=True)
|
||||
elif(modifiers == 5):
|
||||
#press Shift + Ctrl
|
||||
if edgeAlignRecord == 0:
|
||||
alignEdge()
|
||||
edgeAlignRecord = 1
|
||||
mc.refresh(cv=True,f=True)
|
||||
elif(modifiers == 1):
|
||||
#press Shift -----> dulpicate current mesh
|
||||
if instDul == 0:
|
||||
newD = mc.duplicate(('|instPicker|instRot|'+targetMeshName),rr=1)
|
||||
if len(parentDir) == 0:
|
||||
mc.select('instPicker|instRot|'+targetMeshName)
|
||||
mc.parent(w=1)
|
||||
else:
|
||||
mc.parent(('|instPicker|instRot|'+targetMeshName),(parentDir))
|
||||
targetMeshName = newD[0]
|
||||
mc.select(targetMeshName)
|
||||
instDul = 1
|
||||
mc.refresh(cv=True,f=True)
|
||||
else:
|
||||
pos = om.MPoint()
|
||||
dir = om.MVector()
|
||||
omui.M3dView().active3dView().viewToWorld(int(vpX), int(vpY), pos, dir)
|
||||
pos2 = om.MFloatPoint(pos.x, pos.y, pos.z)
|
||||
############################################################
|
||||
checkHit = 0
|
||||
finalMesh = ''
|
||||
hitFaceName = ''
|
||||
finalX = 0
|
||||
finalY = 0
|
||||
finalZ = 0
|
||||
shortDistance = cameraFarClip
|
||||
distanceBetween = cameraFarClip
|
||||
hitpoint = om.MFloatPoint()
|
||||
hitFace = om.MScriptUtil()
|
||||
hitFace.createFromInt(0)
|
||||
hitFacePtr = hitFace.asIntPtr()
|
||||
############################################################
|
||||
for mesh in checkScreenMeshList:
|
||||
selectionList = om.MSelectionList()
|
||||
selectionList.add(mesh)
|
||||
dagPath = om.MDagPath()
|
||||
selectionList.getDagPath(0, dagPath)
|
||||
fnMesh = om.MFnMesh(dagPath)
|
||||
intersection = fnMesh.closestIntersection(
|
||||
om.MFloatPoint(pos2),
|
||||
om.MFloatVector(dir),
|
||||
None,
|
||||
None,
|
||||
False,
|
||||
om.MSpace.kWorld,
|
||||
cameraFarClip,
|
||||
False,
|
||||
None,
|
||||
hitpoint,
|
||||
None,
|
||||
hitFacePtr,
|
||||
None,
|
||||
None,
|
||||
None)
|
||||
if intersection:
|
||||
x = hitpoint.x
|
||||
y = hitpoint.y
|
||||
z = hitpoint.z
|
||||
distanceBetween = math.sqrt( ((float(storeCameraPosition[0]) - x)**2) + ((float(storeCameraPosition[1]) - y)**2) + ((float(storeCameraPosition[2]) - z)**2))
|
||||
if distanceBetween < shortDistance:
|
||||
shortDistance = distanceBetween
|
||||
finalMesh = mesh
|
||||
|
||||
if finalMesh:
|
||||
selectionList = om.MSelectionList()
|
||||
selectionList.add(finalMesh)
|
||||
dagPath = om.MDagPath()
|
||||
selectionList.getDagPath(0, dagPath)
|
||||
fnMesh = om.MFnMesh(dagPath)
|
||||
intersection = fnMesh.closestIntersection(
|
||||
om.MFloatPoint(pos2),
|
||||
om.MFloatVector(dir),
|
||||
None,
|
||||
None,
|
||||
False,
|
||||
om.MSpace.kWorld,
|
||||
cameraFarClip,
|
||||
False,
|
||||
None,
|
||||
hitpoint,
|
||||
None,
|
||||
hitFacePtr,
|
||||
None,
|
||||
None,
|
||||
None)
|
||||
finalX = hitpoint.x
|
||||
finalY = hitpoint.y
|
||||
finalZ = hitpoint.z
|
||||
hitFace = om.MScriptUtil(hitFacePtr).asInt()
|
||||
hitFaceName = (finalMesh + '.f[' + str(hitFace) +']')
|
||||
instDul = 0
|
||||
mc.setAttr('instPicker.translate', finalX,finalY,finalZ)
|
||||
if storeHitFace != hitFaceName:
|
||||
rx, ry, rz = checkFaceAngle(hitFaceName)
|
||||
mc.setAttr('instPicker.rotate', rx,ry,rz)
|
||||
storeHitFace = hitFaceName
|
||||
mc.refresh(cv=True,f=True)
|
||||
|
||||
def screenVisPoly():
|
||||
commonList= []
|
||||
view = omui.M3dView.active3dView()
|
||||
om.MGlobal.selectFromScreen(0, 0, view.portWidth(), view.portHeight(), om.MGlobal.kReplaceList)
|
||||
objects = om.MSelectionList()
|
||||
sel = om.MSelectionList()
|
||||
om.MGlobal.getActiveSelectionList(objects)
|
||||
om.MGlobal.setActiveSelectionList(sel, om.MGlobal.kReplaceList)
|
||||
fromScreen = []
|
||||
objects.getSelectionStrings(fromScreen)
|
||||
shapesOnScreen = mc.listRelatives(fromScreen, shapes=True,f=True)
|
||||
meshList = mc.ls(type='mesh',l=True)#only polygon
|
||||
if len(meshList)>0 and shapesOnScreen is not None:
|
||||
commonList = list(set(meshList) & set(shapesOnScreen))
|
||||
return commonList
|
||||
else:
|
||||
commonList = []
|
||||
return commonList
|
||||
|
||||
|
||||
def checkFaceAngle(faceName):
|
||||
shapeNode = mc.listRelatives(faceName, fullPath=True , parent=True )
|
||||
transformNode = mc.listRelatives(shapeNode[0], fullPath=True , parent=True )
|
||||
obj_matrix = oma.MMatrix(mc.xform(transformNode, query=True, worldSpace=True, matrix=True))
|
||||
face_normals_text = mc.polyInfo(faceName, faceNormals=True)[0]
|
||||
face_normals = [float(digit) for digit in re.findall(r'-?\d*\.\d*', face_normals_text)]
|
||||
v = oma.MVector(face_normals) * obj_matrix
|
||||
upvector = oma.MVector (0,1,0)
|
||||
getHitNormal = v
|
||||
quat = oma.MQuaternion(upvector, getHitNormal)
|
||||
quatAsEuler = oma.MEulerRotation()
|
||||
quatAsEuler = quat.asEulerRotation()
|
||||
rx, ry, rz = math.degrees(quatAsEuler.x), math.degrees(quatAsEuler.y), math.degrees(quatAsEuler.z)
|
||||
return rx, ry, rz
|
||||
|
||||
def alignEdge():
|
||||
mesh=mc.ls(sl=1,fl=1)
|
||||
if len(mesh) == 1:
|
||||
checkLongName = mc.ls(mesh[0],l=1)
|
||||
parentNode = checkLongName[0].split('|')
|
||||
if len(parentNode) > 2:
|
||||
outParent = ''
|
||||
outParent = '|'.join(parentNode[1:-1])
|
||||
mc.parent(mesh[0],w=1)
|
||||
cleanList = ('sampleCurv*','sampleMes*','rotationPlan*')
|
||||
for c in cleanList:
|
||||
if mc.objExists(c):
|
||||
mc.delete(c)
|
||||
gface, gHitp,cEdge,cEdgePos = getClosestEdge()
|
||||
mc.select(cEdge)
|
||||
checkCVList=mc.ls( mc.polyListComponentConversion (cEdge,fe=True,tv=True),flatten=True)
|
||||
mx,my,mz = mc.pointPosition(checkCVList[0],w=1)
|
||||
mc.polyPlane(w=1, h=1, sx=1, sy=1, ax=(0,1,0), cuv=2, ch=0, n='rotationPlane')
|
||||
mc.polyCreateFacet( p=[(mx, my, mz),(cEdgePos[0], cEdgePos[1], cEdgePos[2]),(gHitp[0], gHitp[1], gHitp[2])] )
|
||||
mc.rename('sampleMesh')
|
||||
mc.select("rotationPlane.vtx[0:2]", "sampleMesh.vtx[0:2]")
|
||||
CMD = 'snap3PointsTo3Points(0);'
|
||||
mel.eval(CMD)
|
||||
mc.parent(mesh[0],'rotationPlane')
|
||||
axes = ["X", "Y", "Z"]
|
||||
for a in axes:
|
||||
val = mc.getAttr( mesh[0] + ".rotate" + a)
|
||||
valTmp = ''
|
||||
if val > 0:
|
||||
valTmp = val + 45
|
||||
else:
|
||||
valTmp = val - 45
|
||||
valNew = int (valTmp/90)
|
||||
mc.setAttr(( mesh[0] + ".rotate" + a), (valNew*90))
|
||||
|
||||
mc.move(gHitp[0], gHitp[1], gHitp[2], mesh[0], rpr=True,wd=True)
|
||||
mc.select(mesh[0])
|
||||
mc.parent(w=1)
|
||||
if len(parentNode) > 2:
|
||||
mc.parent(mesh[0],outParent)
|
||||
for c in cleanList:
|
||||
if mc.objExists(c):
|
||||
mc.delete(c)
|
||||
|
||||
def getClosestEdge():
|
||||
mayaMesh = mc.ls(sl=1,fl=1)
|
||||
gFace = ''
|
||||
gHitP = ''
|
||||
gFace,gHitP = getClosestMeshHit(mayaMesh[0])
|
||||
listF2E=mc.ls( mc.polyListComponentConversion (gFace,ff=True,te=True),flatten=True)
|
||||
cEdge = ''
|
||||
smallestDist = 1000000
|
||||
cEdgePos = []
|
||||
for l in listF2E:
|
||||
mc.select(l)
|
||||
mc.polyToCurve(form=2, degree=1, conformToSmoothMeshPreview=1)
|
||||
sampleCurve = mc.ls(sl=1)
|
||||
selectionList = om.MSelectionList()
|
||||
selectionList.add(sampleCurve[0])
|
||||
dagPath = om.MDagPath()
|
||||
selectionList.getDagPath(0, dagPath)
|
||||
omCurveOut = om.MFnNurbsCurve(dagPath)
|
||||
pointInSpace = om.MPoint(gHitP[0],gHitP[1],gHitP[2])
|
||||
closestPoint = om.MPoint()
|
||||
closestPoint = omCurveOut.closestPoint(pointInSpace)
|
||||
getDist = math.sqrt( ((closestPoint[0] - gHitP[0])**2) + ((closestPoint[1]- gHitP[1])**2) + ((closestPoint[2] - gHitP[2])**2))
|
||||
if getDist < smallestDist:
|
||||
smallestDist = getDist
|
||||
cEdge = l
|
||||
cEdgePos = [closestPoint[0],closestPoint[1],closestPoint[2]]
|
||||
mc.delete(sampleCurve)
|
||||
mc.select(cEdge)
|
||||
return(gFace,gHitP,cEdge,cEdgePos)
|
||||
|
||||
def getClosestMeshHit(mayaMesh):
|
||||
myShape = mc.listRelatives(mayaMesh, f=True,ad =True)
|
||||
checkList = screenVisPoly()
|
||||
removeList = list(set(checkList) - set(myShape))
|
||||
checkList = removeList
|
||||
meshPos = mc.xform(mayaMesh,q=1, ws=1, a=1, piv=1)
|
||||
posXXX = [meshPos[0],meshPos[1],meshPos[2]]
|
||||
shortDistanceCheck = 10000
|
||||
resultFace = []
|
||||
resultCV =[]
|
||||
resultHitPoint = []
|
||||
for c in checkList:
|
||||
transNode = mc.listRelatives(c, p=True,f=True)
|
||||
getFaceDist,getFace,getHitPoint = getClosestPointOnFace(transNode[0],posXXX)
|
||||
#print(getCV, getFaceDist, getFace)
|
||||
if getFaceDist < shortDistanceCheck:
|
||||
shortDistanceCheck = getFaceDist
|
||||
resultFace = getFace
|
||||
resultHitPoint = getHitPoint
|
||||
return (resultFace,resultHitPoint)
|
||||
|
||||
def getClosestPointOnFace(mayaMesh,pos=[0,0,0]):
|
||||
mVector = oma.MVector(pos)#using MVector type to represent position
|
||||
selectionList = oma.MSelectionList()
|
||||
selectionList.add(mayaMesh)
|
||||
dPath= selectionList.getDagPath(0)
|
||||
mMesh=oma.MFnMesh(dPath)
|
||||
ID = mMesh.getClosestPoint(oma.MPoint(mVector),space=oma.MSpace.kWorld)[1] #getting closest face ID
|
||||
closestPoint= mMesh.getClosestPoint(oma.MPoint(mVector),space=oma.MSpace.kWorld)[0]
|
||||
cpx = closestPoint[0]
|
||||
cpy = closestPoint[1]
|
||||
cpz = closestPoint[2]
|
||||
hitPointPosition = [cpx,cpy,cpz]
|
||||
hitFaceName = (mayaMesh+'.f['+str(ID)+']')
|
||||
getFaceDist = math.sqrt( ((pos[0] - cpx)**2) + ((pos[1]- cpy)**2) + ((pos[2] - cpz)**2))
|
||||
return (getFaceDist, hitFaceName,hitPointPosition)
|
||||
|
||||
def getPolyFaceCenter(faceName):
|
||||
meshFaceName = faceName.split('.')[0]
|
||||
findVtx = mc.polyInfo(faceName, fv=1)
|
||||
getNumber = []
|
||||
checkNumber = ((findVtx[0].split(':')[1]).split('\n')[0]).split(' ')
|
||||
for c in checkNumber:
|
||||
findNumber = ''.join([n for n in c.split('|')[-1] if n.isdigit()])
|
||||
if findNumber:
|
||||
getNumber.append(findNumber)
|
||||
centerX = 0
|
||||
centerY = 0
|
||||
centerZ = 0
|
||||
for g in getNumber:
|
||||
x,y,z = mc.pointPosition((meshFaceName + '.vtx['+g + ']'),w=1)
|
||||
centerX = centerX + x
|
||||
centerY = centerY + y
|
||||
centerZ = centerZ + z
|
||||
|
||||
centerX = centerX/len(getNumber)
|
||||
centerY = centerY/len(getNumber)
|
||||
centerZ = centerZ/len(getNumber)
|
||||
return centerX,centerY,centerZ
|
674
Scripts/Modeling/Edit/LDMT/faceTransfer/LICENSE
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
412
Scripts/Modeling/Edit/LDMT/faceTransfer/__init__.py
Normal file
@ -0,0 +1,412 @@
|
||||
"""
|
||||
Retarget your blendshapes between meshes with the same topology.
|
||||
|
||||
.. figure:: https://github.com/robertjoosten/rjRetargetBlendshape/raw/master/README.png
|
||||
:align: center
|
||||
|
||||
`Link to Video <https://vimeo.com/170360738>`_
|
||||
|
||||
Installation
|
||||
============
|
||||
Copy the **rjRetargetBlendshape** folder to your Maya scripts directory
|
||||
::
|
||||
C:/Users/<USER>/Documents/maya/scripts
|
||||
|
||||
Usage
|
||||
=====
|
||||
Command line
|
||||
::
|
||||
import rjRetargetBlendshape
|
||||
rjRetargetBlendshape.convert(
|
||||
source,
|
||||
blendshape,
|
||||
target,
|
||||
scale=True,
|
||||
rotate=True,
|
||||
smooth=0,
|
||||
smoothIterations=0,
|
||||
space=OpenMaya.MSpace.kObject,
|
||||
)
|
||||
|
||||
Display UI
|
||||
::
|
||||
import rjRetargetBlendshape.ui
|
||||
rjRetargetBlendshape.ui.show()
|
||||
|
||||
Note
|
||||
====
|
||||
Retarget your blendshapes between meshes with the same topology. There are a
|
||||
few options that can be helpful to achieve the desired results.
|
||||
|
||||
* Scaling your delta depending on the size difference between the source and the target vertex.
|
||||
* Rotating the delta depending on the normal difference between the source and the target vertex.
|
||||
* Smoothing based on the vertex size between the retarget mesh and the blendshape mesh.
|
||||
|
||||
Code
|
||||
====
|
||||
"""
|
||||
|
||||
import math
|
||||
from maya import OpenMaya, cmds
|
||||
|
||||
__author__ = "Robert Joosten"
|
||||
__version__ = "0.8.2"
|
||||
__email__ = "rwm.joosten@gmail.com"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def convert(
|
||||
source,
|
||||
blendshape,
|
||||
target,
|
||||
scale=True,
|
||||
rotate=True,
|
||||
smooth=0,
|
||||
smoothIterations=2,
|
||||
space=OpenMaya.MSpace.kObject
|
||||
):
|
||||
"""
|
||||
Create a new target based on the difference between the source and
|
||||
blendshape. The target mesh is duplicated and the difference between
|
||||
source and target is transfered onto the duplicated mesh. When
|
||||
transfering both scale and rotation of the delta vectors can be taken
|
||||
into account. Once the data is transfered an Laplacian smoothing algorithm
|
||||
can be applied onto the newly created target to create a more desired
|
||||
result.
|
||||
|
||||
:param str source:
|
||||
:param str blendshape:
|
||||
:param str target:
|
||||
:param bool scale: Take scale between delta vectors into account
|
||||
:param bool rotate: Take rotation between normal vectors into account
|
||||
:param float smooth: Smoothing strength
|
||||
:param int smoothIterations: Times the smooth algorithm repeats
|
||||
:param OpenMaya.MSpace space:
|
||||
:raises RuntimeError: If the vertex count doesn't match
|
||||
:return: Name of transfered blendshape mesh
|
||||
:rtype: str
|
||||
"""
|
||||
# convert objects to dag
|
||||
dags = []
|
||||
meshes = []
|
||||
|
||||
for i, name in enumerate([source, target, blendshape]):
|
||||
dags.append(asMDagPath(asMObject(name)))
|
||||
meshes.append(asMFnMesh(dags[i]))
|
||||
|
||||
sourceD, targetD, blendshapeD = dags
|
||||
sourceM, targetM, blendshapeM = meshes
|
||||
|
||||
# compare vertex count
|
||||
count = set([m.numVertices() for m in meshes])
|
||||
|
||||
if len(count) != 1:
|
||||
raise RuntimeError(
|
||||
"Input geometry doesn't have matching vertex counts!"
|
||||
)
|
||||
|
||||
# duplicate target to manipulate mesh
|
||||
targetB = getBasename(target)
|
||||
blendshapeB = getBasename(blendshape)
|
||||
|
||||
target = cmds.duplicate(
|
||||
target,
|
||||
rr=True,
|
||||
n="{0}_{1}".format(targetB, blendshapeB)
|
||||
)[0]
|
||||
|
||||
# parent duplicated target
|
||||
if cmds.listRelatives(target, p=True):
|
||||
target = cmds.parent(target, world=True)[0]
|
||||
|
||||
targetD = asMDagPath(asMObject(target))
|
||||
targetM = asMFnMesh(targetD)
|
||||
|
||||
# iterate vertices
|
||||
count = next(iter(count))
|
||||
positions = OpenMaya.MPointArray()
|
||||
|
||||
# variables
|
||||
dags = [sourceD, targetD, blendshapeD]
|
||||
|
||||
points = [OpenMaya.MPoint(), OpenMaya.MPoint(), OpenMaya.MPoint()]
|
||||
normals = [OpenMaya.MVector(), OpenMaya.MVector()]
|
||||
|
||||
length = [0,0,0]
|
||||
lengths = [[],[],[]]
|
||||
|
||||
# get new positions
|
||||
for i in range(count):
|
||||
# initialize component
|
||||
component = asComponent(i)
|
||||
|
||||
# loop meshes
|
||||
for j, dag in enumerate(dags):
|
||||
# get points
|
||||
meshes[j].getPoint(i, points[j], space)
|
||||
|
||||
# get length
|
||||
l = getAverageLength(dag, component, space)
|
||||
|
||||
length[j] = l
|
||||
lengths[j].append(l)
|
||||
|
||||
# variables
|
||||
sourceP, targetP, blendshapeP = points
|
||||
sourceL, targetL, blendshapeL = length
|
||||
|
||||
# difference vector
|
||||
vector = blendshapeP - sourceP
|
||||
|
||||
# handle scale
|
||||
if scale:
|
||||
scaleFactor = targetL/sourceL if sourceL and targetL else 1
|
||||
|
||||
# scale vector
|
||||
vector = vector * scaleFactor
|
||||
|
||||
# handle normal rotation
|
||||
if rotate:
|
||||
# get normals
|
||||
for j, mesh in enumerate(meshes[:-1]):
|
||||
mesh.getVertexNormal(i, True, normals[j], space)
|
||||
|
||||
# get quaternion between normals
|
||||
sourceN, targetN = normals
|
||||
quaternion = sourceN.rotateTo(targetN)
|
||||
|
||||
# rotate vector
|
||||
vector = vector.rotateBy(quaternion)
|
||||
|
||||
positions.append(targetP + vector)
|
||||
|
||||
# set all points
|
||||
targetM.setPoints(positions, space)
|
||||
|
||||
# loop over smooth iterations
|
||||
for _ in range(smoothIterations):
|
||||
|
||||
# loop over vertices
|
||||
for i, sourceL, targetL, blendshapeL in zip(range(count), *lengths):
|
||||
|
||||
# smooth vertex
|
||||
setLaplacianSmooth(
|
||||
targetD,
|
||||
i,
|
||||
space,
|
||||
sourceL,
|
||||
targetL,
|
||||
blendshapeL,
|
||||
smooth
|
||||
)
|
||||
|
||||
return target
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def setLaplacianSmooth(
|
||||
dag,
|
||||
index,
|
||||
space,
|
||||
sourceL,
|
||||
targetL,
|
||||
blendshapeL,
|
||||
smooth
|
||||
):
|
||||
"""
|
||||
Laplacian smoothing algorithm, where the average of the neighbouring points
|
||||
are used to smooth the target vertices, based on the factor between the
|
||||
average length of both the source and the target mesh.
|
||||
|
||||
:param OpenMaya.MDagPath dag:
|
||||
:param int index: Component index
|
||||
:param OpenMaya.MSpace space:
|
||||
:param float sourceL:
|
||||
:param float targetL:
|
||||
:param float blendshapeL:
|
||||
:param float smooth:
|
||||
"""
|
||||
# calculate factor
|
||||
component = asComponent(index)
|
||||
avgL = getAverageLength(dag, component, space)
|
||||
|
||||
targetF = blendshapeL/sourceL if sourceL and targetL else 1
|
||||
blendshapeF = avgL/targetL if sourceL and blendshapeL else 1
|
||||
|
||||
factor = abs((1-targetF/blendshapeF)*smooth)
|
||||
factor = max(min(factor, 1), 0)
|
||||
|
||||
# ignore if there is not smooth factor
|
||||
if not factor:
|
||||
return
|
||||
|
||||
# get average position
|
||||
component = asComponent(index)
|
||||
vtx = OpenMaya.MItMeshVertex(dag, component)
|
||||
|
||||
origP, avgP = getAveragePosition(dag, component, space)
|
||||
|
||||
# get new position
|
||||
avgP = avgP * factor
|
||||
origP = origP * (1-factor)
|
||||
newP = avgP + OpenMaya.MVector(origP)
|
||||
|
||||
# set new position
|
||||
vtx.setPosition(newP, space)
|
||||
|
||||
def getAveragePosition(dag, component, space):
|
||||
"""
|
||||
Get average position of connected vertices.
|
||||
|
||||
:param OpenMaya.MDagPath dag:
|
||||
:param OpenMaya.MFnSingleIndexedComponent component:
|
||||
:param OpenMaya.MSpace space:
|
||||
:return: Average length of the connected edges
|
||||
:rtype: OpenMaya.MPoint
|
||||
"""
|
||||
averagePos = OpenMaya.MPoint()
|
||||
|
||||
# get connected vertices
|
||||
connected = OpenMaya.MIntArray()
|
||||
|
||||
iterate = OpenMaya.MItMeshVertex(dag, component)
|
||||
iterate.getConnectedVertices(connected)
|
||||
|
||||
# get original position
|
||||
originalPos = iterate.position(space)
|
||||
|
||||
# ignore if no vertices are connected
|
||||
if not connected.length():
|
||||
return averagePos
|
||||
|
||||
# get average
|
||||
component = asComponent(connected)
|
||||
iterate = OpenMaya.MItMeshVertex(dag, component)
|
||||
while not iterate.isDone():
|
||||
averagePos += OpenMaya.MVector(iterate.position(space))
|
||||
iterate.next()
|
||||
|
||||
averagePos = averagePos/connected.length()
|
||||
return originalPos, averagePos
|
||||
|
||||
def getAverageLength(dag, component, space):
|
||||
"""
|
||||
Get average length of connected edges.
|
||||
|
||||
:param OpenMaya.MDagPath dag:
|
||||
:param OpenMaya.MFnSingleIndexedComponent component:
|
||||
:param OpenMaya.MSpace space:
|
||||
:return: Average length of the connected edges
|
||||
:rtype: float
|
||||
"""
|
||||
total = 0
|
||||
|
||||
lengthUtil = OpenMaya.MScriptUtil()
|
||||
lengthPtr = lengthUtil.asDoublePtr()
|
||||
|
||||
# get connected edges
|
||||
connected = OpenMaya.MIntArray()
|
||||
|
||||
iterate = OpenMaya.MItMeshVertex(dag, component)
|
||||
iterate.getConnectedEdges(connected)
|
||||
|
||||
# ignore if no edges are connected
|
||||
if not connected.length():
|
||||
return 0
|
||||
|
||||
# get average
|
||||
component = asComponent(connected, OpenMaya.MFn.kMeshEdgeComponent)
|
||||
iterate = OpenMaya.MItMeshEdge(dag, component)
|
||||
while not iterate.isDone():
|
||||
iterate.getLength(lengthPtr, space)
|
||||
total += lengthUtil.getDouble(lengthPtr)
|
||||
|
||||
iterate.next()
|
||||
|
||||
return total/connected.length()
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def getBasename(name):
|
||||
"""
|
||||
Strip the parent and namespace data of the provided string.
|
||||
|
||||
:param str name:
|
||||
:return: Base name of parsed object
|
||||
:rtype: str
|
||||
"""
|
||||
return name.split("|")[-1].split(":")[-1]
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def asMIntArray(index):
|
||||
"""
|
||||
index -> OpenMaya.MIntArray
|
||||
|
||||
:param int/OpenMaya.MIntArray index: indices
|
||||
:return: Array of indices
|
||||
:rtype: OpenMaya.MIntArray
|
||||
"""
|
||||
if type(index) != OpenMaya.MIntArray:
|
||||
array = OpenMaya.MIntArray()
|
||||
array.append(index)
|
||||
return array
|
||||
|
||||
return index
|
||||
|
||||
def asComponent(index, t=OpenMaya.MFn.kMeshVertComponent):
|
||||
"""
|
||||
index -> OpenMaya.MFn.kComponent
|
||||
Based on the input type it will create a component type for this tool
|
||||
the following components are being used.
|
||||
|
||||
* OpenMaya.MFn.kMeshVertComponent
|
||||
* OpenMaya.MFn.kMeshEdgeComponent
|
||||
|
||||
:param int/OpenMaya.MIntArray index: indices to create component for
|
||||
:param OpenMaya.MFn.kComponent t: can be all of OpenMaya component types.
|
||||
:return: Initialized components
|
||||
:rtype: OpenMaya.MFnSingleIndexedComponent
|
||||
"""
|
||||
# convert input to an MIntArray if it not already is one
|
||||
array = asMIntArray(index)
|
||||
|
||||
# initialize component
|
||||
component = OpenMaya.MFnSingleIndexedComponent().create(t)
|
||||
OpenMaya.MFnSingleIndexedComponent(component).addElements(array)
|
||||
return component
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def asMObject(path):
|
||||
"""
|
||||
str -> OpenMaya.MObject
|
||||
|
||||
:param str path: Path to Maya object
|
||||
:rtype: OpenMaya.MObject
|
||||
"""
|
||||
selectionList = OpenMaya.MSelectionList()
|
||||
selectionList.add(path)
|
||||
|
||||
obj = OpenMaya.MObject()
|
||||
selectionList.getDependNode(0, obj)
|
||||
return obj
|
||||
|
||||
def asMDagPath(obj):
|
||||
"""
|
||||
OpenMaya.MObject -> OpenMaya.MDagPath
|
||||
|
||||
:param OpenMaya.MObject obj:
|
||||
:rtype: OpenMaya.MDagPath
|
||||
"""
|
||||
return OpenMaya.MDagPath.getAPathTo(obj)
|
||||
|
||||
def asMFnMesh(dag):
|
||||
"""
|
||||
OpenMaya.MDagPath -> OpenMaya.MfnMesh
|
||||
|
||||
:param OpenMaya.MDagPath dag:
|
||||
:rtype: OpenMaya.MfnMesh
|
||||
"""
|
||||
|
||||
return OpenMaya.MFnMesh(dag)
|
430
Scripts/Modeling/Edit/LDMT/faceTransfer/ui.py
Normal file
@ -0,0 +1,430 @@
|
||||
from functools import partial
|
||||
from maya import cmds, OpenMaya, OpenMayaUI
|
||||
|
||||
# import pyside, do qt version check for maya 2017 >
|
||||
qtVersion = cmds.about(qtVersion=True)
|
||||
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtWidgets import *
|
||||
from Qt.QtCompat import wrapInstance, getCppPointer
|
||||
|
||||
# import command line convert
|
||||
from . import convert
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
SPACE = []
|
||||
SPACE.append(OpenMaya.MSpace.kObject)
|
||||
SPACE.append(OpenMaya.MSpace.kWorld)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
FONT = QFont()
|
||||
FONT.setFamily("Consolas")
|
||||
|
||||
BOLT_FONT = QFont()
|
||||
BOLT_FONT.setFamily("Consolas")
|
||||
BOLT_FONT.setWeight(QFont.Weight.Light)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def mayaWindow():
|
||||
"""
|
||||
Get Maya's main window.
|
||||
|
||||
:rtype: QMainWindow
|
||||
"""
|
||||
window = OpenMayaUI.MQtUtil.mainWindow()
|
||||
window = wrapInstance(int(window), QMainWindow)
|
||||
|
||||
return window
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def title(parent, name):
|
||||
"""
|
||||
Create title ui widget.
|
||||
|
||||
:param QWidget parent:
|
||||
:param str name:
|
||||
:rtype: QLabel
|
||||
"""
|
||||
title = QLabel(parent)
|
||||
title.setText(name)
|
||||
title.setFont(BOLT_FONT)
|
||||
return title
|
||||
|
||||
def divider(parent):
|
||||
"""
|
||||
Create divider ui widget.
|
||||
|
||||
:param QWidget parent:
|
||||
:rtype: QFrame
|
||||
"""
|
||||
line = QFrame(parent)
|
||||
line.setFrameShape(QFrame.HLine)
|
||||
line.setFrameShadow(QFrame.Sunken)
|
||||
return line
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def getSelectedMeshes():
|
||||
"""
|
||||
Get all selected meshes, the current selection will be looped and checked
|
||||
if any of the selected transforms contain a mesh node. If this is the case
|
||||
the transform will be added to the selection list.
|
||||
|
||||
:return: Parents nodes of all selected meshes
|
||||
:rtype: list
|
||||
"""
|
||||
# get selection
|
||||
selection = cmds.ls(sl=True, l=True)
|
||||
extendedSelection = []
|
||||
|
||||
# extend selection
|
||||
for sel in selection:
|
||||
extendedSelection.extend(
|
||||
cmds.listRelatives(sel, s=True, ni=True, f=True)
|
||||
)
|
||||
|
||||
# return parent of meshes
|
||||
return list(set([
|
||||
cmds.listRelatives(m, p=True, f=True)[0]
|
||||
for m in extendedSelection
|
||||
if cmds.nodeType(m) == "mesh"
|
||||
]))
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
class SelectionWidget(QWidget):
|
||||
signal = Signal()
|
||||
def __init__(self, parent, label, selectionMode="single"):
|
||||
QWidget.__init__(self, parent)
|
||||
|
||||
# selection
|
||||
self._selectionMode = selectionMode
|
||||
self._selection = []
|
||||
|
||||
# create layout
|
||||
layout = QHBoxLayout(self)
|
||||
layout.setContentsMargins(3, 0, 3, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# create label
|
||||
self.label = QLabel(self)
|
||||
self.label.setText("( 0 ) Mesh(es)")
|
||||
self.label.setFont(FONT)
|
||||
layout.addWidget(self.label)
|
||||
|
||||
# create button
|
||||
button = QPushButton(self)
|
||||
button.setText(label)
|
||||
button.setFont(FONT)
|
||||
button.released.connect(self.setSelection)
|
||||
|
||||
layout.addWidget(button)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
@property
|
||||
def selection(self):
|
||||
return self._selection
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def setSelection(self):
|
||||
"""
|
||||
Update the UI with the current selection, a signal is emit so the
|
||||
parent widget can validate the current selection.
|
||||
"""
|
||||
# get selection
|
||||
meshes = getSelectedMeshes()
|
||||
|
||||
# update ui
|
||||
self.label.setText("( {0} ) Mesh(es)".format(len(meshes)))
|
||||
self.label.setToolTip("\n".join(meshes))
|
||||
|
||||
# process selection mode
|
||||
if self._selectionMode == "single" and meshes:
|
||||
meshes = meshes[0]
|
||||
|
||||
self._selection = meshes
|
||||
self.signal.emit()
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
class CheckBoxWidget(QWidget):
|
||||
def __init__(self, parent, label, toolTip):
|
||||
QWidget.__init__(self, parent)
|
||||
|
||||
layout = QHBoxLayout(self)
|
||||
layout.setContentsMargins(3, 0, 3, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# create checkbox
|
||||
self.checkBox = QCheckBox(self)
|
||||
self.checkBox.setText(label)
|
||||
self.checkBox.setFont(FONT)
|
||||
self.checkBox.setChecked(True)
|
||||
self.checkBox.setToolTip(toolTip)
|
||||
layout.addWidget(self.checkBox)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def isChecked(self):
|
||||
return self.checkBox.isChecked()
|
||||
|
||||
class SpinBoxWidget(QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
widget,
|
||||
label,
|
||||
toolTip,
|
||||
value,
|
||||
minimum,
|
||||
maximum,
|
||||
step
|
||||
):
|
||||
QWidget.__init__(self, parent)
|
||||
|
||||
layout = QHBoxLayout(self)
|
||||
layout.setContentsMargins(3, 0, 3, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# create label
|
||||
l = QLabel(self)
|
||||
l.setText(label)
|
||||
l.setFont(FONT)
|
||||
layout.addWidget(l)
|
||||
|
||||
# create spinbox
|
||||
self.spinBox = widget(self)
|
||||
self.spinBox.setFont(FONT)
|
||||
self.spinBox.setToolTip(toolTip)
|
||||
self.spinBox.setValue(value)
|
||||
self.spinBox.setSingleStep(step)
|
||||
self.spinBox.setMinimum(minimum)
|
||||
self.spinBox.setMaximum(maximum)
|
||||
layout.addWidget(self.spinBox)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def value(self):
|
||||
return self.spinBox.value()
|
||||
|
||||
class RetargetBlendshapeWidget(QWidget):
|
||||
def __init__(self, parent):
|
||||
QWidget.__init__(self, parent)
|
||||
|
||||
# set ui
|
||||
self.setParent(parent)
|
||||
self.setWindowFlags(Qt.Window)
|
||||
self.setWindowIcon(QIcon(":/blendShape.png"))
|
||||
|
||||
self.setWindowTitle("Retarget Blendshapes")
|
||||
self.setObjectName("RetargetUI")
|
||||
self.resize(300, 200)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(5, 5, 5, 5)
|
||||
layout.setSpacing(5)
|
||||
|
||||
# create title
|
||||
t = title(self, "Retarget Blendshapes")
|
||||
layout.addWidget(t)
|
||||
|
||||
# create selection widget
|
||||
self.sourceW = SelectionWidget(self, "Set Source")
|
||||
self.blendshapeW = SelectionWidget(self, "Set Blendshape(s)", "multi")
|
||||
self.targetW = SelectionWidget(self, "Set Target")
|
||||
|
||||
for widget in [self.sourceW, self.targetW, self.blendshapeW]:
|
||||
widget.signal.connect(self.validate)
|
||||
layout.addWidget(widget)
|
||||
|
||||
# create divider
|
||||
d = divider(self)
|
||||
layout.addWidget(d)
|
||||
|
||||
# create options
|
||||
t = title(self, "Options")
|
||||
layout.addWidget(t)
|
||||
|
||||
# scale settings
|
||||
self.scaleW = CheckBoxWidget(
|
||||
self,
|
||||
"Scale Delta",
|
||||
(
|
||||
"If checked, the vertex delta will be scaled based on the \n"
|
||||
"difference of the averaged connected edge length between \n"
|
||||
"the source and the target."
|
||||
)
|
||||
)
|
||||
layout.addWidget(self.scaleW)
|
||||
|
||||
# rotate settings
|
||||
self.rotateW = CheckBoxWidget(
|
||||
self,
|
||||
"Rotate Delta",
|
||||
(
|
||||
"If checked, the vertex delta will be rotated based on the \n"
|
||||
"difference of the vertex normal between the source and \n"
|
||||
"the target."
|
||||
)
|
||||
)
|
||||
layout.addWidget(self.rotateW)
|
||||
|
||||
# create divider
|
||||
d = divider(self)
|
||||
layout.addWidget(d)
|
||||
|
||||
# create smoothing
|
||||
t = title(self, "Smoothing")
|
||||
layout.addWidget(t)
|
||||
|
||||
# smooth settings
|
||||
self.smoothW = SpinBoxWidget(
|
||||
self,
|
||||
QDoubleSpinBox,
|
||||
"Factor",
|
||||
(
|
||||
"The targets will be smoothed based on the difference \n"
|
||||
"between source and blendshape and original target and \n"
|
||||
"output"
|
||||
),
|
||||
value=10,
|
||||
minimum=0,
|
||||
maximum=1000,
|
||||
step=0.5
|
||||
)
|
||||
layout.addWidget(self.smoothW)
|
||||
|
||||
# smooth iter settings
|
||||
self.smoothIterW = SpinBoxWidget(
|
||||
self,
|
||||
QSpinBox,
|
||||
"Iterations",
|
||||
(
|
||||
"The amount of time the smoothing algorithm is applied to \n"
|
||||
"the output."
|
||||
),
|
||||
value=2,
|
||||
minimum=0,
|
||||
maximum=10,
|
||||
step=1
|
||||
)
|
||||
layout.addWidget(self.smoothIterW)
|
||||
|
||||
# create divider
|
||||
d = divider(self)
|
||||
layout.addWidget(d)
|
||||
|
||||
# create space
|
||||
t = title(self, "Space")
|
||||
layout.addWidget(t)
|
||||
|
||||
self.spaceW = QComboBox(self)
|
||||
self.spaceW.setFont(FONT)
|
||||
self.spaceW.addItems(["kObject", "kWorld"])
|
||||
self.spaceW.setToolTip(
|
||||
"Determine space in which all the calculations take place."
|
||||
)
|
||||
|
||||
layout.addWidget(self.spaceW)
|
||||
|
||||
# create divider
|
||||
d = divider(self)
|
||||
layout.addWidget(d)
|
||||
|
||||
# create retarget button
|
||||
self.retargetB = QPushButton(self)
|
||||
self.retargetB.setText("Retarget")
|
||||
self.retargetB.setFont(FONT)
|
||||
self.retargetB.setEnabled(False)
|
||||
self.retargetB.released.connect(self.retarget)
|
||||
layout.addWidget(self.retargetB)
|
||||
|
||||
# create spacer
|
||||
spacer = QSpacerItem(1, 1, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||
layout.addItem(spacer)
|
||||
|
||||
# create progress bar
|
||||
self.progressBar = QProgressBar(self)
|
||||
layout.addWidget(self.progressBar)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def validate(self):
|
||||
"""
|
||||
Validate the current selection, if the selection is valid the retarget
|
||||
button will be enabled. If not the button will stay disabled.
|
||||
"""
|
||||
# variables
|
||||
source = self.sourceW.selection
|
||||
target = self.targetW.selection
|
||||
blendshapes = self.blendshapeW.selection
|
||||
|
||||
# set button invisible
|
||||
self.retargetB.setEnabled(False)
|
||||
|
||||
if source and target and blendshapes:
|
||||
self.retargetB.setEnabled(True)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def retarget(self):
|
||||
"""
|
||||
Read the values from the UI and call the command line convert function
|
||||
to retarget the blendshapes. Once the blendshapes are converted the
|
||||
new geometry will be selected.
|
||||
"""
|
||||
# get selection
|
||||
source = self.sourceW.selection
|
||||
target = self.targetW.selection
|
||||
blendshapes = self.blendshapeW.selection
|
||||
|
||||
# get settings
|
||||
scale = self.scaleW.isChecked()
|
||||
rotate = self.rotateW.isChecked()
|
||||
|
||||
# get smoothing
|
||||
smooth = self.smoothW.value()
|
||||
smoothIterations = self.smoothIterW.value()
|
||||
|
||||
# get space
|
||||
space = SPACE[self.spaceW.currentIndex()]
|
||||
|
||||
# set progress bar
|
||||
self.progressBar.setMinimum(0)
|
||||
self.progressBar.setMaximum(len(blendshapes))
|
||||
self.progressBar.setValue(0)
|
||||
|
||||
# convert
|
||||
converted = []
|
||||
for i, blendshape in enumerate(blendshapes):
|
||||
converted.append(
|
||||
convert(
|
||||
source,
|
||||
blendshape,
|
||||
target,
|
||||
scale,
|
||||
rotate,
|
||||
smooth,
|
||||
smoothIterations,
|
||||
space
|
||||
)
|
||||
)
|
||||
|
||||
# update spacebar
|
||||
self.progressBar.setValue(i+1)
|
||||
|
||||
# select output
|
||||
cmds.select(converted)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def show():
|
||||
retargetBlendshape = RetargetBlendshapeWidget(mayaWindow())
|
||||
retargetBlendshape.show()
|
536
Scripts/Modeling/Edit/LDMT/ldmt_checkUVBleed.py
Normal file
@ -0,0 +1,536 @@
|
||||
import os
|
||||
import math
|
||||
from ldmt_function.ldmt_loadUIFile import get_maya_window, load_ui_file
|
||||
import maya.OpenMayaUI as omui
|
||||
from ldmt_core import ldmt_cmds as ld
|
||||
from functools import partial
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
|
||||
try:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtWidgets import *
|
||||
# from Qt.QtUiTools import *
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import loadUi, wrapInstance
|
||||
except ImportError:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
# from Qt.QtUiTools import *
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import wrapInstance
|
||||
|
||||
import maya.OpenMaya as om
|
||||
import maya.api.OpenMaya as om2
|
||||
import random
|
||||
import ast
|
||||
|
||||
LDMTPATH = ld.getPath('LDMT')
|
||||
ldmt_uifile = LDMTPATH + '/ldmt_ui/ldmt_checkUVBleed.ui'
|
||||
ldmt_window_name = 'ldmt_checkUVBleed'
|
||||
ldmt_button_name = 'btn_'+ldmt_window_name.split('_')[1]
|
||||
|
||||
'''
|
||||
Function
|
||||
'''
|
||||
def changeSelectionType(sel,selType): #selType vertex face edge puv
|
||||
mel.eval('if( !`exists doMenuComponentSelection` ) eval( "source dagMenuProc" );')
|
||||
mel.eval('doMenuComponentSelection("'+sel+'","'+selType+'")' )
|
||||
|
||||
def getUVEdgeDic(sel):
|
||||
selList = om2.MSelectionList()
|
||||
selList.add(sel)
|
||||
selPath = selList.getDagPath(0)
|
||||
selMesh = om2.MFnMesh(selPath)
|
||||
selVtxIter = om2.MItMeshVertex(selPath)
|
||||
selEdgeIter = om2.MItMeshEdge(selPath)
|
||||
selFaceIter = om2.MItMeshPolygon(selPath)
|
||||
|
||||
uvid_uv = [] # generate {uvid:[u,v],} from MFnMesh.getUVs()
|
||||
vtxid_uvid = []
|
||||
edgeid_vtxid = []
|
||||
edgeid_uvid = [] #edgeid_vtxid + vtxid_uvid
|
||||
faceid_edgeid = []
|
||||
faceid_uvid = []
|
||||
edgeid_faceid = [] #faceid_edgeid reverse
|
||||
uvedgeid_uvid = [] # get { uvedgeid: [uvid1, uvid2]} On border
|
||||
|
||||
uvid_usi = selMesh.getUvShellsIds() # [usi1,usi2,...]
|
||||
uvid_usi = uvid_usi[1]
|
||||
uvArray = selMesh.getUVs()
|
||||
for i in range(len(uvArray[0])):
|
||||
uvid_uv.append([uvArray[0][i],uvArray[1][i]])
|
||||
|
||||
while not selVtxIter.isDone(): #get {[[uvid1,uvid2],...]}
|
||||
vtxid_uvid.append(list(set(selVtxIter.getUVIndices())))
|
||||
selVtxIter.next()
|
||||
|
||||
while not selEdgeIter.isDone(): #get edge to vtx
|
||||
edgeid_vtxid.append([selEdgeIter.vertexId(0),selEdgeIter.vertexId(1)])
|
||||
edgeid_faceid.append(selEdgeIter.getConnectedFaces())
|
||||
selEdgeIter.next()
|
||||
|
||||
for edgeid in range(len(edgeid_vtxid)): # get {[[uvid1,uvid2,uvid3],...]}
|
||||
vtx0 = edgeid_vtxid[edgeid][0]
|
||||
vtx1 = edgeid_vtxid[edgeid][1]
|
||||
edgeid_uvid.append(vtxid_uvid[vtx0] + vtxid_uvid[vtx1])
|
||||
|
||||
while not selFaceIter.isDone(): # get {[[uvid1,uvid2,uvid3,uvid4], ...] }
|
||||
verts = selFaceIter.getVertices()
|
||||
faceid_edgeid.append(selFaceIter.getEdges())
|
||||
uvids=[]
|
||||
for index in range(len(verts)):
|
||||
uvids.append(selFaceIter.getUVIndex(index)) #necessary for uvedgeid
|
||||
faceid_uvid.append(uvids)
|
||||
selFaceIter.next()
|
||||
|
||||
for edgeid in range(len(edgeid_faceid)):
|
||||
faceids = edgeid_faceid[edgeid]
|
||||
edgeuvids = set(edgeid_uvid[edgeid])
|
||||
numface = len(faceids)
|
||||
if numface == 1: #OnBorder
|
||||
uvids = faceid_uvid[faceids[0]]
|
||||
uvedgeid_uvid.append(list(edgeuvids.intersection(uvids)))
|
||||
continue
|
||||
elif numface == 2:
|
||||
uvidsA = faceid_uvid[faceids[0]]
|
||||
uvidsB = faceid_uvid[faceids[1]]
|
||||
intersectUV = list(set(uvidsA).intersection(uvidsB))
|
||||
intersectUV_len = len(intersectUV)
|
||||
if intersectUV_len<2:
|
||||
if intersectUV_len == 1:
|
||||
for uv in edgeuvids:
|
||||
if uv == intersectUV[0]:
|
||||
continue
|
||||
elif uv in uvidsA or uv in uvidsB:
|
||||
uvedgeid_uvid.append([intersectUV[0],uv])
|
||||
else:
|
||||
uvedgeid_uvid.append(list(edgeuvids.intersection(uvidsA)))
|
||||
uvedgeid_uvid.append(list(edgeuvids.intersection(uvidsB)))
|
||||
else:
|
||||
print("Cleanup mesh first!!!!")
|
||||
|
||||
usi_uvid = {}
|
||||
usi_uvidOnBord = {}
|
||||
usi_uvedgeidOnBord = {}
|
||||
usi_bb = {}
|
||||
|
||||
for uvid in range(len(uvid_usi)):
|
||||
usi = uvid_usi[uvid]
|
||||
if usi in usi_uvid:
|
||||
usi_uvid[usi].append(uvid)
|
||||
else:
|
||||
usi_uvid[usi] = [uvid]
|
||||
|
||||
for uvedgeid in range(len(uvedgeid_uvid)):
|
||||
usi = uvid_usi[uvedgeid_uvid[uvedgeid][0]]
|
||||
if usi in usi_uvedgeidOnBord:
|
||||
usi_uvedgeidOnBord[usi].append(uvedgeid)
|
||||
else:
|
||||
usi_uvedgeidOnBord[usi] = [uvedgeid]
|
||||
|
||||
for usi in usi_uvedgeidOnBord:
|
||||
edgeids = usi_uvedgeidOnBord[usi]
|
||||
uvsOnBord = []
|
||||
for edgeid in edgeids:
|
||||
uvsOnBord = uvsOnBord + uvedgeid_uvid[edgeid]
|
||||
usi_uvidOnBord[usi] = list(set(uvsOnBord))
|
||||
|
||||
for usi in usi_uvidOnBord:
|
||||
umin = 1
|
||||
umax = 0
|
||||
vmin = 1
|
||||
vmax = 0
|
||||
for uvid in usi_uvidOnBord[usi]:
|
||||
u = uvid_uv[uvid][0]
|
||||
v = uvid_uv[uvid][1]
|
||||
if u < umin:
|
||||
umin = u
|
||||
if u > umax:
|
||||
umax = u
|
||||
if v < vmin:
|
||||
vmin = v
|
||||
if v > vmax:
|
||||
vmax = v
|
||||
usi_bb[usi] = [[umin,umax],[vmin,vmax]]
|
||||
|
||||
usi_bbarea = {}
|
||||
for i in range(len(usi_uvid)): #get {usi:area,} from usi_bb
|
||||
usi_bbarea[i] = abs(usi_bb[i][0][1]-usi_bb[i][0][0])*abs(usi_bb[i][1][1]-usi_bb[i][1][0])
|
||||
bbarea_usi = sorted(zip(usi_bbarea.values(), usi_bbarea.keys())) # get [(minarea, usi0),...,( maxarea, usi99)]
|
||||
bbarea_usi.reverse()
|
||||
|
||||
uvedgeid_uv = []
|
||||
for uvedgeid in range(len(uvedgeid_uvid)):
|
||||
uvidA = uvedgeid_uvid[uvedgeid][0]
|
||||
uvidB = uvedgeid_uvid[uvedgeid][1]
|
||||
uA = uvid_uv[uvidA][0]
|
||||
vA = uvid_uv[uvidA][1]
|
||||
uB = uvid_uv[uvidB][0]
|
||||
vB = uvid_uv[uvidB][1]
|
||||
if vB >= vA:
|
||||
uvedgeid_uv.append([[uA,vA],[uB,vB]])
|
||||
else:
|
||||
uvedgeid_uv.append([[uB,vB],[uA,vA]])
|
||||
|
||||
return usi_uvid, uvid_uv, uvedgeid_uvid, usi_uvidOnBord, usi_uvedgeidOnBord, usi_bb, bbarea_usi, uvedgeid_uv
|
||||
|
||||
def LD_CheckUVBleed(uvScale,pixelDistance,borderDistance):
|
||||
uvDistance = float(pixelDistance)/uvScale
|
||||
borderDistance = float(borderDistance)/uvScale
|
||||
TOLERANCE = 4
|
||||
p = 10**TOLERANCE
|
||||
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
sel = sel[0]
|
||||
faceWithNoUV = getFacesHaveNoUV(sel)
|
||||
if faceWithNoUV !=[]:
|
||||
cmds.select(faceWithNoUV,r=1)
|
||||
cmds.warning("Some Faces Have no UVs!")
|
||||
return
|
||||
uvDic = getUVEdgeDic(sel) #uvDic[6]
|
||||
usi_uvid = uvDic[0]
|
||||
uvid_uv = uvDic[1]
|
||||
uvedgeid_uvid = uvDic[2]
|
||||
usi_uvidOnBord = uvDic[3]
|
||||
usi_uvedgeidOnBord = uvDic[4]
|
||||
usi_bb = uvDic[5]
|
||||
bbarea_usi = uvDic[6]
|
||||
uvedgeid_uv = uvDic[7]
|
||||
|
||||
uvNotPass = set() # len(uvNotPass) Filter out stacking uvs
|
||||
skipList = set()
|
||||
skipListTemp = set()
|
||||
intersectList = {}
|
||||
usi_uvidOnBord_final = {}
|
||||
usi_uvedgeidOnBord_final = {}
|
||||
|
||||
# prepare
|
||||
for tupleA in range(len(bbarea_usi)):
|
||||
usiA = bbarea_usi[tupleA][1]
|
||||
skipListTemp.add(usiA)
|
||||
|
||||
# fast detect
|
||||
bb_uminA = usi_bb[usiA][0][0]
|
||||
bb_umaxA = usi_bb[usiA][0][1]
|
||||
bb_vminA = usi_bb[usiA][1][0]
|
||||
bb_vmaxA = usi_bb[usiA][1][1]
|
||||
|
||||
for tupleB in range(len(bbarea_usi)):
|
||||
usiB = bbarea_usi[tupleB][1]
|
||||
|
||||
#faset detect
|
||||
bb_uminB = usi_bb[usiB][0][0]
|
||||
bb_umaxB = usi_bb[usiB][0][1]
|
||||
bb_vminB = usi_bb[usiB][1][0]
|
||||
bb_vmaxB = usi_bb[usiB][1][1]
|
||||
if bb_uminA - uvDistance > bb_umaxB or\
|
||||
bb_umaxA + uvDistance < bb_uminB or\
|
||||
bb_vminA - uvDistance > bb_vmaxB or\
|
||||
bb_vmaxA + uvDistance < bb_vminB :
|
||||
continue
|
||||
|
||||
if usiB in skipListTemp:
|
||||
continue
|
||||
if ifUVShellStack(usiA,usiB,usi_bb, p):
|
||||
skipList.add(usiB)
|
||||
skipListTemp.add(usiB)
|
||||
elif ifUVShellIntersect(usiA,usiB,usi_uvedgeidOnBord, uvedgeid_uv, usi_bb):
|
||||
if usiA in intersectList:
|
||||
intersectList[usiA].append(usiB)
|
||||
else:
|
||||
intersectList[usiA]=[usiB]
|
||||
skipListTemp.add(usiB)
|
||||
skipList.add(usiB)
|
||||
|
||||
elif ifUVShellContain(usiA,usiB,usi_uvedgeidOnBord,usi_uvidOnBord,uvedgeid_uv,uvid_uv):
|
||||
skipList.add(usiB)
|
||||
skipListTemp.add(usiB)
|
||||
|
||||
# calculate final list
|
||||
for usiA in usi_uvidOnBord:
|
||||
if usiA in skipList:
|
||||
continue
|
||||
if usiA in intersectList:
|
||||
tempUVidList = []
|
||||
tempUVedgeidList = []
|
||||
for usiB in intersectList[usiA]:
|
||||
tempUVidList += usi_uvidOnBord[usiB]
|
||||
tempUVedgeidList += usi_uvedgeidOnBord[usiB]
|
||||
usi_uvidOnBord_final[usiA] = tempUVidList + usi_uvidOnBord[usiA]
|
||||
usi_uvedgeidOnBord_final[usiA] = tempUVedgeidList + usi_uvedgeidOnBord[usiA]
|
||||
else:
|
||||
usi_uvidOnBord_final[usiA] = usi_uvidOnBord[usiA]
|
||||
usi_uvedgeidOnBord_final[usiA] = usi_uvedgeidOnBord[usiA]
|
||||
|
||||
usi_bb_final = {}
|
||||
for usi in usi_uvidOnBord_final:
|
||||
umin = 1
|
||||
umax = 0
|
||||
vmin = 1
|
||||
vmax = 0
|
||||
for uvid in usi_uvidOnBord_final[usi]:
|
||||
u = uvid_uv[uvid][0]
|
||||
v = uvid_uv[uvid][1]
|
||||
if u < umin:
|
||||
umin = u
|
||||
if u > umax:
|
||||
umax = u
|
||||
if v < vmin:
|
||||
vmin = v
|
||||
if v > vmax:
|
||||
vmax = v
|
||||
usi_bb_final[usi] = [[umin,umax],[vmin,vmax]]
|
||||
|
||||
# calculate final result
|
||||
for usiA in usi_uvidOnBord_final:
|
||||
if usi_bb_final[usiA][0][0] - borderDistance < 0 or\
|
||||
usi_bb_final[usiA][0][1] + borderDistance > 1 or\
|
||||
usi_bb_final[usiA][1][0] - borderDistance < 0 or\
|
||||
usi_bb_final[usiA][1][1] + borderDistance > 1:
|
||||
for uvid in usi_uvidOnBord_final[usiA]:
|
||||
x = uvid_uv[uvid][0]
|
||||
y = uvid_uv[uvid][1]
|
||||
if x - borderDistance < 0 or\
|
||||
x + borderDistance > 1 or\
|
||||
y - borderDistance < 0 or\
|
||||
y + borderDistance > 1:
|
||||
uvNotPass.add(uvid)
|
||||
|
||||
for usiB in usi_uvidOnBord_final:
|
||||
if usiB == usiA or\
|
||||
usi_bb_final[usiA][0][0] - uvDistance > usi_bb_final[usiB][0][1] or\
|
||||
usi_bb_final[usiA][0][1] + uvDistance < usi_bb_final[usiB][0][0] or\
|
||||
usi_bb_final[usiA][1][0] - uvDistance > usi_bb_final[usiB][1][1] or\
|
||||
usi_bb_final[usiA][1][1] + uvDistance < usi_bb_final[usiB][1][0]:
|
||||
continue
|
||||
for uvid in usi_uvidOnBord_final[usiA]:
|
||||
if uvid in uvNotPass:
|
||||
continue
|
||||
x = uvid_uv[uvid][0]
|
||||
y = uvid_uv[uvid][1]
|
||||
if x - uvDistance > usi_bb_final[usiB][0][1] or\
|
||||
x + uvDistance < usi_bb_final[usiB][0][0] or\
|
||||
y - uvDistance > usi_bb_final[usiB][1][1] or\
|
||||
y + uvDistance < usi_bb_final[usiB][1][0]:
|
||||
continue
|
||||
for edge in usi_uvedgeidOnBord_final[usiB]:
|
||||
uvedgeid = uvedgeid_uvid[edge]
|
||||
x1 = uvid_uv[uvedgeid[0]][0]
|
||||
y1 = uvid_uv[uvedgeid[0]][1]
|
||||
x2 = uvid_uv[uvedgeid[1]][0]
|
||||
y2 = uvid_uv[uvedgeid[1]][1]
|
||||
cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1)
|
||||
if cross <= 0:
|
||||
closest = ((x - x1)**2 + (y - y1)**2)**(0.5)
|
||||
if closest < uvDistance:
|
||||
uvNotPass.add(uvid)
|
||||
continue
|
||||
d2 = (x2 - x1)**2 + (y2 - y1)**2
|
||||
if cross >= d2:
|
||||
closest = ((x - x2)**2 + (y - y2)**2)**(0.5)
|
||||
if closest < uvDistance:
|
||||
uvNotPass.add(uvid)
|
||||
continue
|
||||
r = cross / d2
|
||||
px = x1 + (x2 - x1) * r
|
||||
py = y1 + (y2 - y1) * r
|
||||
closest = ((x - px)**2 + (py - y)**2)**(0.5)
|
||||
if closest < uvDistance:
|
||||
uvNotPass.add(uvid)
|
||||
uvNamesNotPass = []
|
||||
for uv in uvNotPass:
|
||||
uvNamesNotPass.append(sel+'.map['+str(uv)+']')
|
||||
cmds.select(uvNamesNotPass,r=1)
|
||||
changeSelectionType(sel,"puv")
|
||||
|
||||
def ifUVShellStack(usiA, usiB, usi_bb, p):
|
||||
# fast filter
|
||||
usiA_umin = float(int(usi_bb[usiA][0][0] * p))/p
|
||||
usiB_umin = float(int(usi_bb[usiB][0][0] * p))/p
|
||||
if not usiA_umin == usiB_umin:
|
||||
return 0
|
||||
|
||||
usiA_umax = float(int(usi_bb[usiA][0][1] * p))/p
|
||||
usiA_vmin = float(int(usi_bb[usiA][1][0] * p))/p
|
||||
usiA_vmax = float(int(usi_bb[usiA][1][1] * p))/p
|
||||
usiB_umax = float(int(usi_bb[usiB][0][1] * p))/p
|
||||
usiB_vmin = float(int(usi_bb[usiB][1][0] * p))/p
|
||||
usiB_vmax = float(int(usi_bb[usiB][1][1] * p))/p
|
||||
if usiA_umax == usiB_umax and usiA_vmin == usiB_vmin and usiA_vmax == usiB_vmax:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def ifUVShellIntersect(usiA, usiB, usi_uvedgeidOnBord, uvedgeid_uv, usi_bb):
|
||||
for edgeA in usi_uvedgeidOnBord[usiA]:
|
||||
ax = uvedgeid_uv[edgeA][0][0]
|
||||
ay = uvedgeid_uv[edgeA][0][1]
|
||||
bx = uvedgeid_uv[edgeA][1][0]
|
||||
by = uvedgeid_uv[edgeA][1][1]
|
||||
|
||||
# fast filter
|
||||
abx = [ax,bx]
|
||||
aby = [ay,by]
|
||||
if bx<ax:
|
||||
abx = [bx,ax]
|
||||
if by<ay:
|
||||
aby = [by,ay]
|
||||
|
||||
# bound filter
|
||||
bb_usiB = usi_bb[usiB]
|
||||
bb_uminB = bb_usiB[0][0]
|
||||
bb_umaxB = bb_usiB[0][1]
|
||||
bb_vminB = bb_usiB[1][0]
|
||||
bb_vmaxB = bb_usiB[1][1]
|
||||
|
||||
if abx[0] > bb_umaxB or\
|
||||
abx[1] < bb_uminB or\
|
||||
aby[0] > bb_vmaxB or\
|
||||
aby[1] < bb_vminB:
|
||||
continue
|
||||
|
||||
for edgeB in usi_uvedgeidOnBord[usiB]:
|
||||
cx = uvedgeid_uv[edgeB][0][0]
|
||||
cy = uvedgeid_uv[edgeB][0][1]
|
||||
dx = uvedgeid_uv[edgeB][1][0]
|
||||
dy = uvedgeid_uv[edgeB][1][1]
|
||||
|
||||
# fast filter
|
||||
cdx = [cx,dx]
|
||||
cdy = [cy,dy]
|
||||
if dx<cx:
|
||||
cdx = [dx,cx]
|
||||
if dy<cy:
|
||||
cdy = [dy,cy]
|
||||
if abx[0] > cdx[1] or\
|
||||
abx[1] < cdx[0] or\
|
||||
aby[0] > cdy[1] or\
|
||||
aby[1] < cdy[0]:
|
||||
continue
|
||||
|
||||
x1 = bx - ax
|
||||
y1 = by - ay
|
||||
x2 = cx - ax
|
||||
y2 = cy - ay
|
||||
cross1 = x1*y2-x2*y1
|
||||
x2 = dx - ax
|
||||
y2 = dy - ay
|
||||
cross2 = x1*y2-x2*y1
|
||||
x1 = dx - cx
|
||||
y1 = dy - cy
|
||||
x2 = ax - cx
|
||||
y2 = ay - cy
|
||||
cross3 = x1*y2-x2*y1
|
||||
x2 = bx - cx
|
||||
y2 = by - cy
|
||||
cross4 = x1*y2-x2*y1
|
||||
if (cross1*cross2 <= 0 and cross3*cross4<=0):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def ifUVShellContain(usiA,usiB,usi_uvedgeidOnBord,usi_uvidOnBord,uvedgeid_uv,uvid_uv):
|
||||
uvid = usi_uvidOnBord[usiB][0]
|
||||
intersects = 0
|
||||
ray_u = uvid_uv[uvid][0]
|
||||
ray_v = uvid_uv[uvid][1]
|
||||
|
||||
for edge in usi_uvedgeidOnBord[usiA]:
|
||||
u0 = uvedgeid_uv[edge][0][0]
|
||||
v0 = uvedgeid_uv[edge][0][1]
|
||||
u1 = uvedgeid_uv[edge][1][0]
|
||||
v1 = uvedgeid_uv[edge][1][1]
|
||||
if (v1 >= ray_v and v0 < ray_v):
|
||||
if ((u0-ray_u)*(v1-ray_v)-(v0-ray_v)*(u1-ray_u)) < 0:
|
||||
intersects += 1
|
||||
if intersects%2 == 1:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def getFacesHaveNoUV(sel):
|
||||
selList = om2.MSelectionList()
|
||||
selList.add(sel)
|
||||
selPath = selList.getDagPath(0)
|
||||
selVtxIter= om2.MItMeshVertex(selPath)
|
||||
faceidWithNoUV = []
|
||||
faceWithNoUV = []
|
||||
while not selVtxIter.isDone():
|
||||
if selVtxIter.numUVs()==0:
|
||||
faceidWithNoUV += selVtxIter.getConnectedFaces()
|
||||
selVtxIter.next()
|
||||
faceidWithNoUV = list(set(faceidWithNoUV))
|
||||
for faceid in faceidWithNoUV:
|
||||
faceWithNoUV+=[sel+'.f['+str(faceid)+']']
|
||||
return faceWithNoUV
|
||||
# checkUVBleed_0()
|
||||
# cProfile.run('checkUVBleed_0()')
|
||||
|
||||
'''
|
||||
#UI
|
||||
'''
|
||||
class ldmt_cls(QDialog):
|
||||
def __init__(self, parent = get_maya_window()):
|
||||
super(ldmt_cls, self).__init__(parent)
|
||||
self.window_name = ldmt_window_name
|
||||
self.setWindowTitle(ldmt_window_name)
|
||||
self.setWindowFlags(Qt.Window)
|
||||
self.move(QCursor.pos() + QPoint(20,20))
|
||||
# update status bar so it's not only show in help line window.
|
||||
self.set_ui()
|
||||
self.setupBtn()
|
||||
# self.statusbar.showMessage(ld.tag())
|
||||
# self.installStartBar()
|
||||
|
||||
def set_ui(self):
|
||||
main_layout = QVBoxLayout(self)
|
||||
self.ui = load_ui_file(ldmt_uifile)
|
||||
if self.ui is None:
|
||||
cmds.warning("UI file not found: {}".format(ldmt_uifile))
|
||||
return
|
||||
main_layout.addWidget(self.ui)
|
||||
|
||||
def setupBtn(self):
|
||||
self.ui.btn_check.clicked.connect(self.checkUVBleed)
|
||||
|
||||
def checkUVBleed(self):
|
||||
cmds.undoInfo(ock = 1)
|
||||
try:
|
||||
minShellBleed = int(self.ui.text_borderBleed.text())
|
||||
minBorderBleed = int(self.ui.text_borderBleed.text())
|
||||
uvSize = int(self.ui.box_textureSize.currentText())
|
||||
except:
|
||||
ld.msg("Input value is not valid!")
|
||||
LD_CheckUVBleed(uvSize,minShellBleed,minBorderBleed)
|
||||
cmds.undoInfo(cck = 1)
|
||||
|
||||
def installStartBar(self):
|
||||
allQWidgets = self.findChildren(QWidget)
|
||||
for i in allQWidgets:
|
||||
i.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, obj, event ):
|
||||
'''Connect signals on mouse over'''
|
||||
if event.type() == QEvent.Enter:
|
||||
self.oldMessage = ld.tag()
|
||||
self.statusbar.showMessage(' '+obj.statusTip(),0)
|
||||
elif event.type() == QEvent.Leave:
|
||||
self.statusbar.showMessage(' '+self.oldMessage, 0)
|
||||
pass
|
||||
event.accept()
|
||||
return False
|
||||
|
||||
# def closeEvent(self,event):
|
||||
# ld.turnToolBtnOff(self,ldmt_button_name)
|
||||
# cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
def ldmt_show():
|
||||
if cmds.window(ldmt_window_name, exists=True):
|
||||
cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
app = QApplication.instance()
|
||||
dialog = ldmt_cls(parent=app.activeWindow())
|
||||
dialog.show()
|
||||
app.exec_()
|
||||
|
||||
if __name__ == '__main__':
|
||||
ldmt_show()
|
94
Scripts/Modeling/Edit/LDMT/ldmt_cleanMesh.py
Normal file
@ -0,0 +1,94 @@
|
||||
import os
|
||||
import math
|
||||
from ldmt_function.ldmt_loadUIFile import get_maya_window, load_ui_file
|
||||
import maya.OpenMayaUI as omui
|
||||
from ldmt_core import ldmt_cmds as ld
|
||||
from functools import partial
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
|
||||
try:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtWidgets import *
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import loadUi, wrapInstance
|
||||
except ImportError:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtUiTools import *
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import loadUi, wrapInstance
|
||||
|
||||
import maya.OpenMaya as om
|
||||
import maya.api.OpenMaya as om2
|
||||
import random
|
||||
import ast
|
||||
|
||||
LDMTPATH = ld.getPath('LDMT')
|
||||
ldmt_uifile = LDMTPATH + '/ldmt_ui/ldmt_cleanMesh.ui'
|
||||
ldmt_window_name = 'ldmt_cleanMesh'
|
||||
ldmt_button_name = 'btn_'+ldmt_window_name.split('_')[1]
|
||||
|
||||
'''
|
||||
#UI
|
||||
'''
|
||||
class ldmt_cls(QDialog):
|
||||
def __init__(self, parent = get_maya_window()):
|
||||
super(ldmt_cls, self).__init__(parent)
|
||||
self.window_name = ldmt_window_name
|
||||
self.setWindowTitle(ldmt_window_name)
|
||||
self.setWindowFlags(Qt.Window)
|
||||
self.move(QCursor.pos() + QPoint(20,20))
|
||||
# update status bar so it's not only show in help line window.
|
||||
self.set_ui()
|
||||
self.setupBtn()
|
||||
# self.statusbar.showMessage(ld.tag())
|
||||
# self.installStartBar()
|
||||
|
||||
def set_ui(self):
|
||||
main_layout = QVBoxLayout(self)
|
||||
self.ui = load_ui_file(ldmt_uifile)
|
||||
if self.ui is None:
|
||||
cmds.warning("UI file not found: {}".format(ldmt_uifile))
|
||||
return
|
||||
main_layout.addWidget(self.ui)
|
||||
|
||||
def setupBtn(self):
|
||||
self.ui.btn_select.clicked.connect(self.select)
|
||||
self.ui.btn_cleanup.clicked.connect(self.cleanup)
|
||||
def select(self):
|
||||
mel.eval('polyCleanupArgList 4 { "0","2","1","0","1","1","1","0","1","1e-005","1","1e-005","1","1e-005","0","1","1","0" };')
|
||||
def cleanup(self):
|
||||
mel.eval('polyCleanupArgList 4 { "0","1","1","0","1","1","1","0","1","1e-005","1","1e-005","1","1e-005","0","1","1","0" };')
|
||||
def installStartBar(self):
|
||||
allQWidgets = self.findChildren(QWidget)
|
||||
for i in allQWidgets:
|
||||
i.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, obj, event ):
|
||||
'''Connect signals on mouse over'''
|
||||
if event.type() == QEvent.Enter:
|
||||
self.oldMessage = ld.tag()
|
||||
self.statusbar.showMessage(' '+obj.statusTip(),0)
|
||||
elif event.type() == QEvent.Leave:
|
||||
self.statusbar.showMessage(' '+self.oldMessage, 0)
|
||||
pass
|
||||
event.accept()
|
||||
return False
|
||||
|
||||
# def closeEvent(self,event):
|
||||
# ld.turnToolBtnOff(self,ldmt_button_name)
|
||||
# cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
def ldmt_show():
|
||||
if cmds.window(ldmt_window_name, exists=True):
|
||||
cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
app = QApplication.instance()
|
||||
dialog = ldmt_cls(parent=app.activeWindow())
|
||||
dialog.show()
|
||||
app.exec_()
|
||||
|
||||
if __name__ == '__main__':
|
||||
ldmt_show()
|
154
Scripts/Modeling/Edit/LDMT/ldmt_clothTransfer.py
Normal file
@ -0,0 +1,154 @@
|
||||
import os
|
||||
import math
|
||||
import ldmt_function.ldmt_loadUIFile
|
||||
import importlib
|
||||
#importlib.reload(ldmt_function.ldmt_loadUIFile)
|
||||
from ldmt_function.ldmt_loadUIFile import get_maya_window, load_ui_file
|
||||
import maya.OpenMayaUI as omui
|
||||
from ldmt_core import ldmt_cmds as ld
|
||||
from functools import partial
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
|
||||
try:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtWidgets import *
|
||||
# from Qt.QtUiTools import *
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import wrapInstance
|
||||
except ImportError:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
# from Qt.QtUiTools import *
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import wrapInstance
|
||||
|
||||
import maya.OpenMaya as om
|
||||
import maya.api.OpenMaya as om2
|
||||
import random
|
||||
import ast
|
||||
|
||||
LDMTPATH = ld.getPath('LDMT')
|
||||
ldmt_uifile = LDMTPATH + '/ldmt_ui/ldmt_clothTransfer.ui'
|
||||
# ldmt_list_form, ldmt_list_base = load_ui_type(ldmt_uifile)
|
||||
ldmt_window_name = 'ldmt_clothTransfer'
|
||||
ldmt_button_name = 'btn_'+ldmt_window_name.split('_')[1]
|
||||
|
||||
'''
|
||||
#Functions
|
||||
'''
|
||||
|
||||
'''
|
||||
#UI
|
||||
'''
|
||||
class ldmt_cls(QDialog):
|
||||
def __init__(self, parent = get_maya_window()):
|
||||
super(ldmt_cls, self).__init__(parent)
|
||||
self.window_name = ldmt_window_name
|
||||
self.setWindowTitle(ldmt_window_name)
|
||||
self.setWindowFlags(Qt.Window)
|
||||
# self.setupUi(self)
|
||||
# self.move(QCursor.pos() + QPoint(20,20))
|
||||
self.set_ui()
|
||||
# update status bar so it's not only show in help line window.
|
||||
self.setupBtn()
|
||||
# self.statusbar.showMessage(ld.tag())
|
||||
# self.installStartBar()
|
||||
|
||||
def set_ui(self):
|
||||
main_layout = QVBoxLayout(self)
|
||||
|
||||
self.ui = load_ui_file(ldmt_uifile)
|
||||
|
||||
main_layout.addWidget(self.ui)
|
||||
|
||||
def setupBtn(self):
|
||||
self.ui.btn_setCloth.clicked.connect(self.setCloth)
|
||||
self.ui.btn_setOrigin.clicked.connect(self.setOrigin)
|
||||
self.ui.btn_setTarget.clicked.connect(self.setTarget)
|
||||
self.ui.btn_selectCloth.clicked.connect(self.selectCloth)
|
||||
self.ui.btn_selectOrigin.clicked.connect(self.selectOrigin)
|
||||
self.ui.btn_selectTarget.clicked.connect(self.selectTarget)
|
||||
|
||||
self.ui.btn_transfer.clicked.connect(self.transfer)
|
||||
|
||||
def setCloth(self):
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
sel = sel[0]
|
||||
if sel != []:
|
||||
self.ui.btn_selectCloth.setText(str(sel))
|
||||
else:
|
||||
self.ui.btn_selectCloth.setText('...')
|
||||
def setOrigin(self):
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
sel = sel[0]
|
||||
if sel != []:
|
||||
self.ui.btn_selectOrigin.setText(str(sel))
|
||||
else:
|
||||
self.ui.btn_selectOrigin.setText('...')
|
||||
def setTarget(self):
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
sel = sel[0]
|
||||
if sel != []:
|
||||
self.ui.btn_selectTarget.setText(str(sel))
|
||||
else:
|
||||
self.ui.btn_selectTarget.setText('...')
|
||||
def selectSetObjects(self,sel):
|
||||
if sel != '...':
|
||||
cmds.select(sel,r=1)
|
||||
else:
|
||||
ld.msg('Nothing selected')
|
||||
def selectCloth(self):
|
||||
sel = self.ui.btn_selectCloth.text()
|
||||
self.selectSetObjects(sel)
|
||||
def selectOrigin(self):
|
||||
sel = self.ui.btn_selectOrigin.text()
|
||||
self.selectSetObjects(sel)
|
||||
def selectTarget(self):
|
||||
sel = self.ui.btn_selectTarget.text()
|
||||
self.selectSetObjects(sel)
|
||||
def transfer(self):
|
||||
cloth = self.ui.btn_selectCloth.text()
|
||||
origin = self.ui.btn_selectOrigin.text()
|
||||
target = self.ui.btn_selectTarget.text()
|
||||
cmds.makeIdentity(target,apply=True, t=1, r=1, s=1, n=0)
|
||||
cmds.select(cloth,r=1)
|
||||
cmds.select(origin,add=1)
|
||||
mel.eval('DeleteHistory;')
|
||||
mel.eval('CreateWrap;')
|
||||
blendObjs = [target,origin]
|
||||
blendName = cmds.blendShape(blendObjs,o='world',n='clothTransfer#')
|
||||
cmds.blendShape(blendName,e=1,w=(0,1))
|
||||
|
||||
def installStartBar(self):
|
||||
allQWidgets = self.findChildren(QWidget)
|
||||
for i in allQWidgets:
|
||||
i.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, obj, event ):
|
||||
'''Connect signals on mouse over'''
|
||||
if event.type() == QEvent.Enter:
|
||||
self.oldMessage = ld.tag()
|
||||
self.statusbar.showMessage(' '+obj.statusTip(),0)
|
||||
elif event.type() == QEvent.Leave:
|
||||
self.statusbar.showMessage(' '+self.oldMessage, 0)
|
||||
pass
|
||||
event.accept()
|
||||
return False
|
||||
# def closeEvent(self,event):
|
||||
# ld.turnToolBtnOff(self,ldmt_button_name)
|
||||
# cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
def ldmt_show():
|
||||
|
||||
if cmds.window(ldmt_window_name, exists=True):
|
||||
cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
app = QApplication.instance()
|
||||
dialog = ldmt_cls(parent=app.activeWindow())
|
||||
dialog.show()
|
||||
app.exec_()
|
||||
|
||||
if __name__ == '__main__':
|
||||
ldmt_show()
|
2155
Scripts/Modeling/Edit/LDMT/ldmt_core/Qt.py
Normal file
0
Scripts/Modeling/Edit/LDMT/ldmt_core/__init__.py
Normal file
246
Scripts/Modeling/Edit/LDMT/ldmt_core/ldmt_cmds.py
Normal file
@ -0,0 +1,246 @@
|
||||
'''
|
||||
Don't edit the line above, it should be set by ldmt_setup.py automatically according to
|
||||
your installation path.
|
||||
'''
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import maya.api.OpenMaya as om2
|
||||
try:
|
||||
import configparser as ConfigParser
|
||||
except ImportError:
|
||||
import ConfigParser
|
||||
import os
|
||||
try:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtWidgets import *
|
||||
# from Qt.QtUiTools import *
|
||||
# from Qt.QtCompat import loadUi
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import wrapInstance
|
||||
except ImportError:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
# from Qt.QtUiTools import *
|
||||
# from Qt.QtCompat import loadUi
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import wrapInstance
|
||||
|
||||
PM_startTime = cmds.timerX()
|
||||
PM_timeAdder = 0
|
||||
MAYA_version = cmds.about(v=1)
|
||||
MAYA_version_float = float(MAYA_version.split(' ')[0])
|
||||
|
||||
'''
|
||||
GLobal Environment
|
||||
'''
|
||||
def tag():
|
||||
return "Liu Dian xgits@outlook.com"
|
||||
def getPath(whatPath):
|
||||
if whatPath == 'MAYA_SCRIPT_PATH':
|
||||
MAYA_SCRIPT_PATH = os.environ['MAYA_SCRIPT_PATH']
|
||||
MAYA_SCRIPT_PATH = MAYA_SCRIPT_PATH.split(';')
|
||||
for eachPath in MAYA_SCRIPT_PATH:
|
||||
if eachPath.endswith('maya/scripts'):
|
||||
return eachPath
|
||||
elif whatPath == 'LDMT':
|
||||
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
# MAYA_SCRIPT_PATH = os.environ['MAYA_SCRIPT_PATH']
|
||||
# MAYA_SCRIPT_PATH = MAYA_SCRIPT_PATH.split(';')
|
||||
# for eachPath in MAYA_SCRIPT_PATH:
|
||||
# if eachPath.endswith('maya/scripts'):
|
||||
# MAYA_SCRIPT_PATH = eachPath
|
||||
# return MAYA_SCRIPT_PATH + '/' + 'LDMT'
|
||||
|
||||
'''
|
||||
Config
|
||||
'''
|
||||
|
||||
def getLdmtConfig(section,option):
|
||||
cf = ConfigParser.ConfigParser()
|
||||
FOLDERNAME = getPath('LDMT').split('/')[-1]
|
||||
cfPath = getPath("MAYA_SCRIPT_PATH") + "/" + FOLDERNAME + "/ldmt_config.ini"
|
||||
cf.read(cfPath)
|
||||
return cf.get(section,option)
|
||||
## math ##
|
||||
|
||||
def round(number,pre):
|
||||
if number > 0:
|
||||
return float(int(number*10**pre+0.5))/10**pre
|
||||
else:
|
||||
return float(int(number*10**pre-0.5))/10**pre
|
||||
def distance(pointA,pointB):
|
||||
return ((pointB[0]-pointA[0])**2+(pointB[1]-pointA[1])**2+(pointB[2]-pointA[2])**2)**(0.5)
|
||||
## Plugins ##
|
||||
def existsCmd(command):
|
||||
if not mel.eval('exists "'+command+'"'):
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
## Hotkeys ##
|
||||
def getOrCreateCustomHotkeySet():
|
||||
allHotkey= cmds.hotkeySet( q=1, hsa=1 )
|
||||
currentHotkey = cmds.hotkeySet( q=1, current=1 )
|
||||
if currentHotkey == 'Maya_Default' and len(allHotkey)==1:
|
||||
cmds.hotkeySet( "MyHotkeySet", current=1 )
|
||||
elif currentHotkey == 'Maya_Default':
|
||||
for i in allHotkey:
|
||||
if i != "Maya_Default":
|
||||
cmds.hotkeySet(i,e=1,current=1)
|
||||
currentHotkey = cmds.hotkeySet( q=1, current=1 )
|
||||
return currentHotkey
|
||||
## Tranforms ##
|
||||
def freeze(sel):
|
||||
cmds.makeIdentity(sel,apply=True, t=1, r=1, s=1, n=0)
|
||||
def movePivotToCenter(sel):
|
||||
selBB = cmds.polyEvaluate(sel,b=1)
|
||||
selBB_diff_x = selBB[0][1]-selBB[0][0]
|
||||
selBB_diff_y = selBB[1][1]-selBB[1][0]
|
||||
selBB_diff_z = selBB[2][1]-selBB[2][0]
|
||||
selBB_center_x = (selBB[0][1]+selBB[0][0])/2
|
||||
selBB_center_y = (selBB[1][1]+selBB[1][0])/2
|
||||
selBB_center_z = (selBB[2][1]+selBB[2][0])/2
|
||||
cmds.move(selBB_center_x, selBB_center_y, selBB_center_z, sel+".scalePivot", sel+".rotatePivot", absolute=True)
|
||||
def moveObjToZero(sel):
|
||||
cmds.move(0,0,0,sel,rpr=1)
|
||||
## Select ##
|
||||
def ls( index="all", type = "transform" ):
|
||||
# if index is str and not indicating index, then we think this is a type and return all index
|
||||
if index != "all" and index != 0:
|
||||
type = index
|
||||
# specify each type
|
||||
if type == "transform":
|
||||
sel = cmds.ls( sl=1, type = type )
|
||||
elif type in ["poly","polygon","polymesh","mesh","obj"]:
|
||||
sel = cmds.ls (sl=1, type ="transform")
|
||||
sel = cmds.filterExpand(sel,sm=12)
|
||||
elif type in ["c","cr","curve","nurbsCurve"]:
|
||||
sel = cmds.ls (sl=1, type ="transform")
|
||||
sel = cmds.filterExpand(sel,sm=9)
|
||||
# specify index
|
||||
if index == "all" or isinstance(index,(str)):
|
||||
if sel == None:
|
||||
msg("Nothing Selected")
|
||||
return None
|
||||
return sel
|
||||
elif isinstance(index,(int)):
|
||||
if sel == None:
|
||||
msg("Nothing Selected")
|
||||
return None
|
||||
return sel[index]
|
||||
## Warning ##
|
||||
def msg(msg="Heads Up!"):
|
||||
global PM_startTime
|
||||
global PM_timeAdder
|
||||
PM_offset = 0
|
||||
PM_time = cmds.timerX(startTime = PM_startTime)
|
||||
PM_startTime = cmds.timerX()
|
||||
if PM_time<1.5:
|
||||
PM_offset = 10+20*(PM_timeAdder)
|
||||
PM_timeAdder = PM_timeAdder+1
|
||||
else:
|
||||
PM_offset = 10
|
||||
PM_timeAdder = 0
|
||||
if MAYA_version_float >= 2014:
|
||||
cmds.inViewMessage(
|
||||
amg = "<span style=\"color:#ffffff\">"+ msg +"</span>",
|
||||
fade = 1, fit = 150, fst = 800, fot = 150, fof = PM_offset, bkc = 0x2288ff,
|
||||
pos = "topCenter", fontSize = 10, a = 0, ta = 0.68)
|
||||
print(msg)
|
||||
# Get Mesh Info
|
||||
def get(sel,what):
|
||||
if what == "uvid_uv":
|
||||
selMesh = MFnMesh(sel)
|
||||
uvid_uv = []
|
||||
uvArray = selMesh.getUVs()
|
||||
for i in xrange(len(uvArray[0])):
|
||||
uvid_uv.append([uvArray[0][i],uvArray[1][i]])
|
||||
return uvid_uv
|
||||
elif what == "vtxid_uvid":
|
||||
vtxid_uvid = []
|
||||
selVtxIter = MItMeshVertex(sel)
|
||||
while not selVtxIter.isDone():
|
||||
vtxid_uvid.append(list(set(selVtxIter.getUVIndices()))[0])
|
||||
selVtxIter.next()
|
||||
return vtxid_uvid
|
||||
elif what == "bb":
|
||||
return cmds.polyEvaluate(sel,b=1)
|
||||
#API
|
||||
def getDagPath(sel):
|
||||
selList = om2.MSelectionList()
|
||||
selList.add(sel)
|
||||
selPath = selList.getDagPath(0)
|
||||
return selPath
|
||||
def MFnMesh(sel):
|
||||
selPath = getDagPath(sel)
|
||||
selMesh = om2.MFnMesh(selPath)
|
||||
return selMesh
|
||||
def MItMeshVertex(sel):
|
||||
selPath = getDagPath(sel)
|
||||
selVtxIter = om2.MItMeshVertex(selPath)
|
||||
return selVtxIter
|
||||
def MItMeshEdge(sel):
|
||||
selPath = getDagPath(sel)
|
||||
selEdgeIter = om2.MItMeshEdge(selPath)
|
||||
return selEdgeIter
|
||||
def MItMeshPolygon(sel):
|
||||
selPath = getDagPath(sel)
|
||||
selPolyIter = om2.MItMeshPolygon(selPath)
|
||||
return selPolyIter
|
||||
def MItMeshFaceVertex(sel):
|
||||
selPath = getDagPath(sel)
|
||||
selFaceIter = om2.MItMeshFaceVertex(selPath)
|
||||
return selFaceIter
|
||||
#UVSet
|
||||
def switchUVSet(sel):
|
||||
allUVSets = cmds.polyUVSet(sel,q=1,auv=1)
|
||||
if len(allUVSets) < 2:
|
||||
cmds.warning("Found less than 2 UV Sets!")
|
||||
return
|
||||
firstUVSetName = allUVSets[0]
|
||||
secondUVSetName = allUVSets[1]
|
||||
currentUVSetName = cmds.polyUVSet(q=1,cuv=1)
|
||||
otherUVSetName = list( set(allUVSets).difference(set(currentUVSetName)))
|
||||
try:
|
||||
cmds.polyUVSet(sel,uvs = firstUVSetName, nuv=secondUVSetName, reorder=1)
|
||||
except:
|
||||
newUVSet = cmds.polyUVSet(sel,cr=1)
|
||||
newUVSet = newUVSet[0]
|
||||
cmds.polyUVSet(sel, uvs = firstUVSetName ,nuv= newUVSet, cp=1)
|
||||
cmds.polyUVSet(sel, uvs = secondUVSetName ,nuv= firstUVSetName, cp=1)
|
||||
cmds.polyUVSet(sel, uvs = newUVSet ,nuv= secondUVSetName, cp=1)
|
||||
cmds.polyUVSet(sel, uvs = newUVSet, d=1)
|
||||
if currentUVSetName == firstUVSetName:
|
||||
cmds.polyUVSet(sel,uvs = secondUVSetName , e=1,cuv=1)
|
||||
else:
|
||||
cmds.polyUVSet(sel,uvs = firstUVSetName , e=1,cuv=1)
|
||||
cmds.polyUVSet(sel,currentUVSet=1,uvSet=otherUVSetName)
|
||||
#UI
|
||||
|
||||
def turnToolBtnOff(self,buttonName):
|
||||
pass
|
||||
windowName = self.window_name
|
||||
mainWindow = self.parentWidget()
|
||||
ldmt_mainWindow = mainWindow.findChild(QMainWindow,'LDMT_mainUI')
|
||||
exec("buttonObj = ldmt_mainWindow.ui.%s"%(buttonName))
|
||||
buttonObj.setStyleSheet('\
|
||||
QPushButton{\
|
||||
border:1px solid rgb(30,30,30);\
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(175, 175,175, 235), stop:1 rgba(235, 235, 235, 195));\
|
||||
color:rgb(25,25,25);}\
|
||||
QPushButton:hover{\
|
||||
border:1px solid rgb(30,30,30);\
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(190, 190, 190, 225), stop:1 rgba(245, 245, 245, 195));\
|
||||
color:rgb(25,25,25);\
|
||||
}\
|
||||
QPushButton:pressed{ \
|
||||
border:1px solid rgb(30,30,30);\
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(215, 215, 215, 195), stop:1 rgba(155, 155,155, 235));\
|
||||
color:rgb(25,25,25);}\
|
||||
')
|
||||
def showAttributeEditor():
|
||||
mel.eval("AttributeEditor;")
|
||||
def showChannelBox():
|
||||
mel.eval("AttributeEditor;")
|
||||
cmds.ShowAttributeEditorOrChannelBox()
|
||||
|
517
Scripts/Modeling/Edit/LDMT/ldmt_core/mayaMixin.py
Normal file
@ -0,0 +1,517 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Maya mixin classes to add common functionality for custom PyQt/PySide widgets in Maya.
|
||||
|
||||
* MayaQWidgetBaseMixin Mixin that should be applied to all custom QWidgets created for Maya
|
||||
to automatically handle setting the objectName and parenting
|
||||
|
||||
* MayaQWidgetDockableMixin Mixin that adds dockable capabilities within Maya controlled with
|
||||
the show() function
|
||||
"""
|
||||
|
||||
import uuid
|
||||
|
||||
from maya import cmds
|
||||
from maya import mel
|
||||
from maya import OpenMayaUI as omui
|
||||
|
||||
# Import available PySide or PyQt package, as it will work with both
|
||||
try:
|
||||
from Qt.QtCore import Qt, QPoint, QSize
|
||||
from Qt.QtCore import Signal
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtCompat import wrapInstance
|
||||
_qtImported = 'PySide'
|
||||
except ImportError, e1:
|
||||
try:
|
||||
from Qt.QtCore import Qt, QPoint, QSize
|
||||
from Qt.QtCore import pyqtSignal as Signal
|
||||
from Qt.QtGui import *
|
||||
from sip import wrapinstance as wrapInstance
|
||||
_qtImported = 'PyQt4'
|
||||
except ImportError, e2:
|
||||
raise ImportError, '%s, %s'%(e1,e2)
|
||||
|
||||
class MayaQWidgetBaseMixin(object):
|
||||
'''
|
||||
Handle common actions for Maya Qt widgets during initialization:
|
||||
* auto-naming a Widget so it can be looked up as a string through
|
||||
maya.OpenMayaUI.MQtUtil.findControl()
|
||||
* parenting the widget under the main maya window if no parent is explicitly
|
||||
specified so not to have the Window disappear when the instance variable
|
||||
goes out of scope
|
||||
|
||||
Integration Notes:
|
||||
Inheritance ordering: This class must be placed *BEFORE* the Qt class for proper execution
|
||||
This is needed to workaround a bug where PyQt/PySide does not call super() in its own __init__ functions
|
||||
|
||||
Example:
|
||||
class MyQWidget(MayaQWidgetBaseMixin, QPushButton):
|
||||
def __init__(self, parent=None):
|
||||
super(MyQWidget, self).__init__(parent=parent)
|
||||
self.setText('Push Me')
|
||||
myWidget = MyQWidget()
|
||||
myWidget.show()
|
||||
print myWidget.objectName()
|
||||
'''
|
||||
def __init__(self, parent=None, *args, **kwargs):
|
||||
super(MayaQWidgetBaseMixin, self).__init__(parent=parent, *args, **kwargs) # Init all baseclasses (including QWidget) of the main class
|
||||
self._initForMaya(parent=parent)
|
||||
|
||||
def _initForMaya(self, parent=None, *args, **kwargs):
|
||||
'''
|
||||
Handle the auto-parenting and auto-naming.
|
||||
|
||||
:Parameters:
|
||||
parent (string)
|
||||
Explicitly specify the QWidget parent. If 'None', then automatically
|
||||
parent under the main Maya window
|
||||
'''
|
||||
# Set parent to Maya main window if parent=None
|
||||
if parent == None:
|
||||
self._makeMayaStandaloneWindow()
|
||||
|
||||
# Set a unique object name string so Maya can easily look it up
|
||||
if self.objectName() == '':
|
||||
self.setObjectName('%s_%s'%(self.__class__.__name__, uuid.uuid4()))
|
||||
|
||||
def _makeMayaStandaloneWindow(self):
|
||||
'''Make a standalone window, though parented under Maya's mainWindow.
|
||||
The parenting under Maya's mainWindow is done so that the QWidget will not
|
||||
auto-destroy itself when the instance variable goes out of scope.
|
||||
'''
|
||||
origParent = self.parent()
|
||||
|
||||
# Parent under the main Maya window
|
||||
mainWindowPtr = omui.MQtUtil.mainWindow()
|
||||
mainWindow = wrapInstance(long(mainWindowPtr), QMainWindow)
|
||||
self.setParent(mainWindow)
|
||||
|
||||
# Make this widget appear as a standalone window even though it is parented
|
||||
if isinstance(self, QDockWidget):
|
||||
self.setWindowFlags(Qt.Dialog|Qt.FramelessWindowHint)
|
||||
else:
|
||||
self.setWindowFlags(Qt.Window)
|
||||
|
||||
# Delete the parent QDockWidget if applicable
|
||||
if isinstance(origParent, QDockWidget):
|
||||
origParent.close()
|
||||
|
||||
|
||||
class MayaQDockWidget(MayaQWidgetBaseMixin,QDockWidget):
|
||||
'''QDockWidget tailored for use with Maya.
|
||||
Mimics the behavior performed by Maya's internal QMayaDockWidget class and the dockControl command
|
||||
|
||||
:Signals:
|
||||
closeEventTriggered: emitted when a closeEvent occurs
|
||||
|
||||
:Known Issues:
|
||||
* Manually dragging the DockWidget to dock in the Main MayaWindow will have it resize to the 'sizeHint' size
|
||||
of the child widget() instead of preserving its existing size.
|
||||
'''
|
||||
# Custom Signals
|
||||
closeEventTriggered = Signal() # Qt Signal triggered when closeEvent occurs
|
||||
|
||||
def __init__(self, parent=None, *args, **kwargs):
|
||||
super(MayaQDockWidget, self).__init__(parent=parent, *args, **kwargs) # Init all baseclasses (including QWidget) of the main class
|
||||
|
||||
# == Mimic operations performed by Maya internal QmayaDockWidget ==
|
||||
self.setAttribute(Qt.WA_MacAlwaysShowToolWindow)
|
||||
|
||||
# WORKAROUND: The mainWindow.handleDockWidgetVisChange may not be present on some PyQt and PySide systems.
|
||||
# Handle case if it fails to connect to the attr.
|
||||
mainWindowPtr = omui.MQtUtil.mainWindow()
|
||||
mainWindow = wrapInstance(long(mainWindowPtr), QMainWindow)
|
||||
try:
|
||||
self.visibilityChanged.connect(mainWindow.handleDockWidgetVisChange)
|
||||
except AttributeError, e:
|
||||
# Error connecting visibilityChanged trigger to mainWindow.handleDockWidgetVisChange.
|
||||
# Falling back to using MEL command directly.
|
||||
mel.eval('evalDeferred("updateEditorToggleCheckboxes()")') # Currently mainWindow.handleDockWidgetVisChange only makes this updateEditorToggleCheckboxes call
|
||||
|
||||
def setArea(self, area):
|
||||
'''Set the docking area
|
||||
'''
|
||||
# Skip setting the area if no area value passed in
|
||||
if area == Qt.NoDockWidgetArea:
|
||||
return
|
||||
# Mimic operations performed by Maya dockControl command
|
||||
mainWindowPtr = omui.MQtUtil.mainWindow()
|
||||
mainWindow = wrapInstance(long(mainWindowPtr), QMainWindow)
|
||||
childrenList = mainWindow.children()
|
||||
foundDockWidgetToTab = False
|
||||
for child in childrenList:
|
||||
# Create Tabbed dock if a QDockWidget already at that area
|
||||
if (child != self) and (isinstance(child, QDockWidget)):
|
||||
if not child.isHidden() and not child.isFloating():
|
||||
if mainWindow.dockWidgetArea(child) == area:
|
||||
mainWindow.tabifyDockWidget(child, self)
|
||||
self.raise_()
|
||||
foundDockWidgetToTab = True
|
||||
break
|
||||
# If no other QDockWidget at that area, then just add it
|
||||
if not foundDockWidgetToTab:
|
||||
mainWindow.addDockWidget(area, self)
|
||||
|
||||
|
||||
def resizeEvent(self, evt):
|
||||
'''Store off the 'savedSize' property used by Maya's QMainWindow to set the
|
||||
size of the widget when it is being docked.
|
||||
'''
|
||||
self.setProperty('savedSize', self.size())
|
||||
return super(MayaQDockWidget, self).resizeEvent(evt)
|
||||
|
||||
def closeEvent(self, evt):
|
||||
'''Hide the QDockWidget and trigger the closeEventTriggered signal
|
||||
'''
|
||||
# Handle the standard closeEvent()
|
||||
super(MayaQDockWidget, self).closeEvent(evt)
|
||||
|
||||
if evt.isAccepted():
|
||||
# Force visibility to False
|
||||
self.setVisible(False) # since this does not seem to have happened already
|
||||
|
||||
# Emit that a close event is occurring
|
||||
self.closeEventTriggered.emit()
|
||||
|
||||
class MayaQWidgetDockableMixin(MayaQWidgetBaseMixin):
|
||||
'''
|
||||
Handle Maya dockable actions controlled with the show() function.
|
||||
|
||||
Integration Notes:
|
||||
Inheritance ordering: This class must be placed *BEFORE* the Qt class for proper execution
|
||||
This is needed to workaround a bug where PyQt/PySide does not call super() in its own __init__ functions
|
||||
|
||||
Example:
|
||||
class MyQWidget(MayaQWidgetDockableMixin, QPushButton):
|
||||
def __init__(self, parent=None):
|
||||
super(MyQWidget, self).__init__(parent=parent)
|
||||
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred )
|
||||
self.setText('Push Me')
|
||||
myWidget = MyQWidget()
|
||||
myWidget.show(dockable=True)
|
||||
myWidget.show(dockable=False)
|
||||
print myWidget.showRepr()
|
||||
'''
|
||||
def setDockableParameters(self, dockable=None, floating=None, area=None, allowedArea=None, width=None, height=None, x=None, y=None, *args, **kwargs):
|
||||
'''
|
||||
Set the dockable parameters.
|
||||
|
||||
:Parameters:
|
||||
dockable (bool)
|
||||
Specify if the window is dockable (default=False)
|
||||
floating (bool)
|
||||
Should the window be floating or docked (default=True)
|
||||
area (string)
|
||||
Default area to dock into (default='left')
|
||||
Options: 'top', 'left', 'right', 'bottom'
|
||||
allowedArea (string)
|
||||
Allowed dock areas (default='all')
|
||||
Options: 'top', 'left', 'right', 'bottom', 'all'
|
||||
width (int)
|
||||
Width of the window
|
||||
height (int)
|
||||
Height of the window
|
||||
x (int)
|
||||
left edge of the window
|
||||
y (int)
|
||||
top edge of the window
|
||||
|
||||
:See: show(), hide(), and setVisible()
|
||||
'''
|
||||
if (dockable == True) or (dockable == None and self.isDockable()): # == Handle docked window ==
|
||||
# Conversion parameters (used below)
|
||||
dockAreaStrMap = {
|
||||
'left' : Qt.LeftDockWidgetArea,
|
||||
'right' : Qt.RightDockWidgetArea,
|
||||
'top' : Qt.TopDockWidgetArea,
|
||||
'bottom' : Qt.BottomDockWidgetArea,
|
||||
'all' : Qt.AllDockWidgetAreas,
|
||||
'none' : Qt.NoDockWidgetArea, # Note: Not currently supported in maya dockControl command
|
||||
}
|
||||
|
||||
# Create dockControl (QDockWidget) if needed
|
||||
if dockable == True and not self.isDockable():
|
||||
# Retrieve original position and size
|
||||
# Position
|
||||
if x == None:
|
||||
x = self.x()
|
||||
if y == None:
|
||||
y = self.y()
|
||||
# Size
|
||||
unininitializedSize = QSize(640,480) # Hardcode: (640,480) is the default size for a QWidget
|
||||
if self.size() == unininitializedSize:
|
||||
# Get size from widget sizeHint if size not yet initialized (before the first show())
|
||||
widgetSizeHint = self.sizeHint()
|
||||
else:
|
||||
widgetSizeHint = self.size() # use the current size of the widget
|
||||
if width == None:
|
||||
width = widgetSizeHint.width()
|
||||
if height == None:
|
||||
height = widgetSizeHint.height()
|
||||
|
||||
# Create the QDockWidget
|
||||
dockWidget = MayaQDockWidget()
|
||||
dockWidget.setWindowTitle(self.windowTitle())
|
||||
dockWidget.setWidget(self)
|
||||
|
||||
# By default, when making dockable, make it floating
|
||||
# This addresses an issue on Windows with the window decorators
|
||||
# not showing up. Setting this here will cause setFloating() to be called below.
|
||||
if floating == None:
|
||||
floating = True
|
||||
|
||||
# Hook up signals
|
||||
dockWidget.topLevelChanged.connect(self.floatingChanged)
|
||||
dockWidget.closeEventTriggered.connect(self.dockCloseEventTriggered)
|
||||
else:
|
||||
if floating == True:
|
||||
# Retrieve original position (if floating)
|
||||
pos = self.parent().mapToGlobal( QPoint(0,0) )
|
||||
if x == None:
|
||||
x = pos.x()
|
||||
if y == None:
|
||||
y = pos.y()
|
||||
|
||||
# Retrieve original size
|
||||
if width == None:
|
||||
width = self.width()
|
||||
if height == None:
|
||||
height = self.height()
|
||||
|
||||
# Get dock widget identifier
|
||||
dockWidget = self.parent()
|
||||
|
||||
# Update dock values
|
||||
if area != None:
|
||||
areaValue = dockAreaStrMap.get(area, Qt.LeftDockWidgetArea)
|
||||
dockWidget.setArea(areaValue)
|
||||
if allowedArea != None:
|
||||
areaValue = dockAreaStrMap.get(allowedArea, Qt.AllDockWidgetAreas)
|
||||
dockWidget.setAllowedArea(areaValue)
|
||||
if floating != None:
|
||||
dockWidget.setFloating(floating)
|
||||
|
||||
# Position window
|
||||
if dockWidget.isFloating() and ((x != None) or (y != None)):
|
||||
dockPos = dockWidget.mapToGlobal( QPoint(0,0) )
|
||||
if x == None:
|
||||
x = dockPos.x()
|
||||
if y == None:
|
||||
y = dockPos.y()
|
||||
dockWidget.move(x,y)
|
||||
if (width != None) or (height != None):
|
||||
if width == None:
|
||||
width = self.width()
|
||||
if height == None:
|
||||
height = self.height()
|
||||
# Perform first resize on dock, determine delta with widget, and resize with that adjustment
|
||||
# Result: Keeps the content widget at the same size whether under the QDockWidget or a standalone window
|
||||
dockWidget.resize(width, height) # Size once to know the difference in the dockWidget to the targetSize
|
||||
dockWidgetSize = dockWidget.size() + QSize(width,height)-self.size() # find the delta and add it to the current dock size
|
||||
# Perform the final resize (call MayaQDockWidget.resize() which also sets the 'savedSize' property used for sizing when docking to the Maya MainWindow)
|
||||
dockWidget.resize(dockWidgetSize)
|
||||
|
||||
else: # == Handle Standalone Window ==
|
||||
# Make standalone as needed
|
||||
if dockable == False and self.isDockable():
|
||||
# Retrieve original position and size
|
||||
dockPos = self.parent().mapToGlobal( QPoint(0,0) )
|
||||
if x == None:
|
||||
x = dockPos.x()
|
||||
if y == None:
|
||||
y = dockPos.y()
|
||||
if width == None:
|
||||
width = self.width()
|
||||
if height == None:
|
||||
height = self.height()
|
||||
# Turn into a standalone window and reposition
|
||||
currentVisibility = self.isVisible()
|
||||
self._makeMayaStandaloneWindow() # Set the parent back to Maya and remove the parent dock widget
|
||||
self.setVisible(currentVisibility)
|
||||
|
||||
# Handle position and sizing
|
||||
if (width != None) or (height != None):
|
||||
if width == None:
|
||||
width = self.width()
|
||||
if height == None:
|
||||
height = self.height()
|
||||
self.resize(width, height)
|
||||
if (x != None) or (y != None):
|
||||
if x == None:
|
||||
x = self.x()
|
||||
if y == None:
|
||||
y = self.y()
|
||||
self.move(x,y)
|
||||
|
||||
def show(self, *args, **kwargs):
|
||||
'''
|
||||
Show the QWidget window. Overrides standard QWidget.show()
|
||||
|
||||
:See: setDockableParameters() for a list of parameters
|
||||
'''
|
||||
# Update the dockable parameters first (if supplied)
|
||||
if len(args) or len(kwargs):
|
||||
self.setDockableParameters(*args, **kwargs)
|
||||
|
||||
# Handle the standard setVisible() operation of show()
|
||||
QWidget.setVisible(self, True) # NOTE: Explicitly calling QWidget.setVisible() as using super() breaks in PySide: super(self.__class__, self).show()
|
||||
|
||||
# Handle special case if the parent is a QDockWidget (dockControl)
|
||||
parent = self.parent()
|
||||
if isinstance(parent, QDockWidget):
|
||||
parent.show()
|
||||
|
||||
def hide(self, *args, **kwargs):
|
||||
'''Hides the widget. Will hide the parent widget if it is a QDockWidget.
|
||||
Overrides standard QWidget.hide()
|
||||
'''
|
||||
# Update the dockable parameters first (if supplied)
|
||||
if len(args) or len(kwargs):
|
||||
self.setDockableParameters(*args, **kwargs)
|
||||
|
||||
# Handle special case if the parent is a QDockWidget (dockControl)
|
||||
parent = self.parent()
|
||||
if isinstance(parent, QDockWidget):
|
||||
parent.hide()
|
||||
|
||||
QWidget.setVisible(self, False) # NOTE: Explicitly calling QWidget.setVisible() as using super() breaks in PySide: super(self.__class__, self).show()
|
||||
|
||||
def setVisible(self, makeVisible, *args, **kwargs):
|
||||
'''
|
||||
Show/hide the QWidget window. Overrides standard QWidget.setVisible() to pass along additional arguments
|
||||
|
||||
:See: show() and hide()
|
||||
'''
|
||||
if (makeVisible == True):
|
||||
return self.show(*args, **kwargs)
|
||||
else:
|
||||
return self.hide(*args, **kwargs)
|
||||
|
||||
def raise_(self):
|
||||
'''Raises the widget to the top. Will raise the parent widget if it is a QDockWidget.
|
||||
Overrides standard QWidget.raise_()
|
||||
'''
|
||||
# Handle special case if the parent is a QDockWidget (dockControl)
|
||||
parent = self.parent()
|
||||
if isinstance(parent, QDockWidget):
|
||||
parent.raise_()
|
||||
else:
|
||||
QWidget.raise_(self) # NOTE: Explicitly using QWidget as using super() breaks in PySide: super(self.__class__, self).show()
|
||||
|
||||
def isDockable(self):
|
||||
'''Return if the widget is currently dockable (under a QDockWidget)
|
||||
|
||||
:Return: bool
|
||||
'''
|
||||
parent = self.parent()
|
||||
return isinstance(parent, QDockWidget)
|
||||
|
||||
def isFloating(self):
|
||||
'''Return if the widget is currently floating (under a QDockWidget)
|
||||
Will return True if is a standalone window OR is a floating dockable window.
|
||||
|
||||
:Return: bool
|
||||
'''
|
||||
parent = self.parent()
|
||||
if not isinstance(parent, QDockWidget):
|
||||
return True
|
||||
else:
|
||||
return parent.isFloating()
|
||||
|
||||
def floatingChanged(self, isFloating):
|
||||
'''Triggered when QDockWidget.topLevelChanged() signal is triggered.
|
||||
Stub function. Override to perform actions when this happens.
|
||||
'''
|
||||
pass
|
||||
|
||||
def dockCloseEventTriggered(self):
|
||||
'''Triggered when QDockWidget.closeEventTriggered() signal is triggered.
|
||||
Stub function. Override to perform actions when this happens.
|
||||
'''
|
||||
pass
|
||||
|
||||
def dockArea(self):
|
||||
'''Return area if the widget is currently docked to the Maya MainWindow
|
||||
Will return None if not dockable
|
||||
|
||||
:Return: str
|
||||
'''
|
||||
dockControlQt = self.parent()
|
||||
|
||||
if not isinstance(dockControlQt, QDockWidget):
|
||||
return None
|
||||
else:
|
||||
mainWindowPtr = omui.MQtUtil.mainWindow()
|
||||
mainWindow = wrapInstance(long(mainWindowPtr), QMainWindow)
|
||||
dockAreaMap = {
|
||||
Qt.LeftDockWidgetArea : 'left',
|
||||
Qt.RightDockWidgetArea : 'right',
|
||||
Qt.TopDockWidgetArea : 'top',
|
||||
Qt.BottomDockWidgetArea : 'bottom',
|
||||
Qt.AllDockWidgetAreas : 'all',
|
||||
Qt.NoDockWidgetArea : 'none', # Note: 'none' not supported in maya dockControl command
|
||||
}
|
||||
dockWidgetAreaBitmap = mainWindow.dockWidgetArea(dockControlQt)
|
||||
return dockAreaMap[dockWidgetAreaBitmap]
|
||||
|
||||
def setWindowTitle(self, val):
|
||||
'''Sets the QWidget's title and also it's parent QDockWidget's title if dockable.
|
||||
|
||||
:Return: None
|
||||
'''
|
||||
# Handle the standard setVisible() operation of show()
|
||||
QWidget.setWindowTitle(self, val) # NOTE: Explicitly calling QWidget.setWindowTitle() as using super() breaks in PySide: super(self.__class__, self).show()
|
||||
|
||||
# Handle special case if the parent is a QDockWidget (dockControl)
|
||||
parent = self.parent()
|
||||
if isinstance(parent, QDockWidget):
|
||||
parent.setWindowTitle(val)
|
||||
|
||||
def showRepr(self):
|
||||
'''Present a string of the parameters used to reproduce the current state of the
|
||||
widget used in the show() command.
|
||||
|
||||
:Return: str
|
||||
'''
|
||||
reprDict = {}
|
||||
reprDict['dockable'] = self.isDockable()
|
||||
reprDict['floating'] = self.isFloating()
|
||||
reprDict['area'] = self.dockArea()
|
||||
#reprDict['allowedArea'] = ??
|
||||
if reprDict['dockable'] == True:
|
||||
dockCtrl = self.parent()
|
||||
pos = dockCtrl.mapToGlobal( QPoint(0,0) )
|
||||
else:
|
||||
pos = self.pos()
|
||||
sz = self.geometry().size()
|
||||
reprDict['x'] = pos.x()
|
||||
reprDict['y'] = pos.y()
|
||||
reprDict['width'] = sz.width()
|
||||
reprDict['height'] = sz.height()
|
||||
|
||||
# Construct the repr show() string
|
||||
reprShowList = ['%s=%r'%(k,v) for k,v in reprDict.items() if v != None]
|
||||
reprShowStr = 'show(%s)'%(', '.join(reprShowList))
|
||||
return reprShowStr
|
||||
# Copyright (C) 1997-2014 Autodesk, Inc., and/or its licensors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# The coded instructions, statements, computer programs, and/or related
|
||||
# material (collectively the "Data") in these files contain unpublished
|
||||
# information proprietary to Autodesk, Inc. ("Autodesk") and/or its licensors,
|
||||
# which is protected by U.S. and Canadian federal copyright law and by
|
||||
# international treaties.
|
||||
#
|
||||
# The Data is provided for use exclusively by You. You have the right to use,
|
||||
# modify, and incorporate this Data into other products for purposes authorized
|
||||
# by the Autodesk software license agreement, without fee.
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. AUTODESK
|
||||
# DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTIES
|
||||
# INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF NON-INFRINGEMENT,
|
||||
# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, OR ARISING FROM A COURSE
|
||||
# OF DEALING, USAGE, OR TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS
|
||||
# LICENSORS BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
|
||||
# DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK AND/OR ITS
|
||||
# LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY OR PROBABILITY OF SUCH DAMAGES.
|
||||
|
26
Scripts/Modeling/Edit/LDMT/ldmt_deleteNamespace.py
Normal file
@ -0,0 +1,26 @@
|
||||
import maya.cmds as cmds
|
||||
def remove_namespaces(node):
|
||||
|
||||
try:
|
||||
# Note rsplit instead of split
|
||||
|
||||
namespace, name = node.rsplit(":", 1)
|
||||
except:
|
||||
namespace, name = None, node
|
||||
|
||||
if namespace:
|
||||
try:
|
||||
cmds.rename(node, name)
|
||||
except RuntimeError:
|
||||
# Can't remove namespaces from read-only nodes
|
||||
# E.g. namespaces embedded in references
|
||||
pass
|
||||
|
||||
def deleteNamespace():
|
||||
for node in cmds.ls(sl=1):
|
||||
# Remove namespaces of all children first
|
||||
for descendent in cmds.listRelatives(node, allDescendents=True):
|
||||
remove_namespaces(descendent)
|
||||
|
||||
# Finally, remove namespace from current selection
|
||||
remove_namespaces(node)
|
0
Scripts/Modeling/Edit/LDMT/ldmt_exe/__init__.py
Normal file
BIN
Scripts/Modeling/Edit/LDMT/ldmt_exe/instantMesh.exe
Normal file
2
Scripts/Modeling/Edit/LDMT/ldmt_exe/removePyc.bat
Normal file
@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
python removePyc.py %*
|
14
Scripts/Modeling/Edit/LDMT/ldmt_exe/removePyc.py
Normal file
@ -0,0 +1,14 @@
|
||||
import os
|
||||
def deleteFileWithFormat(targetFolder, targetFormat):
|
||||
for root,dirs,files in os.walk(targetFolder):
|
||||
for file in files:
|
||||
filePath = os.path.join(root,file)
|
||||
if filePath.endswith(targetFormat):
|
||||
os.remove(filePath)
|
||||
def run():
|
||||
getCurrentFolder = os.getcwd()
|
||||
targetFolder = getCurrentFolder[:-9]
|
||||
deleteFileWithFormat(targetFolder,'pyc')
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
47
Scripts/Modeling/Edit/LDMT/ldmt_fixReverse.py
Normal file
@ -0,0 +1,47 @@
|
||||
import maya.cmds as cmds
|
||||
|
||||
def fixReverse():
|
||||
cmds.undoInfo(ock = 1)
|
||||
sel = cmds.ls(sl=1)
|
||||
selAll = cmds.filterExpand(sel,sm=12)
|
||||
if selAll == None:
|
||||
cmds.warning("Nothing Selected!")
|
||||
return
|
||||
for sel_child in selAll:
|
||||
cmds.select(sel_child)
|
||||
fixReverseDo(sel_child)
|
||||
cmds.select(sel,r=1)
|
||||
cmds.undoInfo(cck = 1)
|
||||
|
||||
def dotProduct(vecA,vecB):
|
||||
return vecA[0]*vecB[0] + vecA[1]*vecB[1] + vecA[2]*vecB[2]
|
||||
|
||||
def fixReverseDo(sel_child):
|
||||
selShape = sel_child
|
||||
if len(selShape) == 0:
|
||||
print("Nothing Selected!")
|
||||
else:
|
||||
node_CPOM = cmds.createNode('closestPointOnMesh')
|
||||
selShape_worldMesh = selShape + '.worldMesh[0]'
|
||||
selShape_worldMatrix = selShape + '.worldMatrix[0]'
|
||||
node_CPOM_inMesh = node_CPOM + '.inMesh'
|
||||
node_CPOM_inPosition = node_CPOM + '.inPosition'
|
||||
node_CPOM_inputMatrix = node_CPOM + ".inputMatrix"
|
||||
node_CPOM_result_normal = node_CPOM + ".result.normal"
|
||||
node_CPOM_outPosition = node_CPOM + ".position"
|
||||
persp_translate = "persp.translate"
|
||||
|
||||
cmds.connectAttr(selShape_worldMesh, node_CPOM_inMesh ,force=1)
|
||||
cmds.connectAttr(persp_translate, node_CPOM_inPosition ,force=1)
|
||||
cmds.connectAttr(selShape_worldMatrix, node_CPOM_inputMatrix ,force=1)
|
||||
|
||||
vec_persp_pos = cmds.getAttr(persp_translate)
|
||||
vec_closestPoint_pos = cmds.getAttr(node_CPOM_outPosition)
|
||||
vec_closestPoint_normal = cmds.getAttr(node_CPOM_result_normal)
|
||||
vec_persp_closestPoint = [vec_persp_pos[0][0] - vec_closestPoint_pos[0][0],
|
||||
vec_persp_pos[0][1] - vec_closestPoint_pos[0][1],
|
||||
vec_persp_pos[0][2] - vec_closestPoint_pos[0][2]]
|
||||
dotProductResult = dotProduct(vec_persp_closestPoint,vec_closestPoint_normal[0])
|
||||
if dotProductResult<0:
|
||||
cmds.polyNormal(selShape,normalMode=0,userNormalMode=1,ch=1)
|
||||
cmds.delete(node_CPOM)
|
61
Scripts/Modeling/Edit/LDMT/ldmt_function/ldmt_loadUIFile.py
Normal file
@ -0,0 +1,61 @@
|
||||
#from Qt.QtCompat import wrapInstance, getCppPointer
|
||||
import os
|
||||
import maya.cmds as cmds
|
||||
import maya.OpenMayaUI as omui
|
||||
try:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtWidgets import *
|
||||
from Qt import __version__
|
||||
# from Qt.QtUiTools import QUiLoader
|
||||
from Qt.QtCompat import loadUi
|
||||
# from pyside2uic import *
|
||||
from Qt.QtCompat import wrapInstance
|
||||
|
||||
except ImportError:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
from Qt import __version__
|
||||
# from pysideuic import *
|
||||
from Qt.QtCompat import wrapInstance
|
||||
# from cStringIO import StringIO
|
||||
# import xml.etree.ElementTree as xml
|
||||
|
||||
def get_maya_window():
|
||||
|
||||
ptr = omui.MQtUtil.mainWindow()
|
||||
#if ptr is not None:
|
||||
return wrapInstance(int(ptr), QMainWindow)
|
||||
|
||||
def load_ui_file(ui_path):
|
||||
if not os.path.exists(ui_path):
|
||||
cmds.warning("UI file not found: {}".format(ui_path))
|
||||
return None
|
||||
# ui_file = QFile(ui_path)
|
||||
# ui_file.open(QFile.ReadOnly)
|
||||
# ui = loadUi(ui_file)
|
||||
ui = loadUi(ui_path)
|
||||
# ui = QUiLoader().load(ui_file)
|
||||
# ui_file.close()
|
||||
return ui
|
||||
|
||||
def load_ui_type(ui_path):
|
||||
pass
|
||||
# parsed = xml.parse(ui_file)
|
||||
# widget_class = parsed.find('widget').get('class')
|
||||
# form_class = parsed.find('class').text
|
||||
|
||||
# with open(ui_file,'r') as f:
|
||||
# o = StringIO()
|
||||
# frame = {}
|
||||
|
||||
# compileUi(f, o, indent = 0)
|
||||
# pyc = compile(o.getvalue(), '<string>', 'exec')
|
||||
# exec(pyc, frame)
|
||||
|
||||
|
||||
# # Fetch the base_class and form class based on their type in the xml from design
|
||||
# form_class = frame['Ui_{0}'.format(form_class)]
|
||||
# base_class = eval('{0}'.format(widget_class))
|
||||
|
||||
# return form_class, base_class
|
@ -0,0 +1,30 @@
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import maya.api.OpenMaya as om2
|
||||
|
||||
def morph2UV(baseObj):
|
||||
sel = baseObj
|
||||
selList = om2.MSelectionList()
|
||||
selList.add(sel)
|
||||
path = selList.getDagPath(0)
|
||||
myMesh = om2.MFnMesh(path)
|
||||
newPointArray = om2.MPointArray()
|
||||
space = om2.MSpace.kWorld
|
||||
myMesh_UVs = myMesh.getUVs()
|
||||
myMesh_points = myMesh.getPoints()
|
||||
# for i in range(myMesh.numVertices):
|
||||
myMesh_itVertex = om2.MItMeshVertex(path)
|
||||
points = om2.MPointArray()
|
||||
while not myMesh_itVertex.isDone():
|
||||
vertIndex = myMesh_itVertex.index()
|
||||
gotUV = myMesh_itVertex.getUV()
|
||||
point = om2.MPoint(gotUV[0],gotUV[1],0)
|
||||
points.append(point)
|
||||
myMesh_itVertex.next()
|
||||
myMesh.setPoints(points,space)
|
||||
|
||||
def runMorph2UV(sel):
|
||||
baseObjDup = cmds.duplicate(sel)
|
||||
baseObj = baseObjDup[0]
|
||||
morph2UV(baseObj)
|
||||
return baseObj
|
75
Scripts/Modeling/Edit/LDMT/ldmt_function/ldmt_morphToUV.py
Normal file
@ -0,0 +1,75 @@
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import maya.api.OpenMaya as om2
|
||||
def deleteNoUV():
|
||||
selObj = cmds.ls(sl=1,o=1)
|
||||
selObj = selObj[0]
|
||||
selObj_face = selObj + '.f[*]'
|
||||
selObj_face = cmds.ls(selObj_face, fl=1)
|
||||
selObj_uv = cmds.polyListComponentConversion(selObj,tuv=1)
|
||||
selObj_face_hasUV = cmds.polyListComponentConversion(selObj_uv,tf=1)
|
||||
selObj_face_hasUV = cmds.ls(selObj_face_hasUV, fl=1)
|
||||
selObj_face_noUV = list(set(selObj_face).difference(set(selObj_face_hasUV)))
|
||||
if selObj_face_noUV:
|
||||
cmds.delete(selObj_face_noUV)
|
||||
return selObj
|
||||
|
||||
def splitIfOnUVBorder():
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
sel = sel[0]
|
||||
selList = om2.MGlobal.getActiveSelectionList()
|
||||
selListPath = selList.getDagPath(0)
|
||||
vertIt = om2.MItMeshVertex(selListPath)
|
||||
selMesh = om2.MFnMesh(selListPath)
|
||||
vertIdToSplit = []
|
||||
vertToSplit = []
|
||||
while not vertIt.isDone():
|
||||
uvIndices = vertIt.getUVIndices()
|
||||
uvIndices = list(set(uvIndices))
|
||||
if len(uvIndices)>=2:
|
||||
vertIdToSplit.append(vertIt.index())
|
||||
elif vertIt.onBoundary():
|
||||
vertIdToSplit.append(vertIt.index())
|
||||
vertIt.next()
|
||||
for i in range(len(vertIdToSplit)):
|
||||
vertToSplit.append(sel+'.vtx['+str(vertIdToSplit[i])+']')
|
||||
cmds.polySplitVertex(vertToSplit,cch=0)
|
||||
|
||||
def morph2UV():
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
sel = sel[0]
|
||||
selList = om2.MSelectionList()
|
||||
selList.add(sel)
|
||||
path = selList.getDagPath(0)
|
||||
myMesh = om2.MFnMesh(path)
|
||||
newPointArray = om2.MPointArray()
|
||||
space = om2.MSpace.kWorld
|
||||
myMesh_UVs = myMesh.getUVs()
|
||||
myMesh_points = myMesh.getPoints()
|
||||
# for i in range(myMesh.numVertices):
|
||||
myMesh_itVertex = om2.MItMeshVertex(path)
|
||||
points = om2.MPointArray()
|
||||
while not myMesh_itVertex.isDone():
|
||||
vertIndex = myMesh_itVertex.index()
|
||||
gotUV = myMesh_itVertex.getUV()
|
||||
point = om2.MPoint(gotUV[0],gotUV[1],0)
|
||||
points.append(point)
|
||||
myMesh_itVertex.next()
|
||||
myMesh.setPoints(points,space)
|
||||
|
||||
def runMorph2UV():
|
||||
baseObj = cmds.ls(sl=1,o=1) #1. inputmesh
|
||||
baseObjDup = cmds.duplicate() #2. duplicate
|
||||
mel.eval("FreezeTransformations")
|
||||
mel.eval("ResetTransformations")
|
||||
baseObj = baseObjDup[0]
|
||||
deleteNoUV()
|
||||
splitIfOnUVBorder()
|
||||
morph2UV()
|
||||
currentSelection = cmds.ls(sl=1)
|
||||
currentUV = cmds.polyListComponentConversion(currentSelection,tuv=1)
|
||||
cmds.polyMergeVertex(currentSelection,d=0.0001,cch=0)
|
||||
cmds.polyMergeUV(currentUV,d=0.001,cch=0) # fix split
|
||||
cmds.select(baseObj,r=1)
|
||||
if __name__ == "__main__":
|
||||
runMorph2UV()
|
@ -0,0 +1,7 @@
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
def normalFacet():
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
cmds.polyNormalPerVertex(sel,ufn=1)
|
||||
mel.eval('SetToFaceNormals;')
|
||||
mel.eval('DeleteHistory;')
|
200
Scripts/Modeling/Edit/LDMT/ldmt_instantMeshes.py
Normal file
@ -0,0 +1,200 @@
|
||||
import os
|
||||
import math
|
||||
import tempfile
|
||||
from ldmt_function.ldmt_loadUIFile import get_maya_window
|
||||
import maya.OpenMayaUI as omui
|
||||
import subprocess
|
||||
from ldmt_core import ldmt_cmds as ld
|
||||
from functools import partial
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
|
||||
from Qt.QtWidgets import *
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtCore import *
|
||||
# from Qt.QtUiTools import QUiLoader
|
||||
from Qt.QtCompat import loadUi
|
||||
import maya.cmds as cmds
|
||||
|
||||
import maya.OpenMaya as om
|
||||
import maya.api.OpenMaya as om2
|
||||
import random
|
||||
import ast
|
||||
|
||||
LDMTPATH = ld.getPath('LDMT')
|
||||
ldmt_uifile = LDMTPATH + '/ldmt_ui/ldmt_instantMeshes.ui'
|
||||
# ldmt_list_form, ldmt_list_base = load_ui_type(ldmt_uifile)
|
||||
ldmt_window_name = 'ldmt_instantMeshes'
|
||||
ldmt_button_name = 'btn_'+ldmt_window_name.split('_')[1]
|
||||
|
||||
'''
|
||||
#Functions
|
||||
'''
|
||||
|
||||
'''
|
||||
#UI
|
||||
'''
|
||||
class ldmt_cls(QDialog):
|
||||
def __init__(self, parent = get_maya_window()):
|
||||
super(ldmt_cls, self).__init__(parent)
|
||||
self.window_name = ldmt_window_name
|
||||
self.setWindowTitle(ldmt_window_name)
|
||||
self.setWindowFlags(Qt.Window)
|
||||
# self.move(QCursor.pos() + QPoint(20,20))
|
||||
self.set_ui()
|
||||
self.setupBtn()
|
||||
|
||||
self.temp_dir = os.path.join(LDMTPATH, 'temp')
|
||||
if not os.path.exists(self.temp_dir):
|
||||
os.makedirs(self.temp_dir)
|
||||
# self.installStartBar()
|
||||
|
||||
def set_ui(self):
|
||||
main_layout = QVBoxLayout(self)
|
||||
|
||||
# ui_file = QFile(ldmt_uifile)
|
||||
# ui_file.open(QFile.ReadOnly)
|
||||
self.ui = loadUi(ldmt_uifile)
|
||||
# self.ui = QUiLoader().load(ui_file)
|
||||
# ui_file.close()
|
||||
|
||||
main_layout.addWidget(self.ui)
|
||||
|
||||
def setupBtn(self):
|
||||
self.ui.btn_sendToExe.clicked.connect(self.sendToExe)
|
||||
self.ui.btn_remesh.clicked.connect(self.remesh)
|
||||
self.ui.text_targetCount.setPlainText("50%")
|
||||
self.ui.statusbar.showMessage(ld.tag())
|
||||
|
||||
def sendToExe(self):
|
||||
sel=cmds.ls(sl=1,o=1)
|
||||
if sel == []:
|
||||
InstantMeshPath = LDMTPATH + "/ldmt_exe/instantMesh.exe"
|
||||
subprocess.Popen(InstantMeshPath,False)
|
||||
else:
|
||||
self.instantMeshes_sendToExe()
|
||||
|
||||
def remesh(self):
|
||||
targetCount = self.ui.text_targetCount.toPlainText()
|
||||
try:
|
||||
if targetCount.endswith('%'):
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
sel = sel[0]
|
||||
currentFaceCount = int(cmds.polyEvaluate(sel, f=True))
|
||||
targetCount = 2*int(currentFaceCount*float(targetCount[:-1])/100)
|
||||
else:
|
||||
targetCount = int(targetCount)
|
||||
except:
|
||||
ld.msg('Please input a number or ratio!')
|
||||
return
|
||||
self.instantMeshes_remesh(targetCount)
|
||||
|
||||
def instantMeshes_sendToExe(self,face_count=None):
|
||||
inst_mesh_path = LDMTPATH + "/ldmt_exe/instantMesh.exe"
|
||||
if not os.path.exists(inst_mesh_path):
|
||||
cmds.warning('Instant Mesh path not found!')
|
||||
return
|
||||
# Get current selection
|
||||
sel_obj = cmds.ls(sl=True)
|
||||
if sel_obj:
|
||||
print('Processing Instant Mesh...')
|
||||
# Create temp file for OBJ export
|
||||
# temp = tempfile.NamedTemporaryFile(prefix='instantMesh_', suffix='.obj', delete=False)
|
||||
# temp_path = temp.name
|
||||
|
||||
temp_file = os.path.join(self.temp_dir, 'instantMesh_temp.obj')
|
||||
# Make sure OBJexport is loaded
|
||||
if not cmds.pluginInfo('objExport', q=True, loaded=True):
|
||||
cmds.loadPlugin('objExport')
|
||||
# Save the currently selected object as an OBJ
|
||||
cmds.file(temp_file, force=True, exportSelected=True, type="OBJexport")
|
||||
# run instamesh command on the OBJ
|
||||
# print(temp_path)
|
||||
print("Instant Mesh start")
|
||||
some_command = inst_mesh_path + " " + temp_file
|
||||
p = subprocess.Popen(some_command, False)
|
||||
# temp.close()
|
||||
else:
|
||||
cmds.warning('No objects selected...')
|
||||
|
||||
def instantMeshes_remesh(self,face_count=None):
|
||||
inst_mesh_path = LDMTPATH + "/ldmt_exe/instantMesh.exe"
|
||||
if not os.path.exists(inst_mesh_path):
|
||||
cmds.warning('Instant Mesh path not found!')
|
||||
return
|
||||
# Get current selection
|
||||
sel_obj = cmds.ls(sl=True)
|
||||
if sel_obj:
|
||||
print('Processing Instant Mesh...')
|
||||
# if no polycount set just double the amount of the source object
|
||||
if not face_count:
|
||||
face_count = int(cmds.polyEvaluate(sel_obj, f=True))
|
||||
face_count *= 2
|
||||
face_count /= 2
|
||||
face_count = int(face_count)
|
||||
# Create temp file for OBJ export
|
||||
# temp = tempfile.NamedTemporaryFile(prefix='instantMesh_', suffix='.obj', delete=False)
|
||||
# temp_path = temp.name
|
||||
temp_path = os.path.join(self.temp_dir, 'instantMesh_temp.obj')
|
||||
# Make sure OBJexport is loaded
|
||||
if not cmds.pluginInfo('objExport', q=True, loaded=True):
|
||||
cmds.loadPlugin('objExport')
|
||||
# Save the currently selected object as an OBJ
|
||||
cmds.file(temp_path, force=True, exportSelected=True, type="OBJexport")
|
||||
# run instamesh command on the OBJ
|
||||
print(temp_path)
|
||||
print("Instant Mesh start")
|
||||
some_command = inst_mesh_path + " " + temp_path + " -o " + temp_path + " -f " + str(face_count) + " -D" + " -b"
|
||||
p = subprocess.Popen(some_command, stdout=subprocess.PIPE, shell=True)
|
||||
p.communicate()
|
||||
p.wait()
|
||||
print("Instant Mesh end")
|
||||
print(some_command)
|
||||
# import back the temp OBJ file
|
||||
returnedNodes = cmds.file(temp_path,
|
||||
i=True,
|
||||
type="OBJ",
|
||||
rnn=True,
|
||||
ignoreVersion=True,
|
||||
options="mo=1",
|
||||
loadReferenceDepth="all")
|
||||
# delete the temp file
|
||||
# temp.close()
|
||||
# Select the imported nodes
|
||||
if returnedNodes:
|
||||
cmds.select(returnedNodes, r=True)
|
||||
print("Instant Mesh done...")
|
||||
else:
|
||||
cmds.warning('No objects selected...')
|
||||
|
||||
def installStartBar(self):
|
||||
allQWidgets = self.ui.findChildren(QWidget)
|
||||
for i in allQWidgets:
|
||||
i.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, obj, event ):
|
||||
'''Connect signals on mouse over'''
|
||||
if event.type() == QEvent.Enter:
|
||||
self.oldMessage = ld.tag()
|
||||
# self.statusbar.showMessage(' '+obj.statusTip(),0)
|
||||
elif event.type() == QEvent.Leave:
|
||||
# self.statusbar.showMessage(' '+self.oldMessage, 0)
|
||||
pass
|
||||
event.accept()
|
||||
return False
|
||||
|
||||
def closeEvent(self,event):
|
||||
# ld.turnToolBtnOff(self,ldmt_button_name)
|
||||
cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
def ldmt_show():
|
||||
if cmds.window(ldmt_window_name, exists=True):
|
||||
cmds.deleteUI(ldmt_window_name)
|
||||
app = QApplication.instance()
|
||||
dialog = ldmt_cls(parent=app.activeWindow())
|
||||
|
||||
dialog.show()
|
||||
app.exec_()
|
||||
|
||||
if __name__ == '__main__':
|
||||
ldmt_show()
|
190
Scripts/Modeling/Edit/LDMT/ldmt_marvelousTool.py
Normal file
@ -0,0 +1,190 @@
|
||||
import os
|
||||
import math
|
||||
from ldmt_function.ldmt_loadUIFile import get_maya_window, load_ui_file
|
||||
import maya.OpenMayaUI as omui
|
||||
from ldmt_core import ldmt_cmds as ld
|
||||
from ldmt_function import ldmt_morphToUV
|
||||
from functools import partial
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
|
||||
try:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtWidgets import *
|
||||
# from Qt.QtUiTools import *
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import wrapInstance
|
||||
except ImportError:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtUiTools import *
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import wrapInstance
|
||||
|
||||
import maya.OpenMaya as om
|
||||
import maya.api.OpenMaya as om2
|
||||
import random
|
||||
import ast
|
||||
|
||||
LDMTPATH = ld.getPath('LDMT')
|
||||
ldmt_uifile = LDMTPATH + '/ldmt_ui/ldmt_marvelousTool.ui'
|
||||
ldmt_window_name = 'ldmt_marvelousTool'
|
||||
ldmt_button_name = 'btn_'+ldmt_window_name.split('_')[1]
|
||||
|
||||
'''
|
||||
#UI
|
||||
'''
|
||||
class ldmt_cls(QDialog):
|
||||
def __init__(self, parent = get_maya_window()):
|
||||
super(ldmt_cls, self).__init__(parent)
|
||||
self.window_name = ldmt_window_name
|
||||
self.setWindowTitle(ldmt_window_name)
|
||||
self.setWindowFlags(Qt.Window)
|
||||
self.move(QCursor.pos() + QPoint(50,-200))
|
||||
# update status bar so it's not only show in help line window.
|
||||
self.set_ui()
|
||||
self.setupBtn()
|
||||
# self.statusbar.showMessage(ld.tag())
|
||||
# self.installStartBar()
|
||||
|
||||
def set_ui(self):
|
||||
main_layout = QVBoxLayout(self)
|
||||
self.ui = load_ui_file(ldmt_uifile)
|
||||
if self.ui is None:
|
||||
cmds.warning("UI file not found: {}".format(ldmt_uifile))
|
||||
return
|
||||
main_layout.addWidget(self.ui)
|
||||
|
||||
def setupBtn(self):
|
||||
self.ui.btn_flatten.clicked.connect(self.flatten)
|
||||
self.ui.btn_makePairs.clicked.connect(self.makePairs)
|
||||
self.ui.slider_blendshape.setMinimum(0)
|
||||
self.ui.slider_blendshape.setMaximum(100)
|
||||
self.ui.slider_blendshape.valueChanged.connect(self.blendshapeChange)
|
||||
# self.btn_makePairs.clicked.connect(self.makePairs)
|
||||
|
||||
def blendshapeChange(self):
|
||||
blendValue = float(self.ui.slider_blendshape.value())/100
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
for i in sel:
|
||||
selShape = cmds.listRelatives(i,s=1)
|
||||
selShape = selShape[0]
|
||||
wrapNode = cmds.listConnections(selShape,type="wrap")
|
||||
wrapNode = wrapNode[0]
|
||||
wrapNode_connections = cmds.listConnections(wrapNode)
|
||||
patternName = ''
|
||||
for j in wrapNode_connections:
|
||||
if j.endswith('_pattern'):
|
||||
patternName = j
|
||||
patternShape = cmds.listRelatives(patternName,s=1)
|
||||
patternShape = patternShape[0]
|
||||
blendshapeNode = cmds.listConnections(patternShape,type="blendShape")
|
||||
cmds.blendShape(blendshapeNode,e=1,w=(0,blendValue))
|
||||
|
||||
def flatten(self):
|
||||
from ldmt_function import ldmt_morphToGarment
|
||||
from ldmt_function import ldmt_normalFacet
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
selBB = cmds.polyEvaluate(b=1)
|
||||
garmentHeight = selBB[1][1]-selBB[1][0]
|
||||
garmentMaxX = selBB[0][1]
|
||||
patternList = []
|
||||
for i in sel:
|
||||
pattern = ldmt_morphToGarment.runMorph2UV(i)
|
||||
newName = i+'_pattern'
|
||||
patternList.append(newName)
|
||||
cmds.rename(pattern,newName)
|
||||
cmds.select(patternList)
|
||||
ldmt_normalFacet.normalFacet()
|
||||
cmds.scale(garmentHeight,garmentHeight,garmentHeight)
|
||||
cmds.move(garmentMaxX,0,0)
|
||||
cmds.group(sel,n="ldmt_marvelousTool_garments#")
|
||||
cmds.group(patternList,n="ldmt_marvelousTool_patterns#")
|
||||
|
||||
def makePairs(self):
|
||||
sel = cmds.ls(os=1)
|
||||
garments = cmds.listRelatives(sel[0]) # len(garments)
|
||||
patterns = cmds.listRelatives(sel[1]) # len(patterns)
|
||||
retopos = cmds.listRelatives(sel[2]) # len(retopos)
|
||||
retopos_BB_width = {}
|
||||
retopos_BB_length = {}
|
||||
retopos_BB_center = {}
|
||||
patterns_BB_width = {}
|
||||
patterns_BB_length = {}
|
||||
patterns_BB_center = {}
|
||||
# In case that uv doesn't exists.
|
||||
cmds.select(retopos,r=1)
|
||||
mel.eval("performPolyAutoProj 0;")
|
||||
cmds.select(sel,r=1)
|
||||
# In case wrong bb
|
||||
for i in retopos:
|
||||
cmds.polyMergeVertex(i, d=0.001)
|
||||
# Matching
|
||||
for i in retopos:
|
||||
BB = cmds.polyEvaluate(i,b=1)
|
||||
retopos_BB_width[i] = BB[0][1] - BB[0][0]
|
||||
retopos_BB_length[i] = BB[1][1] - BB[1][0]
|
||||
retopos_BB_center[i] = [(BB[0][1] + BB[0][0])/2, (BB[1][1] + BB[1][0])/2]
|
||||
for i in patterns:
|
||||
BB = cmds.polyEvaluate(i,b=1)
|
||||
patterns_BB_width[i] = BB[0][1] - BB[0][0]
|
||||
patterns_BB_length[i] = BB[1][1] - BB[1][0]
|
||||
patterns_BB_center[i] = [(BB[0][1] + BB[0][0])/2, (BB[1][1] + BB[1][0])/2]
|
||||
pair_pattern_retopo={} # len(pair_pattern_retopo)
|
||||
for i in patterns:
|
||||
for j in retopos:
|
||||
if abs(patterns_BB_width[i] - retopos_BB_width[j]) < 1 \
|
||||
and abs(patterns_BB_length[i] - retopos_BB_length[j]) < 1 \
|
||||
and abs(patterns_BB_center[i][0] - retopos_BB_center[j][0]) < 1 \
|
||||
and abs(patterns_BB_center[i][1] - retopos_BB_center[j][1]) < 1:
|
||||
pair_pattern_retopo[i] = j
|
||||
for i in pair_pattern_retopo:
|
||||
cmds.transferAttributes(i,pair_pattern_retopo[i],transferUVs=1)
|
||||
for i in pair_pattern_retopo:
|
||||
cmds.select(pair_pattern_retopo[i],i,r=1)
|
||||
cmds.CreateWrap()
|
||||
for i in pair_pattern_retopo:
|
||||
pairGarment = i[:-8]
|
||||
pattern = i
|
||||
blendObjs = [pairGarment,pattern] # 0: target 1: origin
|
||||
blendName = cmds.blendShape(blendObjs,o='world',n='clothTransfer#')
|
||||
cmds.hide(sel[1])
|
||||
cmds.displaySurface(sel[0],x=1)
|
||||
cmds.hide(sel[1])
|
||||
cmds.displaySurface(sel[0],x=1)
|
||||
layerName = cmds.createDisplayLayer( n = "garment#", e=1)
|
||||
cmds.editDisplayLayerMembers(layerName,sel[0])
|
||||
cmds.setAttr(layerName+'.displayType',2)
|
||||
|
||||
def installStartBar(self):
|
||||
allQWidgets = self.findChildren(QWidget)
|
||||
for i in allQWidgets:
|
||||
i.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, obj, event ):
|
||||
'''Connect signals on mouse over'''
|
||||
if event.type() == QEvent.Enter:
|
||||
self.oldMessage = ld.tag()
|
||||
self.statusbar.showMessage(' '+obj.statusTip(),0)
|
||||
elif event.type() == QEvent.Leave:
|
||||
self.statusbar.showMessage(' '+self.oldMessage, 0)
|
||||
pass
|
||||
event.accept()
|
||||
return False
|
||||
|
||||
# def closeEvent(self,event):
|
||||
# ld.turnToolBtnOff(self,ldmt_button_name)
|
||||
# cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
def ldmt_show():
|
||||
if cmds.window(ldmt_window_name, exists=True):
|
||||
cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
app = QApplication.instance()
|
||||
dialog = ldmt_cls(parent=app.activeWindow())
|
||||
dialog.show()
|
||||
app.exec_()
|
||||
|
||||
if __name__ == '__main__':
|
||||
ldmt_show()
|
32
Scripts/Modeling/Edit/LDMT/ldmt_move2rdUVIn.py
Normal file
@ -0,0 +1,32 @@
|
||||
# About ####################
|
||||
# Auther: Liu Dian
|
||||
# Email: xgits@outlook.com
|
||||
# Website: www.xgits.com
|
||||
# Version: Pro
|
||||
# License: GPL
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import maya.api.OpenMaya as om2
|
||||
|
||||
def LD_move2rdUVIn():
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
sel = sel[0]
|
||||
selList = om2.MSelectionList()
|
||||
selList.add(sel)
|
||||
selPath = selList.getDagPath(0)
|
||||
selMesh = om2.MFnMesh(selPath)
|
||||
|
||||
uvid_uv = [] # generate {uvid:[u,v],} from MFnMesh.getUVs()
|
||||
uvNameIn2rd = []
|
||||
uvArray = selMesh.getUVs()
|
||||
|
||||
for i in range(len(uvArray[0])):
|
||||
uvid_uv.append([uvArray[0][i],uvArray[1][i]])
|
||||
|
||||
for uvid in range(len(uvid_uv)):
|
||||
u = uvid_uv[uvid][0]
|
||||
v = uvid_uv[uvid][1]
|
||||
if u>1 and u<2 and v>0 and v<1:
|
||||
uvNameIn2rd.append(sel+'.map['+str(uvid)+']')
|
||||
cmds.polyEditUV(uvNameIn2rd,u=-1)
|
||||
|
361
Scripts/Modeling/Edit/LDMT/ldmt_moveOverlapUVOut.py
Normal file
@ -0,0 +1,361 @@
|
||||
# About ####################
|
||||
# Auther: Liu Dian
|
||||
# Email: xgits@outlook.com
|
||||
# Website: www.xgits.com
|
||||
# Version: Pro
|
||||
# License: GPL
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import maya.api.OpenMaya as om2
|
||||
|
||||
def getUVEdgeDic(sel):
|
||||
selList = om2.MSelectionList()
|
||||
selList.add(sel)
|
||||
selPath = selList.getDagPath(0)
|
||||
selMesh = om2.MFnMesh(selPath)
|
||||
selVtxIter = om2.MItMeshVertex(selPath)
|
||||
selEdgeIter = om2.MItMeshEdge(selPath)
|
||||
selFaceIter = om2.MItMeshPolygon(selPath)
|
||||
|
||||
uvid_uv = [] # generate {uvid:[u,v],} from MFnMesh.getUVs()
|
||||
vtxid_uvid = []
|
||||
edgeid_vtxid = []
|
||||
edgeid_uvid = [] #edgeid_vtxid + vtxid_uvid
|
||||
faceid_edgeid = []
|
||||
faceid_uvid = []
|
||||
edgeid_faceid = [] #faceid_edgeid reverse
|
||||
uvedgeid_uvid = [] # get { uvedgeid: [uvid1, uvid2]} On border
|
||||
|
||||
uvid_usi = selMesh.getUvShellsIds()
|
||||
uvid_usi = uvid_usi[1]
|
||||
uvArray = selMesh.getUVs()
|
||||
for i in range(len(uvArray[0])):
|
||||
uvid_uv.append([uvArray[0][i],uvArray[1][i]])
|
||||
|
||||
while not selVtxIter.isDone(): #get {[[uvid1,uvid2],...]}
|
||||
vtxid_uvid.append(list(set(selVtxIter.getUVIndices())))
|
||||
selVtxIter.next()
|
||||
|
||||
while not selEdgeIter.isDone(): #get edge to vtx
|
||||
edgeid_vtxid.append([selEdgeIter.vertexId(0),selEdgeIter.vertexId(1)])
|
||||
edgeid_faceid.append(selEdgeIter.getConnectedFaces())
|
||||
selEdgeIter.next()
|
||||
|
||||
for edgeid in range(len(edgeid_vtxid)): # get {[[uvid1,uvid2,uvid3],...]}
|
||||
vtx0 = edgeid_vtxid[edgeid][0]
|
||||
vtx1 = edgeid_vtxid[edgeid][1]
|
||||
edgeid_uvid.append(vtxid_uvid[vtx0] + vtxid_uvid[vtx1])
|
||||
|
||||
while not selFaceIter.isDone(): # get {[[uvid1,uvid2,uvid3,uvid4], ...] }
|
||||
verts = selFaceIter.getVertices()
|
||||
faceid_edgeid.append(selFaceIter.getEdges())
|
||||
uvids=[]
|
||||
for index in range(len(verts)):
|
||||
uvids.append(selFaceIter.getUVIndex(index)) #necessary for uvedgeid
|
||||
faceid_uvid.append(uvids)
|
||||
selFaceIter.next()
|
||||
|
||||
for edgeid in range(len(edgeid_faceid)):
|
||||
faceids = edgeid_faceid[edgeid]
|
||||
edgeuvids = set(edgeid_uvid[edgeid])
|
||||
numface = len(faceids)
|
||||
if numface == 1: #OnBorder
|
||||
uvids = faceid_uvid[faceids[0]]
|
||||
uvedgeid_uvid.append(list(edgeuvids.intersection(uvids)))
|
||||
continue
|
||||
elif numface == 2:
|
||||
uvidsA = faceid_uvid[faceids[0]]
|
||||
uvidsB = faceid_uvid[faceids[1]]
|
||||
intersectUV = list(set(uvidsA).intersection(uvidsB))
|
||||
intersectUV_len = len(intersectUV)
|
||||
if intersectUV_len<2:
|
||||
if intersectUV_len == 1:
|
||||
for uv in edgeuvids:
|
||||
if uv == intersectUV[0]:
|
||||
continue
|
||||
elif uv in uvidsA or uv in uvidsB:
|
||||
uvedgeid_uvid.append([intersectUV[0],uv])
|
||||
else:
|
||||
uvedgeid_uvid.append(list(edgeuvids.intersection(uvidsA)))
|
||||
uvedgeid_uvid.append(list(edgeuvids.intersection(uvidsB)))
|
||||
else:
|
||||
print("Cleanup mesh first!!!!")
|
||||
|
||||
usi_uvid = {}
|
||||
usi_uvidOnBord = {}
|
||||
usi_uvedgeidOnBord = {}
|
||||
usi_bb = {}
|
||||
|
||||
for uvid in range(len(uvid_usi)):
|
||||
usi = uvid_usi[uvid]
|
||||
if usi in usi_uvid:
|
||||
usi_uvid[usi].append(uvid)
|
||||
else:
|
||||
usi_uvid[usi] = [uvid]
|
||||
|
||||
for uvedgeid in range(len(uvedgeid_uvid)):
|
||||
usi = uvid_usi[uvedgeid_uvid[uvedgeid][0]]
|
||||
if usi in usi_uvedgeidOnBord:
|
||||
usi_uvedgeidOnBord[usi].append(uvedgeid)
|
||||
else:
|
||||
usi_uvedgeidOnBord[usi] = [uvedgeid]
|
||||
|
||||
for usi in usi_uvedgeidOnBord:
|
||||
edgeids = usi_uvedgeidOnBord[usi]
|
||||
uvsOnBord = []
|
||||
for edgeid in edgeids:
|
||||
uvsOnBord = uvsOnBord + uvedgeid_uvid[edgeid]
|
||||
usi_uvidOnBord[usi] = list(set(uvsOnBord))
|
||||
|
||||
for usi in usi_uvidOnBord:
|
||||
umin = 1
|
||||
umax = 0
|
||||
vmin = 1
|
||||
vmax = 0
|
||||
for uvid in usi_uvidOnBord[usi]:
|
||||
u = uvid_uv[uvid][0]
|
||||
v = uvid_uv[uvid][1]
|
||||
if u < umin:
|
||||
umin = u
|
||||
if u > umax:
|
||||
umax = u
|
||||
if v < vmin:
|
||||
vmin = v
|
||||
if v > vmax:
|
||||
vmax = v
|
||||
usi_bb[usi] = [[umin,umax],[vmin,vmax]]
|
||||
|
||||
usi_bbarea = {}
|
||||
for i in range(len(usi_uvid)): #get {usi:area,} from usi_bb
|
||||
usi_bbarea[i] = abs(usi_bb[i][0][1]-usi_bb[i][0][0])*abs(usi_bb[i][1][1]-usi_bb[i][1][0])
|
||||
bbarea_usi = sorted(zip(usi_bbarea.values(), usi_bbarea.keys())) # get [(minarea, usi0),...,( maxarea, usi99)]
|
||||
bbarea_usi.reverse()
|
||||
|
||||
uvedgeid_uv = []
|
||||
for uvedgeid in range(len(uvedgeid_uvid)):
|
||||
uvidA = uvedgeid_uvid[uvedgeid][0]
|
||||
uvidB = uvedgeid_uvid[uvedgeid][1]
|
||||
uA = uvid_uv[uvidA][0]
|
||||
vA = uvid_uv[uvidA][1]
|
||||
uB = uvid_uv[uvidB][0]
|
||||
vB = uvid_uv[uvidB][1]
|
||||
if vB >= vA:
|
||||
uvedgeid_uv.append([[uA,vA],[uB,vB]])
|
||||
else:
|
||||
uvedgeid_uv.append([[uB,vB],[uA,vA]])
|
||||
|
||||
return usi_uvid, uvid_uv, uvedgeid_uvid, usi_uvidOnBord, usi_uvedgeidOnBord, usi_bb, bbarea_usi, uvedgeid_uv
|
||||
|
||||
def getFacesHaveNoUV(sel):
|
||||
selList = om2.MSelectionList()
|
||||
selList.add(sel)
|
||||
selPath = selList.getDagPath(0)
|
||||
selVtxIter= om2.MItMeshVertex(selPath)
|
||||
faceidWithNoUV = []
|
||||
faceWithNoUV = []
|
||||
while not selVtxIter.isDone():
|
||||
if selVtxIter.numUVs()==0:
|
||||
faceidWithNoUV += selVtxIter.getConnectedFaces()
|
||||
selVtxIter.next()
|
||||
faceidWithNoUV = list(set(faceidWithNoUV))
|
||||
for faceid in faceidWithNoUV:
|
||||
faceWithNoUV+=[sel+'.f['+str(faceid)+']']
|
||||
return faceWithNoUV
|
||||
|
||||
def LD_moveOverlapUVOut():
|
||||
TOLERANCE = 4
|
||||
p = 10**TOLERANCE
|
||||
selForSkip = cmds.ls(sl=1)
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
if selForSkip != sel:
|
||||
selUVidForSkip = cmds.polyListComponentConversion(selForSkip,tuv=1)
|
||||
selUVidForSkip = cmds.ls(selUVidForSkip,fl=1)
|
||||
else:
|
||||
selUVidForSkip = []
|
||||
sel = sel[0]
|
||||
faceWithNoUV = getFacesHaveNoUV(sel)
|
||||
if faceWithNoUV !=[]:
|
||||
cmds.select(faceWithNoUV,r=1)
|
||||
cmds.warning("Some Faces have no UVs!")
|
||||
return
|
||||
uvDic = getUVEdgeDic(sel) #uvDic[6]
|
||||
usi_uvid = uvDic[0]
|
||||
uvid_uv = uvDic[1]
|
||||
uvedgeid_uvid = uvDic[2]
|
||||
usi_uvidOnBord = uvDic[3]
|
||||
usi_uvedgeidOnBord = uvDic[4]
|
||||
usi_bb = uvDic[5]
|
||||
bbarea_usi = uvDic[6]
|
||||
uvedgeid_uv = uvDic[7]
|
||||
|
||||
uvNotPass = set() # len(uvNotPass) Filter out stacking uvs
|
||||
skipList = set()
|
||||
skipListTemp = set()
|
||||
intersectList = {}
|
||||
usi_uvidOnBord_final = {}
|
||||
usi_uvedgeidOnBord_final = {}
|
||||
|
||||
# prepare
|
||||
for tupleA in range(len(bbarea_usi)):
|
||||
usiA = bbarea_usi[tupleA][1]
|
||||
skipListTemp.add(usiA)
|
||||
|
||||
# fast detect
|
||||
bb_uminA = usi_bb[usiA][0][0]
|
||||
bb_umaxA = usi_bb[usiA][0][1]
|
||||
bb_vminA = usi_bb[usiA][1][0]
|
||||
bb_vmaxA = usi_bb[usiA][1][1]
|
||||
|
||||
for tupleB in range(len(bbarea_usi)):
|
||||
usiB = bbarea_usi[tupleB][1]
|
||||
|
||||
#faset detect
|
||||
bb_uminB = usi_bb[usiB][0][0]
|
||||
bb_umaxB = usi_bb[usiB][0][1]
|
||||
bb_vminB = usi_bb[usiB][1][0]
|
||||
bb_vmaxB = usi_bb[usiB][1][1]
|
||||
if bb_uminA > bb_umaxB or\
|
||||
bb_umaxA < bb_uminB or\
|
||||
bb_vminA > bb_vmaxB or\
|
||||
bb_vmaxA < bb_vminB :
|
||||
continue
|
||||
|
||||
if usiB in skipListTemp:
|
||||
continue
|
||||
if ifUVShellStack(usiA,usiB,usi_bb, p):
|
||||
skipList.add(usiB)
|
||||
skipListTemp.add(usiB)
|
||||
elif ifUVShellIntersect(usiA,usiB,usi_uvedgeidOnBord, uvedgeid_uv, usi_bb):
|
||||
skipListTemp.add(usiB)
|
||||
skipList.add(usiB)
|
||||
elif ifUVShellContain(usiA,usiB,usi_uvedgeidOnBord,usi_uvidOnBord,uvedgeid_uv,uvid_uv):
|
||||
skipList.add(usiB)
|
||||
skipListTemp.add(usiB)
|
||||
|
||||
uvidsToMove = []
|
||||
uvNamesToMove = set()
|
||||
# calculate final list
|
||||
for usi in skipList:
|
||||
uvidsToMove += usi_uvid[usi]
|
||||
|
||||
for uvid in uvidsToMove:
|
||||
uvNamesToMove.add(sel+'.map['+str(uvid)+']')
|
||||
|
||||
for index in range(len(selUVidForSkip)):
|
||||
selUVidForSkip[index] = selUVidForSkip[index].split('.')[1]
|
||||
|
||||
selToSkip = []
|
||||
for uvName in uvNamesToMove:
|
||||
uvName = uvName.split('.')[1]
|
||||
if uvName in selUVidForSkip:
|
||||
selToSkip.append(sel+'.'+uvName)
|
||||
for i in selToSkip:
|
||||
uvNamesToMove.remove(i)
|
||||
|
||||
uvNamesToMove = list(uvNamesToMove)
|
||||
cmds.polyEditUV(uvNamesToMove,u=1)
|
||||
|
||||
def ifUVShellStack(usiA, usiB, usi_bb, p):
|
||||
# fast filter
|
||||
usiA_umin = float(int(usi_bb[usiA][0][0] * p))/p
|
||||
usiB_umin = float(int(usi_bb[usiB][0][0] * p))/p
|
||||
if not usiA_umin == usiB_umin:
|
||||
return 0
|
||||
|
||||
usiA_umax = float(int(usi_bb[usiA][0][1] * p))/p
|
||||
usiA_vmin = float(int(usi_bb[usiA][1][0] * p))/p
|
||||
usiA_vmax = float(int(usi_bb[usiA][1][1] * p))/p
|
||||
usiB_umax = float(int(usi_bb[usiB][0][1] * p))/p
|
||||
usiB_vmin = float(int(usi_bb[usiB][1][0] * p))/p
|
||||
usiB_vmax = float(int(usi_bb[usiB][1][1] * p))/p
|
||||
if usiA_umax == usiB_umax and usiA_vmin == usiB_vmin and usiA_vmax == usiB_vmax:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def ifUVShellIntersect(usiA, usiB, usi_uvedgeidOnBord, uvedgeid_uv, usi_bb):
|
||||
for edgeA in usi_uvedgeidOnBord[usiA]:
|
||||
ax = uvedgeid_uv[edgeA][0][0]
|
||||
ay = uvedgeid_uv[edgeA][0][1]
|
||||
bx = uvedgeid_uv[edgeA][1][0]
|
||||
by = uvedgeid_uv[edgeA][1][1]
|
||||
|
||||
# fast filter
|
||||
abx = [ax,bx]
|
||||
aby = [ay,by]
|
||||
if bx<ax:
|
||||
abx = [bx,ax]
|
||||
if by<ay:
|
||||
aby = [by,ay]
|
||||
|
||||
# bound filter
|
||||
bb_usiB = usi_bb[usiB]
|
||||
bb_uminB = bb_usiB[0][0]
|
||||
bb_umaxB = bb_usiB[0][1]
|
||||
bb_vminB = bb_usiB[1][0]
|
||||
bb_vmaxB = bb_usiB[1][1]
|
||||
|
||||
if abx[0] > bb_umaxB or\
|
||||
abx[1] < bb_uminB or\
|
||||
aby[0] > bb_vmaxB or\
|
||||
aby[1] < bb_vminB:
|
||||
continue
|
||||
|
||||
for edgeB in usi_uvedgeidOnBord[usiB]:
|
||||
cx = uvedgeid_uv[edgeB][0][0]
|
||||
cy = uvedgeid_uv[edgeB][0][1]
|
||||
dx = uvedgeid_uv[edgeB][1][0]
|
||||
dy = uvedgeid_uv[edgeB][1][1]
|
||||
|
||||
# fast filter
|
||||
cdx = [cx,dx]
|
||||
cdy = [cy,dy]
|
||||
if dx<cx:
|
||||
cdx = [dx,cx]
|
||||
if dy<cy:
|
||||
cdy = [dy,cy]
|
||||
if abx[0] > cdx[1] or\
|
||||
abx[1] < cdx[0] or\
|
||||
aby[0] > cdy[1] or\
|
||||
aby[1] < cdy[0]:
|
||||
continue
|
||||
|
||||
x1 = bx - ax
|
||||
y1 = by - ay
|
||||
x2 = cx - ax
|
||||
y2 = cy - ay
|
||||
cross1 = x1*y2-x2*y1
|
||||
x2 = dx - ax
|
||||
y2 = dy - ay
|
||||
cross2 = x1*y2-x2*y1
|
||||
x1 = dx - cx
|
||||
y1 = dy - cy
|
||||
x2 = ax - cx
|
||||
y2 = ay - cy
|
||||
cross3 = x1*y2-x2*y1
|
||||
x2 = bx - cx
|
||||
y2 = by - cy
|
||||
cross4 = x1*y2-x2*y1
|
||||
if (cross1*cross2 <= 0 and cross3*cross4<=0):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def ifUVShellContain(usiA,usiB,usi_uvedgeidOnBord,usi_uvidOnBord,uvedgeid_uv,uvid_uv):
|
||||
uvid = usi_uvidOnBord[usiB][0]
|
||||
intersects = 0
|
||||
ray_u = uvid_uv[uvid][0]
|
||||
ray_v = uvid_uv[uvid][1]
|
||||
|
||||
for edge in usi_uvedgeidOnBord[usiA]:
|
||||
u0 = uvedgeid_uv[edge][0][0]
|
||||
v0 = uvedgeid_uv[edge][0][1]
|
||||
u1 = uvedgeid_uv[edge][1][0]
|
||||
v1 = uvedgeid_uv[edge][1][1]
|
||||
if (v1 >= ray_v and v0 < ray_v):
|
||||
if ((u0-ray_u)*(v1-ray_v)-(v0-ray_v)*(u1-ray_u)) < 0:
|
||||
intersects += 1
|
||||
if intersects%2 == 1:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# checkUVBleed_0()
|
||||
# cProfile.run('checkUVBleed_0()')
|
11
Scripts/Modeling/Edit/LDMT/ldmt_normalFacet.py
Normal file
@ -0,0 +1,11 @@
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
def normalFacet():
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
if not sel:
|
||||
cmds.warning("No selected objects")
|
||||
return
|
||||
cmds.polyNormalPerVertex(sel,ufn=1)
|
||||
mel.eval('SetToFaceNormals;')
|
||||
mel.eval('DeleteHistory;')
|
||||
|
139
Scripts/Modeling/Edit/LDMT/ldmt_quickExport.py
Normal file
@ -0,0 +1,139 @@
|
||||
import os
|
||||
import math
|
||||
from ldmt_function.ldmt_loadUIFile import get_maya_window, load_ui_file
|
||||
import maya.OpenMayaUI as omui
|
||||
from ldmt_core import ldmt_cmds as ld
|
||||
from functools import partial
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
from functools import partial
|
||||
|
||||
try:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
from Qt.QtWidgets import *
|
||||
# from Qt.QtUiTools import *
|
||||
# from Qt.QtCompat import loadUi
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import wrapInstance
|
||||
except ImportError:
|
||||
from Qt.QtCore import *
|
||||
from Qt.QtGui import *
|
||||
# from Qt.QtUiTools import *
|
||||
from Qt import __version__
|
||||
from Qt.QtCompat import wrapInstance
|
||||
|
||||
import maya.OpenMaya as om
|
||||
import maya.api.OpenMaya as om2
|
||||
import random
|
||||
import ast
|
||||
|
||||
LDMTPATH = ld.getPath('LDMT')
|
||||
ldmt_uifile = LDMTPATH + '/ldmt_ui/ldmt_quickExport.ui'
|
||||
ldmt_window_name = 'ldmt_quickExport'
|
||||
ldmt_button_name = 'btn_'+ldmt_window_name.split('_')[1]
|
||||
|
||||
'''
|
||||
#UI
|
||||
'''
|
||||
class ldmt_cls(QDialog):
|
||||
def __init__(self, parent = get_maya_window()):
|
||||
super(ldmt_cls, self).__init__(parent)
|
||||
self.window_name = ldmt_window_name
|
||||
self.setWindowTitle(ldmt_window_name)
|
||||
self.setWindowFlags(Qt.Window)
|
||||
self.set_ui()
|
||||
# self.setupUi(self)
|
||||
self.move(QCursor.pos() + QPoint(20,20))
|
||||
# update status bar so it's not only show in help line window.
|
||||
self.setupBtn()
|
||||
# self.statusbar.showMessage(ld.tag())
|
||||
# self.installStartBar()
|
||||
|
||||
def set_ui(self):
|
||||
main_layout = QVBoxLayout(self)
|
||||
self.ui = load_ui_file(ldmt_uifile)
|
||||
main_layout.addWidget(self.ui)
|
||||
|
||||
def setupBtn(self):
|
||||
self.ui.btn_export.clicked.connect(self.quickExport)
|
||||
|
||||
def quickExport(self):
|
||||
currentFormat = self.ui.box_format.currentText()
|
||||
print(currentFormat)
|
||||
fullpath = cmds.file(query=1 ,location=1)
|
||||
|
||||
if fullpath == 'unknown':
|
||||
mayaPath = mel.eval('getenv MAYA_LOCATION')
|
||||
exportPath = mayaPath+'/bin/'
|
||||
else:
|
||||
filename = cmds.file(q=1,sn=1,shn=1)
|
||||
exportPath = fullpath[:-len(filename)]
|
||||
# subprocess.Popen('explorer '+ filepath +'', shell=True)
|
||||
sel = cmds.ls(sl=1,o=1)
|
||||
exportFolder = exportPath+"models/"
|
||||
if not os.path.exists(exportFolder):
|
||||
os.mkdir(exportFolder)
|
||||
exportName = exportFolder+sel[0].split('|')[-1]
|
||||
|
||||
if currentFormat == 'OBJ':
|
||||
if cmds.pluginInfo("objExport",q=1,l=1) !=1:
|
||||
cmds.loadPlugin("objExport",qt=1)
|
||||
cmds.file(exportName, force=True, options='groups=1;ptgroups=1;materials=1;smoothing=1;normals=1', type='OBJexport', pr=True, es=True)
|
||||
|
||||
elif currentFormat == 'FBX':
|
||||
if cmds.pluginInfo("fbxmaya",q=1,l=1) !=1:
|
||||
cmds.loadPlugin("fbxmaya",qt=1)
|
||||
fbxExportName = exportName+".fbx"
|
||||
mel.eval('FBXExportScaleFactor 1;')
|
||||
mel.eval('FBXExportInAscii -v 1;')
|
||||
mel.eval('FBXExportSmoothingGroups -v 1;')
|
||||
mel.eval('FBXExportSmoothMesh -v 1;')
|
||||
mel.eval('FBXExportTriangulate -v 0;')
|
||||
mel.eval('FBXExportUpAxis y;')
|
||||
mel.eval('FBXExport -f "'+ fbxExportName +'" -s;')
|
||||
|
||||
elif currentFormat == 'UE4':
|
||||
if cmds.pluginInfo("fbxmaya",q=1,l=1) !=1:
|
||||
cmds.loadPlugin("fbxmaya",qt=1)
|
||||
fbxExportName = exportName+".fbx"
|
||||
mel.eval('FBXExportScaleFactor 1;')
|
||||
mel.eval('FBXExportInAscii -v 1;')
|
||||
mel.eval('FBXExportSmoothingGroups -v 1;')
|
||||
mel.eval('FBXExportSmoothMesh -v 1;')
|
||||
mel.eval('FBXExportTriangulate -v 1;')
|
||||
mel.eval('FBXExportUpAxis y;')
|
||||
mel.eval('FBXExport -f "'+ fbxExportName +'" -s;')
|
||||
mel.eval('system("load '+ exportFolder +'");')
|
||||
|
||||
def installStartBar(self):
|
||||
allQWidgets = self.findChildren(QWidget)
|
||||
for i in allQWidgets:
|
||||
i.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, obj, event ):
|
||||
'''Connect signals on mouse over'''
|
||||
if event.type() == QEvent.Enter:
|
||||
self.oldMessage = ld.tag()
|
||||
self.statusbar.showMessage(' '+obj.statusTip(),0)
|
||||
elif event.type() == QEvent.Leave:
|
||||
self.statusbar.showMessage(' '+self.oldMessage, 0)
|
||||
pass
|
||||
event.accept()
|
||||
return False
|
||||
|
||||
# def closeEvent(self,event):
|
||||
# ld.turnToolBtnOff(self,ldmt_button_name)
|
||||
# cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
def ldmt_show():
|
||||
if cmds.window(ldmt_window_name, exists=True):
|
||||
cmds.deleteUI(ldmt_window_name)
|
||||
|
||||
app = QApplication.instance()
|
||||
dialog = ldmt_cls(parent=app.activeWindow())
|
||||
dialog.show()
|
||||
app.exec_()
|
||||
|
||||
if __name__ == '__main__':
|
||||
ldmt_show()
|
288
Scripts/Modeling/Edit/LDMT/ldmt_ui/ldmt_checkUVBleed.ui
Normal file
@ -0,0 +1,288 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ldmt_checkUVBleed</class>
|
||||
<widget class="QMainWindow" name="ldmt_checkUVBleed">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>200</width>
|
||||
<height>120</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>120</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>120</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Check UV Bleed</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QMainWindow{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QWidget#centralwidget{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
|
||||
QRadioButton{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
QCheckBox{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar QLabel {
|
||||
border: 3px solid white;
|
||||
}
|
||||
|
||||
QStatusBar {
|
||||
color: rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar::item {
|
||||
border: 1px solid red;
|
||||
border-radius: 3px;
|
||||
}</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="statusTip">
|
||||
<string>Input group count.</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color:rgb(200,200,200);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Shell Bleed ( pixel )</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="text_shellBleed">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Set shell to shell min distance.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border-radius: 4px;
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(30,30,30, 215);
|
||||
color:rgb(175,175,175);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>32</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="btn_check">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Check if uv bleed is too short.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="statusTip">
|
||||
<string>Input group count.</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color:rgb(200,200,200);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Border Bleed ( pixel )</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="text_borderBleed">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Set shell to border min distance.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border-radius: 4px;
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(30,30,30, 215);
|
||||
color:rgb(175,175,175);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>16</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="statusTip">
|
||||
<string>Input group count.</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color:rgb(200,200,200);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Texture Size</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="box_textureSize">
|
||||
<property name="statusTip">
|
||||
<string>Choose texture size.</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4096</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2048</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1024</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>512</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar">
|
||||
<property name="statusTip">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
212
Scripts/Modeling/Edit/LDMT/ldmt_ui/ldmt_cleanMesh.ui
Normal file
@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ldmt_cleanMesh</class>
|
||||
<widget class="QMainWindow" name="ldmt_cleanMesh">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>180</width>
|
||||
<height>90</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>180</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>180</width>
|
||||
<height>120</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Clean Mesh</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QMainWindow{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QWidget#centralwidget{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
|
||||
QRadioButton{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
QCheckBox{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar QLabel {
|
||||
border: 3px solid white;
|
||||
}
|
||||
|
||||
QStatusBar {
|
||||
color: rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar::item {
|
||||
border: 1px solid red;
|
||||
border-radius: 3px;
|
||||
}</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>180</width>
|
||||
<height>70</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>180</width>
|
||||
<height>70</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="btn_cleanup">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Cleanup n edge or sharing edges.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cleanup Matching Polygons</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="btn_select">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Select n edge or sharing edges.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select Matching Polygons</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar">
|
||||
<property name="statusTip">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
480
Scripts/Modeling/Edit/LDMT/ldmt_ui/ldmt_clothTransfer.ui
Normal file
@ -0,0 +1,480 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ldmt_clothTransfer</class>
|
||||
<widget class="QMainWindow" name="ldmt_clothTransfer">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>200</width>
|
||||
<height>130</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>130</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Cloth Transfer Tool</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QMainWindow{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QWidget#centralwidget{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
|
||||
QRadioButton{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
QCheckBox{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar QLabel {
|
||||
border: 3px solid white;
|
||||
}
|
||||
|
||||
QStatusBar {
|
||||
color: rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar::item {
|
||||
border: 1px solid red;
|
||||
border-radius: 3px;
|
||||
}</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="btn_setCloth">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Set cloth you want to transfer.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set Cloth</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="btn_setOrigin">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Set origin body.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set Origin</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QPushButton" name="btn_setTarget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Set target body.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set Target</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="btn_selectTarget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Click to select the faces you set as target.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(30, 30, 30, 215);
|
||||
color:rgb(175,175,175);}
|
||||
|
||||
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(75,75,75, 215);
|
||||
color:rgb(175,175,175);}
|
||||
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(100,100,100, 215);
|
||||
color:rgb(175,175,175);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="btn_selectOrigin">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Click to select the verts you set as target.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(30, 30, 30, 215);
|
||||
color:rgb(175,175,175);}
|
||||
|
||||
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(75,75,75, 215);
|
||||
color:rgb(175,175,175);}
|
||||
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(100,100,100, 215);
|
||||
color:rgb(175,175,175);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="btn_selectCloth">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Click to select the meshes you set as base mesh.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(30, 30, 30, 215);
|
||||
color:rgb(175,175,175);}
|
||||
|
||||
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(75,75,75, 215);
|
||||
color:rgb(175,175,175);}
|
||||
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(100,100,100, 215);
|
||||
color:rgb(175,175,175);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="btn_transfer">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Click to run the trasferring process.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">*.QPushButton{
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(135, 135, 135, 225), stop:1 rgba(185,185, 185, 195));
|
||||
border:1px solid rgb(30,30,30);
|
||||
color: rgb( 25,25,25 );
|
||||
}
|
||||
*.QPushButton:hover{
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(155, 155, 155, 225), stop:1 rgba(205,205, 205, 195));
|
||||
border:1px solid rgb(30,30,30);
|
||||
color: rgb( 25,25,25 );
|
||||
text-decoration: underline!important;
|
||||
}
|
||||
*.QPushButton:pressed{
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(175, 175, 175, 195), stop:1 rgba(115,115, 115, 225));
|
||||
border:1px solid rgb(30,30,30);
|
||||
color: rgb( 25,25,25 );
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Transfer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar">
|
||||
<property name="statusTip">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
259
Scripts/Modeling/Edit/LDMT/ldmt_ui/ldmt_instantMeshes.ui
Normal file
@ -0,0 +1,259 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ldmt_instantMeshes</class>
|
||||
<widget class="QMainWindow" name="ldmt_instantMeshes">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>240</width>
|
||||
<height>80</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>240</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>240</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Instant Meshes Tool</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QMainWindow{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QWidget#centralwidget{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
|
||||
QRadioButton{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
QCheckBox{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar QLabel {
|
||||
border: 3px solid white;
|
||||
}
|
||||
|
||||
QStatusBar {
|
||||
color: rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar::item {
|
||||
border: 1px solid red;
|
||||
border-radius: 3px;
|
||||
}</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>240</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>240</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="btn_remesh">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Remesh selection right on sight.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remesh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="btn_sendToExe">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Send selection to InstantMeshes.exe</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Send To InstantMeshes.exe</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="statusTip">
|
||||
<string>Input a count like 5000, or target ratio 50%.</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color:rgb(200,200,200);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Target Triangle Count or %</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPlainTextEdit" name="text_targetCount">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border-radius: 4px;
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: rgb(30,30,30, 215);
|
||||
color:rgb(175,175,175);</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar">
|
||||
<property name="statusTip">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
245
Scripts/Modeling/Edit/LDMT/ldmt_ui/ldmt_marvelousTool.ui
Normal file
@ -0,0 +1,245 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ldmt_marvelousTool</class>
|
||||
<widget class="QMainWindow" name="ldmt_marvelousTool">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>285</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>285</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>285</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Marvelous Tool</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QMainWindow{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QWidget#centralwidget{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
|
||||
QRadioButton{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
QCheckBox{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar QLabel {
|
||||
border: 3px solid white;
|
||||
}
|
||||
|
||||
QStatusBar {
|
||||
color: rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar::item {
|
||||
border: 1px solid red;
|
||||
border-radius: 3px;
|
||||
}</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>265</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>265</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="btn_flatten">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Select all unweld garments first.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Flatten Garment</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="4">
|
||||
<widget class="QSlider" name="slider_blendshape">
|
||||
<property name="statusTip">
|
||||
<string>Change blendshape value.</string>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2" colspan="2">
|
||||
<widget class="QPushButton" name="btn_makePairs">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Select all groups of garment, pattern and retopo first. </string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Make Pairs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="4">
|
||||
<widget class="QTextBrowser" name="textBrowser">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color:rgb(40,40,40);
|
||||
color:rgb(200,200,200);
|
||||
border-radius:5px;
|
||||
padding:5px;</string>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">1. Select <span style=" color:#43ffa3;">Unweld Garments</span> in viewport.</p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2. Click <span style=" color:#d49768;">Flatten Garment</span>, you'll get two groups, <span style=" color:#43ffa3;">Garment Group</span> and <span style=" color:#43ffa3;">Pattern Group</span>.</p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">3. Import <span style=" color:#43ffa3;">Retopoed Patterns</span> from Zbrush.</p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">4. Make a <span style=" color:#43ffa3;">Group</span> of Retopoed Patterns.</p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">5. In outliner, select all <span style=" color:#43ffa3;">Garment Group</span>, <span style=" color:#43ffa3;">Pattern Group</span> and <span style=" color:#43ffa3;">Retopoed Group </span>in order.</p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">6. Click <span style=" color:#d49768;">Make Pairs</span>.</p></body></html></string>
|
||||
</property>
|
||||
<property name="overwriteMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar">
|
||||
<property name="statusTip">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
182
Scripts/Modeling/Edit/LDMT/ldmt_ui/ldmt_quickExport.ui
Normal file
@ -0,0 +1,182 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ldmt_quickExport</class>
|
||||
<widget class="QMainWindow" name="ldmt_quickExport">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>150</width>
|
||||
<height>50</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>70</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Quick Export</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QMainWindow{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QWidget#centralwidget{
|
||||
background-color: rgb(68,68,68);
|
||||
}
|
||||
QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
|
||||
QRadioButton{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
QCheckBox{
|
||||
color:rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar QLabel {
|
||||
border: 3px solid white;
|
||||
}
|
||||
|
||||
QStatusBar {
|
||||
color: rgb(175,175,175);
|
||||
}
|
||||
|
||||
QStatusBar::item {
|
||||
border: 1px solid red;
|
||||
border-radius: 3px;
|
||||
}</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="box_format">
|
||||
<property name="statusTip">
|
||||
<string>Export format.</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>OBJ</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>FBX</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>UE4</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="btn_export">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Export using format.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
border:1px solid rgb(30,30,30);
|
||||
border-radius:4px;
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(235, 158, 108, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:hover{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(189, 118, 57, 225), stop:1 rgba(255, 178, 128, 195));
|
||||
color:rgb(25,25,25);}
|
||||
QPushButton:pressed{
|
||||
border:1px solid rgb(30,30,30);
|
||||
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(235, 158, 108, 195), stop:1 rgba( 189, 118, 57, 225));
|
||||
color:rgb(25,25,25);}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar">
|
||||
<property name="statusTip">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
440
Scripts/Modeling/Edit/LDMT/patternRename.mel
Normal file
@ -0,0 +1,440 @@
|
||||
// Copyright (C) 1997-2014 Autodesk, Inc., and/or its licensors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// The coded instructions, statements, computer programs, and/or related
|
||||
// material (collectively the "Data") in these files contain unpublished
|
||||
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its licensors,
|
||||
// which is protected by U.S. and Canadian federal copyright law and by
|
||||
// international treaties.
|
||||
//
|
||||
// The Data is provided for use exclusively by You. You have the right to use,
|
||||
// modify, and incorporate this Data into other products for purposes authorized
|
||||
// by the Autodesk software license agreement, without fee.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. AUTODESK
|
||||
// DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTIES
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF NON-INFRINGEMENT,
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, OR ARISING FROM A COURSE
|
||||
// OF DEALING, USAGE, OR TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS
|
||||
// LICENSORS BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
|
||||
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK AND/OR ITS
|
||||
// LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY OR PROBABILITY OF SUCH DAMAGES.
|
||||
|
||||
//
|
||||
//
|
||||
// Bonus Tools Pattern Rename V1.02
|
||||
// 27.1.2011
|
||||
// (c) Autodesk 2011
|
||||
//
|
||||
// uses a list of patterns to remove or replace in object names
|
||||
//
|
||||
|
||||
proc string[] standardWindow( string $windowName, string $title, string $buttons[])
|
||||
{
|
||||
// this proc creates a standard window with a columnLayout and some button below
|
||||
// if $windowName is empty, a new window will be created (otherwise the existing window is shown
|
||||
//
|
||||
// it returns an array with:
|
||||
// - the UI name of the window
|
||||
// - the UI name of the columnLayout
|
||||
// - the names of the buttons
|
||||
//
|
||||
// The window is NOT shown, so that the contents can be added before the window appears
|
||||
global string $PRlistResultsUI;
|
||||
|
||||
if (!size($buttons)) error "The Window should have at least one Button";
|
||||
|
||||
string $result[];
|
||||
|
||||
if (!size($windowName)) $windowName = `window -w 406 -h 655 -title $title`;
|
||||
else if (`window -exists $windowName`) {
|
||||
showWindow $windowName;
|
||||
return { $windowName } ;
|
||||
} else window -w 406 -h 655 -title $title $windowName;
|
||||
|
||||
$result[0] = $windowName;
|
||||
|
||||
$form = `formLayout -nd 100`;
|
||||
|
||||
$tab = `tabLayout -tv 0 -scr 0 -cr 1`; // fuer ein saubers resize der children muss -scr auf 0 stehen !!
|
||||
|
||||
$form2 = `formLayout -nd 100`;
|
||||
|
||||
$result[1] = `columnLayout -adj true`;
|
||||
|
||||
setParent $form2;
|
||||
|
||||
$t = `text -label "Resulting Names"`;
|
||||
$PRlistResultsUI = `textScrollList`;
|
||||
|
||||
formLayout -edit
|
||||
-attachForm $result[1] "top" 0 // the columnLayout
|
||||
-attachForm $result[1] "left" 0
|
||||
-attachForm $result[1] "right" 0
|
||||
-attachNone $result[1] "bottom"
|
||||
|
||||
-attachControl $t "top" 0 $result[1] // the text above the results list
|
||||
-attachForm $t "left" 5
|
||||
-attachForm $t "right" 5
|
||||
-attachNone $t "bottom"
|
||||
|
||||
-attachControl $PRlistResultsUI "top" 0 $t // the results list
|
||||
-attachForm $PRlistResultsUI "left" 0
|
||||
-attachForm $PRlistResultsUI "right" 0
|
||||
-attachForm $PRlistResultsUI "bottom" 0
|
||||
$form2;
|
||||
|
||||
setParent $form;
|
||||
|
||||
$sep = `separator -h 10`;
|
||||
|
||||
for ( $b in $buttons ) $result[size($result)] = `button -label $b`;
|
||||
|
||||
formLayout -edit
|
||||
-attachForm $tab "top" 10 // the tabLayout
|
||||
-attachForm $tab "left" 5
|
||||
-attachForm $tab "right" 5
|
||||
-attachControl $tab "bottom" 5 $sep
|
||||
|
||||
-attachNone $sep "top" // the separator
|
||||
-attachForm $sep "left" 5
|
||||
-attachForm $sep "right" 5
|
||||
-attachControl $sep "bottom" 5 $result[2]
|
||||
$form;
|
||||
|
||||
int $last = size($result) - 1;
|
||||
|
||||
// attach the first button at left and bottom - and the last at right and bottom
|
||||
// if its only one button, then its completely attached
|
||||
formLayout -edit
|
||||
-attachNone $result[2] "top"
|
||||
-attachForm $result[2] "left" 5
|
||||
-attachForm $result[2] "bottom" 5
|
||||
|
||||
-attachNone $result[$last] "top"
|
||||
-attachForm $result[$last] "right" 5
|
||||
-attachForm $result[$last] "bottom" 5
|
||||
$form;
|
||||
|
||||
int $gapStep = 100 / size($buttons);
|
||||
for ( $i=3 ; $i<size($result) ; $i++ ) { // attach all the gaps between the buttons
|
||||
formLayout -edit
|
||||
|
||||
-attachPosition $result[$i-1] "right" 2 ($gapStep * ($i-2))
|
||||
|
||||
-attachPosition $result[$i] "left" 2 ($gapStep * ($i-2))
|
||||
-attachForm $result[$i] "bottom" 5
|
||||
$form;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
global string $PRobjects[];
|
||||
global string $PRsearchUI; // UI name of the input field
|
||||
global string $PRsearch[]; // list of strings to search
|
||||
global string $PRreplaceUI; // UI name of the input field
|
||||
global string $PRreplace[]; // list of strings to replace
|
||||
global string $PRlistSubsUI;
|
||||
global string $PRlistResultsUI;
|
||||
global string $PRmaxIterUI;
|
||||
global string $PRprefixUI;
|
||||
|
||||
global proc PRsaveToShelf()
|
||||
// saves the current rename to shelf
|
||||
// that shelf button will rename the current selection
|
||||
{
|
||||
global string $PRsearch[]; // list of strings to search
|
||||
global string $PRreplace[]; // list of strings to replace
|
||||
global string $PRmaxIterUI;
|
||||
global string $PRprefixUI;
|
||||
|
||||
string $text;
|
||||
string $iconText;
|
||||
string $result = `promptDialog
|
||||
-title "Rename Object"
|
||||
-message "Please enter a short help text for the Shelf Button: \n(the first four letters are the icon label)"
|
||||
-button "OK" -button "Cancel"
|
||||
-defaultButton "OK" -cancelButton "Cancel"
|
||||
-dismissString "Cancel"`;
|
||||
|
||||
if ($result == "OK") {
|
||||
$text = `promptDialog -query -text`;
|
||||
if (size($text)>3) $iconText = substring( $text, 1, 4);
|
||||
else $iconText = $text;
|
||||
if ($iconText=="") $iconText = "PatR";
|
||||
} else return; // User hit Cancel
|
||||
|
||||
int $maxIter = `intFieldGrp -q -v1 $PRmaxIterUI`;
|
||||
string $prefix = `textFieldGrp -q -text $PRprefixUI`;
|
||||
|
||||
string $shelfScript = "// Pattern Rename 1.02 (c) Autodesk 2011 - Roland Reyer\n// " + encodeString($text) + "\n\n";
|
||||
$shelfScript += "source patternRename.mel;\n";
|
||||
$shelfScript += "{\nstring $prefix = \"" + $prefix + "\";\n";
|
||||
$shelfScript += "int $maxIter = " + $maxIter + ";\n";
|
||||
|
||||
$shelfScript += "string $search[] = {";
|
||||
for ( $i=0 ; $i<size($PRsearch) ; $i++ ) $shelfScript += " \"" + encodeString( $PRsearch[$i]) + "\"" + ($i<size($PRsearch)-1 ? "," : " };\n");
|
||||
$shelfScript += "string $replace[] = {";
|
||||
for ( $i=0 ; $i<size($PRsearch) ; $i++ ) $shelfScript += " \"" + encodeString( $PRreplace[$i]) + "\"" + ($i<size($PRsearch)-1 ? "," : " };\n");
|
||||
|
||||
$shelfScript += "string $objects[];\n";
|
||||
$shelfScript += "string $namespaces[];\n";
|
||||
$shelfScript += "patternRenameLoadSelected( $objects, $namespaces);\n";
|
||||
$shelfScript += "string $newName[] = $objects;\n";
|
||||
$shelfScript += "$newName = doPatternRename( $newName, $search, $replace, $prefix, $maxIter);\n";
|
||||
$shelfScript += "for ( $i=0 ; $i<size($objects) ; $i++ ) {\n";
|
||||
$shelfScript += "\tcatch( \`rename ($namespaces[$i] + \":\" + $objects[$i]) ($namespaces[$i] + \":\" + $newName[$i])\`);\n";
|
||||
$shelfScript += "}\n}\n";
|
||||
|
||||
scriptToShelf ( $iconText, $shelfScript, true);
|
||||
// print("-----------------------------------\n");
|
||||
// print $shelfScript;
|
||||
// print("-----------------------------------\n");
|
||||
}
|
||||
|
||||
global proc int PRisValidObjectName( string $name)
|
||||
{
|
||||
// bug fix / the function isValidObjectName (match) has a bug
|
||||
if ( !size(match( "^[a-zA-Z]", $name)) || size(match( "([^a-zA-Z0-9_])", $name)) || size( match( " ", $name))) return 0; // special character or spaces or number at beginning found
|
||||
else return 1;
|
||||
}
|
||||
|
||||
global proc string[] doPatternRename( string $obj[], string $search[], string $replace[], string $prefix, int $maxIter)
|
||||
{
|
||||
int $warn = 0;
|
||||
|
||||
for ( $i=0 ; $i<size( $obj) ; $i++ ) {
|
||||
for ( $j=0 ; $j<size($search) ; $j++ ) {
|
||||
int $before = -1;
|
||||
int $iter = 0;
|
||||
do {
|
||||
$before = size($obj[$i]);
|
||||
$obj[$i] = substitute( $search[$j], $obj[$i], $replace[$j]);
|
||||
$iter++;
|
||||
} while ($before != size($obj[$i]) && $iter<$maxIter);
|
||||
}
|
||||
$obj[$i] = $prefix + $obj[$i];
|
||||
if (!PRisValidObjectName( $obj[$i])) $warn = 1;
|
||||
}
|
||||
if ($warn) warning "At least one name in the list is not valid!";
|
||||
return $obj;
|
||||
}
|
||||
|
||||
global proc patternRename()
|
||||
{
|
||||
global string $PRobjects[];
|
||||
global string $PRmaxIterUI;
|
||||
global string $PRprefixUI;
|
||||
global string $PRsearchUI; // UI name of the input field
|
||||
global string $PRsearch[]; // list of strings to search
|
||||
global string $PRreplaceUI; // UI name of the input field
|
||||
global string $PRreplace[]; // list of strings to replace
|
||||
global string $PRlistSubsUI;
|
||||
global string $PRlistResultsUI;
|
||||
|
||||
string $uiNames[] = standardWindow( "patternRenameWin", "Pattern Rename", { "Rename", "Reload", "Refresh"
|
||||
, "Shelf Button"
|
||||
, "Close"});
|
||||
int $closeIndex = size($uiNames);
|
||||
setParent $uiNames[1];
|
||||
$PRmaxIterUI = `intFieldGrp -label "Max Iterations" -v1 20 -cc "patternRenameUpdate()"`;
|
||||
$PRprefixUI = `textFieldGrp -cc "patternRenameUpdate()" -label "Prefix" -text ""`;
|
||||
separator -style "none" -h 10;
|
||||
|
||||
$PRsearchUI = `textFieldGrp -cc "patternRenameUpdate()" -label "Search" -text ""`;
|
||||
$PRreplaceUI = `textFieldGrp -cc "patternRenameUpdate()" -label "Replace" -text ""`;
|
||||
separator -style "none" -h 10;
|
||||
|
||||
// $form = `formLayout -adj true`;
|
||||
button -label "Add" -c "patternRenameAdd()";
|
||||
text -label "Search and Replace Strings";
|
||||
|
||||
$PRlistSubsUI = `textScrollList -allowMultiSelection 1`;
|
||||
textScrollList -e -sc ("{int $i[0] = `textScrollList -q -selectIndexedItem $PRlistSubsUI`; textFieldGrp -e -text$PRsearch[$i[0]-1] $PRsearchUI; textFieldGrp -e -text $PRreplace[$i[0]-1] $PRreplaceUI ;patternRenameUpdate();}") $PRlistSubsUI;
|
||||
button -label "Remove" -c "patternRenameRemove()";
|
||||
separator -style "none" -h 10;
|
||||
// text -label "Resulting Names";
|
||||
// $PRlistResultsUI = `textScrollList`;
|
||||
|
||||
button -e -c ("patternRenameGo()") $uiNames[2]; // RENAME Button
|
||||
button -e -c ("patternRenameReload()") $uiNames[3]; // RELOAD Button
|
||||
button -e -c ("patternRenameReload();PRredrawSearchReplaceList(0);") $uiNames[4]; // REFRESH Button
|
||||
button -e -c ("PRsaveToShelf();") $uiNames[5]; // SHELF BUTTON
|
||||
//button -e -c ("deleteUI " + $uiNames[0]) $uiNames[$closeIndex]; // CLOSE Button
|
||||
patternRenameReload();
|
||||
PRredrawSearchReplaceList(0);
|
||||
showWindow $uiNames[0];
|
||||
}
|
||||
|
||||
global proc PRredrawSearchReplaceList( int $select)
|
||||
{
|
||||
global string $PRobjects[];
|
||||
global string $PRmaxIterUI;
|
||||
global string $PRsearchUI; // UI name of the input field
|
||||
global string $PRsearch[]; // list of strings to search
|
||||
global string $PRreplaceUI; // UI name of the input field
|
||||
global string $PRreplace[]; // list of strings to replace
|
||||
global string $PRlistSubsUI;
|
||||
global string $PRlistResultsUI;
|
||||
|
||||
textScrollList -e -ra $PRlistSubsUI;
|
||||
for ( $i=0 ; $i<size($PRsearch) ; $i++ ) {
|
||||
if (size($PRreplace[$i])) textScrollList -e -a ($PRsearch[$i] + " ----> " + $PRreplace[$i]) $PRlistSubsUI;
|
||||
else textScrollList -e -a ($PRsearch[$i]) $PRlistSubsUI;
|
||||
}
|
||||
int $totalItems = `textScrollList -q -numberOfItems $PRlistSubsUI`;
|
||||
if ($totalItems) textScrollList -e -selectIndexedItem (min($totalItems, ($select+1))) $PRlistSubsUI;
|
||||
}
|
||||
|
||||
|
||||
|
||||
global proc patternRenameUpdate()
|
||||
{
|
||||
global string $PRobjects[];
|
||||
global string $PRsearchUI; // UI name of the input field
|
||||
global string $PRsearch[]; // list of strings to search
|
||||
global string $PRreplaceUI; // UI name of the input field
|
||||
global string $PRreplace[]; // list of strings to replace
|
||||
global string $PRlistResultsUI;
|
||||
|
||||
global string $PRmaxIterUI;
|
||||
global string $PRprefixUI;
|
||||
|
||||
int $maxIter = `intFieldGrp -q -v1 $PRmaxIterUI`;
|
||||
string $prefix = `textFieldGrp -q -text $PRprefixUI`;
|
||||
$obj = $PRobjects;
|
||||
string $newSearch = `textFieldGrp -q -text $PRsearchUI`;
|
||||
string $newReplace = `textFieldGrp -q -text $PRreplaceUI`;
|
||||
|
||||
string $orgSearch[] = $PRsearch;
|
||||
string $orgReplace[] = $PRreplace;
|
||||
|
||||
if ($newReplace=="" || isValidString( $newReplace, "([a-zA-Z0-9_])*")) { // no spaces allowed
|
||||
if ($newSearch != "") {
|
||||
for ( $i=0 ; $i<size($PRsearch) && $newSearch!=$PRsearch[$i] ; $i++ ) ;
|
||||
|
||||
$PRsearch[$i] = $newSearch;
|
||||
$PRreplace[$i] = $newReplace;
|
||||
}
|
||||
} else warning "No spaces or special characters in replace string allowed - input ignored";
|
||||
|
||||
textScrollList -e -ra $PRlistResultsUI;
|
||||
|
||||
if (size($obj) && size($PRsearch)) $obj = doPatternRename( $obj, $PRsearch, $PRreplace, $prefix, $maxIter);
|
||||
|
||||
for ( $o in $obj ) textScrollList -e -a $o $PRlistResultsUI;
|
||||
|
||||
$PRsearch = $orgSearch;
|
||||
$PRreplace = $orgReplace;
|
||||
|
||||
}
|
||||
|
||||
global proc patternRenameRemove()
|
||||
{
|
||||
global string $PRobjects[];
|
||||
global string $PRsearchUI; // UI name of the input field
|
||||
global string $PRsearch[]; // list of strings to search
|
||||
global string $PRreplaceUI; // UI name of the input field
|
||||
global string $PRreplace[]; // list of strings to replace
|
||||
global string $PRlistSubsUI;
|
||||
global string $PRlistResultsUI;
|
||||
|
||||
int $sel[] = `textScrollList -q -selectIndexedItem $PRlistSubsUI`;
|
||||
for ( $i=size($sel)-1 ; $i>-1 ; $i-- ) {
|
||||
textScrollList -e -removeIndexedItem $sel[$i] $PRlistSubsUI;
|
||||
stringArrayRemoveAtIndex( $sel[$i]-1, $PRsearch);
|
||||
stringArrayRemoveAtIndex( $sel[$i]-1, $PRreplace);
|
||||
}
|
||||
|
||||
textFieldGrp -e -text "" $PRsearchUI;
|
||||
textFieldGrp -e -text "" $PRreplaceUI;
|
||||
patternRenameUpdate();
|
||||
}
|
||||
|
||||
global proc patternRenameAdd()
|
||||
{
|
||||
global string $PRobjects[];
|
||||
global string $PRsearchUI; // UI name of the input field
|
||||
global string $PRsearch[]; // list of strings to search
|
||||
global string $PRreplaceUI; // UI name of the input field
|
||||
global string $PRreplace[]; // list of strings to replace
|
||||
global string $PRlistSubsUI;
|
||||
global string $PRlistResultsUI;
|
||||
|
||||
|
||||
string $newSearch = `textFieldGrp -q -text $PRsearchUI`;
|
||||
string $newReplace = `textFieldGrp -q -text $PRreplaceUI`;
|
||||
|
||||
if ( $newReplace=="" || isValidString( $newReplace, "([a-zA-Z0-9_])*")) { // no spaces allowed
|
||||
if ($newSearch == "") return;
|
||||
for ( $i=0 ; $i<size($PRsearch) && $newSearch!=$PRsearch[$i] ; $i++ ) ;
|
||||
|
||||
$PRsearch[$i] = $newSearch;
|
||||
$PRreplace[$i] = $newReplace;
|
||||
textFieldGrp -e -text "" $PRsearchUI;
|
||||
textFieldGrp -e -text "" $PRreplaceUI;
|
||||
|
||||
PRredrawSearchReplaceList( $i);
|
||||
patternRenameUpdate();
|
||||
} else warning "No spaces or special characters in replace string allowed - nothing added";
|
||||
}
|
||||
|
||||
global proc patternRenameLoadSelected( string $objects[], string $namespaces[])
|
||||
{
|
||||
$objects = `ls -sl`;
|
||||
clear $namespaces;
|
||||
// now cut off ALL namespaces
|
||||
for ( $i=0 ; $i<size($objects) ; $i++ ) {
|
||||
string $tmp[];
|
||||
int $anz = tokenize( $objects[$i], ":", $tmp);
|
||||
// take the complete name, cut off the last portion = ALL trailing namespaces
|
||||
if ($anz>1) $namespaces[$i] = substring( $objects[$i], 1, size($objects[$i]) - size($tmp[$anz-1]) - 1);
|
||||
else $namespaces[0] = "";
|
||||
// the last token is the object name
|
||||
$objects[$i] = $tmp[$anz-1];
|
||||
}
|
||||
}
|
||||
|
||||
global proc patternRenameReload()
|
||||
{
|
||||
global string $PRobjects[];
|
||||
global string $PRnamespaces[];
|
||||
|
||||
patternRenameLoadSelected( $PRobjects, $PRnamespaces);
|
||||
/* $PRobjects = `ls -sl`;
|
||||
clear $PRnamespaces;
|
||||
// now cut off ALL namespaces
|
||||
for ( $i=0 ; $i<size($PRobjects) ; $i++ ) {
|
||||
string $tmp[];
|
||||
int $anz = tokenize( $PRobjects[$i], ":", $tmp);
|
||||
// take the complete name, cut off the last portion = ALL trailing namespaces
|
||||
if ($anz>1) $PRnamespaces[$i] = substring( $PRobjects[$i], 1, size($PRobjects[$i]) - size($tmp[$anz-1]) - 1);
|
||||
else $PRnamespaces[0] = "";
|
||||
// the last token is the object name
|
||||
$PRobjects[$i] = $tmp[$anz-1];
|
||||
}
|
||||
*/ patternRenameUpdate();
|
||||
}
|
||||
|
||||
global proc patternRenameGo()
|
||||
{
|
||||
global string $PRobjects[];
|
||||
global string $PRnamespaces[];
|
||||
global string $PRsearch[]; // list of strings to search
|
||||
global string $PRreplace[]; // list of strings to replace
|
||||
|
||||
global string $PRmaxIterUI;
|
||||
global string $PRprefixUI;
|
||||
|
||||
int $maxIter = `intFieldGrp -q -v1 $PRmaxIterUI`;
|
||||
string $prefix = `textFieldGrp -q -text $PRprefixUI`;
|
||||
$obj = $PRobjects;
|
||||
$obj = doPatternRename( $obj, $PRsearch, $PRreplace, $prefix, $maxIter);
|
||||
for ( $i=0 ; $i<size($PRobjects) ; $i++ ) {
|
||||
catch( `rename ($PRnamespaces[$i] + ":" + $PRobjects[$i]) ($PRnamespaces[$i] + ":" + $obj[$i])`);
|
||||
}
|
||||
clear $PRobjects;
|
||||
clear $PRnamespaces;
|
||||
PRredrawSearchReplaceList( $i);
|
||||
patternRenameUpdate();
|
||||
}
|
6
Scripts/Modeling/Edit/LDMT/temp/instantMesh_temp.mtl
Normal file
@ -0,0 +1,6 @@
|
||||
newmtl initialShadingGroup
|
||||
illum 4
|
||||
Kd 0.50 0.50 0.50
|
||||
Ka 0.00 0.00 0.00
|
||||
Tf 1.00 1.00 1.00
|
||||
Ni 1.00
|
74
Scripts/Modeling/Edit/LDMT/temp/instantMesh_temp.obj
Normal file
@ -0,0 +1,74 @@
|
||||
v -0.334695 -0.333687 0.327576
|
||||
v -0.291528 -0.00363303 0.290792
|
||||
v -0.5 -0.00132766 -0.000194052
|
||||
v -0.290984 -0.292044 -0.00310225
|
||||
v -0.00172517 -0.294468 0.288113
|
||||
v -0.000869247 -0.00853432 0.5
|
||||
v -0.333118 0.325223 0.335069
|
||||
v -0.290686 0.289218 0.0027514
|
||||
v -0.289914 -0.000383876 -0.291317
|
||||
v -0.331467 -0.329182 -0.335327
|
||||
v -0.00169757 -0.5 -0.00594062
|
||||
v 0.329869 -0.337513 0.325327
|
||||
v 0.289696 -0.00521014 0.290542
|
||||
v -0.00039845 0.286972 0.293759
|
||||
v -0.00085076 0.5 0.0058371
|
||||
v -0.333335 0.331917 -0.327388
|
||||
v -5.72049e-05 0.00275149 -0.5
|
||||
v 0.00068111 -0.289822 -0.293995
|
||||
v 0.290004 -0.292403 -0.0031428
|
||||
v 0.5 -0.00221821 -0.0018842
|
||||
v 0.332859 0.324151 0.332961
|
||||
v 0.29023 0.289194 0.00233276
|
||||
v -0.000121877 0.291596 -0.288312
|
||||
v 0.290765 0.0008099 -0.291527
|
||||
v 0.331206 -0.32928 -0.335457
|
||||
v 0.33075 0.334572 -0.327378
|
||||
vn -0.936133 -0.252615 0.244623
|
||||
vn -0.252594 -0.26096 0.931717
|
||||
vn -0.935508 0.250647 0.248999
|
||||
vn -0.934493 -0.249386 -0.254028
|
||||
vn -0.248922 -0.937364 0.243692
|
||||
vn 0.255397 -0.26256 0.930502
|
||||
vn -0.248013 0.244705 0.937341
|
||||
vn -0.255431 0.930561 0.262318
|
||||
vn -0.935341 0.249493 -0.250781
|
||||
vn -0.248122 -0.244255 -0.93743
|
||||
vn -0.249774 -0.933245 -0.2582
|
||||
vn 0.245743 -0.938359 0.24309
|
||||
vn 0.933076 -0.254126 0.254536
|
||||
vn 0.251923 0.245147 0.936183
|
||||
vn 0.257129 0.92935 0.264939
|
||||
vn -0.251658 0.935371 -0.248493
|
||||
vn -0.250509 0.260182 -0.932497
|
||||
vn 0.250541 -0.242097 -0.937347
|
||||
vn 0.251564 -0.933547 -0.255353
|
||||
vn 0.934524 0.247687 0.255571
|
||||
vn 0.934234 -0.252621 -0.251773
|
||||
vn 0.2512 0.936379 -0.245139
|
||||
vn 0.251523 0.259795 -0.932332
|
||||
vn 0.934311 0.254613 -0.249472
|
||||
f 1//1 2//1 3//1 4//1
|
||||
f 1//2 5//2 6//2 2//2
|
||||
f 7//3 8//3 3//3 2//3
|
||||
f 3//4 9//4 10//4 4//4
|
||||
f 1//5 4//5 11//5 5//5
|
||||
f 6//6 5//6 12//6 13//6
|
||||
f 7//7 2//7 6//7 14//7
|
||||
f 7//8 14//8 15//8 8//8
|
||||
f 3//9 8//9 16//9 9//9
|
||||
f 17//10 18//10 10//10 9//10
|
||||
f 11//11 4//11 10//11 18//11
|
||||
f 11//12 19//12 12//12 5//12
|
||||
f 20//13 13//13 12//13 19//13
|
||||
f 6//14 13//14 21//14 14//14
|
||||
f 15//15 14//15 21//15 22//15
|
||||
f 15//16 23//16 16//16 8//16
|
||||
f 17//17 9//17 16//17 23//17
|
||||
f 17//18 24//18 25//18 18//18
|
||||
f 11//19 18//19 25//19 19//19
|
||||
f 20//20 22//20 21//20 13//20
|
||||
f 20//21 19//21 25//21 24//21
|
||||
f 15//22 22//22 26//22 23//22
|
||||
f 17//23 23//23 26//23 24//23
|
||||
f 20//24 24//24 26//24 22//24
|
119
Scripts/Modeling/Edit/ModIt/Class/Collapsible.py
Normal file
@ -0,0 +1,119 @@
|
||||
##--------------------------------------------------------------------------
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
from maya import cmds as mc
|
||||
import maya.mel as mel
|
||||
import json
|
||||
from ..Qt import QtWidgets, QtCore, QtCompat
|
||||
import os
|
||||
import maya.cmds as cmds
|
||||
from maya import OpenMayaUI as omui
|
||||
import mtoa.core as core
|
||||
from functools import partial
|
||||
|
||||
# Special cases for different Maya versions
|
||||
from Qt.QtCompat import wrapInstance
|
||||
from Qt.QtGui import QIcon
|
||||
from Qt.QtWidgets import QWidget
|
||||
|
||||
from .. import ModIt_Global
|
||||
|
||||
##______________________GLOBAL VAR
|
||||
##PATH_SET
|
||||
IconPath = ModIt_Global.IconsPathThemeClassic
|
||||
PreferencePath = ModIt_Global.PreferencePath
|
||||
|
||||
WIN_DISPLAY_SIZE =(json.load(open(PreferencePath + 'WinSize.json',"r"))['VALUE'])
|
||||
|
||||
class CollapsibleHeader(QtWidgets.QWidget):
|
||||
COLLAPSED_PIXMAP = QtGui.QPixmap(IconPath + "Arrow_Collapse")
|
||||
EXPANDED_PIXMAP = QtGui.QPixmap(IconPath + "Arrow_Down")
|
||||
|
||||
clicked = QtCore.Signal()
|
||||
|
||||
def __init__(self, text, parent=None):
|
||||
super(CollapsibleHeader, self).__init__(parent)
|
||||
|
||||
self.setAutoFillBackground(True)
|
||||
self.set_background_color(True)
|
||||
|
||||
self.icon_label = QtWidgets.QLabel()
|
||||
self.icon_label.setFixedWidth(self.COLLAPSED_PIXMAP.width())
|
||||
|
||||
self.text_label = QtWidgets.QLabel()
|
||||
self.text_label.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
|
||||
|
||||
self.main_layout = QtWidgets.QHBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(4, 4, 4, 0)
|
||||
self.main_layout.setSpacing(12) # DISTANCE du TITRE de la FLECHE
|
||||
self.main_layout.addWidget(self.icon_label)
|
||||
self.main_layout.addWidget(self.text_label)
|
||||
|
||||
self.set_text(text)
|
||||
self.set_expanded(False)
|
||||
|
||||
def set_text(self, text):
|
||||
self.text_label.setText("<b>{0}</b>".format(text))
|
||||
if WIN_DISPLAY_SIZE == 1: #150%
|
||||
self.text_label.setFont(QtGui.QFont('Candara', 6))
|
||||
|
||||
def set_background_color(self, color):
|
||||
if not color:
|
||||
color = QtWidgets.QPushButton().palette().color(QtGui.QPalette.Button)
|
||||
|
||||
palette = self.palette()
|
||||
palette.setColor(QtGui.QPalette.Window, color)
|
||||
self.setPalette(palette)
|
||||
|
||||
def is_expanded(self):
|
||||
return self._expanded
|
||||
|
||||
def set_expanded(self, expanded):
|
||||
self._expanded = expanded
|
||||
|
||||
if (self._expanded):
|
||||
self.icon_label.setPixmap(self.EXPANDED_PIXMAP)
|
||||
else:
|
||||
self.icon_label.setPixmap(self.COLLAPSED_PIXMAP)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
self.clicked.emit() # pylint: disable=E1101
|
||||
|
||||
class CollapsibleWidget(QtWidgets.QWidget):
|
||||
collapsed_signal = QtCore.Signal(bool)
|
||||
|
||||
def __init__(self, text, parent=None):
|
||||
super(CollapsibleWidget, self).__init__(parent)
|
||||
|
||||
self.header_wdg = CollapsibleHeader(text)
|
||||
self.header_wdg.clicked.connect(self.on_header_clicked) # pylint: disable=E1101
|
||||
|
||||
self.Body_wdg = QtWidgets.QWidget()
|
||||
self.Body_wdg.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.MAIN_lyt = QtWidgets.QVBoxLayout(self.Body_wdg)
|
||||
self.MAIN_lyt.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.main_layout = QtWidgets.QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.main_layout.addWidget(self.header_wdg)
|
||||
self.main_layout.addWidget(self.Body_wdg)
|
||||
|
||||
self.set_expanded(False)
|
||||
|
||||
def add_widget(self, widget):
|
||||
self.MAIN_lyt.addWidget(widget)
|
||||
|
||||
def add_layout(self, layout):
|
||||
self.MAIN_lyt.addLayout(layout)
|
||||
|
||||
def set_expanded(self, expanded):
|
||||
self.header_wdg.set_expanded(expanded)
|
||||
self.Body_wdg.setVisible(expanded)
|
||||
self.collapsed_signal.emit(expanded)
|
||||
|
||||
def set_header_background_color(self, color):
|
||||
self.header_wdg.set_background_color(color)
|
||||
|
||||
def on_header_clicked(self):
|
||||
self.set_expanded(not self.header_wdg.is_expanded())
|
||||
# TestDialog.btnAction(self)
|
0
Scripts/Modeling/Edit/ModIt/Class/__init__.py
Normal file
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/2Bevel.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/ARROW.psd
Normal file
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/AddEdgeFlow.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/AlignFace.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/AlignFace2.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/AlignPivot.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/Apply.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 7.0 KiB |
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/Arrow_Down.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/Arrow_Down2.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 4.8 KiB |
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/Arrow_Up2.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 186 KiB |
BIN
Scripts/Modeling/Edit/ModIt/Icons/Theme_Classic/BatchProcess.png
Normal file
After Width: | Height: | Size: 8.6 KiB |