キャラクターセットを仲介したノードの接続/再接続について考える
そんなひとはいない、はやく目を覚ませ!
※この場合のお強い方というのは、下図の様子を見て「なるほどね」と神妙に頷きながら一緒に酒を飲める地球人のことを差します。
例えば、あるコントローラーに挿さっているアニメーションノードを、
別のコントローラへ挿したい(移植したい)ことがあります。
コントローラーノードとアニメーションカーブノードの対応関係がシンプルなら
把握もしやすくスクリプト化もちょっと頑張ればできそうです。
でもアトリビュートがキャラクターセットに登録されていると、
アニメーションノードは直にアトリビュートに挿さらず、キャラクターセットを仲介してコントローラーのアトリビュートを駆動させます。
冒頭の図でいうところの、左側のノード群のことですね。
中央の間抜けづらした細長いやつがキャラクターセット、それを挟んで右にくっついてるのがコントローラ、左のエイヒレの出来損ないみたいなのがアニメーションノードです(口が悪い)。
この左ノード群のエイヒレ部分を、右ノード群の適切なところに挿したい。
つまり、
キャラクターセットを介している場合にアニメーションノードをつなぎかえようと思ったら、
- 元コントローラーのキャラクターセットとのコネクションを確認
- キャラクターセットとアニメーションノードのコネクションを確認
- 差し替え先コントローラーのキャラクターセットのどこに新たに挿せばいいか確認
みたいな手順を踏む必要があります。
めんどくせ。
アトリビュートの接続の確認
アトリビュートの接続状況を確認したい時には、
基本的には listConnections を使います。
ここでは横着をして PyMEL の inputs/outputs を使うことにします。
inputs
outputs
あるノードを対象に inputs / outputs を実行すると、そのノードとコネクト関係にあるノードや、挿さっているアトリビュートを調べられます。
中で実際仕事しているのはlistConnectionsなので、
listConnectionsに備わっているオプションがそのまま使えます。
主に p
c
を使いますが、どっちを付けたらどうなるんだっけ?
というのをよく忘れるので、ちょっとまとめてみます。
inputs/outpus (オプションなし)
「緑」ノードを対象にしてinputs/outputsを オプションなしで 実行すると、
実行結果として「赤」のノードが返ってきます。
.inputs()
で左側のノード、
.outputs()
で右側のノード
それぞれ複数接続されている可能性があり、リストで返ってきます。
p=True
p(plugs)
を有効にして実行してみます。
同じく赤で示してある要素が返ってきます。
対象ノード(緑)から出ているコネクションが挿さっているノード(オプションなしで得られたノード)の
挿さっているアトリビュートが返ってきます。
.inputs(p=True)
で、対象ノードに入力しているコネクションの、出し側ノードのアトリビュート
.outputs(p=True)
で、対象ノードから出力しているコネクションの、受け側ノードのアトリビュート
が得られます。
c=True
c(connections)
を有効にすると、
微妙にわかりづらいんですが、
オプションなしで返ってきたノードと、そのノードが挿さっている(対象ノード側の)アトリビュート
が二重配列になって返ってきます。
(二重配列に整形してくれるのはたしかPyMELの設計で、cmdsだと単純にリストが返ってきた気がする。)
.inputs(c=True)
で、コネクションを挿しているノードと挿さっているアトリビュート
.outputs(c=True)
で、コネクションを挿しているアトリビュートと挿さっているノード
……この組み合わせで返ってくるの、いつ嬉しいんですかね??
p=True, c=True
p
もc
も有効にしたら?
両方の結果を合わせて、つながっているノードとつながっている出し/受け側アトリビュート
全部が返ってくる気がします。
でも直感に反して、返ってくるのは下図の赤の要素です↓
コネクションの、出し/受け側アトリビュートが二重配列で返ってきます。
これにノードも返ってきたとして、配列が複雑になっても使いづらいですし、
アトリビュートオブジェクト自体に node メソッドがあってそこからノードを取得できるので、
必要に応じてそれを使えば良さそうです。
キャラクターセットを介してアニメーションノードを探ってみる
まず対象ノードに挿さっているキャラクターセットのアトリビュートを調べて、
そこからアニメーションノードのアトリビュートを調べます。
対象ノードを target_ctl という変数に入れたとして、
target_ctl.inputs(p=True,c=True)
これで、受け側(target_ctl)/出し側(キャラクターセット)双方のアトリビュートが得られます。
ひとまず前者を target_input、後者を src_outputに入れることにします。
for target_input,src_output in target_ctl.inputs(p=True,c=True):
# target_input : 受け側(対象ノード側のアトリビュート)
# src_output : 出し側(キャラクターセット側のアトリビュート)
src_output にはAttributeオブジェクトが入っていますが、
こいつも inputs / outputs メソッドを持っていて、
そのアトリビュートへの入出力を調べられます。
ここに入力しているアニメーションノードを調べるには .inputs()
オプションなしで実行すればいいですが、
今回はアニメーションノードそのものには用事はなく、アトリビュートのつなぎ換えが目的なので
.inputs(p=True)
とpオプションを有効にして実行します。
これもリストで返ってきますが、
キャラクターセットのひとつのアトリビュートに複数のノードが入力しているとは考えづらいため、 [0] でパパッと取り出します。
for target_input,src_output in target_ctl.inputs(p=True,c=True):
anim_node_output = src_output.inputs(p=True)[0]
つなぎ変えられる側のキャラクターセットも調べる
同じようにして、先ほど調べたアニメーションノードのアトリビュートを
別のキャラクターセットのどこに挿せばいいか確認します。
for new_target_input,chr_attr in new_target_ctl.inputs(p=True,c=True):
# new_target_input : 受け側(対象ノード側のアトリビュート)
# chr_attr : 出し側(キャラクターセット側のアトリビュート)
やってることは一緒ですが、なにやらそろそろ
スクリプトの難しさ云々より変数名考えたり、どれになにが入ってるかイメージする(覚えておく)のが微妙に厄介な感じになってきました。
アニメーションノードをつなぎ変える
あとは、両方の for をぐるぐる回しながら、
対象ノードとnew対象ノードのアトリビュート名が一致する時に、
アニメーションノードのアトリビュートをキャラクターセットのアトリビュートへ接続する、
という内容で書けばいけそうです。
# 前略
attr_longName = target_input.longName()
# 中略
new_attr_longName = new_target_input.longName()
if new_attr_longName == attr_longName:
anim_node_output >> chr_attr
↑実際には、for を二重に回すのでインデントがずれます。
まとめると、こんな感じ↓
from pymel import core as pm
target_ctl,new_target_ctl = pm.selected()
for target_input,src_output in target_ctl.inputs(p=True,c=True):
anim_node_output = src_output.inputs(p=True)[0]
attr_longName = target_input.longName()
for new_target_input,chr_attr in new_target_ctl.inputs(p=True,c=True):
new_attr_longName = new_target_input.longName()
if new_attr_longName == attr_longName:
anim_node_output >> chr_attr
キャラクターセットに登録されているノードAとBがあったとして、
A→Bの順で選択してこのスクリプトを実行すると、
A側のキャラクターセットにささっていたアニメーションノードが、B側のキャラクターセットのおなじアトリビュートに挿さります。
説明変数に入れたりしているので思ったより行数がありますが、
そういうことをしなければ半分くらいの行数になります。
このつなぎ替え、手でやってもいいですが、
キャラクターセットは登録したアトリビュート分タテに伸び、数百行分におよぶこともあるので、
ノードエディタ上でも非常にめんどくさいことになります。冒頭画像のように。
スクリプトで処理するのが人道的です。
あとコントローラにコンストレインもかかっているとpairblendも考慮しないといけなくなるので、もう一歩めんどくなります。
Discussion