CG・コンテンツ制作
  1. CG・コンテンツ制作トップ
  2. DAIKIN CG Channel
  3. UsersNotes
  4. スクリプト
  5. Maya Pythonで作成するFBXモーションリターゲットツール
SUITE USERS NOTES

Maya Pythonで作成するFBXモーションリターゲットツール
Making FBX Motion Retarget Tool with Maya Python

プロローグ;Prologue

CG Channel Week 2024の講演内容である
「Maya Pythonで作成するFBXモーションリターゲットツール」
の内容を文字ベースで残しておこうと考えました。

MayaのバージョンはMaya 2024で作業をしています。
それはこのバージョンでNodeEditorの扱えるノードに「数学」という項目が増えたこと によります。このことで「MayaNodeEditorを使ったアップベクター作成」で紹介しているアップベクターの計算が非常に楽になっているからです。

このモーションリターゲットツールは、アップベクターの計算が仕組まれたMayaシーンを中間ファイルとして利用している、という点で一連の関連性があります。
その部分を除けば、全ての工程をスクリプトで処理している例として紹介しています。

ツールを作成している方は、ぜひチャレンジしてみてください!
<多少の前知識は必要かもですが...>

もちろん、Pythonの機能紹介を基本としたページですが、
Python3に対応したツールだけをダウンロードして利用することも可能です。

なお、このサイトに掲載している事例の決まり事ですが、
使用に関しては自己責任でよろしくお願い申し上げます。

Please use this information as your own risk.

「Maya Pythonで作成するFBXモーションリターゲットツール」

Windows 10 Pro 64bit、Intel Core i7-3930K(3.20GHz、6コア/12スレッド)
メモリ16GB、NVIDIA GeForce RTX 4060Ti 16GB


maya_13_ritaro_ml



ワークフロー について

始めにMVMというモーションキャプチャー機材を使って収録されたFBXがあり、そのデータをmGearで作成されたキャラクターのコントロールリグにアニメーションを乗せるまでをPythonで作成したツールのみで実装するという、ワークフローを紹介します。

MAYA_16_CGCW2024_011

スクリプティングの内容としては、FBXを扱う部分やHIKの対応としてキャラクタライズを行う部分、既存のシーンにデータを読み込む部分や不要になったデータの削除方法など、色々なノウハウが詰め込まれているので、興味を持った部分だけでも見てもらえたら幸いです。




モーションキャプチャーしたFBXのスクリプティングについて
・MVNのキャプチャーデータ(240FPS)

会社内の会議室等を使って気軽にモーションキャプチャーができるMVNでモーションを収録した、とします。
キャプチャーした.mvn形式のデータは、そのソフトウェアでリプロセッシングを行った後FBXに変換します。
FBXに出力する時、オプションで3を指定すれば、60FPSに変換して出力することが出来ます。

MAYA_16_CGCW2024_012
・FBXのインポート

MayaでFBXを扱う時fbxmaya.mllプラグインがロードされている必要があります。
「MayaでFBXが読み込めない」とユーザーから報告が来る前に、スクリプトで自動的にfbxmaya.mllプラグインをロードしてくれるようにしておきましょう。

# fbxmaya.mll のロード
try:
    if not cmds.pluginInfo("fbxmaya",q=True,loaded=True):
        cmds.loadPlugin( 'fbxmaya' )
        cmds.pluginInfo( 'fbxmaya', edit=True, autoload=True )
except:
    print('FBX plugin is not installed')

まずは新規Mayaシーンを準備します。タイムラインも新規シーンの最初のフレームに設定します。

# 新規Mayaシーン
cmds.file( force=True, new=True)
cmds.select(clear=True)

o_min = cmds.playbackOptions(query=True,animationStartTime=True)
o_max = cmds.playbackOptions(query=True,animationEndTime=True)
cmds.currentTime( o_min )

新規シーンをロードすると、カメラが中心を見てしまいます。
そこで、入力した物が見えるように設定するのも1つの手です。

# 新規Mayaシーンのカメラの設定
cmds.setAttr('persp.translate', 350, 360, 340, type="double3")
cmds.setAttr('persp.rotate', -28, 45, 0, type="double3")
cmds.setAttr('perspShape.centerOfInterest', 448.000)

FBX関連のスクリプティングはMELになります。
PythonでMELを扱うにはmel.evalを使います。
FBXの入力設定はリセットをしてから追記します

# FBXの入力設定
mel.eval('FBXResetImport')
mel.eval('FBXImportConvertUnitString "cm"')
mel.eval('FBXImportFillTimeline -v false;')

FBXの入力モードをmergeとして設定します。

● FBXImportMode ; FBXImportMode -v [exmerge|add|merge;]

https://download.autodesk.com/global/docs/maya2014/ja_jp/index.html?url=files/GUID-FE7D0C63-3144-47D6-98C8-AFBCDE6FB818.htm,topicNumber=d30e151464新しいウィンドウで開きます

exmerge ;FBX入力画面のアニメーションを更新(Update animation)
add ;FBX入力画面の追加(Add)
merge ;FBX入力画面のアニメーションを追加と更新(Add and update animation)と同じです。
# FBXの入力モード merge を使用
mel.eval('FBXImportMode -v merge;')

FBXのフルパス(ファイル名)をGUIから取得して入力を実行します。

# FBXのフルパスから入力
mvn_fbx_file = cmds.textFieldButtonGrp('mvn_fbx_file_name', query=True,text=True)
mvn_fbx_file_name = os.path.basename(mvn_fbx_file)

mel.eval('string $import_filepass = "%s"' %  mvn_fbx_file)
mel.eval('FBXImport -file $import_filepass;')

ここまでの説明をGUIを設定してFBXを読み込んだ状態はこのようになります。

MAYA_16_CGCW2024_013

スクリプトは以下のようになります。

# -*- coding: utf-8 -*-

"""!
Import MotionBuilder MVN FBX and Retarget Motion to mGear Rig V 2.2
              - 1st Step

@file
@author TA Ritaro

"""
import pymel.core as pm
import maya.cmds as cmds
import maya.mel as mel
import os
import imp, os.path, sys

import glob
import shutil
import _thread
import math


def do_import_mvn_fbx(*args):

    if  cmds.textFieldButtonGrp('mvn_fbx_file_name', query=True,text=True) == '':
        cmds.confirmDialog( title='ERROR', message='Set Import FBX File!!  ')
        _thread.exit()

    cmds.file( force=True, new=True)
    cmds.select(clear=True)

    o_min = cmds.playbackOptions(query=True,animationStartTime=True)
    o_max = cmds.playbackOptions(query=True,animationEndTime=True)
    cmds.currentTime( o_min )

    cmds.setAttr('persp.translate', 350, 360, 340, type="double3")
    cmds.setAttr('persp.rotate', -28, 45, 0, type="double3")
    cmds.setAttr('perspShape.centerOfInterest', 448.000)

    mel.eval('FBXResetImport')
    mel.eval('FBXImportConvertUnitString "cm"')
    mel.eval('FBXImportFillTimeline -v false;')

    mel.eval('FBXImportMode -v merge;')

    mvn_fbx_file = cmds.textFieldButtonGrp('mvn_fbx_file_name', query=True,text=True)
    mvn_fbx_file_name = os.path.basename(mvn_fbx_file)

    print(mvn_fbx_file)

    mel.eval('string $import_filepass = "%s"' %  mvn_fbx_file)
    mel.eval('FBXImport -file $import_filepass;')


#*----------------

def set_mvnfbx_file(*args):
    if cmds.optionVar( exists='ri_mvn_fbx_importpass' ):
        mvn_fbx_dir = cmds.optionVar( q='ri_mvn_fbx_importpass' )
    else:
        mvn_fbx_dir = os.path.dirname(cmds.file( query=True, sceneName=True))

    multipleFilters = "All Files (*.*)"
    import_mvn_fbx_dir_name = cmds.fileDialog2(fileMode=1,fileFilter=multipleFilters,
        dialogStyle=2,startingDirectory=mvn_fbx_dir)

    if import_mvn_fbx_dir_name:
        cmds.textFieldButtonGrp('mvn_fbx_file_name', edit=True, text=import_mvn_fbx_dir_name[0])
        cmds.optionVar( sv=('ri_mvn_fbx_importpass', import_mvn_fbx_dir_name[0] ) )


