Marvelous Designer Pin の打ち方

Marvelous Designer Pin の打ち方
「w」キー押しながら+左クリック

ピンは選択できなくなりパーツの全ピンを解除することになるかもしれないので、
ピンはピンを打つ専用のパーツをフリーズしてしまった方が取り扱いが楽に無限に行えるようになる。

パーツの固定フリーズ Freeze
Ctrl+ K

フリーズしたパーツに引っ張られる布

nHairを使用した揺れものリグセットアップの自動化をしたよ。JointChain Head Select To Make SplineIK nHair Rig

リグブログさんの
https://backbone-studio.com/blog-nhairrig/

この記事を参考にやった。

nHairを使った感じの揺れものシミュレーション

①ジョイントを作成します。
ジョイントはX軸が子供を向くように設定しましょう。
X軸を子供に向けておくと、SplineIK機能のAdvanced Twist Controls(根元と先端のツイストを制御できる)の設定がしやすくなります。
※Advanced Twist Controlsの説明に関しては割愛します。

②ジョイントにSplineIKを設定します。

③作られたcurve1をルートに移動しておく。
curve1を選択して、FX > nHair > Assign Hair System > New Hair Systemを実行します。
※curve1は、自動的にfollicle1の階層に移動します。

④再生すると、シミュレーションによりcurve2は動いていますが、curve1は動いていないことが確認できます。
ノードエディターでSplineIKとnHairの接続関係を確認してみましょう。
上側がSplineIK、下側がnHairの入出力です。
nHairからの情報はcurveShape2へ繋がっているので、nHairのシミュレーションによりcurveShape2が動きます。
一方で、ikHandle1にはnHairからの情報が繋がっていません。

⑤curveShape2のworldSpace[0]出力をikHandle1のInCurveに入力することで、nHairの情報がSplineIKに渡り、ジョイントが動くようになります。
curveShape2のWorldSpace[0]をikHandle1のinCurveに繋ぎます。
各機能の関係性は、下図のようになります。

⑥再生して、ジョイントが動くことを確認しましょう。
あんまり動かないことも。。。

動かないのでいきなり⑨にとんでみた。

⑨ジョイント全体を制御したい場合は、curve1を制御してください。
今回はジョイントの根元に別のジョイントchain_handle_01を作成し、curve1をペアレントコンストレインしてみました。
再生するとエンドフレームでループしていい感じに動き始めた。

なのでリグの方のジョイントチェーンをスケルトンのジョイントチェーンに方向コンストレインしていく

最後にスケルトンのジョイントチェーンにchain_handle_01を位置コンストレインしてあげれば動くはず。
ルートを選択して再生しながら動かしたらスケルトン通りに動きました。

ーーーー以下連続したいときのショート版ーーーーー
1,ジョイントチェーンの複製してグループ化しておく
2,リギング>スケルトン>SplineIKで親と最短クリック
3,できたcurve3に(curveの名前を変えちゃダメ)
FX > nHair > Assign Hair System > New Hair System
していく※curve3は、自動的にfollicle1の階層に移動
hairSystem2OutputCurvesの下にいる
curve4
ノードエディタの+で新規タブを開き入力と出力を接続ボタンを押し
curveShape4のWorldSpace[0]をikHandle1のinCurveに繋ぎます。
ジョイントから複製そしてchain_handle_01を作成し、curve1をペアレントコンストレインでリグ1本完成
リグの方のジョイントチェーンをスケルトンのジョイントチェーンに方向コンストレインしていく
最後にスケルトンのジョイントチェーン頭にchain_handle_01を位置コンストレインしてあげれば動くはず。

この作業を自動化した。
ジョイントチェーンの頭のジョイントを選択して実行するとnHairリグが作られる。
ーーMELーーーーーーーーーーーーーーーーーー

//JointChain Head Select To Make nHairRig 
string $selectedArr[] = `ls -shortNames -sl`;
string $selectedArrFUllPath[] = `ls -long -sl`;
print($selectedArr);
string $selectedJoint = $selectedArr[0];
string $buffer[];
$numTokens = `tokenize $selectedArr[0] "|" $buffer`;
$bufferlong=size($buffer);
string $selectedJointShort = $buffer[$bufferlong-1]+"";

print("$selectedJoint= "+$selectedJoint+"\n");
print("$selectedJointShort= "+$selectedJointShort+"\n");
string $selectedJointFullPath = $selectedArrFUllPath[0];
print("$selectedJointFullPath= "+$selectedJointFullPath+"\n");
string $jointNodes[] = `ls -dag $selectedJoint`;
string $jointNodesFullPath[] = `ls -long -dag $selectedJointFullPath`;
//duplicate
string $RigJoints[] =`duplicate $selectedJoint`;

$RigJointslong=size($RigJoints);
//name check
for($i = 0; $i <$RigJointslong;$i++){
    string $rig_j_name=$RigJoints[$i];
    print("$rig_j_name= "+$rig_j_name+"\n");
}
string $RigJointTop = $RigJoints[0];
print("RigJointTop= "+$RigJointTop+"\n");
parent -world $RigJointTop;
string $groupName = $selectedJointShort+"_group";
group -n $groupName $RigJointTop;
print("groupName= "+$groupName+"\n");
string $rigJointTop =`rename $RigJointTop $selectedJoint`;
//SplineIK
print("$rigJointTop= "+$rigJointTop+"\n");
string $rigjointNodes[] = `ls -dag $rigJointTop`;
print($rigjointNodes);
$rigjointNodeslong=size($rigjointNodes);
print("$rigjointNodeslong= "+$rigjointNodeslong+"\n");
string $rigJointEnd = $rigjointNodes[$rigjointNodeslong-1];
print("$rigJointEnd= "+$rigJointEnd+"\n");

