476 lines
18 KiB
Python
476 lines
18 KiB
Python
import os
|
|
import time
|
|
import inspect
|
|
import webbrowser
|
|
import urllib.request as urllib
|
|
import random
|
|
import datetime
|
|
import maya.mel as mel
|
|
import pymel.core as pm
|
|
|
|
import Animation.springmagic.core as core
|
|
|
|
from shutil import copyfile
|
|
|
|
|
|
kSpringMagicVersion = 30500
|
|
|
|
scriptName = inspect.getframeinfo(inspect.currentframe()).filename
|
|
scriptPath = os.path.dirname(os.path.abspath(scriptName))
|
|
|
|
# Parameter Initialization
|
|
ui_file = scriptPath + os.sep + 'springMagic.ui'
|
|
|
|
# Constants
|
|
kPaypalLink = r'https://www.paypal.me/Yanbin'
|
|
kLinkedinLink = r'https://ca.linkedin.com/in/baiyanbin'
|
|
kVimeoLink = r''
|
|
kBilibiliLink = r'https://animbai.com/zh/2017/10/14/skintools-tutorials/'
|
|
kYoutubeLink = r'https://animbai.com/2017/10/14/skintools-tutorials/'
|
|
|
|
kUpdateLink = r'https://animbai.com/category/download/'
|
|
kVersionCheckLink = r'http://animbai.com/skintoolsver/'
|
|
kOldPersonalLink = r'http://www.scriptspot.com/3ds-max/scripts/spring-magic'
|
|
|
|
|
|
def widgetPath(windowName, widgetNames):
|
|
"""
|
|
@param windowName: Window instance name to search
|
|
@param widgetNames: list of names to search for
|
|
"""
|
|
returnDict = {}
|
|
mayaWidgetList = pm.lsUI(dumpWidgets=True)
|
|
|
|
for widget in widgetNames:
|
|
for mayaWidget in mayaWidgetList:
|
|
if windowName in mayaWidget:
|
|
if mayaWidget.endswith(widget):
|
|
returnDict[widget] = mayaWidget
|
|
|
|
return returnDict
|
|
|
|
class SpringMagicWidget():
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.init()
|
|
|
|
def init(self):
|
|
try:
|
|
pm.deleteUI(self.ui)
|
|
except:
|
|
pass
|
|
|
|
# title = pm.window(pm.loadUI(ui_file = ui_file))
|
|
|
|
self.ui = pm.loadUI(f=ui_file)
|
|
|
|
ui_widget_list = [
|
|
'donatePayPal_button',
|
|
'main_progressBar',
|
|
'main_processLabel',
|
|
'main_textEdit',
|
|
'main_lang_id',
|
|
'spring_language_list',
|
|
'springSpring_lineEdit',
|
|
'springSubs_lineEdit',
|
|
'springXspring_lineEdit',
|
|
'springTension_lineEdit',
|
|
'springExtend_lineEdit',
|
|
'springInertia_lineEdit',
|
|
'springSubDiv_lineEdit',
|
|
'springLoop_checkBox',
|
|
'springPoseMatch_checkBox',
|
|
'springClearSubFrame_checkBox',
|
|
'springFrom_lineEdit',
|
|
'springEnd_lineEdit',
|
|
'springActive_radioButton',
|
|
'springFrom_radioButton',
|
|
# 'springUpAxis_comboBox',
|
|
'springApply_Button',
|
|
'springCapsule_checkBox',
|
|
'springFastMove_checkBox',
|
|
'springFloor_checkBox',
|
|
'springFloor_lineEdit',
|
|
'springBindPose_button',
|
|
'springStraight_button',
|
|
'springCopy_button',
|
|
'springPaste_button',
|
|
# 'donateBitcoin_lineEdit',
|
|
'miscUpdate_pushButton',
|
|
'springAddBody_Button',
|
|
'springClearBody_Button',
|
|
'springAddPlane_Button',
|
|
'springAddWindCmd',
|
|
'springBind_Button',
|
|
'springBake_Button',
|
|
'shelf_button',
|
|
'link_pushButton',
|
|
'vimeo_pushButton',
|
|
'bilibili_pushButton',
|
|
'language_button',
|
|
'statusbar',
|
|
'springWind_Button']
|
|
|
|
self.uiObjects = widgetPath(self.ui, ui_widget_list)
|
|
|
|
# Main UI
|
|
self.main_progressBar = pm.progressBar(self.uiObjects['main_progressBar'], edit=True)
|
|
self.main_processLabel = pm.text(self.uiObjects['main_processLabel'], edit=True)
|
|
self.main_lineEdit = pm.ui.PyUI(self.uiObjects['main_textEdit'], edit=True)
|
|
self.lang_id = pm.text(self.uiObjects['main_lang_id'], edit=True)
|
|
|
|
self.language_list = pm.textScrollList(self.uiObjects['spring_language_list'], edit=True, selectCommand=self.languageSelectedCmd, visible=False)
|
|
|
|
self.spring_lineEdit = pm.textField(self.uiObjects['springSpring_lineEdit'], edit=True, changeCommand=self.springRatioChangeCmd)
|
|
self.subs_lineEdit = pm.textField(self.uiObjects['springSubs_lineEdit'], edit=True)
|
|
self.Xspring_lineEdit = pm.textField(self.uiObjects['springXspring_lineEdit'], edit=True, changeCommand=self.twistChangeCmd)
|
|
self.tension_lineEdit = pm.textField(self.uiObjects['springTension_lineEdit'], edit=True, changeCommand=self.tensionChangeCmd)
|
|
self.extend_lineEdit = pm.textField(self.uiObjects['springExtend_lineEdit'], edit=True, changeCommand=self.extendChangeCmd)
|
|
self.inertia_lineEdit = pm.textField(self.uiObjects['springInertia_lineEdit'], edit=True, changeCommand=self.inertiaChangeCmd)
|
|
self.sub_division_lineEdit = pm.textField(self.uiObjects['springSubDiv_lineEdit'], edit=True, changeCommand=self.subDivChangeCmd)
|
|
self.loop_checkBox = pm.checkBox(self.uiObjects['springLoop_checkBox'], edit=True)
|
|
self.pose_match_checkBox = pm.checkBox(self.uiObjects['springPoseMatch_checkBox'], edit=True)
|
|
self.clear_subframe_checkBox = pm.checkBox(self.uiObjects['springClearSubFrame_checkBox'], edit=True)
|
|
self.from_lineEdit = pm.textField(self.uiObjects['springFrom_lineEdit'], edit=True)
|
|
self.end_lineEdit = pm.textField(self.uiObjects['springEnd_lineEdit'], edit=True)
|
|
self.active_radioButton = pm.radioButton(self.uiObjects['springActive_radioButton'], edit=True)
|
|
self.from_radioButton = pm.radioButton(self.uiObjects['springFrom_radioButton'], edit=True)
|
|
# self.upAxis_comboBox = pm.optionMenu(self.uiObjects['springUpAxis_comboBox'], edit=True)
|
|
self.apply_button = pm.button(self.uiObjects['springApply_Button'], edit=True, command=self.applyCmd)
|
|
self.add_body_button = pm.button(self.uiObjects['springAddBody_Button'], edit=True, command=self.addBodyCmd)
|
|
self.clear_body_button = pm.button(self.uiObjects['springClearBody_Button'], edit=True, command=self.clearBodyCmd)
|
|
self.add_plane_button = pm.button(self.uiObjects['springAddPlane_Button'], edit=True, command=self.createColPlaneCmd)
|
|
self.wind_button = pm.button(self.uiObjects['springWind_Button'], edit=True, command=self.addWindCmd)
|
|
self.bind_button = pm.button(self.uiObjects['springBind_Button'], edit=True, command=self.bindControlsCmd)
|
|
self.bake_button = pm.button(self.uiObjects['springBake_Button'], edit=True, command=self.clearBindCmd)
|
|
self.shelf_button = pm.button(self.uiObjects['shelf_button'], edit=True, command=self.goShelfCmd)
|
|
self.link_button = pm.button(self.uiObjects['link_pushButton'], edit=True, command=self.linkinCmd)
|
|
self.vimeo_button = pm.button(self.uiObjects['vimeo_pushButton'], edit=True, command=self.youtubeCmd)
|
|
self.bilibili_button = pm.button(self.uiObjects['bilibili_pushButton'], edit=True, command=self.bilibiliCmd)
|
|
self.language_button = pm.button(self.uiObjects['language_button'], edit=True, command=self.languageCmd)
|
|
|
|
self.collision_checkBox = pm.checkBox(self.uiObjects['springCapsule_checkBox'], edit=True)
|
|
self.fast_move_checkBox = pm.checkBox(self.uiObjects['springFastMove_checkBox'], edit=True)
|
|
self.floor_checkBox = pm.checkBox(self.uiObjects['springFloor_checkBox'], edit=True)
|
|
self.floor_lineEdit = pm.textField(self.uiObjects['springFloor_lineEdit'], edit=True, changeCommand=self.twistChangeCmd)
|
|
|
|
self.bind_pose_button = pm.button(self.uiObjects['springBindPose_button'], edit=True, command=self.setCmd)
|
|
self.straight_button = pm.button(self.uiObjects['springStraight_button'], edit=True, command=self.straightCmd)
|
|
self.copy_button = pm.button(self.uiObjects['springCopy_button'], edit=True, command=self.copyCmd)
|
|
self.paste_button = pm.button(self.uiObjects['springPaste_button'], edit=True, command=self.pasteCmd)
|
|
|
|
# self.statusbar = pm.button(self.uiObjects['statusbar'], edit=True, menuItemCommand=self.testCmd)
|
|
|
|
# donate UI
|
|
# self.donate_bitcoin_lineEdit = pm.textField(self.uiObjects['donateBitcoin_lineEdit'], edit=True, text=kBitcoin)
|
|
|
|
self.misc_update_button = pm.button(self.uiObjects['miscUpdate_pushButton'], edit=True, command=self.updatePageCmd)
|
|
|
|
# SpringMagic_mainWindow11|centralwidget|miscUpdate_pushButton
|
|
# SpringMagic_mainWindow11|centralwidget|miscUpdate_pushButton
|
|
# 'miscUpdate_pushButton': ui.Button('SpringMagic_mainWindow11|centralwidget|miscUpdate_pushButton')
|
|
# miscUpdate_button = pm.button( centralwidget + '|miscUpdate_pushButton', edit = True )
|
|
|
|
pm.button(self.uiObjects['donatePayPal_button'], edit=True, command=self.donatePayPalCmd)
|
|
|
|
self.spam_word = ['', '', '', '', '']
|
|
|
|
def show(self):
|
|
|
|
pm.showWindow(self.ui)
|
|
|
|
self.checkUpdate()
|
|
|
|
def progression_callback(self, progression):
|
|
pm.progressBar(self.main_progressBar, edit=True, progress=progression)
|
|
|
|
#############################################
|
|
# Buttons callbacks
|
|
############################################
|
|
|
|
def showSpam(self, *args):
|
|
sWord = self.spam_word[random.randint(0, 4)]
|
|
# print as unicode
|
|
pm.text(self.main_processLabel, edit=True, label=str(sWord))
|
|
|
|
def linkinCmd(self, *args):
|
|
# open my linked in page :)
|
|
url = kLinkedinLink
|
|
|
|
webbrowser.open(url, new=2)
|
|
|
|
def pasteCmd(self, *args):
|
|
core.pasteBonePose()
|
|
|
|
def setCmd(self, *args):
|
|
|
|
picked_bones = pm.ls(sl=1, type='joint')
|
|
|
|
if picked_bones:
|
|
self.apply_button.setEnable(False)
|
|
|
|
core.bindPose()
|
|
|
|
# Select only the joints
|
|
pm.select(picked_bones)
|
|
|
|
self.apply_button.setEnable(True)
|
|
|
|
def straightCmd(self, *args):
|
|
|
|
picked_bones = pm.ls(sl=1, type='joint')
|
|
|
|
if picked_bones:
|
|
self.apply_button.setEnable(False)
|
|
|
|
for bone in picked_bones:
|
|
core.straightBonePose(bone)
|
|
|
|
# Select only the joints
|
|
pm.select(picked_bones)
|
|
|
|
self.apply_button.setEnable(True)
|
|
|
|
def applyCmd(self, *args):
|
|
picked_transforms = pm.ls(sl=1, type='transform')
|
|
|
|
if picked_transforms:
|
|
self.apply_button.setEnable(False)
|
|
|
|
pm.text(self.main_processLabel, edit=True, label='Calculating Bone Spring... (Esc to cancel)')
|
|
|
|
springRatio = 1 - float(self.spring_lineEdit.getText())
|
|
twistRatio = 1 - float(self.Xspring_lineEdit.getText())
|
|
isLoop = bool(self.loop_checkBox.getValue())
|
|
isPoseMatch = bool(self.pose_match_checkBox.getValue())
|
|
isFastMove = self.fast_move_checkBox.getValue()
|
|
isCollision = self.collision_checkBox.getValue()
|
|
|
|
subDiv = 1.0
|
|
if isCollision:
|
|
subDiv = float(self.sub_division_lineEdit.getText())
|
|
|
|
# get frame range
|
|
if self.active_radioButton.getSelect():
|
|
startFrame = int(pm.playbackOptions(q=1, minTime=1))
|
|
endFrame = int(pm.playbackOptions(q=1, maxTime=1))
|
|
else:
|
|
startFrame = int(self.from_lineEdit.getText())
|
|
endFrame = int(self.end_lineEdit.getText())
|
|
|
|
tension = float(self.tension_lineEdit.getText())
|
|
inertia = float(self.inertia_lineEdit.getText())
|
|
extend = float(self.extend_lineEdit.getText())
|
|
|
|
wipeSubFrame = self.clear_subframe_checkBox.getValue()
|
|
|
|
spring = core.Spring(springRatio, twistRatio, tension, extend, inertia)
|
|
springMagic = core.SpringMagic(startFrame, endFrame, subDiv, isLoop, isPoseMatch, isCollision, isFastMove, wipeSubFrame)
|
|
|
|
startTime = datetime.datetime.now()
|
|
|
|
try:
|
|
core.startCompute(spring, springMagic, self.progression_callback)
|
|
|
|
deltaTime = (datetime.datetime.now() - startTime)
|
|
|
|
pm.text(self.main_processLabel, edit=True, label="Spring Calculation Time: {0}s".format(deltaTime.seconds))
|
|
|
|
except ValueError as exception:
|
|
pm.text(self.main_processLabel, edit=True, label='Process aborted')
|
|
pm.warning(exception)
|
|
|
|
# Select only the joints
|
|
pm.select(picked_transforms)
|
|
|
|
pm.progressBar(self.main_progressBar, edit=True, progress=0)
|
|
|
|
self.apply_button.setEnable(True)
|
|
|
|
def copyCmd(self, *args):
|
|
core.copyBonePose()
|
|
|
|
def webCmd(self, *args):
|
|
# open my linked in page :)
|
|
webbrowser.open(kOldPersonalLink, new=2)
|
|
|
|
def twistChangeCmd(self, *args):
|
|
self.limitTextEditValue(self.Xspring_lineEdit, defaultValue=0.7)
|
|
|
|
def extendChangeCmd(self, *args):
|
|
self.limitTextEditValue(self.extend_lineEdit, defaultValue=0.0)
|
|
|
|
def inertiaChangeCmd(self, *args):
|
|
self.limitTextEditValue(self.inertia_lineEdit, defaultValue=0.0)
|
|
|
|
def springRatioChangeCmd(self, *args):
|
|
self.limitTextEditValue(self.spring_lineEdit, defaultValue=0.7)
|
|
|
|
def tensionChangeCmd(self, *args):
|
|
self.limitTextEditValue(self.tension_lineEdit, defaultValue=0.5)
|
|
|
|
def subDivChangeCmd(self, *args):
|
|
# self.limitTextEditValue(self.sub_division_lineEdit, defaultValue=1)
|
|
pass
|
|
|
|
def addWindCmd(self, *args):
|
|
core.addWindObj()
|
|
|
|
def addBodyCmd(self, *args):
|
|
core.addCapsuleBody()
|
|
|
|
def createColPlaneCmd(self, *args):
|
|
core.createCollisionPlane()
|
|
|
|
def removeBodyCmd(self, *args):
|
|
core.removeBody(clear=False)
|
|
|
|
def clearBodyCmd(self, *args):
|
|
core.removeBody(clear=True)
|
|
|
|
def bindControlsCmd(self, *args):
|
|
core.bindControls()
|
|
|
|
def clearBindCmd(self, *args):
|
|
|
|
# get frame range
|
|
if self.active_radioButton.getSelect():
|
|
startFrame = int(pm.playbackOptions(q=1, minTime=1))
|
|
endFrame = int(pm.playbackOptions(q=1, maxTime=1))
|
|
else:
|
|
startFrame = int(self.from_lineEdit.getText())
|
|
endFrame = int(self.end_lineEdit.getText())
|
|
|
|
core.clearBind(startFrame, endFrame)
|
|
|
|
def goShelfCmd(self, *args):
|
|
parentTab = mel.eval('''global string $gShelfTopLevel;string $shelves = `tabLayout -q -selectTab $gShelfTopLevel`;''')
|
|
imageTitlePath = scriptPath + os.sep + "icons" + os.sep + "Title.png"
|
|
# commandLine = "execfile(r'{0}\\springMagic.py')".format(self.scriptPath)
|
|
commandLine = "try:\n\timport springmagic\n\tspringmagic.main()\nexcept:\n\texecfile(r'{0}\springMagic.py')".format(scriptPath)
|
|
|
|
pm.shelfButton(commandRepeatable=True, image1=imageTitlePath, label="Spring Magic", parent=parentTab, command=commandLine)
|
|
|
|
def languageCmd(self, *args):
|
|
self.language_list.setVisible(not self.language_list.getVisible())
|
|
|
|
def languageSelectedCmd(self, *args):
|
|
self.language_list.setVisible(False)
|
|
self.applyLanguage(int(self.language_list.getSelectIndexedItem()[0]))
|
|
|
|
def bilibiliCmd(self, *args):
|
|
try:
|
|
webbrowser.open(kBilibiliLink, new=2)
|
|
except:
|
|
pass
|
|
|
|
def youtubeCmd(self, *args):
|
|
try:
|
|
webbrowser.open(kYoutubeLink, new=2)
|
|
except:
|
|
pass
|
|
|
|
def vimeoCmd(self, *args):
|
|
# try:
|
|
# webbrowser.open(kVimeoLink, new=2)
|
|
# except:
|
|
# pass
|
|
pass
|
|
|
|
def donatePayPalCmd(self, *args):
|
|
try:
|
|
webbrowser.open(kPaypalLink, new=2)
|
|
except:
|
|
pass
|
|
|
|
def updatePageCmd(self, *args):
|
|
try:
|
|
webbrowser.open(kUpdateLink, new=2)
|
|
except:
|
|
pass
|
|
|
|
def applyLanguage(self, lanId):
|
|
lanDict = {1: '_chn', 2: '_eng', 3: '_jpn'}
|
|
|
|
if lanId in lanDict.keys():
|
|
# get new language ui file path
|
|
new_ui_file = scriptPath + os.sep + os.path.basename(ui_file).split('.')[0] + lanDict[lanId] + '.' + os.path.basename(ui_file).split('.')[1]
|
|
copyfile(new_ui_file, ui_file)
|
|
|
|
# Reload interface
|
|
self.init()
|
|
self.show()
|
|
|
|
def detectMayaLanguage(self):
|
|
mayaLan = None
|
|
try:
|
|
mayaLan = os.environ['MAYA_UI_LANGUAGE']
|
|
except:
|
|
import locale
|
|
mayaLan = locale.getdefaultlocale()[0]
|
|
|
|
lanDict = {'zh_CN': 1, 'en_US': 2, 'ja_JP': 3}
|
|
self.applyLanguage(lanDict[mayaLan])
|
|
|
|
def printTextEdit(self, textEdit, inputString):
|
|
ctime = time.ctime()
|
|
ptime = ctime.split(' ')
|
|
inputString = ptime[3] + ' - ' + inputString
|
|
pm.scrollField(textEdit, edit=True, insertionPosition=0, insertText=inputString + '\n')
|
|
|
|
def checkUpdate(self):
|
|
|
|
self.misc_update_button.setVisible(0)
|
|
|
|
page_content = None
|
|
|
|
site = kVersionCheckLink
|
|
hdr = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
|
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
|
|
'Accept-Encoding': 'none',
|
|
'Accept-Language': 'en-US,en;q=0.8',
|
|
'Connection': 'keep-alive'}
|
|
|
|
req = urllib.Request(site, headers=hdr)
|
|
|
|
try:
|
|
page = urllib.urlopen(req, timeout=5)
|
|
page_content = page.read()
|
|
except:
|
|
print('checkUpdate failed')
|
|
|
|
if page_content:
|
|
page_content = page_content.decode('utf-8')
|
|
if len(page_content.split('|springMagic|')) > 1:
|
|
new_kSpringMagicVersion = int(page_content.split('|springMagic|')[1])
|
|
|
|
if new_kSpringMagicVersion > kSpringMagicVersion:
|
|
self.misc_update_button.setVisible(1)
|
|
|
|
self.spam_word = []
|
|
|
|
prefix = '|spam'
|
|
suffix = '|'
|
|
|
|
if self.lang_id.getLabel() == 'chn':
|
|
suffix = 'chn|'
|
|
|
|
self.spam_word = [page_content.split(prefix + str(i) + suffix)[1] for i in range(1, 6)]
|
|
else:
|
|
pm.text(self.main_processLabel, edit=True, label='Check update failed, try later.')
|
|
|
|
self.showSpam()
|
|
|
|
def limitTextEditValue(self, ui_object, minValue=0, maxValue=1, roundF=2, defaultValue=0):
|
|
value = 0
|
|
|
|
try:
|
|
value = float(ui_object.getText())
|
|
value = round(value, roundF)
|
|
value = max(min(maxValue, value), minValue)
|
|
except:
|
|
value = defaultValue
|
|
|
|
ui_object.setText(str(value))
|