def import_mvn_fbx_retarget_to_mgear_rig(*args):
    try:
        if not cmds.pluginInfo("fbxmaya",q=True,loaded=True):
            cmds.loadPlugin( 'fbxmaya' )
            cmds.pluginInfo( 'fbxmaya', edit=True, autoload=True )
    except:
        print('FBX plugin is not installed')

    if cmds.optionVar( exists='ri_mvn_fbx_importpass' ):
        mvn_fbx_dir = cmds.optionVar( q='ri_mvn_fbx_importpass' )
    else:
        mvn_fbx_dir = ""



    if cmds.window('ImportMVNFBX_ToMGEAR', exists=True):
        cmds.deleteUI('ImportMVNFBX_ToMGEAR')

    window = cmds.window('ImportMVNFBX_ToMGEAR', title='Import MVN FBX and retarget to mGear Rig  V2.2',
        sizeable=True, menuBar=True, topLeftCorner=[200, 200], widthHeight=(200, 200))
    cmds.menu( label='File' )
    cmds.menuItem( label='exit', command='cmds.deleteUI( "ImportMVNFBX_ToMGEAR" )' )
    cmds.columnLayout(columnAttach=('both', 5), rowSpacing=8, columnWidth=200, adjustableColumn=True)

    cmds.text(label='Import MVN FBX and Retarget to mGear Rig   V2.2 - @ Ritaro',
        width=200,bgc=[0.2,0.2,0.2],align='center' )

    cmds.separator(height=3,style='out')

    cmds.textFieldButtonGrp('mvn_fbx_file_name',label='MVN FBX File  ',
        text=mvn_fbx_dir, buttonLabel='Select Motion FBX',
        columnWidth3=[100,300,30],adjustableColumn=2,buttonCommand=set_mvnfbx_file)


    cmds.text(label='',width=5)


    cmds.separator(height=7)
    cmds.separator(height=1,style=None)
    cmds.rowLayout(numberOfColumns=3,adjustableColumn=2)
    cmds.text(label='',width=10)
    cmds.button('import_mobu_fbx_bot',label='Import MVN FBX and Retarget to mGear Rig !',
        width=80,bgc=[1.0,0.2,0.2],command=do_import_mvn_fbx)
    cmds.text(label='',width=10)
    cmds.setParent('..')
    cmds.text(label='',width=10)
    cmds.showWindow(window)


import_mvn_fbx_retarget_to_mgear_rig()
・60FPS化(Butterworthフィルターの適応)

FBXを読み込んだ時にFPSをチェックして、60FPSにするという仕組みを入れておくと良いでしょう。
Mayaは時間設定をUnitで行うので、60FPSで設定したい場合はUnitをntscfとして設定します。

# 60FPS化
def setFPS(value):
    if value == 15:     unit = 'game'
    elif value == 24:   unit = 'film'
    elif value == 25:   unit = 'pal'
    elif value == 30:   unit = 'ntsc'
    elif value == 48:   unit = 'show'
    elif value == 50:   unit = 'palf'
    elif value == 60:   unit = 'ntscf'
    else:               unit = '%sfps' % fps
    cmds.currentUnit(time=unit)

setFPS(60)
・Butterworthフィルターの適応

アニメーションのキーにフィルターカーブ処理をします。
Butterworthフィルターの設定にも60FPS指定が出来ます。便利!

● filterCurve

https://help.autodesk.com/cloudhelp/2022/JPN/Maya-Tech-Docs/CommandsPython/filterCurve.html新しいウィンドウで開きます

filter(f) ;フィルタ タイプを指定 butterworth
samplingRate(sr) ;Fフィルタに追加されるレートをフレーム/秒(FPS)で定義
keepKeysOnFrame(kof) ;フレーム全体にキーを配置する

※読み込んだFBXのアニメーションの最終フレーム値(例;last_key_value)が欲しい。
 > HipsのX移動値から取得する、とします。

# TopノードからJoint階層の全選択
o_top_obj = "Reference"
cmds.select(o_top_obj,r=True)
cmds.select(hierarchy=True)
all_joints = cmds.ls(selection=True)

# Butterworthフィルターを 60FPS で設定 
cmds.select(all_joints,add=True )
mel.eval('string $last_key_value = "%s"' %  last_key_value)
mel.eval('filterCurve -f butterworth -cof 7 -sr 60 -kof -s 0 -e $last_key_value ;')

# 最終フレーム値の取得
for o_ch3 in all_joints:
    if 'Hips' in o_ch3:
        o_hip = o_ch3
        break

cmds.selectKey(o_hip,replace=True,attribute='translateX')
all_key_list =  cmds.keyframe( query=True, selected=True)

key_length = len(all_key_list) -1

last_key_value = math.ceil(all_key_list[key_length])
● math.ceil(x)

https://docs.python.org/ja/3/library/math.html新しいウィンドウで開きます

x 以上の最小の整数を返します。

MAYA_16_CGCW2024_014

カーブにフィルターがかかり、最終フレーム値を取得している状態です。

・HIKの自動定義化(キャラクタライズ)

MayaのHIK画面のJointの定義(キャラクタライズ)は、番号で場所が決められています。
例えば、0:Reference、1:Hips 決まっていて、全部で212個あります。
同じJoint名を持つ階層構造のデータなら、定義を一度作成すれば他のキャラクターにも使えます。

番号に対する名前はプリントして見ることが出来ます。

# Joint定義化時の番号と名前

hik_count = cmds.hikGetNodeCount()
print(hik_count)   #== 212

for i in range( hik_count ):
    list = cmds.GetHIKNodeName( i )
    print(i,list)

プリント結果

0 Reference
1 Hips
2 LeftUpLeg
3 LeftLeg
4 LeftFoot
5 RightUpLeg
6 RightLeg
7 RightFoot
8 Spine
9 LeftArm
10 LeftForeArm
11 LeftHand
12 RightArm
13 RightForeArm
14 RightHand
15 Head
16 LeftToeBase
17 RightToeBase
18 LeftShoulder
19 RightShoulder
20 Neck
21 LeftFingerBase
22 RightFingerBase
23 Spine1
24 Spine2
 ~
210 LeafRightArmRoll5
211 LeafRightForeArmRoll5

順番としてはLeftが先でRightが後、Spineは8番だけど、Spine1以降は23番から始まります。

番号と名前から対応するMVNのJoint名を設定してキャラクタライズ用の定義が完成します。

# キャラクタの作成
mel.eval('HIKCharacterControlsTool') # キャラクタコントロールの作成

MAYA_LOCATION = os.environ['MAYA_LOCATION']
mel.eval('source "'+MAYA_LOCATION+'/scripts/others/hikGlobalUtils.mel"')
mel.eval('source "'+MAYA_LOCATION+'/scripts/others/hikCharacterControlsUI.mel"')
mel.eval('source "'+MAYA_LOCATION+'/scripts/others/hikDefinitionOperations.mel"')

mel.eval('hikCreateDefinition()')  #キャラクタに Character1 が作成される!!!!

mel.eval('setCharacterObject("Reference", "Character1",0,0);') # Joint名と番号で定義化
mel.eval('setCharacterObject("Hips", "Character1",1,0);')
mel.eval('setCharacterObject("LeftUpLeg", "Character1",2,0);')
mel.eval('setCharacterObject("LeftLeg", "Character1",3,0);')
#~
mel.eval('setCharacterObject("Neck", "Character1",20,0);')
#~
mel.eval('setCharacterObject("RightHandPinky3","Character1",92,0);')
mel.eval('hikUpdateDefinitionUI;') # 画面の更新
MAYA_16_CGCW2024_015

キャラクターの定義化が設定され、HIK画面のキャラクタに Character1が作成されている状態です。

・HIKのコントロールリグの作成

Joint名の定義化(キャラクタライズ)が済めば、それに対応するコントロールリグの作成がスクリプトから実行出来ます。

コントロールリグを作成します。

# コントロールリグの作成
mel.eval('hikCreateControlRig;')

# 出来たコントロールリグを親子化
cmds.parent('Character1_Ctrl_Reference', 'Reference')
HIKのコントロールリグの作成

コントロールリグが作成された状態です。

HIKの画面を更新し、一旦コントロールリグのソースをなしにします。

# HIK画面の更新
char_name = mel.eval('hikGetCurrentCharacter()')
print(char_name)

# 一旦作成したコントロールリグをソースから外す
mel.eval('string $char_name = "%s"' % char_name)
cmd='hikEnableCharacter( $char_name, 2 );'
mel.eval(cmd)
mel.eval('hikUpdateSourceList()')
mel.eval('hikUpdateCurrentSourceFromUI()')
mel.eval('hikUpdateContextualUI()')
mel.eval('hikControlRigSelectionChangedCallback')
HIKのコントロールリグの作成

コントロールリグのソースをなしにした状態です。

・HIKのコントロールリグへのベイク

コントロールリグを作成し、一旦ソースを外したら、コントロールリグにアニメーションをベイクします。

# Bake to Contrl Rig
mel.eval('hikBakeToControlRig 0;')
HIKのコントロールリグの作成

コントロールリグにアニメーションがベイクされた状態です。

ここまでの説明のスクリプトです。

# -*- coding: utf-8 -*-

"""!
Import MotionBuilder MVN FBX and Retarget Motion to mGear Rig V 2.2
              - 1st Step
              - 2nd Step
              - 3rd Step
              - 4rd Step
@file
@author TA Ritaro

"""
import pymel.core as pm
import maya.cmds as cmds
import maya.mel as mel
import os
import imp, os.path, sys

import glob
import shutil
import _thread
import math