string $ikHandleReturn[] =`ikHandle -solver ikSplineSolver -startJoint $rigJointTop -endEffector $rigJointEnd`;
print($ikHandleReturn);
string $ikHandleRef = $ikHandleReturn[0];
print("$ikHandleRef= "+$ikHandleRef+"\n");
$ikHandleReturnlong=size($ikHandleReturn);
//name check
string $curveRef;
for($i = 0; $i <$ikHandleReturnlong;$i++){
    string $ikHandleRet_name=$ikHandleReturn[$i];
    print("$ikHandleRet_name= "+$ikHandleRet_name+"\n");
    string $type = `objectType $ikHandleRet_name`;
    string $nodetype= `nodeType $ikHandleRet_name`;
    print("$type= "+$type+"\n");
    //if($type == "transform"){
    print("$ikHandleRet_name= "+$ikHandleRet_name+" type= "+$type+" nodetype= "+$nodetype+"\n");
    //}
    $index=`gmatch $ikHandleRet_name "curve*"`;
    if($index > 0){
        $curveRef = $ikHandleRet_name;
        print("$ikHandleRet_name= "+$ikHandleRet_name+" isHit!! "+"\n");
    }
}

//string $curveRef = $ikHandleReturn[2];
print("$curveRef= "+$curveRef+"\n");

select -r $curveRef;
assignNewHairSystem;
//root hairsystem all arr
string $rootHairSystemAll[]=`ls -assemblies "hairSystem*"`;
print($rootHairSystemAll);
print("$groupName= "+$groupName+"\n");
//parent $rootHairSystemAll[0] $groupName;
//parent $rootHairSystemAll[1] $groupName;
//parent $rootHairSystemAll[2] $groupName;
$rootHairSystemAlllong=size($rootHairSystemAll);
//name check
string $hairSystemShape;
for($i = 0; $i <$rootHairSystemAlllong;$i++){
    string $rig_hs_name=$rootHairSystemAll[$i];
    print("$rig_hs_name= "+$rig_hs_name+"\n");
    parent $rig_hs_name $groupName;
    string $rig_hs_Arr[]=`listRelatives -children $rig_hs_name`;
    print($rig_hs_Arr);
    $rig_hs_Arrlong=size($rig_hs_Arr);
    if($rig_hs_Arrlong>0){
        $index=`gmatch $rig_hs_Arr[0] "hairSystemShape*"`;
        if($index>0){
            $hairSystemShape = $rig_hs_Arr[0];
            print("$hairSystemShape= "+$hairSystemShape+"\n");
        }
    }

}
string $hairSystemShapeAtt1=$hairSystemShape+".solverDisplay";
string $hairSystemShapeAtt2=$hairSystemShape+".collideWidthOffset";
setAttr $hairSystemShapeAtt1 1;
setAttr $hairSystemShapeAtt2 1;

parent $ikHandleRef $groupName;
string $rootNucleus[]=`ls -assemblies "nucleus*"`;
print($rootNucleus);
$rootNucleuslong=size($rootNucleus);
if($rootNucleuslong>0){
    print("$rootNucleuslong= "+$rootNucleuslong+"\n");
    if( `objExists $rootNucleus[0]` ) {
        parent $rootNucleus[0] $groupName;
    } else {
        warning("No nucleus exists");
    }
}

string $hairSystem2OutputCurves=$rootHairSystemAll[2];
string $hairSystem2OutputCurveArr[]=`listRelatives -children $hairSystem2OutputCurves`;
print($hairSystem2OutputCurveArr);
string $curve2 = $hairSystem2OutputCurveArr[0];
string $curve2Arr[]=`listRelatives -children $curve2`;
print($curve2Arr);
string $curveShape2 = $curve2Arr[0];
print("$curveShape2= "+$curveShape2+"\n");
string $output1 = $curveShape2 + ".worldSpace[0]";
print("$output1= "+$output1+"\n");
string $input1 = $ikHandleRef + ".inCurve";
print("$input1= "+$input1+"\n");
connectAttr -f $output1 $input1;

//select -r $rigJointTop;
string $RigHandleJoints[] =`duplicate $rigJointTop`;
$RigHandJointslong=size($RigHandleJoints);
//name check
for($i = 0; $i <$RigHandJointslong;$i++){
    string $rig_hj_name=$RigHandleJoints[$i];
    print("$rig_hj_name= "+$rig_hj_name+"\n");
}
string $RigHandleJointsChild=$groupName+"|"+$RigHandleJoints[0]+"|"+$RigHandleJoints[1];
print("$RigHandleJointsChild= "+$RigHandleJointsChild+"\n");
delete $RigHandleJointsChild;
string $RigHandleJointsTop=$groupName+"|"+$RigHandleJoints[0];
print("$RigHandleJointsTop= "+$RigHandleJointsTop+"\n");
string $RigHandleJointsTopName=$selectedJointShort+"_handle";
print("$RigHandleJointsTopName= "+$RigHandleJointsTopName+"\n");
string $rigJointTop =`rename $RigHandleJointsTop $RigHandleJointsTopName`;
string $RigHandleJointsTopNameRadiusAtt  = $RigHandleJointsTopName + ".radius";
setAttr $RigHandleJointsTopNameRadiusAtt 15;
parentConstraint -maintainOffset $RigHandleJointsTopName $curveRef;
/////////////////////////////////////////RIG OK
$rigJointlong=size($rigjointNodes);
for($i = 0; $i <$rigJointlong;$i++){
    string $rig_j_name=$rigjointNodes[$i];
    print("$rig_j_name= "+$rig_j_name+"\n");
    string $joint_j_name = $jointNodesFullPath[$i];
    print("$joint_j_name= "+$joint_j_name+"\n");
    orientConstraint -maintainOffset $rig_j_name $joint_j_name;
}
////////////////////////////Joint Move OK
pointConstraint -maintainOffset $selectedJointFullPath $RigHandleJointsTopName;
string $blendShapeReturn[] =`blendShape $curveRef $curve2`;
$blendShapeReturnlong=size($blendShapeReturn);
string $blendShapeName;
for($i = 0; $i <$blendShapeReturnlong;$i++){
    string $blendshapeRet_name=$blendShapeReturn[$i];
    print("$blendshapeRet_name= "+$blendshapeRet_name+"\n");
    $index=`gmatch $blendshapeRet_name "blendShape*"`;
    if($index > 0){
        $blendShapeName = $blendshapeRet_name;
        print("$blendshapeRet_name= "+$blendshapeRet_name+" isHit!! "+"\n");
    }
}

string $blendShapeAtt=$blendShapeName+"."+$curveRef;
//0 is Move 1 is Stop
setAttr $blendShapeAtt 0.90;

