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をコンストレインします。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です