def characterizeBaked(*args):

    # Load Plugins
    if not cmds.pluginInfo('mayaHIK',q=True,l=True):
        cmds.loadPlugin('mayaHIK')
    if not cmds.pluginInfo('mayaCharacterization',q=True,l=True):
        cmds.loadPlugin('mayaCharacterization')
    if not cmds.pluginInfo('retargeterNodes',q=True,l=True):
        cmds.loadPlugin('retargeterNodes')

    # HIK Character Controls Tool
    mel.eval('HIKCharacterControlsTool')

    # Source the following scripts. If the code fails, then you'll have to load them and run them:
    MAYA_LOCATION = os.environ['MAYA_LOCATION']
    mel.eval('source "'+MAYA_LOCATION+'/scripts/others/hikGlobalUtils.mel"')
    mel.eval('source "'+MAYA_LOCATION+'/scripts/others/hikCharacterControlsUI.mel"')
    mel.eval('source "'+MAYA_LOCATION+'/scripts/others/hikDefinitionOperations.mel"')

    mel.eval('hikCreateDefinition()')

    mel.eval('setCharacterObject("Reference", "Character1",0,0);')
    mel.eval('setCharacterObject("Hips", "Character1",1,0);')
    mel.eval('setCharacterObject("LeftUpLeg", "Character1",2,0);')
    mel.eval('setCharacterObject("LeftLeg", "Character1",3,0);')
    mel.eval('setCharacterObject("LeftFoot", "Character1",4,0);')
    mel.eval('setCharacterObject("RightUpLeg", "Character1",5,0);')
    mel.eval('setCharacterObject("RightLeg", "Character1",6,0);')
    mel.eval('setCharacterObject("RightFoot", "Character1",7,0);')
    mel.eval('setCharacterObject("Spine", "Character1",8,0);')
    mel.eval('setCharacterObject("LeftArm", "Character1",9,0);')
    mel.eval('setCharacterObject("LeftForeArm", "Character1",10,0);')
    mel.eval('setCharacterObject("LeftHand", "Character1",11,0);')
    mel.eval('setCharacterObject("RightArm", "Character1",12,0);')
    mel.eval('setCharacterObject("RightForeArm", "Character1",13,0);')
    mel.eval('setCharacterObject("RightHand", "Character1",14,0);')
    mel.eval('setCharacterObject("Head", "Character1",15,0);')
    mel.eval('setCharacterObject("LeftToeBase", "Character1",16,0);')
    mel.eval('setCharacterObject("RightToeBase", "Character1",17,0);')
    mel.eval('setCharacterObject("LeftShoulder", "Character1",18,0);')
    mel.eval('setCharacterObject("RightShoulder", "Character1",19,0);')
    mel.eval('setCharacterObject("Neck", "Character1",20,0);')
    mel.eval('setCharacterObject("Spine1", "Character1",23,0);')
    mel.eval('setCharacterObject("Spine2", "Character1",24,0);')
    mel.eval('setCharacterObject("Spine3", "Character1",25,0);')
    mel.eval('setCharacterObject("LeftHandThumb1","Character1",50,0);')
    mel.eval('setCharacterObject("LeftHandThumb2","Character1",51,0);')
    mel.eval('setCharacterObject("LeftHandThumb3","Character1",52,0);')
    mel.eval('setCharacterObject("LeftHandIndex1","Character1",54,0);')
    mel.eval('setCharacterObject("LeftHandIndex2","Character1",55,0);')
    mel.eval('setCharacterObject("LeftHandIndex3","Character1",56,0);')
    mel.eval('setCharacterObject("LeftHandMiddle1","Character1",58,0);')
    mel.eval('setCharacterObject("LeftHandMiddle2","Character1",59,0);')
    mel.eval('setCharacterObject("LeftHandMiddle3","Character1",60,0);')
    mel.eval('setCharacterObject("LeftHandRing1","Character1",62,0);')
    mel.eval('setCharacterObject("LeftHandRing2","Character1",63,0);')
    mel.eval('setCharacterObject("LeftHandRing3","Character1",64,0);')
    mel.eval('setCharacterObject("LeftHandPinky1","Character1",66,0);')
    mel.eval('setCharacterObject("LeftHandPinky2","Character1",67,0);')
    mel.eval('setCharacterObject("LeftHandPinky3","Character1",68,0);')
    mel.eval('setCharacterObject("RightHandThumb1","Character1",74,0);')
    mel.eval('setCharacterObject("RightHandThumb2","Character1",75,0);')
    mel.eval('setCharacterObject("RightHandThumb3","Character1",76,0);')
    mel.eval('setCharacterObject("RightHandIndex1","Character1",78,0);')
    mel.eval('setCharacterObject("RightHandIndex2","Character1",79,0);')
    mel.eval('setCharacterObject("RightHandIndex3","Character1",80,0);')
    mel.eval('setCharacterObject("RightHandMiddle1","Character1",82,0);')
    mel.eval('setCharacterObject("RightHandMiddle2","Character1",83,0);')
    mel.eval('setCharacterObject("RightHandMiddle3","Character1",84,0);')
    mel.eval('setCharacterObject("RightHandRing1","Character1",86,0);')
    mel.eval('setCharacterObject("RightHandRing2","Character1",87,0);')
    mel.eval('setCharacterObject("RightHandRing3","Character1",88,0);')
    mel.eval('setCharacterObject("RightHandPinky1","Character1",90,0);')
    mel.eval('setCharacterObject("RightHandPinky2","Character1",91,0);')
    mel.eval('setCharacterObject("RightHandPinky3","Character1",92,0);')

    mel.eval('hikUpdateDefinitionUI;')


    mel.eval('hikCreateControlRig;')
    cmds.parent('Character1_Ctrl_Reference', 'Reference')

    char_name = mel.eval('hikGetCurrentCharacter()')
    print(char_name)
    mel.eval('string $char_name = "%s"' % char_name)
    cmd='hikEnableCharacter( $char_name, 2 );'
    mel.eval(cmd)
    mel.eval('hikUpdateSourceList()')
    mel.eval('hikUpdateCurrentSourceFromUI()')
    mel.eval('hikUpdateContextualUI()')
    mel.eval('hikControlRigSelectionChangedCallback')

    # Bake to Contrl Rig
    mel.eval('hikBakeToControlRig 0;')


def setFPS(value):
    if value == 15:     unit = 'game'
    elif value == 24:   unit = 'film'
    elif value == 25:   unit = 'pal'
    elif value == 30:   unit = 'ntsc'
    elif value == 48:   unit = 'show'
    elif value == 50:   unit = 'palf'
    elif value == 60:   unit = 'ntscf'
    else:               unit = '%sfps' % fps
    cmds.currentUnit(time=unit)

def do_import_mvn_fbx(*args):

    if  cmds.textFieldButtonGrp('mvn_fbx_file_name', query=True,text=True) == '':
        cmds.confirmDialog( title='ERROR', message='Set Import FBX File!!  ')
        _thread.exit()

    cmds.file( force=True, new=True)
    cmds.select(clear=True)

    o_min = cmds.playbackOptions(query=True,animationStartTime=True)
    o_max = cmds.playbackOptions(query=True,animationEndTime=True)
    cmds.currentTime( o_min )

    cmds.setAttr('persp.translate', 350, 360, 340, type="double3")
    cmds.setAttr('persp.rotate', -28, 45, 0, type="double3")
    cmds.setAttr('perspShape.centerOfInterest', 448.000)

    mel.eval('FBXResetImport')
    mel.eval('FBXImportConvertUnitString "cm"')
    mel.eval('FBXImportFillTimeline -v false;')

    mel.eval('FBXImportMode -v merge;')

    mvn_fbx_file = cmds.textFieldButtonGrp('mvn_fbx_file_name', query=True,text=True)
    mvn_fbx_file_name = os.path.basename(mvn_fbx_file)

    print(mvn_fbx_file)

    mel.eval('string $import_filepass = "%s"' %  mvn_fbx_file)
    mel.eval('FBXImport -file $import_filepass;')


    setFPS(60)


# Set Start End frame from Hip Key

    o_top_obj = "Reference"
    cmds.select(o_top_obj,r=True)
    cmds.select(hierarchy=True)
    all_joints = all_joints = cmds.ls(selection=True)



    for o_ch3 in all_joints:
        if 'Hips' in o_ch3:
            o_hip = o_ch3
            break

#    print(o_hip)

    cmds.selectKey(o_hip,replace=True,attribute='translateX')
    all_key_list =  cmds.keyframe( query=True, selected=True)

    try:
        start_key = all_key_list[0]
    except:
        cmds.confirmDialog( title='WARNING', message='No Animation on Hip Joint !!')
        _thread.exit() 

    print ("start_key",start_key)
    key_length = len(all_key_list) -1
    last_key = start_key + key_length
    last_key_value = math.ceil(all_key_list[key_length])
    print ("last_key_value",last_key_value)

    cmds.playbackOptions(edit=True,animationStartTime=start_key)
    cmds.playbackOptions(edit=True,animationEndTime=last_key_value)
    cmds.playbackOptions(edit=True,minTime=start_key)
    cmds.playbackOptions(edit=True,maxTime=last_key_value)
    cmds.currentTime(start_key, edit=True)


#*---- Do Butterworth Filter With 60 FPS

    cmds.select(all_joints,add=True )
    mel.eval('string $last_key_value = "%s"' %  last_key_value)
    mel.eval('filterCurve -f butterworth -cof 7 -sr 60 -kof -s 0 -e $last_key_value ;')


#*---- Do HIK Charactrize & Bake to Control Rig

    characterizeBaked()



#*----------------

def set_mvnfbx_file(*args):
    if cmds.optionVar( exists='ri_mvn_fbx_importpass' ):
        mvn_fbx_dir = cmds.optionVar( q='ri_mvn_fbx_importpass' )
    else:
        mvn_fbx_dir = os.path.dirname(cmds.file( query=True, sceneName=True))

    multipleFilters = "All Files (*.*)"
    import_mvn_fbx_dir_name = cmds.fileDialog2(fileMode=1,fileFilter=multipleFilters,
        dialogStyle=2,startingDirectory=mvn_fbx_dir)

    if import_mvn_fbx_dir_name:
        cmds.textFieldButtonGrp('mvn_fbx_file_name', edit=True, text=import_mvn_fbx_dir_name[0])
        cmds.optionVar( sv=('ri_mvn_fbx_importpass', import_mvn_fbx_dir_name[0] ) )