こんなかんじ 画像をクリックしてGIF再生

——–以下しらべもの———–
whatIs assignNewHairSystem;
// 結果: Mel procedure found in: C:/Program Files/Autodesk/Maya2016/scripts/startup/DynCreateHairMenu.mel //

string $hsystems[] = ls -type hairSystem;
int $i;
for( $i = 0; $i < size( $hsystems ); $i++ ){
string $hsys = $hsystems[$i];
string $annot = format -s $hsys $fmt;
menuItem -label $hsys
-image “hairAssignHairSystem.png”
-command (“assignHairSystem “+$hsys)
-annotation $annot;
}

whatIs assignHairSystem;

// 結果: Mel procedure found in: C:/Program Files/Autodesk/Maya2016/scripts/others/assignHairSystem.mel //
ーーーーーーーーーーーーーーーーーーー
⑦次に衝突判定(コリジョン)の設定を行います。
体はバラバラなので体を結合したメッシュを用意して
Genesis8FemaleSkin_for_MantCollider
衝突判定用のジオメトリに使います。
Genesis8FemaleSkin_for_MantColliderを選択して FX > nCloth > Create Passive Collider を実行します。
nRigid1が作成されます。

ーーーブレンドシェイプをつかった調整ーー
⑩揺れ具合などの調整は、hairSystem1の各アトリビュートで行いますが、タイミング毎に調整するのは大変です。
揺れを抑えるのみであれば、BlendShapeのWeightアトリビュートを使用すると簡単に制御できます。
curve1とcurve2を選択してBlendShapeを設定し、
curve2のアトリビュートについたBlendShape1のcurve1というWeightアトリビュート(0~1)を調整して制御します。

ーーーオフセット用のコントローラーを付ける場合ーー
⑪次に、オフセット用のジョイント(コントローラー)の設定を行います。
再生を止めて0フレーム目に戻し、初期ポーズにします。
joint1~7のジョイントを複製し、joint1Offset~7に名前を変更します。
joint1Offset~7のジョイントを複製し、joint1Offset_space~7に名前を変更します。
joint1Offset_spaceジョイントの中にそれぞれjoint1Offsetを入れていきます。
※Spaceノードについてはこちら。https://backbone-studio.com/post-237-3/

⑫一番親のjoint1Offset_spaceを選択し、FreezeTransformationsを実行してRotateの値をjointOrientに移植します。

joint1Offset_space~7からjoint1~7へTranslateとRotateをコンストレインします。

UE4 でパッケージ化のエラーを特定する方法

UE4 でパッケージ化のエラーを特定する方法
アウトプットログは右クリックして
ログをクリアしてからパッケージ化することで
しっかり
パッケージ化のエラーだけを
表示してくれるようになる。

あたりまえだけどしっかりやらないと他の関係ないエラーを追う結果になる。

プログラムを含んでないコンテンツの場合だいたいが

UATHelper: パッケージ化 (Windows (64-bit)): BUILD COMMAND COMPLETED
UATHelper: パッケージ化 (Windows (64-bit)): COOK COMMAND STARTED

コンテンツのクックが始まり

ここらへんのエラーを治したら直った。
いらないアニメーションブレンド1Dを削除したら解決した。

UATHelper: パッケージ化 (Windows (64-bit)): LogCook: Display: Cooked packages 587 Packages Remain 44 Total 631
UATHelper: パッケージ化 (Windows (64-bit)): LogCook: Display: Cooked packages 588 Packages Remain 16 Total 604
UATHelper: パッケージ化 (Windows (64-bit)): LogCook: Display: Cooked packages 588 Packages Remain 0 Total 588
UATHelper: パッケージ化 (Windows (64-bit)): LogCook: Display: Finishing up…

となるみたい。

AポーズからTポーズにしてモーションの流し込みやすいMaya HumanIKスケルトンにキレイにモデルを合わせる方法1~Duplicateで固まるメッシュ※0 を使う方法~

AポーズからTポーズにしてモーションの流し込みやすいMaya HumanIKスケルトンにキレイにモデルを合わせる方法1~Duplicateで固まるメッシュ※0 を使う方法~

きれいにというのは「もとのAポーズのモデルのスキンウェイトをつかってTポーズにしようとしてる」と「新しいjointはできるだけHumanIKのジョイントで作ることでHumanIKでバグらせないことが目標」

大きな流れは
 ジョイントをHIKに差し替え→AポーズからTポーズ→Tポーズでのバインド

1,AposeモデルにMeshEdit用のApose JointをHumanIKスケルトンを複製して作る
(これでHumanIKのベースのジョイント方向や階層構造をキープすると流し込みが完璧になる)
2,そのMeshEdit用のjointをスケールしないで移動と回転などしてApose Meshに合わせる。
2.5,MayaHumanIKにはない頭のジョイントをコピーして親子付けする※1

3,Apose MeshにあわせたそのMeshEdit用のApose jointとApose meshをスキンのバインドする 
3.5 元のAposeモデルからのAposeのスキンウェイトのコピーをする。

4.1見る用にHumanIKスケルトンデフォルトのTposeを複製して作る
4.2 AposeジョイントをAポーズからTポーズにアニメーション30でTポーズに全部「S」でキーを打つ 必要なのは肩のキーだけだ。他に見る用にHumanIKスケルトンに足幅が合ってなければもも関節もキーを打つ。
5,Tposeへ変形+バインドされたApose MeshグループをTポーズの30フレームで「Mesh+Joint」のグループごと複製したグループC(groupMeshHIK_Tpose)とする

6.Cのスキンバインドを外す、
6a. フレームヘッドを0フレームに移動し、
6b. Tポーズのジャケットなどをインポートして、
6c. 0 frame Tposeでスキンバインドここ重要
6d. 30 frameでスキンウェイトをコピーここが肝
※Genesis8Femaleの場合スキンウェイトのコピーの設定※
7a,CをHumanIKスケルトン(groupMeshHIK_TposeSK)として定義
モーション流し込みなおすならこのファイルから
 Genesis8Skelton_HumanIK_049_group_meshHIK_AposeToTposeFr30humanIKfinger.mb
https://www.mediafire.com/file/n4ekyeubv6c4m96/Genesis8Skelton_HumanIK_049_group_meshHIK_AposeToTposeFr30humanIKfinger.zip/file
 これに下のMotionMergeツールで作成したmbアニメーション全体を読み込む
 >>full_end_python_saved__all_onetimeline.mb 読み込まれるファイル
7b.HumanIkの青のボタン>定義>スケルトン
 HumanIkの青のボタン>編集>定義>名前の変更
 SK_manequinを「Character_SK_Mannequin」としてHumanIKに定義する。
 全身Assainしていくことになるが、アサイン結果が左右ミラーされるのでちょっと楽
 手の指先までやる。
 

8,groupMeshHIK_TposeSKに2段目のソースにアニメーションを指定してモーションを流し込む 詳細は※2
8a, スケルトン定義に移動して青のボタンから編集>定義>プロパティを編集
8b.リターゲット指定からソースの一致をONにすると歩く歩幅が合わせることが可能
8c.達成度の項目の左手首を1に右手首を1に指定すると手の位置が合ってテーブルのコップがつかめます。ここでやっとアニメーションの見た目が完璧近い状態で合う。
8d.HumanIKパネルに戻って、青いボタンからベイク処理>コントロールリグのベイク処理
8f. ソースのモーションスケルトンをアウトライナから削除とソースキャラクタ定義へ移動しをゴミ箱アイコンで削除。

8f2ここまでやったらアニメーションレイヤー使うかどうかはモーション作業内容次第。
  
 8g.アニメーションレイヤーでの調整作業。
  ベイクしたTポーズ定義でなく「コントロール」タブで背景を
  ダブルクリックしてコントロールリグを全選択し
  (とビデオではいっているがアウトライナで選択しないと指が全部にならなかった。)、  
   チャネルボックス・レイヤエディタ・アニメーションタブを開く。
 8h.アニメーションタブで「選択項目からレイヤを作成」でAnimLayer1が
  作成され上乗せでキーをうつことが可能になる。
 8i.二つのアニメーションレイヤーを選択して右クリックから
  レイヤーのマージでアニメーションをベイクします。

  1. コントロールリグアニメーションを直接編集しなおす。
      リグのキーフレームをフレーム間で削除して修正
      前のリグアニメを書き出した
      それはこれを使った
    https://furcraea.verse.jp/wp/2021/03/18/humanik-effector-ctrl-animationexporter-importer/

10a groupMeshHIK_TposeSK にコントローラのアニメーションをベイクします。
ジョイントツリーを全部開いて選択後、タイムレンジを0~1000(最終fr)に設定
 アニメーションタブのアニメーションのベイクボタンを押す。
10b,FBXをエクスポート スムーズメッシュをオン FBX2014/2015 で書き出し。
E:\Download\Game\UE4_SK_Mannequin_Motions\scenes\OneTimeLine\Unity_fbx
motion_000_000_all_unkeyReduction_fbx.fbx
を上書きします。

10c,UE4で編集>プラグイン>プラグインウィンドウで検索pythonと入力し
Python Editor Script Pluginの Enables をチェックを入れONにします。
プラグインウィンドウで検索Editorと入力し
Editor Scripting Utilitiesの Enables をチェックを入れONにします。
UE4を再起動してプラグインをアクティブにします。

10d.UE4で
見た目でいうと
/コンテンツ/UE4_OneTimeLineGenesis8Tpose/
パスでいうと
/Game/UE4_OneTimeLineGenesis8Tpose/
にmotion_000_000_all_unkeyReduction_fbx.fbxを
インポートウィンドウでスケルトン設定を「なし」で
スケルタルメッシュとしてインポートします。
スケルタルメッシュmotion_000_000_all_unkeyReduction_fbxと
ひとつなぎのアニメーション motion_000_000_all_unkeyReduction_fbx_Anim
ができていたら
/Game/UE4_OneTimeLineGenesis8Tpose/motion/ フォルダを作成しておきます
command + shift + s またはファイル>すべて保存します。

10d.アニメーションスプリットツールでインポートします。

UE4_SK_Mannequin_Motions\script\
UE4_chara_anim_split_importer.py
をテキストエディタで開き
E:\Download\Game\UE4_SK_Mannequin_Motions\
を自分のパスに検索置換します。

それをすべて選択して
UE4 >ウィンドウ>デベロッパーツール>アウトプットログの一番下のCmdのプルダウンをPython(無印)を選択して
Enter Python script…の部分に
さきほどのコピーしたものをペーストし+Enterを押します。
8~10分で読み込まれます。
~やっと終わり~

モーションを修正する場合には

  1. コントロールリグアニメーションを直接編集しなおす。
    からやりなおせばOK

と思ったが
Paragliderのプロジェクトに入れたら問題が出てきた。
UE4のジョイント名でないとr_handからik_handが探せないエラー
できあがったモーションセットをソースとしてSK_mannequinに戻す作業が必要そうだ。
motion_000_000_all_unkeyReduction_fbx.fbxを
つづく。。。

※0 Duplicateで固まるメッシュ というのは スキンバインドしてアニメーションさせたMeshをモーション後のフレームでDuplicate するとその複製されたメッシュがその形状のまま固まる現象をいう。ちょっとしたバグなのか仕様なのかわからない。都合がいい。

※1 MayaHumanIKにはない頭のジョイントをコピーして親子付けすると変なtransformができるが、全ジョイントのスケールをフリーズして、ヒストリ削除したら親子付けできる。
https://www.deathponta.com/entry/190724_parentingAjointProduceMysteriousTransformNode

※2 HumanIKモーションの流し込みのビデオ
https://area.autodesk.jp/movie/start-at-maya/24.html
https://www.youtube.com/watch?v=S214-96uXqc

使用+橋渡しができたプラットフォームのデータ
ここでは DAZ 3D Studio のGenesis8FemaleはAposeなのでHumanIKのTposeにする。
モーションはUnrealEngine4のデフォルトSK_Mannequinのモーションを
書き出して使った。

UE4 FBX Export Options 設定は
▼Exporter
 Fbx Export Compatibility > FBX2013
▼Mesh
 Vertex Color OFF
 Level Of Detail OFF
