以前作ったアクティブなボーングループに所属する隠したボーンを再表示するアドオンですが、懸念材料がありました。
複数のアーマチュアが1シーンにあり、複数のアーマチュアを同時選択した場合はどうなるのか?
そんなわけで作ってみたモデルがこちら
…棒を持って、謎のナマモノを追いかける人っぽいですが…
手前のナマモノが「hebi」そして奥が「hito」です。で、コイツらはこんな感じのボーングループで構成されています。
…つまり、atama と buki のボーングループ名がかぶります。
で、「hito」アーマチュアを選択し、「buki」ボーングループを選んで実行してみると…
うまく行きません。
コレは以前アップしたコードに2種類のバグがあった為でした。
一つ目はボーングループのインデックス値が0だった場合はグループ無しとして処理をしなかった事。ボーングループのインデックス値は0始まりなのでボーングループに所属している可能性があります(後述)。
この為、bone_hito のインデックス0の「buki」は処理されなかった訳です。
もう一つ、「hebi」の再表示されたグループが「atama」になっている件ですが、これは単純に「hebi」の最後にアクティブにしたボーングループだったからです。
つまり、各アーマチュア毎にアクティブボーンを処理していました。
ただし、編集画面はこの通り
最後にアクティブになったアーマチュアのボーングループしか表示されない(つまり hebi のアクティブボーングループが何かは判らない)ので違和感が出ます。
…なら、名前ベースで再表示すれば良いよね?…と、思ったら予想以上に重かった。
ソース
import bpy
while bpy.context.mode == 'POSE':
amt = bpy.context.active_object
exist_bg = False
for bone in amt.pose.bones:
if (bone.bone_group):
exist_bg = True
break
if not(exist_bg):
break
idx_active = amt.pose.bone_groups.active_index
name_active = amt.pose.bone_groups[idx_active].name
for obj in bpy.context.selected_objects:
# Use armature only
if obj.type != 'ARMATURE':
continue
idx = 0
for bg in obj.pose.bone_groups:
if bg.name == name_active:
break
idx = idx+1
for bone in obj.pose.bones:
if not(bone.bone_group):
continue
if bone.bone_group_index == idx:
bone.bone.hide = False
break
- アクティブなアーマチュアを求める
まず編集画面に表示されているボーングループを持っているアーマチュアを特定しないと話になりません
while 〜 amt = bpy...
ポーズモードで一旦選別しているのでアクティブなオブジェクトはアーマチュア以外ありえない事になっている…ハズ。
今後のアップデートで増えたらどうしよう…
- そのアーマチュアにはボーングループは存在するのか?
そう、アーマチュアは初期状態ではボーングループは持っていません。
exist_bg 〜 if not(exist_bg):
やたらに冗長な処理ですが、短くする方法もあります
amt.pose.bone_groups[0]
等の要素にいきなりアクセスして、エラーを返されたら分岐…でも良いのですが、流石にあまりに乱暴なのでアーマチュアに所属するボーンに対してボーングループを発見するまでループさせています。
… if(amt.pose.bone_groups) が駄目で if (bone.bone_group) は大丈夫とは…
そもそも amt.pose.bone_groups に対して len 関数が使えるならば問題なかったのですが。
- アクティブなボーングループ名を特定する
idx_active = ~
最終的に name_active にアクティブボーングループ名が入る
- 次はお決まりのシーン内全オブジェクト検索ループ
このループではカメラやライトなどのアーマチュア以外のオブジェクトもひっかかる為、アーマチュア以外を最初に除外します。
- ループ内で取得したアーマチュアに先の処理で特定したアクティブなボーングループ名( name_active )と合致するボーングループが有るかの検索
idx = 0 ~
合致すれば idx に値が入り、合致しなければ要素数(つまり絶対に合致しないインデックス値)が idx に入る。
- アーマチュア内ボーン処理ループ
for bone in obj.pose.bones: ~
- そのボーンにボーングループは割り当てられているのか?
if not(bone.bone_group): ~
Blender のガバガバ仕様が垣間見える処理。
ボーンには割り当てられたボーングループのインデックス値を格納する bone_group_index というメンバー変数があるのですが…
その初期値は0。親のアーマチュアにボーングループを作っていない状態でも同じです。
次に、親のアーマチュアにボーングループを作り、1個だけグループを追加してボーンに割り当てます。
ボーングループ内に1個だけグループを作れば、当然リストのインデックスは0になるので bone_group_index には0が格納されます。
結果、 bone_group_index が0の場合、ボーングループが割り当てられていないので0なのか、割り当てられて0なのか判別ができません。
…初めて見た時に「初期値を-1とかにしておけば良くね?」と思った私は浅いのでしょうか?
この為、わざわざ毎回上記の処理を行って本当にボーングループを割り当てられているのか判別しています。
※5/30追記
まあ、そんな偉そうな事書いておいて、ソースにガバがあるんですよね…
idx = 0 ~の処理の部分でボーングループを持っていない、もしくは該当するボーングループが無い場合は確定するのでその後のボーンループをする必要がないのです。
ここらのガバはアドオンに統合する際に修正します。
- ボーンの所属しているボーングループとインデックスが合致すれば再表示する処理
if bone.bone_group_index == idx: ~
…現在ボーンは1個のボーングループにしか所属できない事になっているので、コレで良いのだけど、今後頂点グループのように複数グループに所属できるようになったら改修が必要になりそう…
で、再度動かしてみると…
両方の「buki」グループ所属ボーンだけが再表示できました。
…正直最初にアクティブなアーマチュアを特定できているのだから1アーマチュアだけ処理すれば良い…とも思ったのですが。
複数のアーマチュアの同名操作用ボーン(視線目標ボーン等)やら、風の影響を受ける髪の毛ボーン等をいっぺんに再表示させたい場面は今後あるかもしれないと思い上記の形にしました。
まあ、複数アーマチュアに対して操作を行うのは終盤も終盤(動画制作)なのでどれだけ未来の話になるか見当もつきませんが。
0 件のコメント:
コメントを投稿