# -*- 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()