▼Static Mesh
 コリジョン OFF
▼Skeletal Mesh
 Export Morph Targets OFF
▼Animation
 Export Preview Mesh ON
 Map Skeletal Motion to Root OFF

全モーションのマージにはこれを作った (書き直したが疲れた。)
https://www.mediafire.com/file/x9tdw9jtrl0t2p2/MotionMerge_p030motionbuilder_chimera_0_Start2.zip/file
インストール方法
C:\Users\whaison\Documents\maya\2016\ja_JP\scripts
にpythonフォルダごと突っ込む。

Maya起動後
プロジェクト設定で指定するワークスペースのディレクトリ構造はこう。
scenes\motion\motionbuilder_fbx\ にUE4SK_mannequinのモーションfbxを全部突っ込む
ファイル名は名前順で並ぶようにちゃんとリネームしておくこと。

C:\Users\whaison\Documents\maya\2016\ja_JP\scripts\python\MotionMerge\p030motionbuilder_chimera_0_Start
Motionbuilder_chimera_0_Start_______Maya_____Starter.py
の内容をスクリプトエディタで実行する。と30分後?ぐらいに
scenes\OneTimeLine\
full_end_python_saved__all_onetimeline.mb
ができあがる。

フレームのどこの部分に該当のモーションが入ってるかは下に出力した。
scenes\motion に
frameCSV.txt
motion_[C0000]_000_Tstance,0,0,OnMotionStart,0.0,OnMotionEnd,1.0,
motion_0000_00_000_Tstance_manual,10,10,OnMotionStart,0.0,OnMotionEnd,1.0,
motion_0000_00_000_Tstance,20,20,OnMotionStart,0.0,OnMotionEnd,1.0,
motion_ZZZZ_ZZ_999_TposeEnd,30,30,OnMotionStart,0.0,OnMotionEnd,1.0,
ThirdPersonIdle,40,103,OnMotionStart,0.0,OnMotionEnd,1.0,
ThirdPersonJump_Loop_previewMesh2,113,130,OnMotionStart,0.0,OnMotionEnd,1.0,
ThirdPersonJump_Start,140,151,OnMotionStart,0.0,OnMotionEnd,1.0,
ThirdPersonRun,161,175,OnMotionStart,0.0,OnMotionEnd,1.0,
ThirdPersonSwordAttack2,185,206,OnMotionStart,0.0,OnMotionEnd,1.0,
ThirdPersonWalk,216,240,OnMotionStart,0.0,OnMotionEnd,1.0,
ThirdPerson_Jump,250,254,OnMotionStart,0.0,OnMotionEnd,1.0,
TJ_Paraglider_Deploy_Additive,264,356,OnMotionStart,0.0,OnMotionEnd,1.0,
TJ_Paraglider_Idle,366,606,OnMotionStart,0.0,OnMotionEnd,1.0,
TJ_Paraglider_Idle_AdditiveBase,616,616,OnMotionStart,0.0,OnMotionEnd,1.0,
TJ_Paraglider_Moving_Additive,626,650,OnMotionStart,0.0,OnMotionEnd,1.0,
TJ_Paraglider_Turning_Additive,660,684,OnMotionStart,0.0,OnMotionEnd,1.0,
TK_0-26_A_LedgeClimb,694,720,OnMotionStart,0.0,OnMotionEnd,1.0,
TK_30-102_A_LedgeHold,730,802,OnMotionStart,0.0,OnMotionEnd,1.0,
TK_120-144_A_wallclimb,812,836,OnMotionStart,0.0,OnMotionEnd,1.0,
TK_160-184_A_wallclimbDown,846,870,OnMotionStart,0.0,OnMotionEnd,1.0,
TK_200-249_A_wallCling_01,880,929,OnMotionStart,0.0,OnMotionEnd,1.0,

これをUE4読み込み時にUE4Pythonスクリプトで使う
同じZIPの
https://www.mediafire.com/file/d82x4dd6o1a4k28/MotionMerge_p030motionbuilder_chimera_0_Start4.zip/file
UE4_chara_anim_split_importer.py
E:\Download\Game\UE4_SK_Mannequin_Motions\script\UE4_chara_anim_split_importer.py
に移動して使う
E:/Download/Game/UE4_SK_Mannequin_Motions/scenes/OneTimeLine/
Unity_fbx/motion_000_000_all_unkeyReduction_fbx.fbxが入ってる前提で動く
必要な場合はパスを検索置換したらうごく。


※Genesis8Femaleの場合スキンウェイトのコピーの設定※は
サーフェースの関連付け:UV空間 or レイキャスト
インフルエンスの関連付け1:名前
インフルエンスの関連付け2:なし
インフルエンスの関連付け3:なし
にしたら顔のウェイトが比較的良く転送できた。目の周りのジョイントは位置が同じでUVは違うのでこうなったようだ。
といってもこんなもん
コピー元


コピー先

ほかの設定だと全くコピーされないか、完全におかしいコピーだった。

キーワード
Daz3D,DazStudio
UE4,UnrealEngine4,UnrealEngine
HumanIK
Apose,Tpose,AposeからTposeへの変換
スキニング,スキンウェイト

UE4 IK Two Bone IKノード Paraglider アセットで手をグライダーにかける方法

UE4 IK Two Bone IKノード Paraglider アセットで手をグライダーにかける方法

モデルを変更した結果
Paragliderのプロジェクトに入れたら問題が出てきた。
UE4のジョイント名でないとr_handからik_hand、lowerarm_lが探せないエラー

LogAnimation: Warning: FBoneReference::Initialize BoneIndex for Bone ‘lowerarm_l’ does not exist in Skeleton ‘motion_000_000_all_unkeyReduction_fbx_Skeleton’


lowerarm_lだけならTwo Bone IKノードのJointTarget>JointTarget>lowerarm_lを別のヒジジョイント名に変えるだけでOKだ。


そんなことは後で知ったので
Mayaでjointの名前を変更した。>大変だったが必要なのは
root>ik_hand_root>ik_hand_r と
root>(hipあってもなくてもいい)>pelvis >spine_01>spine_2>spine_03>clavicle_r>upperarm_r>lowerarm_r>hand_r
の流れだった。
実はヒジのジョイント名だけでいいかもしれない
あとはちゃんとik_hand がhandジョイントにコンストレインされて同じように動いていることかもしれない。

