This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
このツールは、Translate , Rotate , Scale アトリビュートでポーズを付けているものにのみ効果を発揮する。 | |
カスタムアトリビュートの値は残念ながら対応していない。 | |
[x]選択したオブジェクトのTRSの現在の値を保持する。 | |
[x]ペースト時は強制的にオブジェクトにキーを打つ | |
[x]リストをダブルクリックすると、ペースト対象となるオブジェクトを選択 | |
[x]poseがコピーされたら、追加されたList内要素を選択 | |
[x]poseが削除されたら、List内の下の要素を選択、無い場合は上を選択、上も無い場合は選択しない。 | |
[x]セーブで poses データを外部ファイルに保存する。json形式。 | |
[x]ロードで、poses データを外部ファイルから読み込み、poses変数に格納する。 | |
[x]リスト内の項目をリネームできる。 | |
""" | |
import maya.cmds as cmds | |
import maya.mel as mel | |
import os | |
from functools import partial | |
import re | |
import json | |
class TRS_PoseLibrary(object): | |
# これは poseList 内のデータです。二次元配列として[ "リストに表示する名前" , "キーデータ" ]を扱います | |
poses = [[]] | |
del poses[0] | |
def __init__(self): | |
btnWidth = 60 | |
win = 'TRS_PoseLibrary' | |
if(cmds.window(win, ex=True)): | |
cmds.deleteUI(win) | |
w = cmds.window(win, tlb=True) | |
cmds.columnLayout(p=w) | |
cmds.rowLayout( nc=2 ) | |
cmds.button( l=u'Load', w=btnWidth , c=partial(self.openFileDialog), ann=u'poseLibファイルからポーズリストを読み込み。' ) | |
cmds.button( l=u'Save', w=btnWidth , c=partial(self.saveFileDialog), ann=u'poseLibファイルに現在のポーズを保存。' ) | |
cmds.setParent('..') | |
cmds.text( l=u'-- poseList --' ) | |
cmds.textScrollList( 'tsl_poseList' ,ams=False , numberOfRows=6 , doubleClickCommand=partial(self.selectPasteTgt,'') , deleteKeyCommand=partial(self.deletePose,'')) | |
cmds.rowLayout( nc=4 ) | |
cmds.button( l=u'Copy' , w=btnWidth, c=partial(self.copyPose) , ann=u'選択したオブジェクトの現在のTRSを保持します。' ) | |
cmds.button( l=u'Paste' , w=btnWidth , c=partial(self.pastePose ) , ann=u'選択した項目のポーズをシーン内のオブジェクトに貼り付けます。\nターゲットオブジェクトは選択しなくていいです。' ) | |
cmds.button( l=u'Delete', w=btnWidth , c=partial(self.deletePose) , ann=u'選択した項目のポーズを削除します。') | |
cmds.button( l=u'Rename', w=btnWidth , c=partial(self.renamePose) , ann=u'選択した項目のポーズをリネームします。(リストの表示上だけなり)') | |
cmds.setParent('..') | |
cmds.showWindow() | |
# poseList に現在選択しているオブジェクトのキーを保存する | |
def copyPose(self,v): | |
# 現在のシーン名とフレームを取得(poseList に表示する文字列) | |
poseListName = '%s [ %s F ]'%( cmds.file( q=True , sceneName=True , shortName=True ) , cmds.currentTime(q=True) ) | |
# オブジェクトの絶対パスとPRSのアトリビュートを attr に保管(改行有り) | |
selObjs = cmds.ls(sl=True,long=True) | |
# データ変数定義(まずは選択ノード数追加。その後選択ノード名、選択TRSアトリビュート情報を追加) | |
datas = '%s,'%(len(selObjs)) | |
for obj in selObjs: | |
datas = '%s%s'%(datas,obj+',') # オブジェクト名追加 | |
datas = '%s%s'%(datas,self.getAttrKey(obj)) # アトリビュート追加 | |
self.poses.append( [ poseListName , datas ] ) | |
self.listRefresh() | |
# 追加した項目を選択(最後の行を選択)) | |
cmds.textScrollList( 'tsl_poseList' , e=True , selectIndexedItem=len(self.poses) ) | |
# poseList で選択した項目を削除 | |
def deletePose(self,v): | |
# 削除確認 | |
res = cmds.confirmDialog( title='Confirm', message='delete pose?', button=['Yes','No'], defaultButton='Yes', cancelButton='No', dismissString='No' ) | |
if res == 'No': | |
return | |
# リストが選択されていなかったらリターン | |
if cmds.textScrollList( 'tsl_poseList' , q=True , selectItem=True ) == None: | |
return | |
# アクティブインデックスを取得し、対応する項目をグローバル変数から削除 | |
selIdx = int(cmds.textScrollList( 'tsl_poseList' , q=True , selectIndexedItem=True )[0]) - 1 | |
del self.poses[selIdx] | |
self.listRefresh() | |
# 削除された項目の下を選択。無かったら上を選択。無かったら何も選択しない。 | |
if len(self.poses) == 0: | |
return | |
try: | |
cmds.textScrollList( 'tsl_poseList' , e=True , selectIndexedItem=selIdx+1 ) | |
except: | |
cmds.textScrollList( 'tsl_poseList' , e=True , selectIndexedItem=len(self.poses) ) | |
# 配列の要素とList表示をあわせる | |
def listRefresh(self): | |
# 2次元配列の名前に当たる部分だけ取り出す(リスト表示用) | |
poseNames = [] | |
for name in self.poses: | |
poseNames.append( name[0] ) | |
cmds.textScrollList( 'tsl_poseList' , e=True , removeAll=True ) | |
cmds.textScrollList( 'tsl_poseList' , e=True , append=poseNames ) | |
def getAttrKey(self, obj): | |
at = ['']*9 | |
t = cmds.currentTime(q=True) | |
# 小数点第2以下は切り上げます | |
at[0] = round( cmds.getAttr(obj+'.tx', t=t) , 2 ) | |
at[1] = round( cmds.getAttr(obj+'.ty', t=t) , 2 ) | |
at[2] = round( cmds.getAttr(obj+'.tz', t=t) , 2 ) | |
at[3] = round( cmds.getAttr(obj+'.rx', t=t) , 2 ) | |
at[4] = round( cmds.getAttr(obj+'.ry', t=t) , 2 ) | |
at[5] = round( cmds.getAttr(obj+'.rz', t=t) , 2 ) | |
at[6] = round( cmds.getAttr(obj+'.sx', t=t) , 2 ) | |
at[7] = round( cmds.getAttr(obj+'.sy', t=t) , 2 ) | |
at[8] = round( cmds.getAttr(obj+'.sz', t=t) , 2 ) | |
attrs = '' | |
for a in at: | |
attrs = '%s%s%s'%(attrs,a,',') | |
return attrs | |
def pastePose(self,v): | |
# poseList で選択した項目のdataを流し込む | |
poseIdx = int( cmds.textScrollList( 'tsl_poseList' , q=True , selectIndexedItem=True )[0]) - 1 | |
paramList = self.poses[poseIdx][1].split(',') # data をアトリビュート毎に分解 | |
# 配列内の削除とか行うので、temp変数として逃がす | |
tempParamList = paramList | |
# 処理を行う回数 = datasの0番目の数値 | |
count = int(tempParamList[0]) | |
del tempParamList[0] # 処理回数用の数値は用済みなので削除 | |
print u'ポーズをペーストしました', | |
# ノード名数分処理 | |
for i in range(count): | |
obj = tempParamList[0] | |
txa = float(tempParamList[1]) | |
tya = float(tempParamList[2]) | |
tza = float(tempParamList[3]) | |
rxa = float(tempParamList[4]) | |
rya = float(tempParamList[5]) | |
rza = float(tempParamList[6]) | |
sxa = float(tempParamList[7]) | |
sya = float(tempParamList[8]) | |
sza = float(tempParamList[9]) | |
# 上記アトリビュートをセットしたら、セットし終わったものは削除 | |
del tempParamList[0:10] | |
# TRSアトリビュートに対して値をセットしキーを打つ | |
try: | |
cmds.setAttr(obj+'.tx', txa) | |
cmds.setKeyframe(obj, at='tx', v=txa) | |
except:pass | |
try: | |
cmds.setAttr(obj+'.ty', tya) | |
cmds.setKeyframe(obj, at='ty', v=tya) | |
except:pass | |
try: | |
cmds.setAttr(obj+'.tz', tza) | |
cmds.setKeyframe(obj, at='tz', v=tza) | |
except:pass | |
try: | |
cmds.setAttr(obj+'.rx', rxa) | |
cmds.setKeyframe(obj, at='rx', v=rxa) | |
except:pass | |
try: | |
cmds.setAttr(obj+'.ry', rya) | |
cmds.setKeyframe(obj, at='ry', v=rya) | |
except:pass | |
try: | |
cmds.setAttr(obj+'.rz', rza) | |
cmds.setKeyframe(obj, at='rz', v=rza) | |
except:pass | |
try: | |
cmds.setAttr(obj+'.sx', sxa) | |
cmds.setKeyframe(obj, at='sx', v=sxa) | |
except:pass | |
try: | |
cmds.setAttr(obj+'.sy', sya) | |
cmds.setKeyframe(obj, at='sy', v=sya) | |
except:pass | |
try: | |
cmds.setAttr(obj+'.sz', sza) | |
cmds.setKeyframe(obj, at='sz', v=sza) | |
except:pass | |
# poseLibファイルを保存(list → json) | |
def saveFileDialog(self,v): | |
print self.poses, | |
# JSONで書き出しを行わう | |
d = os.path.dirname(cmds.file(q=True, exn=True)) | |
f = cmds.fileDialog(m=1, dm=d+'\/*.poseLib' , title=u'poseLib ファイルを保存します') | |
if f == "": | |
print u"保存をキャンセルしました。", | |
return | |
fp = open(f, 'w') | |
# リストを辞書に変換してJson形式で保存 | |
dictPoses = dict( self.poses ) | |
json.dump( dictPoses , fp , ensure_ascii=False , indent=4 ) | |
# poseLibファイルをロード(json → list) | |
def openFileDialog(self,v): | |
d = os.path.dirname(cmds.file(q=True, exn=True)) | |
f = cmds.fileDialog(m=0, dm=d+'\/*.poseLib' , title=u'poseLib ファイルを読み込みます') | |
if f == "": | |
print u"読み込みをキャンセルしました。", | |
return | |
fp = open(f, 'r') | |
# 読み込んだJsonを2次元配列に変換 | |
jsonData = json.load(fp) | |
tempPoses = jsonData.items() # [('aaaa','1')] この時点ではタプルが含まれているので、下でlistに変換 | |
for i in range(len(tempPoses)): | |
tempPoses[i] = list(tempPoses[i]) | |
self.poses = tempPoses | |
self.listRefresh() | |
# リストの項目をWクリックした際に、ペースト対象となるオブジェクトを選択する( ポーズを作成したいときとかに便利) | |
def selectPasteTgt(self,v): | |
# 選択されているIndexからposesの値を取得し・・・ | |
poseIdx = int( cmds.textScrollList( 'tsl_poseList' , q=True , selectIndexedItem=True )[0]) - 1 | |
paramList = self.poses[poseIdx][1].split(',') # data をアトリビュート毎に分解 | |
# アルファベットの含む要素を、ノード名として収集し・・・ | |
pasteTgtObj = [] | |
for p in paramList: | |
if re.search( '[a-z]|[A-Z]' , p ) : | |
pasteTgtObj.append( p ) | |
# 選択する | |
cmds.select( pasteTgtObj ) | |
# リストの項目をリネームする | |
def renamePose(self,v): | |
poseIdx = int( cmds.textScrollList( 'tsl_poseList' , q=True , selectIndexedItem=True )[0]) - 1 | |
poseName = self.poses[poseIdx][0] # data をアトリビュート毎に分解 | |
# 入力ダイアログを表示 | |
result = cmds.promptDialog( | |
title=u'Rename Object', | |
message=u'Enter Name(日本語は駄目):', | |
button=[u'OK', u'Cancel'], | |
defaultButton=u'OK', | |
cancelButton=u'Cancel', | |
dismissString=u'Cancel', | |
text=poseName | |
) | |
# 決定したら、poses に入力内容を反映 | |
if result == 'OK': | |
renameText = cmds.promptDialog(query=True, text=True) | |
self.poses[poseIdx][0] = renameText | |
self.listRefresh() | |
# 開発中用 インポートして使用した場合は下記は実行されない | |
if __name__ == '__main__' : | |
TRS_PoseLibrary() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment