最終更新: rt_3dcg_tips 2020年05月05日(火) 12:03:45履歴
- ターゲット側のメッシュも予めバインドしておく必要があります
- デフォルト機能のウェイトコピーを使用しているためコピーの精度等はデフォルト機能と同じです
- スキンクラスターが無い、ターゲットが見つからない場合はスクリプトエディターに出力されます
スクリプト本文をkmjCopyWeightMatchName.pyという名前で保存し、スクリプトフォルダにコピー、
スクリプトエディターに下記を入力して実行します。
スクリプトエディターに下記を入力して実行します。
import kmjCopyWeightMatchName kmjCopyWeightMatchName.main()
# -*- coding: utf-8 -*- ################################################### """ ■概要 kmjCopyWeightMatchName ・名前の一致するメッシュにウェイトコピーを行うスクリプトです ・複数メッシュをまとめて処理できます ・バインド先が違うジョイント階層でも構造が同じであればコピーできます ■実行方法 kmjCopyWeightMatchName.pyファイルをスクリプトフォルダにコピーして、スクリプトエディターに下記を入力して実行します。 ###ここから import kmjCopyWeightMatchName kmjCopyWeightMatchName.main() ###ここまで ■注意事項 ・ターゲット側のメッシュも予めバインドしておく必要があります ・デフォルト機能のウェイトコピーを使用しているためコピーの精度等はデフォルト機能と同じです ・スキンクラスターが無い、ターゲットが見つからない場合はスクリプトエディターに出力されます ■更新履歴 ・2019/11/26 作成 ■製作者 作成者:kmj URL:https://seesaawiki.jp/realtime3dcg/ """ ################################################### from functools import partial import maya.cmds as cmds class KMJ_CopyWeightMatchName(object): def __init__(self): self.window = 'kmjCopyWeightMatchName' self.title = 'kmjCopyWeightMatchName' self.size = (640, 680) self.copySkinWeightsInfluenceAssociationOption1_value = self.get_option_value('copySkinWeightsInfluenceAssociationOption1') self.copySkinWeightsInfluenceAssociationOption2_value = self.get_option_value('copySkinWeightsInfluenceAssociationOption2') self.copySkinWeightsInfluenceAssociationOption3_value = self.get_option_value('copySkinWeightsInfluenceAssociationOption3') self.copySkinWeightsNormalize_value = self.get_option_value('copySkinWeightsNormalize') self.copySkinWeightsSurfaceAssociationOption_value = self.get_option_value('copySkinWeightsSurfaceAssociationOption') self.sa_val_list = ["", "closestPoint", "rayCast", "closestComponent", "UV space"] def create(self): if cmds.window('kmjCopyWeightMatchName', exists=True): cmds.deleteUI('kmjCopyWeightMatchName', window=True) self.window = cmds.window( self.window, t=self.title, widthHeight=self.size ) self.layout() cmds.showWindow() # レイアウト def layout(self): self.formLayout01 = cmds.formLayout(numberOfDivisions=100) # ソース側 self.sourceMeshLabel = cmds.text(l='Source meshes:', align='left') self.sourceMeshList = cmds.textScrollList( numberOfRows=8, allowMultiSelection=True ) cmds.textScrollList( self.sourceMeshList, edit=True, deleteKeyCommand=partial(self.removeItemCmd, self.sourceMeshList), selectCommand=partial(self.selectItem, self.sourceMeshList) ) self.buttonAddSource = cmds.button(l='Add Source Meshes', c=partial(self.addMeshesCmd, self.sourceMeshList)) self.buttonSelectSource = cmds.button(l='Find Items by Selection', c=partial(self.selectItemByMeshCmd, self.sourceMeshList)) self.buttonRemoveSource = cmds.button(l='Remove from List', c=partial(self.removeItemCmd, self.sourceMeshList)) self.buttonClearSource = cmds.button(l='Clear', c=partial(self.clearListCmd,self.sourceMeshList)) # ターゲット側 self.targetMeshLabel = cmds.text(l='Target meshes:', align='left') self.targetMeshList = cmds.textScrollList( numberOfRows=8, allowMultiSelection=True ) cmds.textScrollList( self.targetMeshList, edit=True, deleteKeyCommand=partial(self.removeItemCmd, self.targetMeshList), selectCommand=partial(self.selectItem, self.targetMeshList) ) self.buttonAddTarget = cmds.button(l='Add Target Meshes', c=partial(self.addMeshesCmd, self.targetMeshList)) self.buttonSelectTarget = cmds.button(l='Find Items by Selection', c=partial(self.selectItemByMeshCmd, self.targetMeshList)) self.buttonRemoveTarget = cmds.button(l='Remove from List', c=partial(self.removeItemCmd, self.targetMeshList)) self.buttonClearTarget = cmds.button(l='Clear', c=partial(self.clearListCmd,self.targetMeshList)) # Surface Associationラジオボタン self.columnLayout01 = cmds.columnLayout() self.radioButton01 = cmds.radioButtonGrp( label='Surface Association: ', labelArray4=['Closest point on surface', 'Ray cast', 'Closest component', 'UV space'], numberOfRadioButtons=4 ,vr=True) cmds.radioButtonGrp(self.radioButton01, e=True, sl=self.copySkinWeightsSurfaceAssociationOption_value) cmds.setParent('..') # Influence Associationプルダウン self.columnLayout02 = cmds.columnLayout() self.optionMenu01 = cmds.optionMenuGrp(l='Influence Association 1:') cmds.menuItem(self.optionMenu01, l='closestJoint') cmds.menuItem(self.optionMenu01, l='closestBone') cmds.menuItem(self.optionMenu01, l='oneToOne') cmds.menuItem(self.optionMenu01, l='label') cmds.menuItem(self.optionMenu01, l='name') cmds.optionMenuGrp(self.optionMenu01, e=True, sl=self.copySkinWeightsInfluenceAssociationOption1_value) self.optionMenu02 = cmds.optionMenuGrp(l='Influence Association 2:') cmds.menuItem(self.optionMenu02, l='None') cmds.menuItem(self.optionMenu02, l='closestJoint') cmds.menuItem(self.optionMenu02, l='closestBone') cmds.menuItem(self.optionMenu02, l='oneToOne') cmds.menuItem(self.optionMenu02, l='label') cmds.menuItem(self.optionMenu02, l='name') cmds.optionMenuGrp(self.optionMenu02, e=True, sl=self.copySkinWeightsInfluenceAssociationOption2_value) self.optionMenu03 = cmds.optionMenuGrp(l='Influence Association 3:') cmds.menuItem(self.optionMenu03, l='None') cmds.menuItem(self.optionMenu03, l='closestJoint') cmds.menuItem(self.optionMenu03, l='closestBone') cmds.menuItem(self.optionMenu03, l='oneToOne') cmds.menuItem(self.optionMenu03, l='label') cmds.menuItem(self.optionMenu03, l='name') cmds.optionMenuGrp(self.optionMenu03, e=True, sl=self.copySkinWeightsInfluenceAssociationOption3_value) cmds.setParent('..') # Normalizeチェックボックス self.columnLayout03 = cmds.columnLayout() self.checkBox01 = cmds.checkBox(label='Normalize') cmds.checkBox(self.checkBox01, e=True, v=self.copySkinWeightsNormalize_value) cmds.setParent('..') # コピー実行ボタン self.copyButton = cmds.button(label=u'Copy', c=self.duplicateCopyWeight) # 閉じるボタン self.closeButton = cmds.button(label=u'Close', c=('cmds.deleteUI("' + self.window + '")')) # 配置 cmds.formLayout(self.formLayout01, edit=True,\ attachPosition=( (self.sourceMeshLabel, 'top', 0, 0),\ (self.sourceMeshLabel, 'left', 5, 0),\ (self.sourceMeshLabel, 'bottom', 0, 5),\ (self.sourceMeshLabel, 'right', 5, 50),\ (self.sourceMeshList, 'top', 0, 5),\ (self.sourceMeshList, 'left', 5, 0),\ (self.sourceMeshList, 'bottom', 5, 60),\ (self.sourceMeshList, 'right', 5, 50),\ (self.buttonAddSource, 'top', 1, 60),\ (self.buttonAddSource, 'left', 5, 0),\ (self.buttonAddSource, 'bottom', 1, 65),\ (self.buttonAddSource, 'right', 5, 50),\ (self.buttonSelectSource, 'top', 1, 65),\ (self.buttonSelectSource, 'left', 5, 0),\ (self.buttonSelectSource, 'bottom', 1, 70),\ (self.buttonSelectSource, 'right', 5, 50),\ (self.buttonRemoveSource, 'top', 1, 70),\ (self.buttonRemoveSource, 'left', 5, 0),\ (self.buttonRemoveSource, 'bottom', 1, 75),\ (self.buttonRemoveSource, 'right', 5, 50),\ (self.buttonClearSource, 'top', 1, 75),\ (self.buttonClearSource, 'left', 5, 0),\ (self.buttonClearSource, 'bottom', 1, 80),\ (self.buttonClearSource, 'right', 5, 50),\ (self.targetMeshLabel, 'top', 0, 0),\ (self.targetMeshLabel, 'left', 5, 50),\ (self.targetMeshLabel, 'bottom', 0, 5),\ (self.targetMeshLabel, 'right', 5, 100),\ (self.targetMeshList, 'top', 0, 5),\ (self.targetMeshList, 'left', 5, 50),\ (self.targetMeshList, 'bottom', 5, 60),\ (self.targetMeshList, 'right', 5, 100),\ (self.buttonAddTarget, 'top', 1, 60),\ (self.buttonAddTarget, 'left', 5, 50),\ (self.buttonAddTarget, 'bottom', 1, 65),\ (self.buttonAddTarget, 'right', 5, 100),\ (self.buttonSelectTarget, 'top', 1, 65),\ (self.buttonSelectTarget, 'left', 5, 50),\ (self.buttonSelectTarget, 'bottom', 1, 70),\ (self.buttonSelectTarget, 'right', 5, 100),\ (self.buttonRemoveTarget, 'top', 1, 70),\ (self.buttonRemoveTarget, 'left', 5, 50),\ (self.buttonRemoveTarget, 'bottom', 1, 75),\ (self.buttonRemoveTarget, 'right', 5, 100),\ (self.buttonClearTarget, 'top', 1, 75),\ (self.buttonClearTarget, 'left', 5, 50),\ (self.buttonClearTarget, 'bottom', 1, 80),\ (self.buttonClearTarget, 'right', 5, 100),\ (self.columnLayout01, 'top', 5, 80),\ (self.columnLayout01, 'left', 5, 0),\ (self.columnLayout01, 'bottom', 1, 85),\ (self.columnLayout01, 'right', 5, 5),\ (self.columnLayout02, 'top', 5, 80),\ (self.columnLayout02, 'left', 5, 50),\ (self.columnLayout02, 'bottom', 1, 85),\ (self.columnLayout02, 'right', 5, 100),\ (self.columnLayout03, 'top', 5, 90),\ (self.columnLayout03, 'left', 5, 75),\ (self.columnLayout03, 'bottom', 1, 95),\ (self.columnLayout03, 'right', 5, 100),\ (self.copyButton, 'top', 5, 95),\ (self.copyButton, 'left', 30, 0),\ (self.copyButton, 'bottom', 5, 100),\ (self.copyButton, 'right', 30, 50),\ (self.closeButton, 'top', 5, 95),\ (self.closeButton, 'left', 50, 50),\ (self.closeButton, 'bottom', 5, 100),\ (self.closeButton, 'right', 50, 100) ) ) # コマンド # prefからウェイトコピーの設定を読み込み def get_option_value(self, option_name, *args): if cmds.optionVar(exists=option_name) == False: return 1 # 無ければ暫定で1を返す else: option_value = (cmds.optionVar(q=option_name)) return option_value # selectCommand:選択アイテムの実体を選択 def selectItem(self, meshListName, *args): selItem = cmds.textScrollList( meshListName, q=True, selectItem=True) cmds.select(cl=True) cmds.select(selItem) # Add Source(Target) Meshes:選択メッシュをリストに追加 def addMeshesCmd(self, meshListName, *args): allItems = self.getAllItems(meshListName) selectMeshes = self.getSelectMeshes(meshListName) if allItems is not None: for selectMesh in selectMeshes: if selectMesh not in allItems: cmds.textScrollList(meshListName, edit=True, append=selectMesh) else: cmds.textScrollList(meshListName, edit=True, append=selectMeshes) # Find Items by Selection:選択したメッシュをリスト上で選択 def selectItemByMeshCmd(self, meshListName, *args): selectMeshes = self.getSelectMeshes(meshListName) cmds.textScrollList(meshListName, edit=True, deselectAll=True) cmds.textScrollList(meshListName, edit=True, selectItem=selectMeshes) # Clear:リストをクリア def clearListCmd(self, meshListName, *args): cmds.textScrollList(meshListName, edit=True, removeAll=True) # Remove from List:選択アイテムをリストから削除 def removeItemCmd(self, meshListName, *args): selItem = cmds.textScrollList(meshListName, q=True, selectItem=True) if selItem is not None: cmds.textScrollList(meshListName, edit=True, removeItem=selItem) # リスト上のアイテムを全て取得 def getAllItems(self, meshListName, *args): allItems = cmds.textScrollList(meshListName, q=True, allItems=True) return allItems # 選択メッシュを再帰的に取得 def getSelectMeshes(self, meshListName, *args): meshes = [] cmds.select((cmds.ls(sl=True)), hierarchy=True) transformNodes = cmds.ls(sl=True, long=True, type='transform') for transformNode in transformNodes: if (cmds.listRelatives(transformNode, shapes=True)) is not None: meshes.append(transformNode) return meshes # アンドゥインフォ設定 def undoRecord(func): def wrapper(*args, **kwargs): cmds.undoInfo(openChunk=True) try: return func(*args, **kwargs) except Exception, e: raise finally: cmds.undoInfo(closeChunk=True) return wrapper # ウェイトコピー実行部 @undoRecord def duplicateCopyWeight(self, *args): # UIの設定値を読み込み sourceMeshList = cmds.textScrollList(self.sourceMeshList, q=True, ai=True) targetMeshList = cmds.textScrollList(self.targetMeshList, q=True, ai=True) sa_val_idx = cmds.radioButtonGrp(self.radioButton01, q=True, sl=True) sa_val = self.sa_val_list[sa_val_idx] ia_val = [] ia1_val = cmds.optionMenuGrp(self.optionMenu01, q=True, v=True) ia_val.append(ia1_val) ia2_val = cmds.optionMenuGrp(self.optionMenu02, q=True, v=True) if ia2_val != 'None': ia_val.append(ia2_val) ia3_val = cmds.optionMenuGrp(self.optionMenu03, q=True, v=True) if ia3_val != 'None': ia_val.append(ia3_val) normalize_val = cmds.checkBox(self.checkBox01, q=True, v=True) for targetMesh in targetMeshList: target_sc = self.get_skinCluster(targetMesh) if target_sc is not None: sourceMesh = self.name_matching(targetMesh, sourceMeshList) if sourceMesh is not None: source_sc = self.get_skinCluster(sourceMesh) if source_sc is not None: print ("sourceMesh:" + str(sourceMesh) + ", targetMesh:" + str(targetMesh)) if sa_val_idx < 4: # UV設定だった場合別処理 print("ss=" + str(source_sc[0]) + ", ds=" + str(target_sc[0]) + ", nr=" + str(normalize_val) + ", sa=" + str(sa_val) + ", ia=" + str(ia_val)) cmds.copySkinWeights(ss=source_sc[0], ds=target_sc[0], nm=True, nr=normalize_val, sa=sa_val, ia=(ia_val)) else: sourceUVSet = self.get_currentUVSet(sourceMesh) targetUVSet = self.get_currentUVSet(targetMesh) print("ss=" + str(source_sc[0]) + ", ds=" + str(target_sc[0]) + ", nr=" + str(normalize_val) + ", sa=" + str(sa_val) + ", ia=" + str(ia_val)) cmds.copySkinWeights(ss=source_sc[0], ds=target_sc[0], nm=True, nr=normalize_val, uv=(str(sourceUVSet), str(targetUVSet)), ia=(ia_val)) else: print(sourceMesh + " : skinCluster is not found") else: print(targetMesh + " : source mesh is not found") else: print(targetMesh + " : skinCluster is not found") # UVセット取得 def get_currentUVSet(self, obj, *args): currentUVSet = cmds.polyUVSet(obj, q=True, cuv=True) return currentUVSet # ヒストリからskinClusterを検索して返す def get_skinCluster(self, obj, *args): history = cmds.ls(cmds.listHistory(obj), type='skinCluster') if history == []: return None else: return history # ネームスペースやパスを削除した名前を取得 def get_shortname(self, name, *args): name = name.rsplit("|", 1) name = name[-1].rsplit(":", 1) return name[-1] # ターゲットリストに同じ名前があれば返す(ひとつ目を見つけると終了) def name_matching(self, obj_name, searchobj_list, *args): obj_name_short = self.get_shortname(obj_name) for searchobj in searchobj_list: searchobj_short = self.get_shortname(searchobj) if obj_name_short == searchobj_short: return searchobj return None def main(): kmjCopyWeightMatchNameWindow = KMJ_CopyWeightMatchName() kmjCopyWeightMatchNameWindow.create()
タグ
コメントをかく