def import_mvn_fbx_retarget_to_mgear_rig(*args):
    try:
        if not cmds.pluginInfo("fbxmaya",q=True,loaded=True):
            cmds.loadPlugin( 'fbxmaya' )
            cmds.pluginInfo( 'fbxmaya', edit=True, autoload=True )
    except:
        print('FBX plugin is not installed')

    if cmds.optionVar( exists='ri_mvn_fbx_importpass' ):
        mvn_fbx_dir = cmds.optionVar( q='ri_mvn_fbx_importpass' )
    else:
        mvn_fbx_dir = ""



    if cmds.window('ImportMVNFBX_ToMGEAR', exists=True):
        cmds.deleteUI('ImportMVNFBX_ToMGEAR')

    window = cmds.window('ImportMVNFBX_ToMGEAR', title='Import MVN FBX and retarget to mGear Rig  V2.2',
        sizeable=True, menuBar=True, topLeftCorner=[200, 200], widthHeight=(200, 200))
    cmds.menu( label='File' )
    cmds.menuItem( label='exit', command='cmds.deleteUI( "ImportMVNFBX_ToMGEAR" )' )
    cmds.columnLayout(columnAttach=('both', 5), rowSpacing=8, columnWidth=200, adjustableColumn=True)

    cmds.text(label='Import MVN FBX and Retarget to mGear Rig   V2.2 - @ Ritaro',
        width=200,bgc=[0.2,0.2,0.2],align='center' )

    cmds.separator(height=3,style='out')

    cmds.textFieldButtonGrp('mvn_fbx_file_name',label='MVN FBX File  ',
        text=mvn_fbx_dir, buttonLabel='Select Motion FBX',
        columnWidth3=[100,300,30],adjustableColumn=2,buttonCommand=set_mvnfbx_file)


    cmds.text(label='',width=5)


    cmds.separator(height=7)
    cmds.separator(height=1,style=None)
    cmds.rowLayout(numberOfColumns=3,adjustableColumn=2)
    cmds.text(label='',width=10)
    cmds.button('import_mobu_fbx_bot',label='Import MVN FBX and Retarget to mGear Rig !',
        width=80,bgc=[1.0,0.2,0.2],command=do_import_mvn_fbx)
    cmds.text(label='',width=10)
    cmds.setParent('..')
    cmds.text(label='',width=10)
    cmds.showWindow(window)


import_mvn_fbx_retarget_to_mgear_rig()



キャラクターのリグコントローラへ
リターゲッティングをするスクリプティングについて

・事前HIK化されたキャラクターMayaシーンデータの読み込み(UpVector設定済み)

mGearで作成したキャラクターのリグコントローラーをキャラクタライズ(HIK)したMayaシーンデータは事前に用意しておきます。
UpVectorの位置を計算できるようにノードエディタで仕組みを仕込んであるとします。
この事前設定をしたMayaシーンを追加で読み込んできます。

# Mayaシーンデータの読み込み

import_SK_Rig_HIK_file = cmds.textFieldButtonGrp('retarget_maya_scene', query=True,text=True)
mel.eval('string $import_SK_Rig_HIK_file = "%s"' %  import_SK_Rig_HIK_file)

mel.eval('file -import -type "mayaAscii"  
    -ignoreVersion -ra true -mergeNamespacesOnClash true -namespace ":" 
    -options "v=0;"  -pr  -importTimeRange "combine" $import_SK_Rig_HIK_file ;')

# HIK画面の更新
mel.eval('HIKCharacterControlsTool')
mel.eval('hikUpdateContextualUI()')
mel.eval('hikControlRigSelectionChangedCallback')

# Maya画面の更新
cmds.colorManagementPrefs(edit=True,cmEnabled=False)
mel.eval('setAttr "hardwareRenderingGlobals.floatingPointRTEnable" 1;')
mel.eval('setAttr "hardwareRenderingGlobals.defaultLightIntensity" 1;')
mel.eval('modelEditor -e -displayTextures true modelPanel4;')
中間データとして使うmGearのリグにHIKの設定がされているシーン。

中間データとして使うmGearのリグにHIKの設定がされているシーン。
ノードエディタでアップベクターの位置が計算されている。

この事前設定をしたMayaシーンを追加で読み込んできます。

・HIKのキャラクタとソースの指定

MayaのHIK画面のキャラクタとソースの指定をスクリプティングで設定するには少しコツが要ります。
HIK画面のキャラクタとソース名はプルダウンメニューになっていて、名前ではなくリスト番号が返ってきます。

例えば  
キャラクター リストの変更 3番目 = mGear_Mocap_interface
ソース リストの変更 2番目 = Character1
で設定したい場合、  
# キャラクタとソースのリスト番号での指定

optChrMenu = "hikCharacterList|OptionMenu"
cmds.optionMenu(optChrMenu, edit=True, select=3)
mel.eval('hikUpdateCurrentCharacterFromUI()')

optSourceMenu = "hikSourceList|OptionMenu"
cmds.optionMenu(optSourceMenu, edit=True, select=2)
mel.eval('hikUpdateCurrentSourceFromUI()')

mel.eval('hikUpdateContextualUI()')
mel.eval('hikControlRigSelectionChangedCallback')

と、selectの後の番号で指定すれば良い気がしますが、この名前の上からの順番は、変化するので注意が必要です。
つまり番号だけを信じてスクリプティングをすると思わぬミスを引き起こすことを意味しています。

そこで、ちゃんと目当ての名前が出て来た時の番号を取得してその番号で指定する方法に変更しておきます。

★ キャラクタ; #* Get Character from List

MayaシーンにあるHIKのキャラクタのリストの名前をプリントしてみると更なる問題を見つけます。
キャラクタ名を表示してみましょう。

#キャラクタ名の取得 

allChar = cmds.optionMenuGrp("hikCharacterList", query=True, itemListLong=True)

for i in range(0, len(allChar)):
    char_name = cmds.menuItem(allChar[i], query=True, label=True)
    print(i,char_name))

その結果は...

0 なし
1 mGear_Mocap_interface

と最初の番号0、設定が None ですが、日本語の”なし”で返されるのはちょっと...といった感じです。

Mayaシーン内にある全てのキャラクタ名のリストを取得して、"mGear_Mocap_interface" という文字列があった時の番号をキャラクタの番号として設定します。

#キャラクタ名の取得 

allChar = cmds.optionMenuGrp("hikCharacterList", query=True, itemListLong=True)
optChrMenu = "hikCharacterList|OptionMenu"

for i in range(0, len(allChar)):
    char_name = cmds.menuItem(allChar[i], query=True, label=True)

    if char_name == "mGear_Mocap_interface":
        cmds.optionMenu(optChrMenu, edit=True, select=i+1)
        mel.eval('hikUpdateCurrentCharacterFromUI()')
        break
★ ソース; #* Get Source from List

ソースも同じ手法をとります。
Mayaシーン内にある全てのソース名前をリストとして取得して探しているソース名がリストにあった時の番号でソースを指定します。

ただし、ソースを プリント すると、またあることに気が付きます。

#ソース名の取得 

allSource = cmds.optionMenuGrp("hikSourceList", query=True, itemListLong=True)
for i in range(0, len(allSource)):
    source_name = cmds.menuItem(allSource[i], query=True, label=True)
    print(i,source_name)

その結果は...

0  なし
1  Character1
2  スタンス
3  コントロール リグ
MAYA_16_CGCW2024_022

先頭に1半角文字分に空白" "が存在することに気が付きます。
日本語で返される部分があるのは...同じですが。

これを踏まえ、Mayaシーン内にある全てのソース名のリストを取得して"Character1"という文字列があった時の番号をソースの番号として設定をします。 

#ソース名の取得 

allSource = cmds.optionMenuGrp("hikSourceList", query=True, itemListLong=True)
optSourceMenu = "hikSourceList|OptionMenu"

for i in range(0, len(allSource)):
    source_name = cmds.menuItem(allSource[i], query=True, label=True)

    if source_name == " Character1":
        cmds.optionMenu(optSourceMenu, edit=True, select=i+1)
        mel.eval('hikUpdateCurrentSourceFromUI()')
        break

#最後にGUIを更新

    mel.eval('hikUpdateContextualUI()')
    mel.eval('hikControlRigSelectionChangedCallback')
・キャラクターのリグコントローラへベイク

mGearのリグにアニメーションをベイクする部分は、mGear > Shifter > Mocap > Bake Mocap Bipedの部分を利用することができます。
mGearの任意のコントローラーを選択した状態で実行します。

#キャラクターのリグコントローラ へ ベイク 

cmds.select( clear=True )
cmds.select("body_C0_ctl", r=True )

from mgear.shifter import mocap_tools
mocap_tools.bakeMocap()

標準のスクリプトではシミュレーションのベイク処理を使います。オプション記述が非常に長いです。

#キャラクターのリグコントローラへベイク 