いがいと、ジョイント名が変わっても
ジョイント位置+メッシュ頂点位置でスキンウェイトコピーが反応してくれて大丈夫だった

「余裕がないとかからない」ので
ThirdPersonCharactor_BPのビューポートでParagliderComponent_BPの位置を
位置
z:109.897697
から
z:85にして近づけたら
グライダーとモデルの間隔がせばまって届くようになった。

HumanIK Effector Ctrl AnimationExporter/importer

HumanIKのコントローラーのRIGアニメーション作成は楽しいので昔のKeyFrameを新しいファイルに入れたいときに使う。

animでもatomでも持っていけなかったのでに作るしかないかーと思った。

どうしてもRigなので引っ張られている分ポーズがまんま持っていけるわけではないので確認と調整が必要、ポーズによってはそのまま使える

OKなポーズ

NGだったポーズ

ほとんどNGやないかい。まあ大体の大枠のポーズはインポートできてる。
膝のまがりとかがダメだった。

腰の位置にキーが入らないというかそこらへんが問題ある
HumanIKのHipはへんな記事があった。
HumanIKヒップエフェクターが壊れています
https://forums.cgsociety.org/t/humanik-hip-effector-broken/1583635/3
によると設定をデフォルトにすると直るとか直らないとか神のみぞ知るとか。。

まあとりあえずソース公開

HumanIK Effecter Ctrl Animation Exporter

import maya.cmds as cmds

class HumanIK_Effecter_Ctrl_AnimationExporter:
	def MainWay(self):

		selects=cmds.ls(sl=True)
		selectRef=selects[0]
		print("selectRef= "+selectRef)

		_CtrlIndex=selectRef.find("_Ctrl")
		print("_CtrlIndex= "+str(_CtrlIndex))

		SkeletonName=selectRef[0:_CtrlIndex]
		print("SkeletonName= "+SkeletonName)

		HipsCtrlName=SkeletonName+"_Ctrl_Hips"
		print("HipsCtrlName= "+HipsCtrlName)
		
		startFrame=185
		endFrame=206


		self.keyAllLineStr=""
		
		self.NodeAllLoop(selectRef,HipsCtrlName,startFrame,endFrame)
	
	def key_to_add_line(self,CtrlNode,Way,key_arr):
		if(str(key_arr)=="None"):
			pass
		else:
			 key_arr.insert(0, Way)
			 key_arr.insert(0, CtrlNode)
			 key_str_arr=[]
			 #1度数値を文字列に変換する
			 for v in key_arr:
				str_v=str(v)
				key_str_arr.append(str_v)
			 key_arr_str=",".join(key_str_arr)
			 
			 print("key_arr_str= "+key_arr_str)
			 self.keyAllLineStr=self.keyAllLineStr+key_arr_str+"\n"

	def NodeAllLoop(self,selectRef,HipsCtrlName,startFrame,endFrame):
		
		# 指定したノード以下全てを検索
		CtrlNodeAll=cmds.ls(HipsCtrlName,dag=1)
		#CtrlNodeAll=cmds.ls(selectRef,dag=1)
		print("CtrlNodeAll= "+str(CtrlNodeAll))
		
		CtrlNodeAll.insert(0,selectRef)
		
		for CtrlNode in CtrlNodeAll:
			keyFrameData=""
			#0〜20の時間範囲内のオブジェクト「surface1」のすべてのキーフレームをクエリします。
			#
			keyFrames_rx = cmds.keyframe( CtrlNode,at='rx', time=(startFrame,endFrame), query=True, valueChange=True, timeChange=True);
			keyFrames_ry = cmds.keyframe( CtrlNode,at='ry', time=(startFrame,endFrame), query=True, valueChange=True, timeChange=True);
			keyFrames_rz = cmds.keyframe( CtrlNode,at='rz', time=(startFrame,endFrame), query=True, valueChange=True, timeChange=True);
			
			self.key_to_add_line(CtrlNode,"rx",keyFrames_rx)
			self.key_to_add_line(CtrlNode,"ry",keyFrames_ry)
			self.key_to_add_line(CtrlNode,"rz",keyFrames_rz)
			#print("keyFrames_rx= "+str(keyFrames_rx))
			#print("keyFrames_ry= "+str(keyFrames_ry))
			#print("keyFrames_rz= "+str(keyFrames_rz))

			keyFrames_tx = cmds.keyframe( CtrlNode,at='tx', time=(startFrame,endFrame), query=True, valueChange=True, timeChange=True);
			keyFrames_ty = cmds.keyframe( CtrlNode,at='ty', time=(startFrame,endFrame), query=True, valueChange=True, timeChange=True);
			keyFrames_tz = cmds.keyframe( CtrlNode,at='tz', time=(startFrame,endFrame), query=True, valueChange=True, timeChange=True);
			#keyFrames=cmds.keyframe( sel、time =(185,206)、query = True、valueChange = True、timeChange = True);

			self.key_to_add_line(CtrlNode,"tx",keyFrames_tx)
			self.key_to_add_line(CtrlNode,"ty",keyFrames_ty)
			self.key_to_add_line(CtrlNode,"tz",keyFrames_tz)

			#print("keyFrames_tx= "+str(keyFrames_tx))
			#print("keyFrames_ty= "+str(keyFrames_ty))
			#print("keyFrames_tz= "+str(keyFrames_tz))
			
			keyFrames_sx = cmds.keyframe( CtrlNode,at='sx', time=(startFrame,endFrame), query=True, valueChange=True, timeChange=True);
			keyFrames_sy = cmds.keyframe( CtrlNode,at='sy', time=(startFrame,endFrame), query=True, valueChange=True, timeChange=True);
			keyFrames_sz = cmds.keyframe( CtrlNode,at='sz', time=(startFrame,endFrame), query=True, valueChange=True, timeChange=True);
			
			self.key_to_add_line(CtrlNode,"sx",keyFrames_sx)
			self.key_to_add_line(CtrlNode,"sy",keyFrames_sy)
			self.key_to_add_line(CtrlNode,"sz",keyFrames_sz)
			
		print("self.keyAllLineStr ="+ self.keyAllLineStr)
		self.fileWrite()
		
	def fileWrite(self):
		
		import codecs

		fout = codecs.open('C:/Users/whaison/Documents/maya/2016/ja_JP/scripts/python/MotionMerge/p030motionbuilder_chimera_0_Start/HumanIK_Effecter_Ctrl_AnimationExporter_keyFrameData.csv', 'w', 'utf_8')
		fout.write(self.keyAllLineStr)




