2014年11月3日月曜日

UV座標からワールド座標を求める云々を使ったツール

前回の記事でご紹介してたMELを公開するだけという手抜き記事です。

使い方は動画を見てください。
最近使ってるわけでもないのでツールの
エラーとかも対応しかねますので自己責任でお試しください。

仕様としてはnearestPointOnMeshを使って
UV座標 ⇔ ワールド座標 を行き来してjointを配置していくものだったと思います。
※ペイントエフェクトのような規則正しいUVを使わないとうまくいかなかった気がします。

jointはIKスプラインの仕込み、hairの仕込みなどが選択できたと思います。


TYGrassesSetUpTool();
global proc TYGrassesSetUpTool(){
    //同じウインドウが存在する場合は削除
    if(`window -exists TYGrassesSetUpToolWindow`){
        deleteUI TYGrassesSetUpToolWindow;
    }
    //ウインドウの作成
    window -t "草花セットアップツール" -w 250 TYGrassesSetUpToolWindow;
    string $windowRootLayout =`columnLayout -adj true`;
        string $stepTabs =`tabLayout -imw 5 -imh 5`;
            //STEP1
            string $step1 =`columnLayout -adj true`;
                text -fn "boldLabelFont" -bgc 0.18 0.18 0.18
                 -l "\n ターゲットメッシュのUV座標を基に\n等間隔でジョイントを配置しします\n";
                columnLayout;
                    text -l "\n ① 配置させるジョイントの数と半径を指定します\n";
                    intFieldGrp -l "ジョイント " -v1 10 -el "個" jointQuantityField;
                    floatFieldGrp -l "半径 " -v1 0.01 -pre 3 jointRadiusField;
                    text -l "\n ② 配置させるUVマップの名前を指定します\n";
                    rowLayout -nc 2;
                        text -l "                            ";
                        textField -tx "map1" UVmapNameField;
                    setParent..;
                    text -l "\n ③ 配置させるUV座標を指定します\n";
                    floatFieldGrp -l "U座標位置の割合 " -v1 50.0 -el "%" -pre 2
                     -ann "V座標上に一列で配置するときのU座標位置の割合です" uPerPositionField;
                    text -l "\n ④ バインドするか指定します\n";
                    rowLayout -nc 3;
                        text -l "     ";
                        radioCollection bindSelectField;
                       radioButton -l "バインドする" -sl -w 100 bindSelectTrue;
                       radioButton -l "バインドしない" -w 100 bindSelectFalse;
                    setParent..;
                    text -l "\n ⑤ ヘアシステムとカーブを作成するか指定します\n";
                    rowLayout -nc 3;
                        text -l "     ";
                        radioCollection makeRigSelectField;
                       radioButton -l "作成する" -sl -w 100
                        -onc "text -e -en true nucleusText;\
                        radioButton -e -en true makeNsysSelectTrue;\
                        radioButton -e -en true makeNsysSelectFalse;"
                        makeRigSelectTrue;
                       radioButton -l "作成しない" -w 100
                        -onc "text -e -en false nucleusText;\
                        radioButton -e -en false makeNsysSelectTrue;\
                        radioButton -e -en false makeNsysSelectFalse;"
                        makeRigSelectFalse;
                    setParent..;
                    text -l "\n   ヘアシステムにnucleusを接続するか指定します\n" nucleusText;
                    rowLayout -nc 3;
                        text -l "     ";
                        radioCollection makeNsysSelectField;
                       radioButton -l "接続する" -sl -w 100 makeNsysSelectTrue;
                       radioButton -l "接続しない" -w 100 makeNsysSelectFalse;
                    setParent..;
                    text -l "\n ⑥ meshノードを選択し、作成を実行します\n";
                setParent..;
                button -l "作成" -c "TYGrassesSetUp(\
                 `intFieldGrp -q -v1 jointQuantityField`\
                 ,`floatFieldGrp -q -v1 jointRadiusField`\
                 ,`textField -q -tx UVmapNameField`\
                 ,`floatFieldGrp -q -v1 uPerPositionField`\
                 ,`radioCollection -q -sl bindSelectField`\
                 ,`radioCollection -q -sl makeRigSelectField`\
                 ,`radioCollection -q -sl makeNsysSelectField`)";
            setParent..;
            //STEP2
            string $step2 =`columnLayout -adj true`;
            text -fn "boldLabelFont" -bgc 0.18 0.18 0.18
             -l "\n バインドされたソースメッシュを基に\nジョイントを配置しバインドやnHair等を実行します\n";
                columnLayout;
                    text -l "\n ① 配置させるジョイントの半径を指定します\n";
                    floatFieldGrp -l "半径 " -v1 0.01 -pre 3 jointCopyRadiusField;
                    text -l "\n ② 配置させるUVマップの名前を指定します\n";
                    rowLayout -nc 2;
                        text -l "                            ";
                        textField -tx "map1" UVmapCopyNameField;
                    setParent..;
                    text -l "\n ③ バインドするか指定します\n";
                    rowLayout -nc 3;
                        text -l "     ";
                        radioCollection bindCopySelectField;
                       radioButton -l "バインドする" -sl -w 100 bindCopySelectTrue;
                       radioButton -l "バインドしない" -w 100 bindCopySelectFalse;
                    setParent..;
                    text -l "\n ③ ヘアシステムを継承し作成するか指定します\n";
                    rowLayout -nc 3;
                        text -l "     ";
                        radioCollection succeedNsysSelectField;
                       radioButton -l "継承し作成する" -sl -w 100
                        -onc "textField -e -en true hairSystemShapeNameField;\
                        text -e -en true succeedHairSysText;"
                        succeedNsysSelectTrue;
                       radioButton -l "作成しない" -w 100
                        -onc "textField -e -en false hairSystemShapeNameField;\
                        text -e -en false succeedHairSysText;"
                        succeedNsysSelectFalse;
                    setParent..;
                    text -l "\n   継承するhairSystemShapeの名前を指定します\n" succeedHairSysText;
                    rowLayout -nc 2;
                        text -l "                            ";
                        textField -tx "hairSystemShape1" hairSystemShapeNameField;
                    setParent..;
                setParent..;
                button -l "実行" -c "TYGrassesSetUpCopy(\
                `floatFieldGrp -q -v1 jointCopyRadiusField`\
                ,`textField -q -tx UVmapCopyNameField`\
                ,`radioCollection -q -sl bindCopySelectField`\
                ,`radioCollection -q -sl succeedNsysSelectField`\
                ,`textField -q -tx hairSystemShapeNameField`)";
            setParent..;
            //タブレイアウトにペアレント
            tabLayout -e -tl $step1 "STEP1" -tl $step2 "STEP2" $stepTabs;
        setParent..;
        button -l "閉じる" -c "deleteUI TYGrassesSetUpToolWindow";
    setParent..;
    showWindow;
}

