1403 lines
51 KiB
Python
1403 lines
51 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#from distutils import command
|
|
#import subprocess
|
|
import maya.cmds as mc
|
|
import maya.mel as mel
|
|
import pymel.core as pm
|
|
import math
|
|
import os
|
|
|
|
#Define globals
|
|
version = '1.1.2'
|
|
mult = 1
|
|
|
|
#################
|
|
###UI SETTINGS###
|
|
#vvvvvvvvvvvvvvv#
|
|
from .uistates import UiStates
|
|
uis = UiStates.pickleLoad()
|
|
|
|
red = [0.807843,0.364706,0.305882] #[0.8, 0.3, 0.3]
|
|
green = [0.454902,0.752941,0.294118] #[0.3, 0.8, 0.3]
|
|
blue = [0.239216,0.615686,0.819608]
|
|
yellow = [0.819608,0.670588,0.239216]
|
|
#################
|
|
|
|
#*****************#
|
|
# MAKE WINDOW #
|
|
#*****************#
|
|
def createUI(*args):
|
|
if mc.window('UVDeluxe', exists=True):
|
|
#remove when not in dev mode
|
|
mc.deleteUI('UVDeluxe')
|
|
|
|
mc.window('UVDeluxe',s=True,width=440,title='UV Deluxe %s' % version, toolbox=False)
|
|
|
|
#Create Scriptjobs
|
|
jobNumber1 = mc.scriptJob(parent='UVDeluxe',event=['linearUnitChanged',lambda *args:updateUI('units_text')])
|
|
jobNumber2 = mc.scriptJob(parent='UVDeluxe',event=['SelectionChanged',lambda *args:updateUI_TexResAutomatic()])
|
|
|
|
### Define texture panel ###
|
|
# Figure out percentage
|
|
pnSize = (220/float(uis.widthHeight[0]))*100
|
|
|
|
try:
|
|
#swp flag only available in 2011 and beyond
|
|
pane = mc.paneLayout('textureEditorPanel', paneSize=[1,pnSize,1], cn='vertical2', swp=1)
|
|
except:
|
|
pane = mc.paneLayout('textureEditorPanel', paneSize=[1,pnSize,1], cn='vertical2')
|
|
|
|
uvTextureViews = mc.getPanel(scriptType='polyTexturePlacementPanel')
|
|
if len(uvTextureViews):
|
|
mc.scriptedPanel(uvTextureViews[0], e=True, unParent=True)
|
|
|
|
#Load main ui elements#
|
|
mc.columnLayout ('MainColumn', columnWidth=220)
|
|
ui_Settings(0)
|
|
ui_Mover()
|
|
ui_Scaler()
|
|
ui_Ratio()
|
|
ui_MatchUV()
|
|
ui_Straighten()
|
|
ui_Align()
|
|
ui_SelectionSet()
|
|
ui_QuickSnap()
|
|
|
|
#Add texture panel to window
|
|
mc.scriptedPanel(uvTextureViews[0], e=True, parent=pane)
|
|
|
|
# SHOW WINDOW #
|
|
mc.showWindow('UVDeluxe')
|
|
mc.window('UVDeluxe',edit=True,mnb=True,widthHeight=uis.widthHeight)
|
|
updateUI_TexResAutomatic()
|
|
|
|
#-----------------#
|
|
# UPDATE UI #
|
|
#-----------------#
|
|
def updateUI(command,*args):
|
|
if command=='units_text':
|
|
#'Maya Units' text in settings.
|
|
mc.text('unitText',edit=True,label='Working Units: %s' % mc.currentUnit(q=True,f=True))
|
|
mc.text('ratio_unit',edit=True,label='Pixels per %s:' % mc.currentUnit(q=True))
|
|
command = 'texture_res'
|
|
elif 'angle' in command:
|
|
# Straighten Edges Angle dials
|
|
if command.split(':')[1] == 'field':
|
|
aValue = mc.floatField('angleField',q=True,value=True)
|
|
mc.floatSlider('angleSlider',edit=True,value=aValue)
|
|
if command.split(':')[1] == 'slider':
|
|
aValue = mc.floatSlider('angleSlider',q=True,value=True)
|
|
mc.floatField('angleField',edit=True,value=aValue)
|
|
elif command == 'scale_pivot':
|
|
#Check if scaling pivot is based on selection
|
|
if mc.radioButton('PivSel',q=True,sl = True):
|
|
mc.floatField('scalePivotFieldU',edit=True,enable=False)
|
|
mc.floatField('scalePivotFieldV',edit=True,enable=False)
|
|
mc.text('spu',edit=True,enable=False)
|
|
mc.text('spv',edit=True,enable=False)
|
|
mc.button('samplePivotButton',edit=True,enable=False)
|
|
else:
|
|
mc.floatField('scalePivotFieldU',edit=True,enable=True)
|
|
mc.floatField('scalePivotFieldV',edit=True,enable=True)
|
|
mc.text('spu',edit=True,enable=True)
|
|
mc.text('spv',edit=True,enable=True)
|
|
mc.button('samplePivotButton',edit=True,enable=True)
|
|
return
|
|
|
|
## Relevant for Texture Resolution ##
|
|
def updateUI_SetRatio():
|
|
#Updates the floatField containing the value that gets queried by unfold.
|
|
mult = setRatioMultiplier()
|
|
unfold=0.0009765625*(mc.intField('densityField',q=True,value=True))
|
|
mc.floatField('ratioField',edit=True,v=unfold*mult)
|
|
return
|
|
|
|
def isPowerOfTwo(x):
|
|
return (x != 0) and ((x & (x - 1)) == 0)
|
|
|
|
def updateUI_TexResAutomatic():
|
|
checkbox = mc.checkBox('DTR',q=True,v=True)
|
|
if checkbox:
|
|
res = getFileResolution()
|
|
if res:
|
|
mc.text('resTextW', edit=True, label=res[0])
|
|
mc.text('resTextH', edit=True, label=res[1])
|
|
|
|
# Update the resolution text of the manual controls.
|
|
# if isPowerOfTwo(res[0]) and res[0] >= 32 and res[0] <= 8192:
|
|
# mc.text('resTextManW', edit=True, label=res[0])
|
|
# mc.text('resTextManH', edit=True, label=res[1])
|
|
|
|
updateUI_SetRatio()
|
|
return
|
|
|
|
#----------------#
|
|
# Settings #
|
|
#----------------#
|
|
def ui_Settings(flag,*args):
|
|
def retainCompSpace():
|
|
if mc.checkBox ('SCR',q=True,value=True):
|
|
mc.texMoveContext('texMoveContext',e=True,scr=True)
|
|
else: mc.texMoveContext('texMoveContext',e=True,scr=False)
|
|
uis.setUiState()
|
|
|
|
def openPrefs(*args):
|
|
#Open preference window
|
|
mel.eval('preferencesWnd "general";')
|
|
#Change tab
|
|
mel.eval('textScrollList -edit -selectItem (uiRes("m_preferencesWnd.kSettingsTab")) prefIndex;')
|
|
mel.eval('switchPrefTabs 0;')
|
|
return
|
|
|
|
def setResolution(*args):
|
|
# IMPORTANT! SetRatio: multiplier = ((multiplier*(8192/textureResW))/8)
|
|
iterations = 8
|
|
resMin = 32
|
|
#maxRes = 32*(2**iterations)
|
|
|
|
#mel. Create a local var "sliderValue" to whatever textureSliderW is set to.
|
|
sliderValueW = mc.intSlider('textureSliderW', query=True, value=True)
|
|
sliderValueH = mc.intSlider('textureSliderH', query=True, value=True)
|
|
|
|
#Width
|
|
p2 = resMin
|
|
for i in range(0,iterations+1,1):
|
|
if sliderValueW == i:
|
|
mc.text('resTextManW',edit=True,label=str(p2))
|
|
p2*=2
|
|
|
|
#Height
|
|
p2 = resMin
|
|
for i in range(0,iterations+1,1):
|
|
if sliderValueH == i:
|
|
mc.text('resTextManH',edit=True,label=str(p2))
|
|
p2*=2
|
|
|
|
return
|
|
|
|
def callSetResAndUpdateUI():
|
|
"""
|
|
Called when moving either resolution slider
|
|
"""
|
|
setResolution()
|
|
updateUI_SetRatio()
|
|
uis.setUiState()
|
|
return
|
|
|
|
def checkDetectTextureResolution():
|
|
"""
|
|
Called when checkbox value is changed for Detect Resolution
|
|
"""
|
|
if mc.checkBox('DTR',q=True,v=True):
|
|
mc.intSlider('textureSliderW', e=True, enable=False)
|
|
mc.intSlider('textureSliderH', e=True, enable=False)
|
|
mc.text('resTextManW', e=True, enable=False)
|
|
mc.text('resTextManH', e=True, enable=False)
|
|
else:
|
|
mc.intSlider('textureSliderW', e=True, enable=True)
|
|
mc.intSlider('textureSliderH', e=True, enable=True)
|
|
mc.text('resTextManW', e=True, enable=True)
|
|
mc.text('resTextManH', e=True, enable=True)
|
|
|
|
uis.setUiState()
|
|
return
|
|
|
|
mc.frameLayout('layout_Settings',label='Settings',width=220,
|
|
cll=True,
|
|
cl=uis.collapseFrame0,
|
|
cc=lambda *args:uis.setUiState(),
|
|
ec=lambda *args:uis.setUiState())
|
|
|
|
## Units and Preferences button
|
|
mc.columnLayout()
|
|
mc.rowLayout(numberOfColumns=2,cw2=(146,60))
|
|
mc.text('unitText',label='Working Units: %s' % mc.currentUnit(q=True,f=True))
|
|
mc.button(label='Maya Prefs',w=68,c=openPrefs)
|
|
mc.setParent('..')
|
|
|
|
## DeTect Resolution
|
|
s = [110,40,40]
|
|
mc.rowLayout(numberOfColumns=3,cw3=s)
|
|
mc.checkBox ('DTR',label="Detect Resolution:",h=20,
|
|
value = uis.detectTextureSize, cc=lambda *args:checkDetectTextureResolution())
|
|
mc.text('resTextW',label='0')
|
|
mc.text('resTextH',label='0')
|
|
mc.setParent('..')
|
|
|
|
## Width controller
|
|
MAN = not uis.detectTextureSize
|
|
mc.rowLayout(numberOfColumns=3,cw3=(40,30,140))
|
|
mc.text(label='Size W:')
|
|
mc.text('resTextManW', label='', enable = MAN)
|
|
mc.intSlider('textureSliderW' ,w=140, min=0, max=8, step=1,
|
|
enable= not uis.detectTextureSize,
|
|
value = 4,
|
|
cc=lambda *args:callSetResAndUpdateUI())
|
|
mc.setParent('..')
|
|
|
|
## Height controller
|
|
mc.rowLayout(numberOfColumns=3,cw3=(40,30,140))
|
|
mc.text(label='Size H:')
|
|
mc.text('resTextManH', label='', enable = MAN)
|
|
mc.intSlider('textureSliderH', w=140, min=0, max=8, step=1,
|
|
enable= not uis.detectTextureSize%(1+1),
|
|
value = 4,
|
|
cc=lambda *args:callSetResAndUpdateUI())
|
|
mc.setParent('..')
|
|
|
|
## Create Checkboxes
|
|
mc.checkBox ('SCR',label='Retain component spacing (Move tool)',h=20,
|
|
value=uis.retainCS,cc=lambda *args:retainCompSpace())
|
|
|
|
## Set parent for frameLayout & columnLayout
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
setResolution()
|
|
|
|
#--------------#
|
|
# MOVER #
|
|
#--------------#
|
|
def ui_Mover():
|
|
def move(dTuple,*args):
|
|
if not mc.polyListComponentConversion(fuv=True):
|
|
mel.eval('warning "Please select some UVs"')
|
|
else:
|
|
steps = mc.floatField('steps',q=True,v=True)
|
|
sel = mc.ls(sl = True)
|
|
mel.eval('polySelectBorderShell 0')
|
|
mc.polyEditUV(u=(steps*dTuple[0]),v=(steps*dTuple[1]))
|
|
#Refocus selection
|
|
mc.select(sel,r=True)
|
|
return
|
|
|
|
mc.frameLayout('layout_Mover',label='Mover',width=220,cll=True,cl=uis.collapseFrame1,
|
|
cc=lambda *args:uis.setUiState(),
|
|
ec=lambda *args:uis.setUiState())
|
|
mc.columnLayout()
|
|
##
|
|
mc.rowLayout(numberOfColumns=2,cw2=(107,107))
|
|
mc.text(label='Move by steps of:')
|
|
mc.floatField('steps',precision=2,width=107,value=1.0)
|
|
mc.setParent('..')
|
|
##
|
|
mc.rowLayout(numberOfColumns=4,cw4=(52,52,52,52))
|
|
mc.button(label='Up',w=52, c=lambda *args:move((0,1)))
|
|
mc.button(label='Down',w=52, c=lambda *args:move((0,-1)))
|
|
mc.button(label='Left',w=52, c=lambda *args:move((-1,0)))
|
|
mc.button(label='Right',w=52, c=lambda *args:move((1,0)))
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
|
|
#-----------------------#
|
|
# SCALE AND ROTATE #
|
|
#-----------------------#
|
|
def ui_Scaler():
|
|
def getPivot(method):
|
|
## Methods
|
|
## 1 = Updates UI with sampled position
|
|
## 2 = Return Custom UV
|
|
## None = return selection center
|
|
|
|
try:
|
|
pivot = mc.polyEvaluate(mc.ls(sl = True), bc2=True)
|
|
pivotU=((pivot[0][0] + pivot[0][1]) * 0.5)
|
|
pivotV=((pivot[1][0] + pivot[1][1]) * 0.5)
|
|
except:
|
|
mel.eval("warning (\"You do not have any UVs selected!\")")
|
|
|
|
if method == 1:
|
|
mc.floatField('scalePivotFieldU', edit=True, value=pivotU)
|
|
mc.floatField('scalePivotFieldV', edit=True, value=pivotV)
|
|
return
|
|
elif method == 2:
|
|
pivotU = mc.floatField('scalePivotFieldU', query=True, value=True)
|
|
pivotV = mc.floatField('scalePivotFieldV', query=True, value=True)
|
|
|
|
return (pivotU, pivotV)
|
|
|
|
def scale(button_info, *args):
|
|
button_data = button_info.split(':')
|
|
if button_data[0] == 'custom':
|
|
if button_data[1] == 'u':
|
|
button_data[0] = float(mc.floatField('scaleCustomU', query=True, v=True))
|
|
else:
|
|
button_data[0] = float(mc.floatField('scaleCustomV', query=True, v=True))
|
|
else:
|
|
button_data[0] = float(button_data[0])
|
|
|
|
#Get pivot from preset or selection
|
|
if mc.radioButton('CustomPivot', query=True, sl = True): pivotUV = getPivot(2)
|
|
else: pivotUV = getPivot(None)
|
|
|
|
if button_data[1] == 'u':
|
|
mc.polyEditUV(pu=pivotUV[0], pv=pivotUV[1], su=button_data[0], sv=0)
|
|
if button_data[1] == 'v':
|
|
mc.polyEditUV(pu=pivotUV[0], pv=pivotUV[1], su=0, sv=button_data[0])
|
|
return
|
|
|
|
def smartRotate(dir):
|
|
if not mc.ls(sl = True):
|
|
mel.eval("print 'Could not rotate UVs: Nothing selected'")
|
|
return
|
|
|
|
|
|
resolution = getWorkingResolution()
|
|
resW = resolution[0]
|
|
resH = resolution[1]
|
|
|
|
selCenter = getPivot(None)
|
|
#Get pivot from preset or selection
|
|
if mc.radioButton('CustomPivot', query=True, sl = True): pivotUV = getPivot(2)
|
|
else: pivotUV = selCenter
|
|
|
|
#Correct texture/scaling ratio before rotating.
|
|
scaleV = resH/resW
|
|
mc.polyEditUV(pu=selCenter[0], pv=selCenter[1], su=0, sv=scaleV)
|
|
|
|
#Perform rotate
|
|
mc.polyEditUV(pu=pivotUV[0], pv=pivotUV[1], angle=mc.intField('rot_deg', query=True, v=True)*dir)
|
|
|
|
#Reverse correction
|
|
selCenter = getPivot(None) #Update position after potential move
|
|
scaleV = resW/resH
|
|
mc.polyEditUV(pu=selCenter[0], pv=selCenter[1], su=0, sv=scaleV)
|
|
return
|
|
|
|
mc.frameLayout('layout_Scaler',label='Scaling and Rotation',width=220,cll=True,cl=uis.collapseFrame2,
|
|
cc=lambda *args:uis.setUiState(),
|
|
ec=lambda *args:uis.setUiState())
|
|
|
|
mc.columnLayout()
|
|
#mc.text(label='Set scaling pivot to selection or custom cords',height=20)
|
|
mc.text(label='Pivot for scaling and roation:',height=20)
|
|
mc.radioCollection('SP_RC')
|
|
##
|
|
mc.rowLayout(numberOfColumns=4,cw4=(65,75,34,34))
|
|
mc.radioButton('PivSel',w=65,label='Selection',cl='SP_RC',onc=lambda *args:updateUI('scale_pivot'),h=14)
|
|
mc.setParent('..')
|
|
##
|
|
mc.rowLayout(numberOfColumns=3,cw3=(115,33,110))
|
|
mc.radioButton('CustomPivot', h=18, w=80,label='Custom UV',cl='SP_RC',onc=lambda *args:updateUI('scale_pivot'))
|
|
mc.text('spu',l='Pos U:',align="left",w=33,enable=False)
|
|
mc.floatField('scalePivotFieldU',pre=7,w=60,v=0.0,h=18)
|
|
mc.setParent('..')
|
|
##
|
|
mc.rowLayout(numberOfColumns=3,cw3=(115,33,110))
|
|
mc.button('samplePivotButton',l='Sample selection', c=lambda *args:getPivot(1),h=18,w=108)
|
|
mc.text('spv',l='Pos V:',align="left",w=33,enable=False)
|
|
mc.floatField('scalePivotFieldV',pre=7,w=60,v=0.0,h=18)
|
|
mc.setParent('..')
|
|
##
|
|
|
|
##
|
|
mc.radioCollection('SP_RC',edit=True,select='PivSel') #Set default selection pivot mode
|
|
mc.text(label='Scale by:',height=20)
|
|
##
|
|
mc.rowLayout(numberOfColumns=6,cw6=(35,25,25,30,42,47))
|
|
mc.text(l='Width:',h=18)
|
|
mc.button(l='0.5',w=25, c=lambda *args:scale('0.5:u'),h=20, bgc=red)
|
|
mc.button(l='2.0',w=25, c=lambda *args:scale('2.0:u'),h=20, bgc=red)
|
|
mc.button(l='-1.0',w=27, c=lambda *args:scale('-1.0:u'),h=20, bgc=red)
|
|
mc.floatField('scaleCustomU',pre=3, v=0.001 ,w=42,h=20)
|
|
mc.button(l='Custom',w=47, c=lambda *args:scale('custom:u'),h=20, bgc=red)
|
|
mc.setParent('..')
|
|
###
|
|
mc.rowLayout(numberOfColumns=6,cw6=(35,25,25,30,42,47))
|
|
mc.text(l='Height:',h=18)
|
|
mc.button(l='0.5',w=25, c=lambda *args:scale('0.5:v'),h=20,bgc=green)
|
|
mc.button(l='2.0',w=25, c=lambda *args:scale('2.0:v'),h=20,bgc=green)
|
|
mc.button(l='-1.0',w=27, c=lambda *args:scale('-1.0:v'),h=20,bgc=green)
|
|
mc.floatField('scaleCustomV',pre=3, v=0.001, w=42,h=20)
|
|
mc.button(l='Custom',w=47, c=lambda *args:scale('custom:v'),h=20,bgc=green)
|
|
mc.setParent('..')
|
|
|
|
##
|
|
mc.text('')
|
|
mc.text(label='Smart Rotate: Maintain width/height ratio')
|
|
mc.rowLayout(numberOfColumns=3,cw3=(34,89,89))
|
|
mc.intField('rot_deg',min=0,max=360,v=90,w=34)
|
|
mc.button('CW_BTN', label='Rotate CW',w=87, c=lambda *args:(smartRotate(-1)))
|
|
mc.button('CCW_BTN',label='Rotate CCW',w=89, c=lambda *args:(smartRotate(1)))
|
|
mc.setParent('..')
|
|
#mc.text(align='left',label=' (Rotates with width/height ratio correction)')
|
|
##
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
return
|
|
|
|
#----------------#
|
|
# RATIO UI #
|
|
#----------------#
|
|
def ui_Ratio():
|
|
global mult
|
|
RS = RatioSampler()
|
|
|
|
def ratioHelp(*args):
|
|
help =['This tool will scale your selected shells as close as possible to \nthe desired pixel density. Accuracy will vary depending on the \namount of distortion to your UVs. A new or unmodifed polyCube \nwill have no distortion and be scaled correctly.',
|
|
"Shells are scaled using the Unfold tool's -scale flag. \nThe output value is simply the number parsed to the \nUnfold tool, and all other options turned off"]
|
|
mc.confirmDialog(title='Set Ratio',button='Ok, whatever',message=str(help[0])+'\n\n'+str(help[1]));
|
|
return
|
|
|
|
def sampleRatio(*args):
|
|
RS.getSource()
|
|
mc.button('SMR_BTN', e=True, enable=True)
|
|
return
|
|
|
|
def setManRatio(*args):
|
|
RS.setRatio()
|
|
return
|
|
|
|
def updateSourceRatio(*args):
|
|
RS.sourceRatio = mc.floatField('sampledRatioField', q=True, v=True)
|
|
print(RS.sourceRatio)
|
|
return
|
|
|
|
def setRatio(*args):
|
|
if mc.checkBox('DTR',q=True,v=True):
|
|
resW = float(mc.text('resTextW',q=True,l=True))
|
|
resH = float(mc.text('resTextH',q=True,l=True))
|
|
else:
|
|
resW = float(mc.text('resTextManW',q=True,l=True))
|
|
resH = float(mc.text('resTextManH',q=True,l=True))
|
|
|
|
#mult
|
|
#mult = setRatioMultiplier()
|
|
|
|
#Define selection and convert it to uvs
|
|
selection = mc.ls(sl = True)
|
|
selection = mc.polyListComponentConversion(selection, tuv=True)
|
|
|
|
#Find center of selection
|
|
pivot = mc.polyEvaluate(selection,bc2=True)
|
|
pu=((pivot[0][0] + pivot[0][1]) * 0.5)
|
|
pv=((pivot[1][0] + pivot[1][1]) * 0.5)
|
|
|
|
#Correct texture/scaling ratio before rotating.
|
|
if resH != resW:
|
|
scaleV = resH/resW
|
|
mc.polyEditUV(pu=pu,pv=pv,su=0,sv=scaleV)
|
|
|
|
mc.unfold(i=0,us=True,s=mc.floatField('ratioField',q=True,v=True))
|
|
|
|
#Reverse correction
|
|
scaleV = resW/resH
|
|
mc.polyEditUV(pu=pu,pv=pv,su=0,sv=scaleV)
|
|
else:
|
|
mc.unfold(i=0,us=True,s=mc.floatField('ratioField',q=True,v=True))
|
|
|
|
mc.select(selection,replace=True)
|
|
return
|
|
|
|
mc.frameLayout('layout_Ratio',label='Ratio (Pixel density)',width=220,cll=True,cl=uis.collapseFrame3,
|
|
cc=lambda *args:uis.setUiState(),
|
|
ec=lambda *args:uis.setUiState())
|
|
mc.columnLayout()
|
|
##
|
|
mc.rowLayout(numberOfColumns=2,cw2=(170,42))
|
|
mc.text(label='Scale shells to desired pixel density',w=170)
|
|
mc.button(w=42,label='Help', align='right', command=ratioHelp)
|
|
mc.setParent('..')
|
|
|
|
##//Sampled Density Controls//
|
|
##
|
|
s = 109,103
|
|
mc.rowLayout(numberOfColumns=2,cw2=s)
|
|
mc.button(label='Copy Ratio', w=s[0], command=sampleRatio)
|
|
mc.button('SMR_BTN', w=s[1],label='Paste Ratio',command=setManRatio, enable=True)
|
|
mc.setParent('..')
|
|
|
|
##
|
|
mc.rowLayout(numberOfColumns=2,cw2=(108,106))
|
|
mc.text(label='Custom Ratio:',w=108)
|
|
mc.floatField('sampledRatioField', pre=5, enable=True, w=106, v=0, cc=updateSourceRatio)
|
|
mc.setParent('..')
|
|
|
|
##//Pixel Density Controls//
|
|
##
|
|
mc.text(label='')
|
|
s = [72,35,103]
|
|
mc.rowLayout(numberOfColumns=3,cw3=s)
|
|
mc.text('ratio_unit', w=s[0], label='Pixels per %s:' % mc.currentUnit(q=True))
|
|
mc.intField('densityField',v=256,min=0,max=8192,w=s[1],cc=lambda *args:updateUI_SetRatio())
|
|
mc.button(label='Set Pixel Density', w=s[2],command=setRatio)
|
|
mc.setParent('..')
|
|
|
|
##
|
|
mc.rowLayout(numberOfColumns=2,cw2=(108,106))
|
|
mc.text(label='Pixel Density:',w=108)
|
|
#(ignore)
|
|
mc.floatField('ratioField',pre=5,enable=False,w=106, v=0.0009765625*(mc.intField('densityField',q=True,v=True)))
|
|
mc.setParent('..')
|
|
|
|
##
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
updateUI_SetRatio()
|
|
|
|
#-------------------#
|
|
# MatchUV UI #
|
|
#-------------------#
|
|
def ui_MatchUV():
|
|
def matchUVS(*args):
|
|
maxDist = uis.matchDist
|
|
def sortDist(tuple):
|
|
return tuple[1]
|
|
|
|
def getOtherUVS(allSelected,suvs):
|
|
objects = [i.split('.')[0] for i in allSelected]
|
|
objects = [i for i in set(objects)]
|
|
uvs = mc.ls(mc.polyListComponentConversion(objects,tuv=True),fl=True)
|
|
return sorted(set.difference(set(uvs)-set(suvs)))
|
|
|
|
### SETUP ###
|
|
allSelected = mc.ls(sl = True)
|
|
# Create list of uvs
|
|
suvs = mc.ls(mc.polyListComponentConversion(tuv=True),sl = True,fl=True)
|
|
ouvs= getOtherUVS(allSelected,suvs)
|
|
|
|
# Create list of positions
|
|
spos = [mc.polyEditUV(i,query=True) for i in suvs]
|
|
opos = [mc.polyEditUV(i,query=True) for i in ouvs]
|
|
|
|
### PERFORM ###
|
|
for i in range(len(suvs)):
|
|
withinRange = []
|
|
for j in range(len(ouvs)):
|
|
x = spos[i][0]-opos[j][0]
|
|
y = spos[i][1]-opos[j][1]
|
|
dist = math.sqrt((x**2) + (y**2))
|
|
if dist < maxDist:
|
|
withinRange.append((opos[j],dist))
|
|
|
|
withinRange = sorted(withinRange,key=sortDist)
|
|
if len(withinRange):
|
|
mc.polyEditUV(suvs[i],u=withinRange[0][0][0],v=withinRange[0][0][1],relative=False)
|
|
|
|
uis.setUiState()
|
|
return
|
|
|
|
def matchUVS_refresh_ui(*args):
|
|
mc.floatField(matchField, edit=True, value=mc.floatSlider(matchSlider, q=True, v=True))
|
|
uis.matchDist = mc.floatField(matchField, query=True, value=True)
|
|
|
|
|
|
mc.frameLayout('layout_MatchUV',label='Match UVs',width=220,cll=True,cl=uis.collapseFrame7,
|
|
cc=lambda *args:uis.setUiState(),
|
|
ec=lambda *args:uis.setUiState())
|
|
|
|
mc.columnLayout()
|
|
##
|
|
mc.text(label='Snap selected UVs to closest unselected UVs')
|
|
mc.rowLayout(numberOfColumns=3,cw3=(70,60,80))
|
|
mc.text(label="Max distance:")
|
|
matchField = mc.floatField(pre=5,v=uis.matchDist,w=50)
|
|
matchSlider = mc.floatSlider(min=0.0,max=1.0,value=uis.matchDist,w=82,dc=matchUVS_refresh_ui)
|
|
mc.setParent('..')
|
|
mc.button(width=80,label='Match UVs', command=matchUVS)
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
return
|
|
|
|
#-------------------#
|
|
# Straighten UI #
|
|
#-------------------#
|
|
|
|
def ui_Straighten():
|
|
def help(*args):
|
|
help =['This script will flatten edges based on the initial angle of each edge.\nWhat that means is, if an edge is closer to being more vertical than \nhorizontal it can only be flattened vertically, or vice versa.',
|
|
'So before the script flattens anything, it sorts all edges into two lists\nof horizontal and vertical before it decides which lists to flatten.']
|
|
mc.confirmDialog(title='Straighten Edges',button='Got it!',message=help[0]+'\n\n'+help[1])
|
|
return
|
|
## UI ##
|
|
|
|
mc.frameLayout('layout_Straighten',label='Straighten Edges',width=220,cll=True,cl=uis.collapseFrame4,
|
|
cc=lambda *args:uis.setUiState(),
|
|
ec=lambda *args:uis.setUiState())
|
|
mc.columnLayout()
|
|
##
|
|
mc.rowLayout(numberOfColumns=2,cw2=(170,33))
|
|
mc.text(label='Straighten based on UV selection')
|
|
mc.button(w=33,label='Info',align='right',c=help)
|
|
mc.setParent('..')
|
|
##
|
|
mc.rowLayout(numberOfColumns=3,cw3=(82,35,100))
|
|
mc.text(label='Angle tolerance:',align='left',w=82)
|
|
mc.floatField('angleField',min=0,max=45,value=30,pre=1,w=35,cc=lambda *args:updateUI('angle:field'))
|
|
mc.floatSlider('angleSlider',min=0,max=45,value=30,w=82,dc=lambda *args:updateUI('angle:slider'))
|
|
mc.setParent('..')
|
|
##
|
|
mc.rowLayout(numberOfColumns=3,cw3=(71,71,71))
|
|
mc.button(label='Horizontal',w=71, h=20, c=lambda *args:(straightenEdges('hori')), bgc=green)
|
|
mc.button(label='Vertical',w=71, h=20, c=lambda *args:(straightenEdges('vert')), bgc=red)
|
|
mc.button(label='Both',w=71, h=20, c=lambda *args:(straightenEdges('both')), bgc=yellow)
|
|
mc.setParent('..')
|
|
##
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
|
|
#-------------------#
|
|
# Align Tools UI #
|
|
#-------------------#
|
|
|
|
def ui_Align():
|
|
|
|
mc.frameLayout('layout_Align',label='Align Tools',width=220,cll=True,cl=uis.collapseFrame5,
|
|
cc=lambda *args:uis.setUiState(),
|
|
ec=lambda *args:uis.setUiState())
|
|
mc.columnLayout()
|
|
##
|
|
mc.rowLayout(numberOfColumns=2,cw2=(137,77))
|
|
mc.text(align='left',label='Straighten shells by rotation:',w=137)
|
|
mc.button(label='Rotate Align',w=77, c=lambda *args:(align('--shell')))
|
|
mc.setParent('..')
|
|
##
|
|
##
|
|
mc.text(label='Align Selected Shells:')
|
|
mc.rowLayout(numberOfColumns=4,cw4=(52,52,52,52))
|
|
mc.button(label='Top', w=52, c=lambda *args:alignShells("up"), h=20, bgc=green)
|
|
mc.button(label='Bottom', w=52, c=lambda *args:alignShells("down"), h=20, bgc=green)
|
|
mc.button(label='Left', w=52, c=lambda *args:alignShells("left"), h=20, bgc=red)
|
|
mc.button(label='Right', w=52, c=lambda *args:alignShells("right"), h=20, bgc=red)
|
|
mc.setParent('..')
|
|
##
|
|
mc.rowLayout(numberOfColumns=2,cw2=(106,106))
|
|
mc.button(label='Center Vertical', w=106, c=lambda *args:alignShells("centerV"), h=20, bgc=green)
|
|
mc.button(label='Center Horizontal', w=106, c=lambda *args:alignShells("centerH"), h=20, bgc=red)
|
|
mc.setParent('..')
|
|
##
|
|
mc.rowLayout(numberOfColumns=1, w=212)
|
|
mc.button(label='Move Shells to 0-1 Space', w=212, c= gatherShells, h=20, bgc=yellow)
|
|
mc.setParent('..')
|
|
##
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
return
|
|
|
|
#-----------------------#
|
|
# SELECTION SETS #
|
|
#-----------------------#
|
|
|
|
def ui_SelectionSet():
|
|
selectionSlots = ['none','none','none','none','none','none']
|
|
|
|
colorBlank = [0.3,0.3,0.3]
|
|
uColor = [0.584314,0.772549,0.0]
|
|
vColor = [0.913725,0.878431,0.0]
|
|
eColor = [0.870588,0.643137,0.117647]
|
|
oColor = [0.152941,0.729412,0.447059]
|
|
fColor = [0.478431,0.227451,0.227451]
|
|
|
|
def storeLoadSelection(slot):
|
|
#Store selection if button is blank..
|
|
slotLabel = mc.button("slotButton%i" % slot,q=True,label=True)
|
|
if slotLabel == '':
|
|
sel = mc.ls(sl = True)
|
|
if len(sel):
|
|
selectionSlots[slot-1] = sel
|
|
|
|
#fluff
|
|
try:
|
|
type = mc.ls(sl = True,fl=True)[0].split('.')[1]
|
|
type = type.split('[')[0]
|
|
except:
|
|
type = ''
|
|
|
|
if type == 'map':
|
|
color = uColor
|
|
elif type == 'e':
|
|
color = eColor
|
|
elif type == 'vtx' or type == 'vtxFace':
|
|
color = vColor
|
|
elif type == 'f':
|
|
color = fColor
|
|
else:
|
|
color = oColor
|
|
|
|
mc.button("slotButton%i" % slot,edit=True,label='%i' % len(mc.ls(sl = True,fl=True)),bgc=color)
|
|
#..load selection if not
|
|
else:
|
|
mc.select(selectionSlots[slot-1],replace=True)
|
|
|
|
def clearSlot(slot):
|
|
mc.button("slotButton%i" % slot,edit=True,label='',bgc=colorBlank)
|
|
|
|
mc.frameLayout('layout_SelectionSets',label='Selections',width=220,cll=True,cl=uis.collapseFrame8,
|
|
cc=lambda *args:uis.setUiState(),
|
|
ec=lambda *args:uis.setUiState())
|
|
mc.columnLayout()
|
|
mc.button("selectBorderEdges", label='Select Shell Border Edges', w=214, c=lambda *args: selectShellBorderEdges())
|
|
mc.text(l=' Store selections')
|
|
mc.rowLayout(numberOfColumns=6)
|
|
mc.button("slotButton1", label='', w=49, c=lambda *args: storeLoadSelection(1), bgc=colorBlank)
|
|
mc.iconTextButton("slotTrash1",style="iconOnly", ann="Clear Slot 1", image="SP_TrashIcon.png", c=lambda *args: clearSlot(1))
|
|
mc.button("slotButton2", label='', w=49, c=lambda *args: storeLoadSelection(2), bgc=colorBlank)
|
|
mc.iconTextButton("slotTrash2",style="iconOnly", ann="Clear Slot 2", image="SP_TrashIcon.png", c=lambda *args: clearSlot(2))
|
|
mc.button("slotButton3", label='', w=49, c=lambda *args: storeLoadSelection(3), bgc=colorBlank)
|
|
mc.iconTextButton("slotTrash3",style="iconOnly", ann="Clear Slot 3", image="SP_TrashIcon.png", c=lambda *args: clearSlot(3))
|
|
mc.setParent('..')
|
|
mc.rowLayout(numberOfColumns=6)
|
|
mc.button("slotButton4", label='', w=49, c=lambda *args: storeLoadSelection(4), bgc=colorBlank)
|
|
mc.iconTextButton("slotTrash4",style="iconOnly", ann="Clear Slot 4", image="SP_TrashIcon.png", c=lambda *args: clearSlot(4))
|
|
mc.button("slotButton5" ,label='', w=49, c=lambda *args: storeLoadSelection(5), bgc=colorBlank)
|
|
mc.iconTextButton("slotTrash5",style="iconOnly", ann="Clear Slot 5", image="SP_TrashIcon.png", c=lambda *args: clearSlot(5))
|
|
mc.button("slotButton6",label='', w=49, c=lambda *args: storeLoadSelection(6), bgc=colorBlank)
|
|
mc.iconTextButton("slotTrash6",style="iconOnly", ann="Clear Slot 6", image="SP_TrashIcon.png", c=lambda *args: clearSlot(6))
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
|
|
#--------------------#
|
|
# Quck UvSnapshot UI #
|
|
#--------------------#
|
|
def ui_QuickSnap():
|
|
#path = os.path.expanduser('~')
|
|
#path = os.getenv('USERPROFILE') or os.getenv('HOME')
|
|
path = uis.snapPath
|
|
|
|
def performQuicksnap():
|
|
# options #
|
|
resolution = getWorkingResolution()
|
|
width = resolution[0]
|
|
height = resolution[1]
|
|
|
|
if mc.checkBox ('doubleRes', query=True, value=True):
|
|
width *= 2
|
|
height *= 2
|
|
autoLoad = mc.checkBox ('autoLoad', query=True, value=True)
|
|
antiAlias = mc.checkBox ('antiAliasing', query=True, value=True)
|
|
|
|
fileName = 'outUV'
|
|
format = mc.optionMenuGrp('fileFormat', query=True, value=True)
|
|
filePath = mc.textField('pathField',query=True,text=True) + '/' + fileName + '.' + format
|
|
uis.setUiState()
|
|
|
|
# # # Perform # # #
|
|
mc.uvSnapshot(name=filePath,xr=width,yr=height,o=True,aa=antiAlias,ff=format)
|
|
|
|
if autoLoad:
|
|
os.startfile(filePath)
|
|
|
|
mc.frameLayout('layout_QuickSnap',label='Quick UV Snapshot',width=220,cll=True,cl=uis.collapseFrame6,
|
|
cc=lambda *args:uis.setUiState(),
|
|
ec=lambda *args:uis.setUiState())
|
|
mc.columnLayout()
|
|
##
|
|
mc.text(align='left',label='Create UV Snapshot for selected objects')
|
|
##
|
|
mc.rowLayout(numberOfColumns=3,cw3=(65,65,70))
|
|
mc.checkBox ('autoLoad',label='Open file',value=True)
|
|
mc.checkBox ('antiAliasing',label='Anti Alias',value=True)
|
|
mc.checkBox ('doubleRes',label='Double Size',value=False)
|
|
mc.setParent('..')
|
|
##
|
|
#mc.text(label="Output directory:")
|
|
mc.textField("pathField",text=path,width=214)
|
|
##
|
|
mc.rowLayout(numberOfColumns=2,cw2=(89,122))
|
|
mc.optionMenuGrp('fileFormat',label="Format:",cat=[1,'left',0],cw2=[40,48])
|
|
mc.button(label='Save snapshot',w=119, c=lambda *args:performQuicksnap())
|
|
mc.menuItem(label='tga')
|
|
mc.menuItem(label='tif')
|
|
mc.menuItem(label='png')
|
|
mc.setParent('..')
|
|
##
|
|
mc.setParent('..')
|
|
mc.setParent('..')
|
|
|
|
#####////////////////////////////////#####
|
|
##### #####
|
|
##### CLASSES #####
|
|
##### #####
|
|
#####////////////////////////////////#####
|
|
|
|
class RatioSampler:
|
|
def __init__(self):
|
|
self.sourceArea = None
|
|
self.sourceUVArea = None
|
|
self.sourceRatio = None
|
|
def getSource(self):
|
|
if not len(pm.ls(sl=True)): return
|
|
self.sourceArea = self.__getAreaAverage()
|
|
self.sourceUVArea = self.__getUVAreaAverage()
|
|
self.sourceRatio = self.sourceArea/self.sourceUVArea
|
|
|
|
mc.floatField('sampledRatioField', edit=True, v=self.sourceRatio)
|
|
return
|
|
|
|
def setRatio(self):
|
|
selected = UVClass()
|
|
selected.getShells()
|
|
|
|
for shell in selected.shells:
|
|
pm.select(shell.uvs)
|
|
targetArea = self.__getAreaAverage()
|
|
targetUVArea = self.__getUVAreaAverage()
|
|
targetRatio = targetArea/targetUVArea
|
|
|
|
ratio = (targetRatio/self.sourceRatio)**0.5
|
|
pivot = shell.getPivot()
|
|
|
|
pm.polyMoveUV(s=[ratio, ratio], pvt=[pivot[0], pivot[1]], ch=False)
|
|
return
|
|
|
|
def __getAreaAverage(self):
|
|
"""
|
|
Returns the average area of the selected faces (or corresponding faces).
|
|
The script pretends that the user is working in cm, or else the math doesn't work:
|
|
A 1x1cm plane will return an area of 1, and a 1x1m plane will return an area of 10000,
|
|
so the area of objects in meter will be multiplied by 0.0001
|
|
"""
|
|
sel = pm.ls(sl=True)
|
|
|
|
faces = pm.ls(pm.polyListComponentConversion(sel, tf=True), fl=True)
|
|
|
|
# Get multiplier
|
|
unit = pm.currentUnit(q=True, f=True)
|
|
mult = 1.0
|
|
if not unit == 'centimeter':
|
|
if unit == 'meter':
|
|
mult = 0.0001
|
|
elif unit == 'millimeter':
|
|
mult = 1000
|
|
|
|
# Get area of faces
|
|
areas = [f.getArea(space='world') for f in faces]
|
|
sum = 0
|
|
for a in areas:
|
|
sum += a * mult
|
|
|
|
print("Face Area: ", sum/len(areas))
|
|
return sum/len(areas)
|
|
|
|
def __getUVAreaAverage(self):
|
|
"""
|
|
Returns the average area of the selected UVs.
|
|
"""
|
|
sel = pm.ls(sl=True)
|
|
|
|
faces = pm.ls(pm.polyListComponentConversion(sel, tf=True), fl=True)
|
|
|
|
#Get area of UV Faces
|
|
UVAreas = [f.getUVArea() for f in faces]
|
|
sum = 0
|
|
for a in UVAreas:
|
|
sum += a
|
|
|
|
print("UV Area: ", sum/len(UVAreas))
|
|
return sum/len(UVAreas)
|
|
|
|
class UVClass:
|
|
def __init__(self, uvs = "selection"):
|
|
#Set self.uvs from a list of uvs or automatically
|
|
if uvs == "selection": #No list was sent
|
|
self.uvs = mc.ls(mc.polyListComponentConversion(tuv=True),fl=True)
|
|
else: self.uvs = uvs
|
|
|
|
self.type = "standard"
|
|
self.shells = []
|
|
self.borderEdges = []
|
|
|
|
def setMinMax(self):
|
|
xPositions = sorted([mc.polyEditUV(i, query=True)[0] for i in self.uvs])
|
|
yPositions = sorted([mc.polyEditUV(i, query=True)[1] for i in self.uvs])
|
|
|
|
self.minMax = (xPositions[0],xPositions[-1]),(yPositions[0],yPositions[-1])
|
|
self.xMin = self.minMax[0][0]
|
|
self.xMax = self.minMax[0][1]
|
|
self.yMin = self.minMax[1][0]
|
|
self.yMax = self.minMax[1][1]
|
|
|
|
def getPivot(self):
|
|
pivot = mc.polyEvaluate(self.uvs,bc2=True)
|
|
pivU = ((pivot[0][0] + pivot[0][1]) * 0.5)
|
|
pivV = ((pivot[1][0] + pivot[1][1]) * 0.5)
|
|
return pivU,pivV
|
|
|
|
def getShells(self):
|
|
""" This creates a list object (shells) within the class containing a UVClass per shell found"""
|
|
if len(self.shells): #No need to do this twice
|
|
if self.type == "shell":
|
|
print("Class is already of shell type. This function call is redundant")
|
|
return
|
|
|
|
currentSelection = mc.ls(sl = True)
|
|
self.shells = []
|
|
for uv in self.uvs:
|
|
found = False
|
|
for shell in self.shells:
|
|
if uv in shell.uvs:
|
|
found = True
|
|
if not found:
|
|
mc.select(uv)
|
|
mel.eval('polySelectBorderShell 0;')
|
|
thisShell = UVClass()
|
|
thisShell.type = "shell"
|
|
thisShell.setMinMax()
|
|
|
|
self.shells.append(thisShell)
|
|
|
|
mc.select(currentSelection)
|
|
#####///////////////////////////////////////#####
|
|
##### #####
|
|
##### SHARED PROCEDURES #####
|
|
##### #####
|
|
#####///////////////////////////////////////#####
|
|
"""
|
|
def selectShellBorderEdges():
|
|
from borders import BorderEdges
|
|
selected = UVClass()
|
|
selected.getShells()
|
|
|
|
edges = BorderEdges(selected.shells)
|
|
|
|
mc.select(edges)
|
|
return
|
|
"""
|
|
def selectShellBorderEdges():
|
|
selected = UVClass()
|
|
selected.getShells()
|
|
finalEdges = []
|
|
|
|
for shell in selected.shells:
|
|
|
|
#Get border UVs for first uv in shell
|
|
mc.select(shell.uvs[0])
|
|
mel.eval('polySelectBorderShell 1;')
|
|
|
|
#Get this shell's BORDER UVS
|
|
buvs = mc.ls(sl=True,fl=True)
|
|
|
|
#List edges that are connected to uv border
|
|
relatedEdges = pm.ls(pm.polyListComponentConversion(te=True),fl=True)
|
|
print(relatedEdges)
|
|
for edge in relatedEdges:
|
|
|
|
# UVClass uses cmds and not pymel, so we need to use it here as well when comparing names
|
|
# mc.ls > pCube1.map[0]...
|
|
# pm.ls > pCube1Shape1[0]
|
|
|
|
#Get CONNECTED UVS
|
|
cuvs = mc.ls(pm.polyListComponentConversion(edge, tuv=True),fl=True)
|
|
|
|
matches = 0
|
|
for uv in cuvs:
|
|
if uv in buvs: matches +=1
|
|
|
|
if matches > 1:
|
|
#Converting to string because of weird error: Problem with the API object returned by __apiobject__ method
|
|
|
|
matchEdgeFaces = pm.ls(edge.connectedFaces(),fl=True)
|
|
if edge.isOnBoundary():
|
|
#Border edge is auto accept
|
|
finalEdges.append(str(edge))
|
|
else:
|
|
if len(matchEdgeFaces) > 1:
|
|
#A triangulated face may have an edge that's not the shell border but both uvs are.
|
|
#Here we look to see if both faces connected to that edge are within the uv shell.
|
|
#if they are, we discard that edge.
|
|
|
|
""" Working on it
|
|
facesInShell = 0
|
|
for f in matchEdgeFaces:
|
|
fuvs = mc.ls(pm.polyListComponentConversion(matchEdgeFaces, tuv=True),fl=True)
|
|
if fuvs[0] in shell.uvs:
|
|
print f, "is in shell."
|
|
facesInShell +=1
|
|
else: print f, "NOT in shell"
|
|
|
|
if facesInShell == 2:
|
|
finalEdges.append(str(edge))
|
|
"""
|
|
# Partial sollution
|
|
mefUVS = mc.ls(pm.polyListComponentConversion(matchEdgeFaces, tuv=True),fl=True)
|
|
blab = 0
|
|
for uv in mefUVS:
|
|
if uv in buvs: blab += 1
|
|
|
|
if blab < len(mefUVS):
|
|
finalEdges.append(str(edge))
|
|
#SELECT!
|
|
mc.select(finalEdges)
|
|
|
|
def gatherShells(*args):
|
|
selected = UVClass()
|
|
selected.getShells()
|
|
|
|
for shell in selected.shells:
|
|
x_center = (shell.xMin + shell.xMax)/2.0
|
|
y_center = (shell.yMin + shell.yMax)/2.0
|
|
if x_center > 1:
|
|
mc.polyEditUV(shell.uvs, u= -int(x_center),v=0)
|
|
if x_center < 0:
|
|
mc.polyEditUV(shell.uvs, u= -int(x_center) + 1,v=0)
|
|
if y_center > 1:
|
|
mc.polyEditUV(shell.uvs, u= 0, v= -int(y_center))
|
|
if y_center < 0:
|
|
mc.polyEditUV(shell.uvs, u= 0, v= -int(y_center) + 1)
|
|
return
|
|
|
|
def alignShells(dir):
|
|
selected = UVClass()
|
|
selected.getShells()
|
|
|
|
shellsUVs = []
|
|
for shell in selected.shells:
|
|
for uv in shell.uvs:
|
|
shellsUVs.append(uv)
|
|
|
|
allUVs = UVClass(shellsUVs)
|
|
allUVs.setMinMax()
|
|
|
|
#Move shells
|
|
for shell in selected.shells:
|
|
if dir == "right":
|
|
mc.polyEditUV(shell.uvs, u= allUVs.xMax - shell.xMax)
|
|
elif dir == "left":
|
|
mc.polyEditUV(shell.uvs, u= allUVs.xMin - shell.xMin)
|
|
elif dir == "centerH":
|
|
mc.polyEditUV(shell.uvs, u = (allUVs.xMin+allUVs.xMax)/2 - (shell.xMax + shell.xMin)/2)
|
|
elif dir == "up":
|
|
mc.polyEditUV(shell.uvs, v= allUVs.yMax - shell.yMax)
|
|
elif dir == "down":
|
|
mc.polyEditUV(shell.uvs, v= allUVs.yMin - shell.yMin)
|
|
elif dir == "centerV":
|
|
mc.polyEditUV(shell.uvs, v = (allUVs.yMin+allUVs.yMax)/2 - (shell.yMax + shell.yMin)/2)
|
|
return
|
|
|
|
def align(flag,*args): #Rotate align shell(s)
|
|
orgSel = UVClass()
|
|
orgSel.getShells()
|
|
|
|
alignPoints = []
|
|
if len(orgSel.uvs) < 2:
|
|
mel.eval('warning("Select at least two uvs!")')
|
|
return
|
|
|
|
for shell in orgSel.shells:
|
|
#Rotations can be unexpected if more than two uvs per shell were selected
|
|
#This is often because orgSel is not in the same order as uvs were selected
|
|
uvPositions = {}
|
|
for uv in orgSel.uvs:
|
|
if uv in shell.uvs and uv not in uvPositions.keys():
|
|
uvPositions[len(uvPositions.items())] = uv
|
|
|
|
if len(uvPositions) >= 2:
|
|
angle = findAngle(( uvPositions.get(0), uvPositions.get(1) ))
|
|
pivot = shell.getPivot()
|
|
pu = pivot[0]
|
|
pv = pivot[1]
|
|
|
|
#Align to hoizontal
|
|
if angle >-45 and angle < 45:
|
|
mc.polyEditUV (shell.uvs, pu=pu,pv=pv,angle=(0 - angle))
|
|
elif angle >135 and angle < 180:
|
|
mc.polyEditUV (shell.uvs, pu=pu,pv=pv,angle=(180 - angle))
|
|
elif angle >-180 and angle <-135:
|
|
mc.polyEditUV (shell.uvs, pu=pu,pv=pv,angle=(180 - angle))
|
|
|
|
#Align to vertical
|
|
if angle >45 and angle <135:
|
|
mc.polyEditUV (shell.uvs, pu=pu,pv=pv,angle=(90 - angle))
|
|
elif angle <-45 and angle >-135:
|
|
mc.polyEditUV (shell.uvs, pu=pu,pv=pv,angle=(270 - angle))
|
|
return
|
|
|
|
def findAngle(tuple):
|
|
#Returns angle of two UV points
|
|
uv0 = tuple[0]
|
|
uv1 = tuple[1]
|
|
p1 = mc.polyEditUV(uv0,q=True)
|
|
p2 = mc.polyEditUV(uv1,q=True)
|
|
X = (p2[0] - p1[0])
|
|
Y = (p2[1] - p1[1])
|
|
|
|
radians = math.atan2(Y,X)
|
|
angle = radians*57.2957795
|
|
|
|
return angle
|
|
|
|
def findBox2D(uvs):
|
|
xMin = mc.polyEditUV(uvs[0],query=True)[0]
|
|
xMax = mc.polyEditUV(uvs[0],query=True)[0]
|
|
yMin = mc.polyEditUV(uvs[0],query=True)[1]
|
|
yMax = mc.polyEditUV(uvs[0],query=True)[1]
|
|
|
|
for u in uvs:
|
|
posX = mc.polyEditUV(u,query=True)[0]
|
|
posY = mc.polyEditUV(u,query=True)[1]
|
|
|
|
if posX > xMax: xMax = posX
|
|
elif posX < xMin: xMin = posX
|
|
if posY > yMax: yMax = posY
|
|
elif posY < yMin: yMin = posY
|
|
|
|
return (xMin,xMax), (yMin,yMax)
|
|
|
|
def getFileResolution():
|
|
texWinName = mc.getPanel(sty='polyTexturePlacementPanel')
|
|
availableImages = mc.textureWindow(texWinName[0], q=True, imn=True)
|
|
if availableImages:
|
|
currentImage = availableImages[mc.textureWindow(texWinName[0], q=True, imageNumber=True)]
|
|
currentImage = currentImage.split()[-1]
|
|
|
|
#Find currently used image
|
|
if 'outSizeX' in mc.listAttr(currentImage):
|
|
x = mc.getAttr(currentImage + '.outSizeX')
|
|
y = mc.getAttr(currentImage + '.outSizeY')
|
|
return int(x), int(y)
|
|
else: return None
|
|
|
|
def getWorkingResolution():
|
|
"""
|
|
Returns the active "working" resolution, based on whether or not the user
|
|
has set UVDeluxe to to manual or automatic texture resolution detection.
|
|
"""
|
|
checkbox = mc.checkBox('DTR',q=True,v=True)
|
|
if checkbox:
|
|
resW = float(mc.text('resTextW',q=True,l=True))
|
|
resH = float(mc.text('resTextH',q=True,l=True))
|
|
else:
|
|
resW = float(mc.text('resTextManW',q=True,l=True))
|
|
resH = float(mc.text('resTextManH',q=True,l=True))
|
|
|
|
return resW, resH
|
|
|
|
def setRatioMultiplier():
|
|
"""
|
|
Get the correct multiplier for the pixel ratio calculation.
|
|
"""
|
|
resolution = getWorkingResolution()
|
|
resW = resolution[0]
|
|
resH = resolution[1]
|
|
|
|
# resH = float(mc.text('resTextH',q=True,l=True))
|
|
if resW > 0:
|
|
unit = mc.currentUnit(query=True,f=True)
|
|
global mult
|
|
mult = 1
|
|
if not unit == 'centimeter':
|
|
if unit == 'meter':
|
|
mult = 0.01
|
|
elif unit == 'millimeter':
|
|
mult = 10
|
|
else:
|
|
mc.confirmDialog(title='Sorry!',button='ok',message='UVDeluxe\'s Set Ratio does work with unit type: %s\nPlease work in meters, centimeters or millimeters' % unit);
|
|
#mc.error('Script not configured for unit %s' % unit)
|
|
|
|
mult = (mult*(8192/resW))/8
|
|
return mult
|
|
else:
|
|
return 0
|
|
|
|
def straightenEdges(flag):
|
|
def findAdjacentEdges(gotEdges):
|
|
## Accepts list of tuples size 2 ##
|
|
adjacentEdges = {}
|
|
|
|
##STEP ONE##
|
|
#Create list of all UVs in gotEdges
|
|
everyUV = []
|
|
for e in range(0,len(gotEdges),1):
|
|
for u in range(0,len(gotEdges[e]),1):
|
|
if not gotEdges[e][u] in everyUV:
|
|
everyUV.append(gotEdges[e][u])
|
|
|
|
#Check which uvs are in more than one edge, and store those edge connections.
|
|
pairs = []
|
|
loneEdges = []
|
|
for u in range(0,len(everyUV),1):
|
|
#Clear list of found edges for this uv
|
|
foundE = []
|
|
for e in range(0,len(gotEdges),1):
|
|
if everyUV[u] in gotEdges[e]:
|
|
foundE.append(gotEdges[e])
|
|
|
|
#Put edges into appropriate list, right before the loop iteration ends.
|
|
if e==len(gotEdges)-1:
|
|
if len(foundE) == 2 and not foundE in pairs: pairs.append(foundE)
|
|
elif len(foundE) == 1 and not foundE in loneEdges:
|
|
loneEdges.append(foundE)
|
|
|
|
#Remove pair-ends that got put into loneEdges
|
|
for l in range(len(loneEdges)-1,-1,-1):
|
|
found = []
|
|
for p in range(0,len(pairs),1):
|
|
if loneEdges[l][0] in pairs[p]:
|
|
found.append(l)
|
|
for f in range(0,len(found),1):
|
|
loneEdges.pop(found[f])
|
|
|
|
## STEP TWO ##
|
|
incomplete = True
|
|
addNewPair = False
|
|
unsortedPairs = pairs[:]
|
|
keysComplete = []
|
|
|
|
if len(pairs):
|
|
while incomplete:
|
|
keysSkipped = 0
|
|
|
|
if len(unsortedPairs):
|
|
### print "\nAdding new key!"
|
|
adjacentEdges[len(adjacentEdges)] = unsortedPairs[0]
|
|
|
|
for key in adjacentEdges.keys():
|
|
### print "\nNow checking Key %d" % key
|
|
if key not in keysComplete:
|
|
#Pop first pair key from unsortet.
|
|
popPairs = []
|
|
deletePair = []
|
|
for p in unsortedPairs:
|
|
if p == adjacentEdges[key]:
|
|
deletePair.append(p)
|
|
for p in deletePair:
|
|
unsortedPairs.remove(p)
|
|
|
|
addingToKey = True
|
|
while addingToKey:
|
|
uvs_inKey = []
|
|
for pair in adjacentEdges.get(key):
|
|
for uv in pair:
|
|
uvs_inKey.append(uv)
|
|
|
|
popPairs = []
|
|
pairsToAdd = []
|
|
for p in range(0,len(unsortedPairs),1):
|
|
for uv in uvs_inKey:
|
|
#Check if either edge of pair contains key-uv
|
|
if uv in unsortedPairs[p][0] or uv in unsortedPairs[p][1]:
|
|
pairsToAdd.append(unsortedPairs[p])
|
|
|
|
#Add pairs to key and remove from list of unsorted pairs
|
|
for pair in pairsToAdd:
|
|
adjacentEdges[key] += pair
|
|
adjacentEdges[key] = sorted(list(set(adjacentEdges[key])))
|
|
if pair in unsortedPairs:
|
|
unsortedPairs.remove(pair)
|
|
|
|
#Break loop when nothing more to add
|
|
if not len(pairsToAdd):
|
|
addingToKey = False
|
|
keysComplete.append(key)
|
|
|
|
else:
|
|
#Break loop when all keys are complete
|
|
keysSkipped += 1
|
|
if keysSkipped == len(adjacentEdges):
|
|
incomplete = False
|
|
|
|
#Assign key to every single edge left
|
|
for e in loneEdges:
|
|
adjacentEdges[len(adjacentEdges)] = e
|
|
|
|
#Restructure keys from list of tuples to list of uvs
|
|
for key in adjacentEdges.keys():
|
|
uvs_inKey = []
|
|
for tuple in adjacentEdges[key]:
|
|
uvs_inKey.append(tuple[0])
|
|
uvs_inKey.append(tuple[1])
|
|
|
|
#Replace list with new list
|
|
adjacentEdges[key] = sorted(list(set(uvs_inKey)))
|
|
|
|
return adjacentEdges
|
|
|
|
max_angle = mc.floatField('angleField', query=True, value=True)
|
|
|
|
orgSel = mc.ls(sl = True)
|
|
edges = createEdgeList()
|
|
|
|
#Sort edges by horizontal and vertical
|
|
sortedEdges = sortEdges(edges,max_angle)
|
|
|
|
#Determin method of straightening
|
|
## Horizontal
|
|
if not flag == 'vert':
|
|
hz_edges = findAdjacentEdges(sortedEdges[0])
|
|
|
|
for key in hz_edges.keys():
|
|
box = findBox2D(hz_edges[key])
|
|
centerV=((box[1][0] + box [1][1]) * 0.5)
|
|
mc.polyEditUV(hz_edges[key],v=centerV,relative=False)
|
|
|
|
## Vertical
|
|
if not flag == 'hori':
|
|
vt_edges = findAdjacentEdges(sortedEdges[1])
|
|
|
|
for key in vt_edges.keys():
|
|
box = findBox2D(vt_edges[key])
|
|
centerU=((box[0][0] + box[0][1]) * 0.5)
|
|
mc.polyEditUV(vt_edges[key],u=centerU,relative=False)
|
|
return
|
|
|
|
def createEdgeList():
|
|
def addToEdges(uv0, uv1):
|
|
uvTup1 = (uv0,uv1)
|
|
uvTup2 = (uv1,uv0)
|
|
if not uvTup1 in edges.values() and not uvTup2 in edges.values():
|
|
edges[len(edges)] = uvTup1
|
|
return
|
|
|
|
edges = { }
|
|
orgSel = mc.ls(sl = True, fl=True)
|
|
|
|
#####################
|
|
#Build list of edges
|
|
if len(orgSel):
|
|
for u in range(0,len(orgSel),1):
|
|
uv1 = orgSel[u]
|
|
uvToEdge = mc.ls(mc.polyListComponentConversion(orgSel[u], fuv=True, te=True), fl=True)
|
|
|
|
#Get uvs in convertToFace selection
|
|
faces = mc.ls(mc.polyListComponentConversion(orgSel[u], fuv=True, tf=True), fl=True)
|
|
uvGrowSel = []
|
|
for f in faces:
|
|
uv = mc.ls(mc.polyListComponentConversion(f, ff=True, tuv=True), fl=True)
|
|
for point in uv:
|
|
if not point in uvGrowSel:
|
|
uvGrowSel.append(point)
|
|
|
|
#Check if there is a connection between point 1 and points in grow selection.
|
|
for g in uvGrowSel:
|
|
e1 = mc.ls(mc.polyListComponentConversion(uv1, fuv=True, te=True), fl=True)
|
|
e2 = mc.ls(mc.polyListComponentConversion(g, fuv=True, te=True), fl=True)
|
|
se = list(set(e1) & set(e2))
|
|
if len(se):
|
|
#There is
|
|
ed = mc.ls(mc.polyListComponentConversion(se[0], fe=True, tuv=True), fl=True)
|
|
keepUVs = []
|
|
for e in range(len(ed)):
|
|
if ed[e] in uvGrowSel and ed[e] in orgSel:
|
|
keepUVs.append(ed[e])
|
|
if len(keepUVs) == 2:
|
|
addToEdges(keepUVs[0], keepUVs[1])
|
|
|
|
return edges
|
|
|
|
def sortEdges(edges, max_angle):
|
|
hz_edges = {}
|
|
vt_edges = {}
|
|
for e in edges:
|
|
if not e in hz_edges.values() and not e in vt_edges.values():
|
|
angle = findAngle(edges[e])
|
|
#if not uvTup1 in edges.values() and not uvTup2 in edges.values(): edges[len(edges)] = uvTup1
|
|
if angle < 0:
|
|
angle = angle*-1
|
|
#hoizontal
|
|
if angle < 45 and angle >= 0:
|
|
if angle < max_angle:
|
|
hz_edges[len(hz_edges)] = edges[e]
|
|
elif angle >= 135 and angle < 180:
|
|
if angle > 180-max_angle:
|
|
hz_edges[len(hz_edges)] = edges[e]
|
|
#vertical
|
|
elif angle < 135 and angle >= 45:
|
|
if angle < 90 and angle > 90-max_angle:
|
|
vt_edges[len(vt_edges)] = edges[e]
|
|
elif angle > 90 and angle < 90+max_angle:
|
|
vt_edges[len(vt_edges)] = edges[e]
|
|
return hz_edges,vt_edges
|