ClassIns = HumanIK_Effecter_Ctrl_AnimationExporter()
ClassIns.MainWay()

HumanIK Effecter Ctrl Animation Importer

import maya.cmds as cmds

class HumanIK_Effecter_Ctrl_AnimationImporter:
	def MainWay(self):

		selects=cmds.ls(sl=True)
		selectRef=selects[0]
		print("selectRef= "+selectRef)

		_CtrlIndex=selectRef.find("_Ctrl")
		print("_CtrlIndex= "+str(_CtrlIndex))

		SkeletonName=selectRef[0:_CtrlIndex]
		print("SkeletonName= "+SkeletonName)

		HipsCtrlName=SkeletonName+"_Ctrl_Hips"
		print("HipsCtrlName= "+HipsCtrlName)
		
		startFrame=185
		endFrame=206


		self.keyAllLineStr=""
		
		#self.NodeAllLoop(HipsCtrlName,startFrame,endFrame,)
		
		self.fileReader()
		
		
	def fileReader(self):
		
		import codecs
		fin = codecs.open('C:/Users/whaison/Documents/maya/2016/ja_JP/scripts/python/MotionMerge/p030motionbuilder_chimera_0_Start/HumanIK_Effecter_Ctrl_AnimationExporter_keyFrameData.csv', 'r', 'utf_8')
		for line in fin:
			print("line= "+line)
			if(line==""):
				pass
			else:
				self.line_to_setkeyframe(line)
			
	
	def line_to_setkeyframe(self,line):
		key_arr=line.split(",")
		CtrlName=key_arr[0]
		print("CtrlName= "+CtrlName)
		Way=key_arr[1]
		print("Way= "+Way)
		key_arr_len=len(key_arr)
		frameStr=""
		valueStr=""
		for i in range(2,key_arr_len):
			val=key_arr[i]
			
			if i % 2 == 0:
				print('frame= '+val)
				frameStr=val
				framefloat=float(frameStr)
			else:
				print('Value= '+val)
				valueStr=val
				valuefloat=float(valueStr)
				print('cmds.setKeyframe( '+CtrlName+","+Way+",t="+str(framefloat)+",v="+str(valuefloat)+")")
				try:
					cmds.setKeyframe( CtrlName, attribute=Way, t=framefloat,v=valuefloat)
				except OSError as err:
					print("OS error: {0}".format(err))
				except ValueError:
					print("Could not convert data to an integer.")
				except:
					print("Unexpected error:", sys.exc_info()[0])
					raise
				
	
	def NodeAllLoop(self,HipsCtrlNam,startFrame,endFramee):
		
		# 指定したノード以下全てを検索
		CtrlNodeAll=cmds.ls(HipsCtrlName,dag=1)
		print("CtrlNodeAll= "+str(CtrlNodeAll))
		
		for CtrlNode in CtrlNodeAll:
			pass
"""
keyFrames_rx= [185.0, 21.089741780299963, 186.0, 21.15808805587963, 187.0, 21.57269253024709, 188.0, 15.651666434240237, 189.0, -27.60824368710669, 190.0, -66.8640151602632, 191.0, -3.6033652533432057, 192.0, -12.976039746033836, 193.0, 21.089741780299963, 206.0, 21.089741780299963]
keyFrames_ry= [185.0, 64.06950181042265, 186.0, -43.06801204369242, 187.0, -42.23534535644165, 188.0, 6.3003207259776, 189.0, -18.619267402281043, 190.0, -46.39726736167605, 191.0, 7.25455667934068, 192.0, 22.112923704038533, 193.0, 64.06950181042265, 206.0, 64.06950181042265]
keyFrames_rz= [185.0, -39.35273351243995, 186.0, -71.88505367073185, 187.0, -71.23919938208138, 188.0, -12.884469861914607, 189.0, -23.728143045065494, 190.0, 1.5441603410596807, 191.0, -29.38106191819622, 192.0, 27.959038658924545, 193.0, -39.35273351243995, 206.0, -39.35273351243995]

#tx
keyFrames_tx= [185.0, -31.063541412353516, 186.0, 13.386266787875757, 187.0, 12.569612782818957, 188.0, -68.13177446430325, 189.0, -14.078931727868042, 190.0, 34.98399964606946, 191.0, -58.035783944634446, 192.0, 38.62583642912938, 193.0, -31.063541412353516, 206.0, -31.063541412353516]
keyFrames_ty= [185.0, 103.91255187988281, 186.0, 160.48102799110984, 187.0, 135.33789404307092, 188.0, 123.42318340077544, 189.0, 143.61781646990772, 190.0, 162.44752777221774, 191.0, 147.59765966522042, 192.0, 149.99067818831213, 193.0, 103.91255187988281, 206.0, 103.91255187988281]
keyFrames_tz= [185.0, -13.255008697509766, 186.0, 126.81360281638925, 187.0, 114.12469622889218, 188.0, 62.25401426920391, 189.0, 116.16546039338427, 190.0, 124.82832123482547, 191.0, 79.97223357269715, 192.0, 43.964009286900165, 193.0, -13.255008697509766, 206.0, -13.255008697509766]

cmds.setKeyframe( 'group_meshHIK_TposeSK_Ctrl_Reference', attribute='translateX', t=186.0,v=-27.940739594423125,)
cmds.setKeyframe( 'group_meshHIK_TposeSK_Ctrl_Reference', attribute='rotationX', t=186.0,v=-27.940739594423125,)
cmds.setKeyframe( 'group_meshHIK_TposeSK_Ctrl_Reference', attribute='rx', t=186.0,v=-27.940739594423125,)

cmds.setKeyframe( 'group_meshHIK_TposeSK_Ctrl_RightArm', attribute='rx', t=185.0,v=21.15808805587963)
cmds.setKeyframe( 'group_meshHIK_TposeSK_Ctrl_RightArm', attribute='ry', t=185.0,v=-43.06801204369242)
cmds.setKeyframe( 'group_meshHIK_TposeSK_Ctrl_RightArm', attribute='rz', t=185.0,v=-71.88505367073185)
#translate
cmds.setKeyframe( 'group_meshHIK_TposeSK_Ctrl_RightWristEffector', attribute='tx', t=186.0,v= 13.386266787875757)
cmds.setKeyframe( 'group_meshHIK_TposeSK_Ctrl_RightWristEffector', attribute='ty', t=186.0,v=160.48102799110984)
cmds.setKeyframe( 'group_meshHIK_TposeSK_Ctrl_RightWristEffector', attribute='tz', t=186.0,v=126.81360281638925)
"""
ClassIns=HumanIK_Effecter_Ctrl_AnimationImporter()
ClassIns.MainWay()

