スキンウェイトペイントのコツをつかんだ。レベル2
1,あってないウェイト部分は「追加」で0.1450ぐらいで塗っていく
2,最後にスムーズで「追加」で塗ったあたりを「スムーズ」しておくと面がスムーズになり完璧な感じがする。

DESIGN AND FUTURE SCIENCE.
スキンウェイトペイントのコツをつかんだ。レベル2
1,あってないウェイト部分は「追加」で0.1450ぐらいで塗っていく
2,最後にスムーズで「追加」で塗ったあたりを「スムーズ」しておくと面がスムーズになり完璧な感じがする。
メッシュを追加するたびにスキンウェイトをやりなおす?その必要はないスキンウェイトのコピーはメッシュ単位。
スキンウェイトの情報は.mb/.maシーンデータに入っているからシーンごと読み込むことが重要だ。
1,OldシーンにNewシーンを読み込んでOldキャラとNewキャラをそれぞれグループ化して並べる
2,見にくいけど、グループの座標は同じにしてから、
3,OldキャラとNewキャラを順に選択して、Skin > Edit Smooth Skin > Copy Skin Weights を実行したらうまくいった。
ついでに追加したメッシュが体に添うようなスキニーパンツやフィットしたスーツなら
体から服にスキンをコピーしてあげればびったり添う
曲げても平気
こんなに曲げても大丈夫
明日python書く予定は未定。
jointにSkinClasterがないとなるエラー
The plug-in has found the following skin definition problems :
Unable to find the bind pose for : / SK_Mannequin / root / pelvis / spine_01 / spine_02 / spine_03 / clavicle_l / upperarm_l / lowerarm_l / hand_l / ring_01_l. No bind poses in the hierarchy containing the object will be exported.
Unable to find the bind pose for : / SK_Mannequin / root / pelvis / spine_01 / spine_02 / spine_03 / clavicle_r / upperarm_r / lowerarm_r / hand_r / middle_01_r / middle_02_r. No bind poses in the hierarchy containing the object will be exported.
これを解決するためにテストした
jointにSkinClusterがないとなるようで
たとえば下のように手のジョイントがSphereの外に突き出ている場合に
手のjointのSkinClusterがない状態になる。この状態でスキンバインドし保存し開きなおしてFBX出力するとエラーが出る。
このようにメッシュが外をカバーできていればエラーが出ない。
もっと深堀してみると、、、、
このエラーがでるのは
BindPoseにはSkinClusterが必要だということになる。
SkinClusterなかったら、スキンバインド時にBindPoseは作られるがFBX出力時に怒られる
たぶんこのFBX exporter pluginが
・SkinCluster経由で探したBindPoseがみつからないか?
・BindPoseのSkinClusterがないとこの「プラグインは、次のスキン定義の問題を発見しました。」と言っているのでBindPoseのSkin情報がないというのは問題になるみたいだ。
ノードエディタだと正常な場合こうJointノードにバインドポーズがあってBindPose1に接続されてる。
スキンクラスターがないノード
バインドポーズからピンがでていない
・jointのバインドポーズを直接BindPose1へノードエディタでつなげてみる
→やってみたが、どのワールド行列につないだらよいかわからない。
もう少しシンプルなものでテストしてみたほうがいい。
下の例では
・gotoBindPose;をしても問題が出ない
・チャネルボックスエディタでもノードエディタでもBindPoseを持たないjointが存在。
・SkinClusterがなくても保存して開きなおしてFBX出力してもエラーが出ない。
一辺倒な言葉ではなにも語れないことがわかった。
jointがメッシュからはみ出ているなら 、だって骨が皮からはみ出ているなら問題あるでしょ。
・メッシュをかぶせる
・ジョイントをめり込ませるか
このめんどうな作業をやり直したほうがよさそうだ。
選択したノード以下のすべての接続を解除するループ
import maya.cmds as cmds
selectionNode=cmds.ls(sl=True)
# 指定したノード以下全てを検索
selectionNodes=cmds.ls(selectionNode,dag=1)
for node in selectionNodes:
try:
attribute1 =node+"_translateX.output"
attribute2 =node+".translateX"
cmds.disconnectAttr(attribute1, attribute2)
except RuntimeError as err:
pass
try:
attribute1 =node+"_translateY.output"
attribute2 =node+".translateY"
cmds.disconnectAttr(attribute1, attribute2)
except RuntimeError as err:
pass
try:
attribute1 =node+"_translateZ.output"
attribute2 =node+".translateZ"
cmds.disconnectAttr(attribute1, attribute2)
except RuntimeError as err:
pass
try:
attribute1 =node+"_rotateX.output"
attribute2 =node+".rotateX"
cmds.disconnectAttr(attribute1, attribute2)
except RuntimeError as err:
pass
try:
attribute1 =node+"_rotateY.output"
attribute2 =node+".rotateY"
cmds.disconnectAttr(attribute1, attribute2)
except RuntimeError as err:
pass
try:
attribute1 =node+"_rotateZ.output"
attribute2 =node+".rotateZ"
cmds.disconnectAttr(attribute1, attribute2)
except RuntimeError as err:
pass
try:
attribute1 =node+"_scaleX.output"
attribute2 =node+".scaleX"
cmds.disconnectAttr(attribute1, attribute2)
except RuntimeError as err:
pass
try:
attribute1 =node+"_scaleY.output"
attribute2 =node+".scaleY"
cmds.disconnectAttr(attribute1, attribute2)
except RuntimeError as err:
pass
try:
attribute1 =node+"_scaleZ.output"
attribute2 =node+".scaleZ"
cmds.disconnectAttr(attribute1, attribute2)
except RuntimeError as err:
pass
カラーの展開を納品確認するために90パターンをjsxで再現してpng24で書き出すスクリプトを書いた。
メインのオブジェクトの色3色(ホワイト、ブラック、グリーン)
文字5色(ホワイト、ブラック、グレー、グリーン、パープル)
背景6色(透明、ホワイト、ブラック、グレー、グリーン、パープル)
がほしいみたいなすごいこと言われて、マジかと思って。作りました。
//var folder = Folder.selectDialog();
//alert( "filepath= "+filepath);
//var folder =filepath;
var document = app.activeDocument;
//if(document && folder)
if(document)
{
var options = new ExportOptionsPNG24();
options.antiAliasing = true;
options.transparency = true;
options.artBoardClipping = true;
options.verticalScale = 1413.4276;
options.horizontalScale = 1413.4276;
//alert( "document.layers= "+document.layers);
//配列グループの中にレイヤー番号を集める
var kupuTopArr=[];
for(var i=0; i<document.layers.length; ++i)
{
layerName=document.layers[i].name;
if(layerName.indexOf( 'kupu_' )>-1)
kupuTopArr.push(i);
}
//alert( "kupuTopArr= "+kupuTopArr);
var kupuUnderArr=[];
for(var i=0; i<document.layers.length; ++i)
{
layerName=document.layers[i].name;
if(layerName.indexOf( 'kupuunder_' )>-1)
kupuUnderArr.push(i);
}
//alert( "kupuUnderArr= "+kupuUnderArr);
var fontArr=[];
for(var i=0; i<document.layers.length; ++i)
{
layerName=document.layers[i].name;
if(layerName.indexOf( 'font_' )>-1)
fontArr.push(i);
}
//alert( "fontArr= "+fontArr);
var bgArr=[];
for(var i=0; i<document.layers.length; ++i)
{
layerName=document.layers[i].name;
if(layerName.indexOf( 'bg_' )>-1)
bgArr.push(i);
}
//alert( "bgArr= "+bgArr);
//全パターンを出力
//alert( "kupuTopArr.length= "+kupuTopArr.length);
for(var kupuTop_i=0; kupuTop_i<kupuTopArr.length; ++kupuTop_i)
{
//alert( "kupuTop_i= "+kupuTop_i);
//他のkupu_レイヤーを非表示
hideArrAllLayers(kupuTopArr)
//今回のkupu_レイヤーを表示
var kupuTop_Laynum=kupuTopArr[kupuTop_i];
//alert( "kupuTop_Laynum= "+kupuTop_Laynum);
document.layers[kupuTop_Laynum].visible = true;
//他のkupuunderレイヤーを非表示
hideArrAllLayers(kupuUnderArr)
//今回のkupuunderレイヤーを表示
var kupuUnder_Laynum=kupuUnderArr[kupuTop_i];
//alert( "kupuUnder_Laynum= "+kupuUnder_Laynum);
document.layers[kupuUnder_Laynum].visible = true;
var kupuTop_Layer_Name=document.layers[kupuTop_Laynum].name+"";
//alert( "kupuTop_Layer_Name= "+kupuTop_Layer_Name);
for(var font_i=0; font_i<fontArr.length; ++font_i)
{
//他のfontレイヤーを非表示
hideArrAllLayers(fontArr)
//今回のfontイヤーを表示
var font_Laynum=fontArr[font_i];
//alert( "kupuUnder_Laynum= "+kupuUnder_Laynum);
document.layers[font_Laynum].visible = true;
var font_Layer_Name=document.layers[font_Laynum].name+"";
for(var bg_i=0; bg_i<bgArr.length; ++bg_i)
{
//他のbgレイヤーを非表示
hideArrAllLayers(bgArr)
//今回のbgイヤーを表示
var bg_Laynum=bgArr[bg_i];
//alert( "kupuUnder_Laynum= "+kupuUnder_Laynum);
document.layers[bg_Laynum].visible = true;
var bg_Layer_Name=document.layers[bg_Laynum].name+"";
//Export -------------------------------------------------------------
filepath=app.activeDocument.path.fsName.toString()+"";
filepathArr = filepath.split('\\'); // ["apple", "banana", "orange"]
filepath=filepathArr.join('/');
var makedDirPath = mkdirp(filepath + '/nouhin/'+kupuTop_Layer_Name+'/'+font_Layer_Name+"");
//alert("makedDirPath= "+makedDirPath);
var ExportfilePath = makedDirPath+kupuTop_Layer_Name+"_"+font_Layer_Name+"_"+bg_Layer_Name+".png"
//alert( "ExportfilePath= "+ExportfilePath);
var file = new File(ExportfilePath);
document.exportFile(file,ExportType.PNG24,options);
}
}
}
/*
var n = document.layers.length;
for(var i=0; i<n; ++i)
{
hideAllLayers();
var layer = document.layers[i];
alert( "document.layers["+i+"].name= "+document.layers[i].name);
layer.visible = true;
//var file = new File(folder.fsName+"/"+layer.name+".png");
var file = new File(filepath+"/"+layer.name+".png");
document.exportFile(file,ExportType.PNG24,options);
}
*/
showAllLayers();
alert( " 処理が完了しました ");
}
function kupu_white_view(){
forEach(document.layers, function(layer) {
if(layer.name=="kupu_white"){
layer.visible = true;
}
layer.visible = false;
});
}
function hideArrAllLayers(Arr)
{
for(var i=0; i<Arr.length; ++i)
{
var Laynum=Arr[i];
//alert( "kupuTop_Laynum= "+kupuTop_Laynum);
document.layers[Laynum].visible = false;
}
}
function hideAllLayers()
{
forEach(document.layers, function(layer) {
layer.visible = false;
});
}
function showAllLayers()
{
forEach(document.layers, function(layer) {
layer.visible = true;
});
}
function forEach(collection, fn)
{
var n = collection.length;
for(var i=0; i<n; ++i)
{
fn(collection[i]);
}
}
function mkdirp(path) {
var fullPath = "";
path = path.toString();
var arr = path.split("/");
for (var i = 0; i < arr.length; i++) {
var folderName = arr[i];
fullPath += folderName + "/";
var folder = new Folder(fullPath);
if (!folder.exists) folder.create();
}
return fullPath;
}
一度
mGear/Skinning/Export Skinで保存したものを読み込んでみた。
スキンバインドしたてのもの 脇の下がおかしいが読み込んでみると、、、
ちゃんと読み込めてる
ちゃんと使える模様
どうにかこうにか手動でGuidを昔のジョイントに合わせたものがこれ
指が間違ってた。
でこの動画の続きによると
型の始まりのGuidのCUBEをクリックして Dupl.Sym.を押すと左右が対象になるみたいだったけど
つかってない手でやった。
つぎにGuidのトップノードを選択して新シーンとして保存してる
Guidトップ選択して Build From Selection
走りだす。
リグが出力されたようだ このあと動画のようにコントロールリグのサイズを変更しなくてスキンバインドのテストしたほうがいいと思う。
リグデフォーマグループを選択する
上やじるしを押すとすべての読み取るのに必要なジョイントが選択されます。
つまりはこれがしたいみたい。
設定は
ジョイント階層
測地線ボクセル
クラシックリニア
インタラクティブ
距離
チェックあり
5
チェック
チェック
チェック
チェックなし
ではリグを動かしてみましょう!
動かしにくいので最後にリグコントローラーのサイズを変更しよう。
ここでジョイントの表示サイズがでかすぎたのか変更してる。
表示サイズを大きくすると、ジョイントやボーンが選択しやすくなります。 表示サイズを小さくすると、フレクサなど他のオブジェクトが選択しやすくなります。 ディスプレイ > アニメーション > ジョイント サイズ(Display > Animation > Joint Size)を選択します。
一番下の8角形が大きすぎたので
Control+右クリックで「CV」を選択して小さくスケールしてる。
スケールできないからスケールツールをダブルクリックで起動しなおすことで初期化してスケールしてる。
ここでは逆に大きくした。
手の幅ぐらいのサイズにした
コントローラーハンドルここではBOXが頭にめり込んでるので直している。
した
体も同じくボディに埋まっているコントロールハンドルをCVモードで拡大していく
こんな感じ
ちょっとさらに仕切り直し
Maya2016 +mGear2.6.1系 から
Maya2020 +mGear3.7.0_beta_01に乗り換えてみた。
1、モデルを用意した
2,mGear/Shifter/Guide Managerを起動
3,mGear/Shifter/Guid Template Samples/Biped Template を実行
黄色いguidが作成された。
4、guideのサイズをモデルに合わせる
合わせやすいようにシェーディング/ワイヤーフレームシェードにした
ここではもとにあったジョイントに全くびったり合わせるために
ジョイントを読み込みました。
位置合わせツールがあったら楽なのにと思いました。
いろいろやったが下のスクリプトなど作ったがJointの数とターゲットの数も違うし使えなかった仕事としてならたぶん無理やりできるんだろうなと思う。
import maya.cmds as cmds
selectedList=cmds.ls( selection=True )
print("selectedList="+str(selectedList))
jointOriginal=selectedList[0]
guidTarget=selectedList[1]
#world座標を取る いろいろ考えたけど親基準にローカル座標そろえるのやめた。なぜなら親もローカル座標そろえないと話は始まらないから。
#cmds.pointPosition( 'particle1.pt[1]', world=True )
#def getParentWorldPos()
j_parentlist=cmds.listRelatives(jointOriginal, parent=True )
g_parentlist=cmds.listRelatives(jointOriginal, parent=True )
#print("parentlist="+str(parentlist))
jointOriginalParent=j_parentlist[0]
guidTargetParent=g_parentlist[0]
print("jointOriginalParent="+str(jointOriginalParent))
print("guidTargetParent="+str(guidTargetParent))
jointOriginalParentWorldXYZ=cmds.xform(jointOriginalParent,worldSpace=True, q=True, translation=True )
guidTargetParentWorldXYZ=cmds.xform(guidTargetParent,worldSpace=True, q=True, translation=True )
jointOriginalParentWorldXYZrot=cmds.xform(jointOriginalParent,worldSpace=True, q=True, rotation=True )
guidTargetParentWorldXYZrot=cmds.xform(guidTargetParent,worldSpace=True, q=True, rotation=True )
print("jointOriginalParentWorldXYZ="+str(jointOriginalParentWorldXYZ))
print("guidTargetParentWorldXYZ="+str(guidTargetParentWorldXYZ))
w_xDiff=jointOriginalParentWorldXYZ[0]-guidTargetParentWorldXYZ[0]
w_yDiff=jointOriginalParentWorldXYZ[1]-guidTargetParentWorldXYZ[1]
w_zDiff=jointOriginalParentWorldXYZ[2]-guidTargetParentWorldXYZ[2]
w_xDiffR=jointOriginalParentWorldXYZrot[0]-guidTargetParentWorldXYZrot[0]
w_yDiffR=jointOriginalParentWorldXYZrot[1]-guidTargetParentWorldXYZrot[1]
w_zDiffR=jointOriginalParentWorldXYZrot[2]-guidTargetParentWorldXYZrot[2]
worldDiff=[w_xDiff,w_yDiff,w_zDiff]
worldDiffR=[w_xDiffR,w_yDiffR,w_zDiffR]
print("worldDiff="+str(worldDiff))
print("worldDiffR="+str(worldDiffR))
""""""
tx=cmds.getAttr(jointOriginal+".tx")
ty=cmds.getAttr(jointOriginal+".ty")
tz=cmds.getAttr(jointOriginal+".tz")
print(" tx= "+str(tx)+" ty= "+str(ty)+" tz= "+str(tz))
"""
cmds.setAttr(guidTarget+".tx",tx)
cmds.setAttr(guidTarget+".ty",ty)
cmds.setAttr(guidTarget+".tz",tz)
"""
guid_Scale=9.869
goTX=guid_Scale/tx+w_xDiff
goTY=guid_Scale/ty+w_yDiff
goTZ=guid_Scale/tz+w_zDiff
print(" goTX= "+str(goTX)+" goTY= "+str(goTY)+" goTZ= "+str(goTZ))
""""""
#ZをXに
""""""
cmds.setAttr(guidTarget+".tx",goTZ)
cmds.setAttr(guidTarget+".ty",goTY)
cmds.setAttr(guidTarget+".tz",goTX)
"""
cmds.setAttr(guidTarget+".rx",goTZ)
cmds.setAttr(guidTarget+".ry",goTX)
cmds.setAttr(guidTarget+".rz",goTY)
"""
地道にジョイントにguideを合わせるしかない。(今のところ)
単位が違くなってるのはさっきguide側にスケールかけたからだった。
これはjoint rHand
これはguide のarm_R0_wrist
もう少しで、いけそうなのになあ
Maya2016付近のmGearを入れたところから
https://github.com/mgear-dev/mgear/releases/tag/v2.6.1
このビデオにそってみる。
1、モデルを用意した
2,mGear/Shifter/Guide UIを起動
3,mGear/Shifter/Import Biped Guide を実行
黄色いGuidが作成された。Guideのサイズをモデルに合わせる