global proc TYGrassesSetUp(int $jointQuantity, float $jointRadius, string $UVmapName,
 float $uPerPosition, string $bindSelect, string $makeRigSelect, string $makeNsysSelect){
    pickWalk -d down;
    //選択されたオブジェクトのチェック
    string $targetMesh[] = `ls -sl -typ mesh`;
    if(`objExists $targetMesh[0]`){
        if(`objExists $targetMesh[1]`){
            error "複数選択されています。一度に作成できるベースセットアップは一つのみです。";
        }
        else;
    }
    else error "メッシュが選択されていません。";
    //uvSetの名前のチェック
    int $flag = 0;
    if(catch(size(`getAttr -s ($targetMesh[0] + ".uvSet[*].uvSetName")`))){
        if(`getAttr ($targetMesh[0] + ".uvSet[0].uvSetName")` == $UVmapName){
            $flag = 1;
        }
        else;
    }
    else{
        string $uvSetNameList[] = `getAttr ($targetMesh[0] + ".uvSet[*].uvSetName")`;
        int $j;
        for($j=0;$j<=(size($uvSetNameList)) && $flag!=1;$j++){
            if($uvSetNameList[$j] == $UVmapName){
                $flag = 1;
            }
            else;
        }
    }
    //指定した値のチェック
    if($jointQuantity <= 1 || $jointRadius <= 0 || $flag == 0){
        error "入力値が不正です。";
    }
    else;
    //transformノードの取得
    string $targetTransform[] = `pickWalk -d up`;
    //ジョイントの配置
    int $i;
    string $jointList[];
    for($i=1;$i<=$jointQuantity;$i++){
        select -cl;
        string $jointName = `joint -rad $jointRadius -n "TYGrassBaceJoint"`;
        //階層化
        $jointList[($i - 1)] = $jointName;
        if($i!=1){
            parent $jointName $jointList[($i - 2)];
        }
        //UV座標を基にジョイントを配置
        string $constName[] = `pointOnPolyConstraint $targetMesh[0] $jointName`;
        int $k,$UVmapNumber,$nFlag = 0;
        for($k=0;$nFlag!=1;$k++){
            string $selectUVmapName = `getAttr ($targetMesh[0] + ".uvSet[" + $k + "].uvSetName")`;
            if($selectUVmapName == $UVmapName){
                $UVmapNumber = $k;
                $nFlag = 1;
            }
            else;
        }
        connectAttr -f ($targetMesh[0] + ".uvSet[" + $UVmapNumber + "].uvSetName")
         ($constName[0] + ".target[0].targetUVSetName");
        float $uvSize[] = `polyEvaluate -b2 -ae -uvs $UVmapName $targetMesh[0]`;
        float $uPosition = ($uvSize[1] - $uvSize[0]) * $uPerPosition * 0.01 + $uvSize[0];
        float $vPosition = ($uvSize[3] - $uvSize[2]) / ($jointQuantity - 1)
         * ($i - 1) + $uvSize[2];
        setAttr ($constName[0] + "." + $targetTransform[0] + "U0") $uPosition;
        setAttr ($constName[0] + "." + $targetTransform[0] + "V0") $vPosition;
        setAttr ($constName[0] + ".offsetRotateX") -90;
        setAttr ($constName[0] + ".offsetRotateY") 90;
        delete $constName[0];
        //回転値をジョイントの方向へ移行させ0にする
        float $jointRotateValue[] = `getAttr ($jointName + ".rotate")`;
        joint -e -o $jointRotateValue[0] $jointRotateValue[1] $jointRotateValue[2] $jointName;
        setAttr ($jointName + ".rotate") 0 0 0;
    }
    //バインドの実行
    if($bindSelect == "bindSelectTrue"){
        select $targetMesh[0] $jointList;
        SmoothBindSkin;
    }
    else;
    //nHairの作成
    if($makeRigSelect == "makeRigSelectTrue"){
        //ikHandleの作成
        string $ikHandleName[] = `ikHandle -sj $jointList[0]
         -ee $jointList[($jointQuantity -1)] -sol ikSplineSolver -scv false`;
        select $ikHandleName[2];
        //assignHairSystemのプロシージャから引用
        string $sel[] = `ls -sl`;
        string $hsys = `createNode hairSystem`;
        connectAttr time1.outTime (".currentTime");
        string $inCurveName[] = `ls -dag -type nurbsCurve $sel`;
    attachCurvesToHairSystem( $hsys, $inCurveName, false );
    //作成したものを変数に代入
    select $hsys;
    convertHairSelection( "follicles" );
    pickWalk -d down;
    select `listConnections -s false -t "nurbsCurve"`;
        string $outCurveShapeName[] = `pickWalk -d down`;
        select $inCurveName[0];
        string $inCurveShapeName[] = `pickWalk -d down`;
        //ikをhiarカーブに添わせる
        disconnectAttr ($inCurveShapeName[0] + ".worldSpace[0]") ($ikHandleName[0] + ".inCurve");
        connectAttr ($outCurveShapeName[0] + ".worldSpace[0]") ($ikHandleName[0] + ".inCurve");
        //hairのアトリビュート設定
        setAttr ($hsys + ".bendResistance") 50;
        setAttr ($hsys + ".startCurveAttract") 0.1;
        setAttr ($hsys + ".selfCollide") 1;
        setAttr ($hsys + ".selfCollideWidthScale") 0.1;
        //nucleusの接続
        if($makeNsysSelect == "makeNsysSelectTrue"){
            string $nucleusName = `createNode nucleus`;
            connectAttr time1.outTime (".currentTime");
            connectAttr ($hsys + ".currentState") ($nucleusName + ".inputActive[0]");
            connectAttr ($hsys + ".startState") ($nucleusName + ".inputActiveStart[0]");
            connectAttr ($nucleusName + ".outputObjects[0]") ($hsys + ".nextState");
            connectAttr ($nucleusName + ".startFrame") ($hsys + ".startFrame");
            setAttr ($hsys + ".active") 1;
        }
    }
}