cmds.bakeResults(n_gearbake, simulation=True, t=(start,end), sampleBy=1,
    oversamplingRate=1, disableImplicitControl=True, preserveOutsideKeys=True,
    sparseAnimCurveBake=False, removeBakedAttributeFromLayer=False,removeBakedAnimFromLayer=False,
    bakeOnOverrideLayer=False, minimizeRotation=True,
    controlPoints=False, shape=True) 

n_gearbake= [] というリストの中に、ベイクしたいリグコントローラーの移動/回転のXYZまでを記載します。
とても長~いリストになるので、用意したatt_gearPlot行をリストに入れます。

att_gearPlot = [
    u"body_C0_ctl.translateX",
    u"body_C0_ctl.translateY",
    u"body_C0_ctl.translateZ",
#     ~
    u"arm_R0_ikRot_ctl.rotateX",
    u"arm_R0_ikRot_ctl.rotateY",
    u"arm_R0_ikRot_ctl.rotateZ",
]

更にアップベクターを計算するのに使っているロケータも追記しておきます。

att_gearPlot = [
    u"arm_L0_upv.translateX",
    u"arm_L0_upv.translateY",
    u"arm_L0_upv.translateZ",
#     ~
    u"leg_R0_upv.translateX",
    u"leg_R0_upv.translateY",
    u"leg_R0_upv.translateZ",
]

ベイクするリグコントローラーのリストを作成しているのは、キャラクターのリグコントローラーにネームスペースが
あった場合にベイクするリストn_gearbake= [] にネームスペースを追加する対応の為です。

・不要なデータの削除(不要NodeEditorノードの削除)

ベイクを実行した後、複数のキャラクターやソース、
例えば、
 mGear_Mocap_interface_Reference
 mGear_Mocap_interface_Ctrl_Reference
を削除します。

また、HIKを作成する時に使ったMELコマンドはhikEnableCharacterでしたが、今度のHIKを削除する時にはhikDeleteCharacterを使うと関連するデータも削除してくれます。

#不要なデータの削除

allChar = cmds.optionMenuGrp("hikCharacterList", query=True, itemListLong=True)

for i in range(1, len(allChar)):
    delete_char_name = cmds.menuItem(allChar[i], query=True, label=True)

    mel.eval('string $delete_char_name = "%s"' % delete_char_name)
    cmd='hikDeleteCharacter( $delete_char_name);'
    mel.eval(cmd)

hikDeleteCharacter 実行後でもまだ不要なノードが残っています。
アウトライナーのディスプレイの[レ]DAGオブジェクトのみのチェックを外すと
 mGear_Mocap_interface_Reference
 >HIK*** で始まるノード群
 arm_R0_upv 等アップベクターの計算に使ったロケータ
 Reference 以下の入力したFBX
 ~などが残っています。

これらはリストに登録して削除します。

・シーン内で使われていないノードなどの削除

ハイパーシェードの編集 > 未使用ノードの削除を行うと、シーン内で使用していないノードを削除してくれます。

#未使用ノードの削除

mel.eval('MLdeleteUnused;')

「不明なノード」がシーン内に残る場合もあります。
unknownNodesはロックが掛かっていると削除できないので、ロックを外してから削除します。

#不明なノードの削除

cmds.select(allDagObjects=True,hierarchy=True)
cmds.select(allDependencyNodes=True,add=True)

unknownNodes = cmds.ls(type = 'unknown')
try:
    for node in unknownNodes:
        cmds.lockNode(node, lock = False)
        cmds.delete(node)
except:
    print('unknown is done once.')
・オイラーカーブフィルターの設定

最後にキャラクターのリグコントローラーに乗ったアニメーションにEulerのカーブフィルターを実行してあげると良い感じになります。

# Curve Euler filter

cmds.select("rig_controllers_grp",r=True )
cmds.filterCurve(f='euler')
cmds.select( clear=True )

これで全てのスクリプトが揃いました。 お疲れ様です~..。




完成スクリプトの全実行

用意したスクリプトを実行した時の動画を最後に紹介します。

モーションキャプチャーのデータからキャラクターリグへのベイクまで1つのツールで完成しています。

そして、そのスクリプトの全体です。

# -*- coding: utf-8 -*-

"""!
Import MotionBuilder MVN FBX and Retarget Motion to mGear Rig V 2.2
              - Set FBX to 60 FPS Uaing Euler Filter
              - Set MVN FBX HIK to Retarget to mGear Rig and Bake
              - Bake only Once to mGear Rig for retargeting
              - Get HIK GUI Character & Source Name from HikList
@file
@author TA Ritaro

"""
import pymel.core as pm
import maya.cmds as cmds
import maya.mel as mel
import os
import imp, os.path, sys

import _thread
import math