スキンウェイトに詳しくなりたい2 deformerWeights コマンド

https://area.autodesk.jp/column/tutorial/maya_atoz/save-load-skin-weight/にあった最新のスキンウェイトのコピー方法

//XMLexport
deformerWeights -export -deformer “group_mesh|Mesh|Genesis8FemaleSkin” -format “XML” -path “C:/Download/Game/UE4_SK_Mannequin_Motions/scenes/OneTimeLine/” “Genesis8FemaleSkin.xml”;

//XMLimport
deformerWeights -import -method “index” -deformer “group_meshHIK|Mesh|Genesis8FemaleSkin” -path “C:/Download/Game/UE4_SK_Mannequin_Motions/scenes/OneTimeLine/” “Genesis8FemaleSkin.xml”;
skinCluster -e -forceNormalizeWeights “group_meshHIK|Mesh|Genesis8FemaleSkin”; // オプション設定でNormalize weightsがオンだった場合この行も実行。

Maya2016でだと
// エラー: line 1: 無効なフラグ ‘-format’ // になる。
Maya2020で試した
// エラー: line 2: 有効なデフォーマが見つかりません。 //

なんだろ。

モデルがわるかった。スキンバインドが壊れてた。ジョイント曲げても反応なし

スキンウェイトに詳しくなりたい copySkinWeights コマンド

スキンウェイトに詳しくなりたい
スキンウェイトの世界は自動化がまだ追いついてないっぽい。
スキンウェイトのコピーの中身は

copySkinWeights -sourceSkin skinCluster1 -destinationSkin skinCluster2 -noMirror;
http://me.autodesk.jp/wam/maya/docs/Maya2010/Commands/copySkinWeights.html

cmds.copySkinWeights( sourceSkin=’skinCluster1′, destinationSkin=’skinCluster2′, noMirror=True
http://me.autodesk.jp/wam/maya/docs/Maya2010/CommandsPython/copySkinWeights.html

解決できないエラー(モデルがわるかった。スキンバインド全体が壊れてた。)
cmds.copySkinWeights( sourceSkin=’group_mesh|Mesh|Genesis8FemaleSkin’, destinationSkin=’group_meshHIK|Mesh|Genesis8FemaleSkin’, noMirror=True )

エラー: line 1: RuntimeError: file line 1: skinCluster ノードは -sourceSkin/-ss フラグとともに指定する必要があります。 #

解決できないエラー(一般的)(モデルがわるかった。スキンバインド全体が壊れてた。)
copySkinWeights -noMirror -surfaceAssociation closestPoint -influenceAssociation closestJoint;
// 警告: line 0: スキン オブジェクトではないので ‘Genesis8FemaleSkinShape’ をスキップします。 //
// エラー: line 0: 1 つのソース スキンと 1 つの目的のスキン、もしくはソースと目的のスキン上のコンポーネントを選択する必要があります。 //

ーーーーーーーーーーーーーーーーー
Maya2020でも解決できていない様子、(モデルがわるかった。スキンバインド全体が壊れてた。)

copySkinWeights -noMirror -surfaceAssociation closestPoint -influenceAssociation closestJoint;
// 警告: line 0: スキン オブジェクトではないので ‘Genesis8FemaleSkinShape’ をスキップします。 //
// エラー: line 0: 1 つのソース スキンと 1 つの目的のスキン、もしくはソースと目的のスキン上のコンポーネントを選択する必要があります。 //

cmds.copySkinWeights( sourceSkin=’group_mesh|Mesh|Genesis8FemaleSkin’, destinationSkin=’group_meshHIK|Mesh|Genesis8FemaleSkin’, noMirror=True )
エラー: RuntimeError: file line 1: skinCluster ノードは -sourceSkin/-ss フラグとともに指定する必要があります。 #

copySkinWeightsはそのコマンドの処理の中で、skinClusterコマンドを使っているみたいで
その引数部分にsourceSkinがうまくわたせてないみたいに見える。

モデルがわるかった。スキンバインド全体が壊れてた。


これも関係ありそう。
skinClusterは、一度に1つのジオメトリのみをバインドします。したがって、複数のジオメトリをバインドするには、複数のskinClusterコマンドを発行する必要があります。

https://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/Commands/skinCluster.html

一旦1つの裸ジオメトリのスキンウェイトにしておいてそれを複数の服にコピーしていく流れをやったらいいのかもな。
ーーーーーーーーーーーーーーーーーーーーー

ダメだった。
cmds.copySkinWeights( sourceSkin=’group_mesh|Mesh|Genesis8FemaleSkin’, destinationSkin=’group_meshHIK|Mesh|Genesis8FemaleSkin’, noMirror=True )
エラー: line 1: RuntimeError: file line 1: skinCluster ノードは -sourceSkin/-ss フラグとともに指定する必要があります。 #

copySkinWeights -noMirror -surfaceAssociation closestPoint -influenceAssociation closestJoint;
// 警告: line 0: スキン オブジェクトではないので ‘Genesis8FemaleSkinShape’ をスキップします。 //
// エラー: line 0: 1 つのソース スキンと 1 つの目的のスキン、もしくはソースと目的のスキン上のコンポーネントを選択する必要があります。 //