global proc TYGrassesSetUpCopy(float $jointCopyRadius, string $UVmapCopyName
 , string $bindCopySelect, string $succeedNsysSelect, string $hairSystemShapeName){
    pickWalk -d down;
    string $meshList[] = `ls -sl `;
    //uvSetの名前のチェック
    int $k,$flag = 0;;
    for($k=1;$k<size($meshList);$k++){
        if(catch(size(`getAttr -s ($meshList[$k] + ".uvSet[*].uvSetName")`))){
            if(`getAttr ($meshList[$k] + ".uvSet[0].uvSetName")` == $UVmapCopyName){
                $flag = 1;
            }
            else;
        }
        else{
            string $uvSetNameList[] = `getAttr ($meshList[$k] + ".uvSet[*].uvSetName")`;
            int $j;
            for($j=0;$j<=(size($uvSetNameList)) && $flag!=1;$j++){
                if($uvSetNameList[$j] == $UVmapCopyName){
                    $flag = 1;
                }
                else;
            }
        }
    }
    if(`objExists $hairSystemShapeName`){
        if(`nodeType $hairSystemShapeName` != "hairSystem"){
            $flag = 0;
        }
        else;
    }
    else{
        $flag = 0;
    }
    //指定した値のチェック
    if($flag == 0){
        error "入力値が不正です。";
    }
    else;
    //nearestPointOnMeshの作成
    loadPlugin -qt "nearestPointOnMesh.mll";
    string $nPOM = `createNode "nearestPointOnMesh"`;
    string $checker[] = `spaceLocator`;
    connectAttr ($checker[0] + ".translate") ($nPOM + ".inPosition");
    connectAttr ($meshList[0] + ".worldMesh[0]") ($nPOM + ".inMesh");
    //メッシュからジョイントを取得
    string $jointList[] =`skinCluster -q -inf $meshList[0]`;
    //ソースメッシュにバインドされたジョイントのソースメッシュ上UV位置を取得
    float $uPosition[],$vPosition[];
    for($jointName in $jointList){
        float $jointPosition[] = `xform -q -ws -t $jointName`;
        move -rpr $jointPosition[0] $jointPosition[1] $jointPosition[2] $checker[0];
        $uPosition[`size($uPosition)`] = `getAttr ($nPOM + ".parameterU")`;
        $vPosition[`size($vPosition)`] = `getAttr ($nPOM + ".parameterV")`;
    }
    delete $nPOM $checker[0];
    //ジョイントの配置
    int $i;
    string $ikHandleList[],$jointCopyList[];
    for($i=1;$i<size($meshList);$i++){
        float $uvboundingBox[] = `polyEvaluate -b2 -ae -uvs $UVmapCopyName $meshList[$i]`;
        string $jointName[] = {};
        for($j=0;$j<`size($jointList)`;$j++){
            //ソースのバウンディングボックス内にジョイントがあった場合ターゲットも作成(誤差0.0001)
            if(($uvboundingBox[3] + 0.0001) > $vPosition[$j]){
                select -cl;
                $jointName[$j] = `joint -rad $jointCopyRadius -n "TYGrassBaceJoint"`;
                string $constName[] = `pointOnPolyConstraint $meshList[$i] $jointName[$j]`;
                //指定したuvマップが何番目の配列にあるか確認
                int $l,$UVmapNumber,$nFlag = 0;
                for($l=0;$nFlag!=1;$l++){
                    string $selectUVmapName = `getAttr ($meshList[$i] +
                     ".uvSet[" + $l + "].uvSetName")`;
                    if($selectUVmapName == $UVmapCopyName){
                        $UVmapNumber = $k;
                        $nFlag = 1;
                    }
                    else;
                }
                connectAttr -f ($meshList[$i] + ".uvSet[" + $UVmapNumber + "].uvSetName")
                 ($constName[0] + ".target[0].targetUVSetName");
                select $meshList[$i];
                string $transformMesh[] = `pickWalk -d up`;
                setAttr ($constName[0] + "." + $transformMesh[0] + "U0") $uPosition[$j];
                setAttr ($constName[0] + "." + $transformMesh[0] + "V0") $vPosition[$j];
                setAttr ($constName[0] + ".offsetRotateX") -90;
                setAttr ($constName[0] + ".offsetRotateY") 90;
                delete $constName[0];
                //ソースを参考に階層化
                select $jointList[$j];
                int $m,$num = 0;
                string $sourceParentJoint[] =`pickWalk -d up`;
                if($jointList[$j] != $sourceParentJoint[0]){
                    for($m=0;$m<size($jointList);$m++){
                        if($jointList[$m] == $sourceParentJoint[0]){
                            $num = $m;
                        }
                    }
                parent $jointName[$j] $jointName[$num];
                }
            }
        }
        $jointCopyList[size($jointCopyList)] = $jointName[0];
        //バインドの実行
        if($bindCopySelect == "bindCopySelectTrue"){
            select $meshList[$i] $jointName[0];
            SmoothBindSkin;
        }
        else;
        //ikHandleの作成
        if($succeedNsysSelect == "succeedNsysSelectTrue"){
            string $ikHandleName[] = `ikHandle -sj $jointName[0]
             -ee $jointName[(size($jointName) - 1)] -sol ikSplineSolver -scv false`;
            $ikHandleList[size($ikHandleList)] = $ikHandleName[0];
            //assignHairSystemから一部引用
            string $curves[] = `ls -dag -type nurbsCurve $ikHandleName[2]`;
            attachCurvesToHairSystem( $hairSystemShapeName, $curves, false );
            //hairSystemShapeから正しいfollicleshapeを取得
            select $hairSystemShapeName;
            convertHairSelection( "follicles" );
            string $follicleShapeList[] = `pickWalk -d down`;
            string $trueFollicleShape;
            for($follicleShapeName in $follicleShapeList){
                string $plugList[] = `listConnections -d false $follicleShapeName`;
                for($plugName in $plugList){
                    if($plugName == $ikHandleName[2]){
                        $trueFollicleShape = $follicleShapeName;
                    }
                    else;
                }
            }
            //follicleshapeの中から作られたカーブを取得
            select $trueFollicleShape;
            select `listConnections -s false -t "nurbsCurve"`;
            string $outCurveName[] = `pickWalk -d down`;
            //curveShapeを取得
            select $ikHandleName[2];
            string $inCurveName[] = `pickWalk -d down`;
            //ikをhiarカーブに添わせる
            disconnectAttr ($inCurveName[0] + ".worldSpace[0]") ($ikHandleName[0] + ".inCurve");
            connectAttr ($outCurveName[0] + ".worldSpace[0]") ($ikHandleName[0] + ".inCurve");
        }
        else;
    }
    group -n "TYGrassesIkHandleGp" $ikHandleList;
    group -n "TYGrassesJointGp" $jointCopyList;
    select -cl;
}




久しぶりにMELのツールを見るとpythonのほうが
書きやすい感じがしますね。(慣れかな?)

最近はOpenMayaとかPySideと戯れているのでそういうのもあるんでしょうけど。


んーbloggerってtextファイルアップロードできないのかなー