att_gearPlot = [
    u"arm_L0_upv.translateX",
    u"arm_L0_upv.translateY",
    u"arm_L0_upv.translateZ",
    u"arm_R0_upv.translateX",
    u"arm_R0_upv.translateY",
    u"arm_R0_upv.translateZ",
    u"leg_L0_upv.translateX",
    u"leg_L0_upv.translateY",
    u"leg_L0_upv.translateZ",
    u"leg_R0_upv.translateX",
    u"leg_R0_upv.translateY",
    u"leg_R0_upv.translateZ",
    u"body_C0_ctl.translateX",
    u"body_C0_ctl.translateY",
    u"body_C0_ctl.translateZ",
    u"body_C0_ctl.rotateX",
    u"body_C0_ctl.rotateY",
    u"body_C0_ctl.rotateZ",
    u"leg_L0_fk0_ctl.rotateX",
    u"leg_L0_fk0_ctl.rotateY",
    u"leg_L0_fk0_ctl.rotateZ",
    u"leg_L0_fk1_ctl.rotateX",
    u"leg_L0_fk1_ctl.rotateY",
    u"leg_L0_fk1_ctl.rotateZ",
    u"leg_L0_fk2_ctl.rotateX",
    u"leg_L0_fk2_ctl.rotateY",
    u"leg_L0_fk2_ctl.rotateZ",
    u"foot_L0_fk0_ctl.rotateX",
    u"foot_L0_fk0_ctl.rotateY",
    u"foot_L0_fk0_ctl.rotateZ",
    u"leg_R0_fk0_ctl.rotateX",
    u"leg_R0_fk0_ctl.rotateY",
    u"leg_R0_fk0_ctl.rotateZ",
    u"leg_R0_fk1_ctl.rotateX",
    u"leg_R0_fk1_ctl.rotateY",
    u"leg_R0_fk1_ctl.rotateZ",
    u"leg_R0_fk2_ctl.rotateX",
    u"leg_R0_fk2_ctl.rotateY",
    u"leg_R0_fk2_ctl.rotateZ",
    u"foot_R0_fk0_ctl.rotateX",
    u"foot_R0_fk0_ctl.rotateY",
    u"foot_R0_fk0_ctl.rotateZ",
    u"spine_C0_fk0_ctl.rotateX",
    u"spine_C0_fk0_ctl.rotateY",
    u"spine_C0_fk0_ctl.rotateZ",
    u"spine_C0_fk1_ctl.rotateX",
    u"spine_C0_fk1_ctl.rotateY",
    u"spine_C0_fk1_ctl.rotateZ",
    u"spine_C0_fk2_ctl.rotateX",
    u"spine_C0_fk2_ctl.rotateY",
    u"spine_C0_fk2_ctl.rotateZ",
    u"spine_C0_ik1_ctl.rotateX",
    u"spine_C0_ik1_ctl.rotateY",
    u"spine_C0_ik1_ctl.rotateZ",
    u"shoulder_L0_ctl.rotateX",
    u"shoulder_L0_ctl.rotateY",
    u"shoulder_L0_ctl.rotateZ",
    u"arm_L0_fk0_ctl.rotateX",
    u"arm_L0_fk0_ctl.rotateY",
    u"arm_L0_fk0_ctl.rotateZ",
    u"arm_L0_fk1_ctl.rotateX",
    u"arm_L0_fk1_ctl.rotateY",
    u"arm_L0_fk1_ctl.rotateZ",
    u"arm_L0_fk2_ctl.rotateX",
    u"arm_L0_fk2_ctl.rotateY",
    u"arm_L0_fk2_ctl.rotateZ",
    u"thumb_L0_fk0_ctl.rotateX",
    u"thumb_L0_fk0_ctl.rotateY",
    u"thumb_L0_fk0_ctl.rotateZ",
    u"thumb_L0_fk1_ctl.rotateX",
    u"thumb_L0_fk1_ctl.rotateY",
    u"thumb_L0_fk1_ctl.rotateZ",
    u"thumb_L0_fk2_ctl.rotateX",
    u"thumb_L0_fk2_ctl.rotateY",
    u"thumb_L0_fk2_ctl.rotateZ",
    u"finger_L0_fk0_ctl.rotateX",
    u"finger_L0_fk0_ctl.rotateY",
    u"finger_L0_fk0_ctl.rotateZ",
    u"finger_L0_fk1_ctl.rotateX",
    u"finger_L0_fk1_ctl.rotateY",
    u"finger_L0_fk1_ctl.rotateZ",
    u"finger_L0_fk2_ctl.rotateX",
    u"finger_L0_fk2_ctl.rotateY",
    u"finger_L0_fk2_ctl.rotateZ",
    u"finger_L1_fk0_ctl.rotateX",
    u"finger_L1_fk0_ctl.rotateY",
    u"finger_L1_fk0_ctl.rotateZ",
    u"finger_L1_fk1_ctl.rotateX",
    u"finger_L1_fk1_ctl.rotateY",
    u"finger_L1_fk1_ctl.rotateZ",
    u"finger_L1_fk2_ctl.rotateX",
    u"finger_L1_fk2_ctl.rotateY",
    u"finger_L1_fk2_ctl.rotateZ",
    u"finger_L2_fk0_ctl.rotateX",
    u"finger_L2_fk0_ctl.rotateY",
    u"finger_L2_fk0_ctl.rotateZ",
    u"finger_L2_fk1_ctl.rotateX",
    u"finger_L2_fk1_ctl.rotateY",
    u"finger_L2_fk1_ctl.rotateZ",
    u"finger_L2_fk2_ctl.rotateX",
    u"finger_L2_fk2_ctl.rotateY",
    u"finger_L2_fk2_ctl.rotateZ",
    u"finger_L3_fk0_ctl.rotateX",
    u"finger_L3_fk0_ctl.rotateY",
    u"finger_L3_fk0_ctl.rotateZ",
    u"finger_L3_fk1_ctl.rotateX",
    u"finger_L3_fk1_ctl.rotateY",
    u"finger_L3_fk1_ctl.rotateZ",
    u"finger_L3_fk2_ctl.rotateX",
    u"finger_L3_fk2_ctl.rotateY",
    u"finger_L3_fk2_ctl.rotateZ",
    u"shoulder_R0_ctl.rotateX",
    u"shoulder_R0_ctl.rotateY",
    u"shoulder_R0_ctl.rotateZ",
    u"arm_R0_fk0_ctl.rotateX",
    u"arm_R0_fk0_ctl.rotateY",
    u"arm_R0_fk0_ctl.rotateZ",
    u"arm_R0_fk1_ctl.rotateX",
    u"arm_R0_fk1_ctl.rotateY",
    u"arm_R0_fk1_ctl.rotateZ",
    u"arm_R0_fk2_ctl.rotateX",
    u"arm_R0_fk2_ctl.rotateY",
    u"arm_R0_fk2_ctl.rotateZ",
    u"thumb_R0_fk0_ctl.rotateX",
    u"thumb_R0_fk0_ctl.rotateY",
    u"thumb_R0_fk0_ctl.rotateZ",
    u"thumb_R0_fk1_ctl.rotateX",
    u"thumb_R0_fk1_ctl.rotateY",
    u"thumb_R0_fk1_ctl.rotateZ",
    u"thumb_R0_fk2_ctl.rotateX",
    u"thumb_R0_fk2_ctl.rotateY",
    u"thumb_R0_fk2_ctl.rotateZ",
    u"finger_R0_fk0_ctl.rotateX",
    u"finger_R0_fk0_ctl.rotateY",
    u"finger_R0_fk0_ctl.rotateZ",
    u"finger_R0_fk1_ctl.rotateX",
    u"finger_R0_fk1_ctl.rotateY",
    u"finger_R0_fk1_ctl.rotateZ",
    u"finger_R0_fk2_ctl.rotateX",
    u"finger_R0_fk2_ctl.rotateY",
    u"finger_R0_fk2_ctl.rotateZ",
    u"finger_R1_fk0_ctl.rotateX",
    u"finger_R1_fk0_ctl.rotateY",
    u"finger_R1_fk0_ctl.rotateZ",
    u"finger_R1_fk1_ctl.rotateX",
    u"finger_R1_fk1_ctl.rotateY",
    u"finger_R1_fk1_ctl.rotateZ",
    u"finger_R1_fk2_ctl.rotateX",
    u"finger_R1_fk2_ctl.rotateY",
    u"finger_R1_fk2_ctl.rotateZ",
    u"finger_R2_fk0_ctl.rotateX",
    u"finger_R2_fk0_ctl.rotateY",
    u"finger_R2_fk0_ctl.rotateZ",
    u"finger_R2_fk1_ctl.rotateX",
    u"finger_R2_fk1_ctl.rotateY",
    u"finger_R2_fk1_ctl.rotateZ",
    u"finger_R2_fk2_ctl.rotateX",
    u"finger_R2_fk2_ctl.rotateY",
    u"finger_R2_fk2_ctl.rotateZ",
    u"finger_R3_fk0_ctl.rotateX",
    u"finger_R3_fk0_ctl.rotateY",
    u"finger_R3_fk0_ctl.rotateZ",
    u"finger_R3_fk1_ctl.rotateX",
    u"finger_R3_fk1_ctl.rotateY",
    u"finger_R3_fk1_ctl.rotateZ",
    u"finger_R3_fk2_ctl.rotateX",
    u"finger_R3_fk2_ctl.rotateY",
    u"finger_R3_fk2_ctl.rotateZ",
    u"neck_C0_fk0_ctl.rotateX",
    u"neck_C0_fk0_ctl.rotateY",
    u"neck_C0_fk0_ctl.rotateZ",
#    u"neck_C0_fk1_ctl.rotateX",
#    u"neck_C0_fk1_ctl.rotateY",
#    u"neck_C0_fk1_ctl.rotateZ",
    u"neck_C0_head_ctl.rotateX",
    u"neck_C0_head_ctl.rotateY",
    u"neck_C0_head_ctl.rotateZ",
    u"leg_L0_ik_ctl.translateX",
    u"leg_L0_ik_ctl.translateY",
    u"leg_L0_ik_ctl.translateZ",
    u"leg_L0_ik_ctl.rotateX",
    u"leg_L0_ik_ctl.rotateY",
    u"leg_L0_ik_ctl.rotateZ",
    u"leg_R0_ik_ctl.translateX",
    u"leg_R0_ik_ctl.translateY",
    u"leg_R0_ik_ctl.translateZ",
    u"leg_R0_ik_ctl.rotateX",
    u"leg_R0_ik_ctl.rotateY",
    u"leg_R0_ik_ctl.rotateZ",
    u"leg_L0_upv_ctl.translateX",
    u"leg_L0_upv_ctl.translateY",
    u"leg_L0_upv_ctl.translateZ",
    u"leg_R0_upv_ctl.translateX",
    u"leg_R0_upv_ctl.translateY",
    u"leg_R0_upv_ctl.translateZ",
    u"arm_L0_ik_ctl.translateX",
    u"arm_L0_ik_ctl.translateY",
    u"arm_L0_ik_ctl.translateZ",
    u"arm_L0_upv_ctl.translateX",
    u"arm_L0_upv_ctl.translateY",
    u"arm_L0_upv_ctl.translateZ",
    u"arm_R0_ik_ctl.translateX",
    u"arm_R0_ik_ctl.translateY",
    u"arm_R0_ik_ctl.translateZ",
    u"arm_R0_upv_ctl.translateX",
    u"arm_R0_upv_ctl.translateY",
    u"arm_R0_upv_ctl.translateZ",
    u"leg_L0_mid_ctl.translateX",
    u"leg_L0_mid_ctl.translateY",
    u"leg_L0_mid_ctl.translateZ",
    u"leg_R0_mid_ctl.translateX",
    u"leg_R0_mid_ctl.translateY",
    u"leg_R0_mid_ctl.translateZ",
    u"arm_L0_mid_ctl.translateX",
    u"arm_L0_mid_ctl.translateY",
    u"arm_L0_mid_ctl.translateZ",
    u"arm_R0_mid_ctl.translateX",
    u"arm_R0_mid_ctl.translateY",
    u"arm_R0_mid_ctl.translateZ",
    u"arm_L0_ikRot_ctl.rotateX",
    u"arm_L0_ikRot_ctl.rotateY",
    u"arm_L0_ikRot_ctl.rotateZ",
    u"arm_R0_ikRot_ctl.rotateX",
    u"arm_R0_ikRot_ctl.rotateY",
    u"arm_R0_ikRot_ctl.rotateZ",
]


def bakeMocap(*args):
    start = pm.playbackOptions(query=True, min=True)
    end = pm.playbackOptions(query=True, max=True)

    if pm.selected() and pm.selected()[0].name()[-3:] == "ctl":
        sel_namespace =[ ]
        sel_namespace = cmds.ls( selection=True )
        o_namespace = sel_namespace[0].rpartition(':')[0] + ":"
        print(o_namespace)
        if o_namespace == ":":
            o_namespace = ""

        n_gearbake = []
        for o_bake in att_gearPlot:
            n_gearbake.append(o_namespace + o_bake)

        print (n_gearbake)
        cmds.bakeResults(n_gearbake, simulation=True, t=(start,end),
            sampleBy=1, oversamplingRate=1, disableImplicitControl=True, preserveOutsideKeys=True,
            sparseAnimCurveBake=False, removeBakedAttributeFromLayer=False,
            removeBakedAnimFromLayer=False, bakeOnOverrideLayer=False, minimizeRotation=True,
            controlPoints=False, shape=True)

    else:
        pm.displayWarning("Please select on control of the rig to "
                          "determine wich character will be baked")


def characterizeBaked(*args):

    # Load Plugins
    if not cmds.pluginInfo('mayaHIK',q=True,l=True):
        cmds.loadPlugin('mayaHIK')
    if not cmds.pluginInfo('mayaCharacterization',q=True,l=True):
        cmds.loadPlugin('mayaCharacterization')
    if not cmds.pluginInfo('retargeterNodes',q=True,l=True):
        cmds.loadPlugin('retargeterNodes')

    # HIK Character Controls Tool
    mel.eval('HIKCharacterControlsTool')

    # Source the following scripts. If the code fails, then you'll have to load them and run them:
    MAYA_LOCATION = os.environ['MAYA_LOCATION']
    mel.eval('source "'+MAYA_LOCATION+'/scripts/others/hikGlobalUtils.mel"')
    mel.eval('source "'+MAYA_LOCATION+'/scripts/others/hikCharacterControlsUI.mel"')
    mel.eval('source "'+MAYA_LOCATION+'/scripts/others/hikDefinitionOperations.mel"')

    mel.eval('hikCreateDefinition()')

    mel.eval('setCharacterObject("Reference", "Character1",0,0);')
    mel.eval('setCharacterObject("Hips", "Character1",1,0);')
    mel.eval('setCharacterObject("LeftUpLeg", "Character1",2,0);')
    mel.eval('setCharacterObject("LeftLeg", "Character1",3,0);')
    mel.eval('setCharacterObject("LeftFoot", "Character1",4,0);')
    mel.eval('setCharacterObject("RightUpLeg", "Character1",5,0);')
    mel.eval('setCharacterObject("RightLeg", "Character1",6,0);')
    mel.eval('setCharacterObject("RightFoot", "Character1",7,0);')
    mel.eval('setCharacterObject("Spine", "Character1",8,0);')
    mel.eval('setCharacterObject("LeftArm", "Character1",9,0);')
    mel.eval('setCharacterObject("LeftForeArm", "Character1",10,0);')
    mel.eval('setCharacterObject("LeftHand", "Character1",11,0);')
    mel.eval('setCharacterObject("RightArm", "Character1",12,0);')
    mel.eval('setCharacterObject("RightForeArm", "Character1",13,0);')
    mel.eval('setCharacterObject("RightHand", "Character1",14,0);')
    mel.eval('setCharacterObject("Head", "Character1",15,0);')
    mel.eval('setCharacterObject("LeftToeBase", "Character1",16,0);')
    mel.eval('setCharacterObject("RightToeBase", "Character1",17,0);')
    mel.eval('setCharacterObject("LeftShoulder", "Character1",18,0);')
    mel.eval('setCharacterObject("RightShoulder", "Character1",19,0);')
    mel.eval('setCharacterObject("Neck", "Character1",20,0);')
    mel.eval('setCharacterObject("Spine1", "Character1",23,0);')
    mel.eval('setCharacterObject("Spine2", "Character1",24,0);')
    mel.eval('setCharacterObject("Spine3", "Character1",25,0);')
    mel.eval('setCharacterObject("LeftHandThumb1","Character1",50,0);')
    mel.eval('setCharacterObject("LeftHandThumb2","Character1",51,0);')
    mel.eval('setCharacterObject("LeftHandThumb3","Character1",52,0);')
    mel.eval('setCharacterObject("LeftHandIndex1","Character1",54,0);')
    mel.eval('setCharacterObject("LeftHandIndex2","Character1",55,0);')
    mel.eval('setCharacterObject("LeftHandIndex3","Character1",56,0);')
    mel.eval('setCharacterObject("LeftHandMiddle1","Character1",58,0);')
    mel.eval('setCharacterObject("LeftHandMiddle2","Character1",59,0);')
    mel.eval('setCharacterObject("LeftHandMiddle3","Character1",60,0);')
    mel.eval('setCharacterObject("LeftHandRing1","Character1",62,0);')
    mel.eval('setCharacterObject("LeftHandRing2","Character1",63,0);')
    mel.eval('setCharacterObject("LeftHandRing3","Character1",64,0);')
    mel.eval('setCharacterObject("LeftHandPinky1","Character1",66,0);')
    mel.eval('setCharacterObject("LeftHandPinky2","Character1",67,0);')
    mel.eval('setCharacterObject("LeftHandPinky3","Character1",68,0);')
    mel.eval('setCharacterObject("RightHandThumb1","Character1",74,0);')
    mel.eval('setCharacterObject("RightHandThumb2","Character1",75,0);')
    mel.eval('setCharacterObject("RightHandThumb3","Character1",76,0);')
    mel.eval('setCharacterObject("RightHandIndex1","Character1",78,0);')
    mel.eval('setCharacterObject("RightHandIndex2","Character1",79,0);')
    mel.eval('setCharacterObject("RightHandIndex3","Character1",80,0);')
    mel.eval('setCharacterObject("RightHandMiddle1","Character1",82,0);')
    mel.eval('setCharacterObject("RightHandMiddle2","Character1",83,0);')
    mel.eval('setCharacterObject("RightHandMiddle3","Character1",84,0);')
    mel.eval('setCharacterObject("RightHandRing1","Character1",86,0);')
    mel.eval('setCharacterObject("RightHandRing2","Character1",87,0);')
    mel.eval('setCharacterObject("RightHandRing3","Character1",88,0);')
    mel.eval('setCharacterObject("RightHandPinky1","Character1",90,0);')
    mel.eval('setCharacterObject("RightHandPinky2","Character1",91,0);')
    mel.eval('setCharacterObject("RightHandPinky3","Character1",92,0);')

    mel.eval('hikUpdateDefinitionUI;')


    mel.eval('hikCreateControlRig;')
    cmds.parent('Character1_Ctrl_Reference', 'Reference')

    char_name = mel.eval('hikGetCurrentCharacter()')
    print(char_name)
    mel.eval('string $char_name = "%s"' % char_name)
    cmd='hikEnableCharacter( $char_name, 2 );'
    mel.eval(cmd)
    mel.eval('hikUpdateSourceList()')
    mel.eval('hikUpdateCurrentSourceFromUI()')
    mel.eval('hikUpdateContextualUI()')
    mel.eval('hikControlRigSelectionChangedCallback')

    # Bake to Contrl Rig
    mel.eval('hikBakeToControlRig 0;')


def setFPS(value):
    if value == 15:     unit = 'game'
    elif value == 24:   unit = 'film'
    elif value == 25:   unit = 'pal'
    elif value == 30:   unit = 'ntsc'
    elif value == 48:   unit = 'show'
    elif value == 50:   unit = 'palf'
    elif value == 60:   unit = 'ntscf'
    else:               unit = '%sfps' % fps
    cmds.currentUnit(time=unit)


def do_import_mvn_fbx(*args):

    if  cmds.textFieldButtonGrp('mvn_fbx_file_name', query=True,text=True) == '':
        cmds.confirmDialog( title='ERROR', message='Set Import FBX File!!  ')
        _thread.exit()

    if  cmds.textFieldButtonGrp('retarget_maya_scene', query=True,text=True) == '':
        cmds.confirmDialog( title='ERROR', message='Set Import FBX File!!  ')
        _thread.exit()

    cmds.file( force=True, new=True)
    cmds.select(clear=True)

    o_min = cmds.playbackOptions(query=True,animationStartTime=True)
    o_max = cmds.playbackOptions(query=True,animationEndTime=True)
    cmds.currentTime( o_min )

    cmds.setAttr('persp.translate', 350, 360, 340, type="double3")
    cmds.setAttr('persp.rotate', -28, 45, 0, type="double3")
    cmds.setAttr('perspShape.centerOfInterest', 448.000)

    mel.eval('FBXResetImport')
    mel.eval('FBXImportConvertUnitString "cm"')
    mel.eval('FBXImportFillTimeline -v false;')

    mel.eval('FBXImportMode -v merge;')

    mvn_fbx_file = cmds.textFieldButtonGrp('mvn_fbx_file_name', query=True,text=True)
    mvn_fbx_file_name = os.path.basename(mvn_fbx_file)

    print(mvn_fbx_file)

    mel.eval('string $import_filepass = "%s"' %  mvn_fbx_file)
    mel.eval('FBXImport -file $import_filepass;')

    setFPS(60)


# Set Start End frame from Hip Key

    o_top_obj = "Reference"
    cmds.select(o_top_obj,r=True)

    cmds.select(hierarchy=True)
    all_joints = all_joints = cmds.ls(selection=True)

    for o_ch3 in all_joints:
        if 'Hips' in o_ch3:
            o_hip = o_ch3
            break

#    print(o_hip)

    cmds.selectKey(o_hip,replace=True,attribute='translateX')
    all_key_list =  cmds.keyframe( query=True, selected=True)

    try:
        start_key = all_key_list[0]
    except:
        cmds.confirmDialog( title='WARNING', message='No Animation on Hip Joint !!')
        _thread.exit() 

    print ("start_key",start_key)
    key_length = len(all_key_list) -1
    last_key = start_key + key_length
    last_key_value = math.ceil(all_key_list[key_length])
    print ("last_key_value",last_key_value)

    cmds.playbackOptions(edit=True,animationStartTime=start_key)
    cmds.playbackOptions(edit=True,animationEndTime=last_key_value)
    cmds.playbackOptions(edit=True,minTime=start_key)
    cmds.playbackOptions(edit=True,maxTime=last_key_value)
    cmds.currentTime(start_key, edit=True)



#*---- Do Butterworth Filter With 60 FPS

    cmds.select(all_joints,add=True )
    mel.eval('string $last_key_value = "%s"' %  last_key_value)
    mel.eval('filterCurve -f butterworth -cof 7 -sr 60 -kof -s 0 -e $last_key_value ;')


#*---- Do HIK Charactrize & Bake to Control Rig

    characterizeBaked()


#*---- Import Maya Scene used for Retargetting

    import_SK_Rig_HIK_file = cmds.textFieldButtonGrp('retarget_maya_scene', query=True,text=True)
    mel.eval('string $import_SK_Rig_HIK_file = "%s"' %  import_SK_Rig_HIK_file)

    mel.eval('file -import -type "mayaAscii"  -ignoreVersion -ra true -mergeNamespacesOnClash true -namespace ":" -options "v=0;"  -pr  -importTimeRange "combine" $import_SK_Rig_HIK_file ;')

    mel.eval('HIKCharacterControlsTool')
    mel.eval('hikUpdateContextualUI()')
    mel.eval('hikControlRigSelectionChangedCallback')

    cmds.colorManagementPrefs(edit=True,cmEnabled=False)
    mel.eval('setAttr "hardwareRenderingGlobals.floatingPointRTEnable" 1;')
    mel.eval('setAttr "hardwareRenderingGlobals.defaultLightIntensity" 1;')
    mel.eval('modelEditor -e -displayTextures true modelPanel4;')


#*---- Set Character & Source from List

    allChar = cmds.optionMenuGrp("hikCharacterList", query=True, itemListLong=True)
    optChrMenu = "hikCharacterList|OptionMenu"

    for i in range(0, len(allChar)):
        char_name = cmds.menuItem(allChar[i], query=True, label=True)

        if char_name == "mGear_Mocap_interface":
            cmds.optionMenu(optChrMenu, edit=True, select=i+1)
            mel.eval('hikUpdateCurrentCharacterFromUI()')
            break


    allSource = cmds.optionMenuGrp("hikSourceList", query=True, itemListLong=True)
    optSourceMenu = "hikSourceList|OptionMenu"

    for i in range(0, len(allSource)):
        source_name = cmds.menuItem(allSource[i], query=True, label=True)

        if source_name == " Character1":
            cmds.optionMenu(optSourceMenu, edit=True, select=i+1)
            mel.eval('hikUpdateCurrentSourceFromUI()')
            break


    mel.eval('hikUpdateContextualUI()')
    mel.eval('hikControlRigSelectionChangedCallback')


#*---------------- Bake to mGear Rig

    cmds.select( clear=True )
    cmds.select("body_C0_ctl", r=True )

    bakeMocap()


#*----------------  Delete Unuesed Data

    allChar = cmds.optionMenuGrp("hikCharacterList", query=True, itemListLong=True)

    for i in range(1, len(allChar)):
        delete_char_name = cmds.menuItem(allChar[i], query=True, label=True)
        print(delete_char_name)
        mel.eval('string $delete_char_name = "%s"' % delete_char_name)
        cmd='hikDeleteCharacter( $delete_char_name);'
        mel.eval(cmd)


    del_list = ['mGear_Mocap_interface_Reference',
        'HIKproperties3', 'HIKproperties4','HIKproperties5', 'HIKSK2State1',
        'HIKSkeletonGeneratorNode1', 'HIKSkeletonGeneratorNode2', 'HIKSkeletonGeneratorNode3',
        'HIKSkeletonGeneratorNode4', 'HIKSolverNode3', 'HIKSolverNode4', 'HIKSolverNode5',
        'HIKState2SK3', 'HIKState2SK4', 'HIKState2SK5']
    for o_del in del_list:
        try:
            cmds.delete(o_del)
        except:
            pass

    delete_list_a = ["arm_R0_upv","arm_L0_upv","leg_L0_upv","leg_R0_upv"]
    for del_a in delete_list_a:
        try:
            cmds.delete(del_a)
        except:
            pass

    delete_list_b = ["R_MidP","L_MidP","LL_MidP","LR_MidP"]
    for del_b in delete_list_b:
        try:
            cmds.delete(del_b)
        except:
            pass

    try:
        cmds.select("|Reference",r=True )
        cmds.delete("|Reference")
    except:
        pass


# Delete Unused Nodes

    mel.eval('MLdeleteUnused;')

    cmds.select( clear=True )
    cmds.select(allDagObjects=True,hierarchy=True)
    cmds.select(allDependencyNodes=True,add=True)

    unknownNodes = cmds.ls(type = 'unknown')
    try:
        for node in unknownNodes:
            cmds.lockNode(node, lock = False)
            cmds.delete(node)
    except:
        print('unknown is done once.')


# Curve euler filter

    cmds.select("rig_controllers_grp",r=True )
    cmds.filterCurve(f='euler')
    cmds.select( clear=True )



#*----------------

def set_mvnfbx_file(*args):
    if cmds.optionVar( exists='ri_mvn_fbx_importpass' ):
        mvn_fbx_dir = cmds.optionVar( q='ri_mvn_fbx_importpass' )
    else:
        mvn_fbx_dir = os.path.dirname(cmds.file( query=True, sceneName=True))

    multipleFilters = "All Files (*.*)"
    import_mvn_fbx_dir_name = cmds.fileDialog2(fileMode=1,fileFilter=multipleFilters,
        dialogStyle=2,startingDirectory=mvn_fbx_dir)

    if import_mvn_fbx_dir_name:
        cmds.textFieldButtonGrp('mvn_fbx_file_name', edit=True, text=import_mvn_fbx_dir_name[0])
        cmds.optionVar( sv=('ri_mvn_fbx_importpass', import_mvn_fbx_dir_name[0] ) )



def set_retarget_file(*args):

    multipleFilters = "All Files (*.*)"
    retarget_maya_scene_file = cmds.fileDialog2(fileMode=1,fileFilter=multipleFilters,
        dialogStyle=2,startingDirectory="")

    if retarget_maya_scene_file:
        cmds.textFieldButtonGrp('retarget_maya_scene', edit=True, text=retarget_maya_scene_file[0])
        cmds.optionVar( sv=('ri_retarget_maya_scene_file', retarget_maya_scene_file[0] ) )



def import_mvn_fbx_retarget_to_mgear_rig(*args):
    try:
        if not cmds.pluginInfo("fbxmaya",q=True,loaded=True):
            cmds.loadPlugin( 'fbxmaya' )
            cmds.pluginInfo( 'fbxmaya', edit=True, autoload=True )
    except:
        print('FBX plugin is not installed')

    if cmds.optionVar( exists='ri_mvn_fbx_importpass' ):
        mvn_fbx_dir = cmds.optionVar( q='ri_mvn_fbx_importpass' )
    else:
        mvn_fbx_dir = ""


    if cmds.optionVar( exists='ri_retarget_maya_scene_file' ):
        retarget_maya_file = cmds.optionVar( q='ri_retarget_maya_scene_file' )
    else:
        retarget_maya_file = ""




    if cmds.window('ImportMVNFBX_ToMGEAR', exists=True):
        cmds.deleteUI('ImportMVNFBX_ToMGEAR')

    window = cmds.window('ImportMVNFBX_ToMGEAR', title='Import MVN FBX and retarget to mGear Rig  V2.2',
        sizeable=True, menuBar=True, topLeftCorner=[200, 200], widthHeight=(200, 200))
    cmds.menu( label='File' )
    cmds.menuItem( label='exit', command='cmds.deleteUI( "ImportMVNFBX_ToMGEAR" )' )
    cmds.columnLayout(columnAttach=('both', 5), rowSpacing=8, columnWidth=200, adjustableColumn=True)

    cmds.text(label='Import MVN FBX and Retarget to mGear Rig   V2.2 - @ Ritaro',
        width=200,bgc=[0.2,0.2,0.2],align='center' )

    cmds.separator(height=3,style='out')

    cmds.textFieldButtonGrp('mvn_fbx_file_name',label='MVN FBX File  ',
        text=mvn_fbx_dir, buttonLabel='Select Motion FBX',
        columnWidth3=[100,300,30],adjustableColumn=2,buttonCommand=set_mvnfbx_file)


    cmds.text(label='',width=5)

    cmds.textFieldButtonGrp('retarget_maya_scene',label='Maya Retarget Scene file  ',
        text=retarget_maya_file, buttonLabel='SetFile',
        columnWidth3=[155,300,30],adjustableColumn=2,buttonCommand=set_retarget_file)

    cmds.separator(height=7)
    cmds.separator(height=1,style=None)
    cmds.rowLayout(numberOfColumns=3,adjustableColumn=2)
    cmds.text(label='',width=10)
    cmds.button('import_mobu_fbx_bot',label='Import MVN FBX and Retarget to mGear Rig !',
        width=80,bgc=[1.0,0.2,0.2],command=do_import_mvn_fbx)
    cmds.text(label='',width=10)
    cmds.setParent('..')
    cmds.text(label='',width=10)
    cmds.showWindow(window)

実行はもちろん、 import_mvn_fbx_retarget_to_mgear_rig() です。

何かの役に立てれば幸いです。 自己責任でご利用ください。


次回もMayaかも?・・・
乞う、ご期待!! Stay tuned ..

戻る

お気軽にお問い合わせください

Daikin CG News お申し込み

CGクリエイター向けのセミナー・イベントやキャンペーン、製品情報をメールマガジンでお届けします。(登録無料)

Twitter

ページの先頭へ