技術

選択した複数の頂点のボーンウェイトを削除するスクリプト

例えばblenderで「体に硬いものを身につけた人」をモデリングして、自動でボーンウェイトを設定した場合、硬いはずの部分に複数のボーンのウェイトが設定されてしまって、ボーンを動かすとその硬いはずの部分がグネグネ曲がって全然硬そうに見えないって事になりがちです。

例えば以下の画像のように、硬く見せたい部分に複数のボーンウェイトが設定されてる状況です。
スクリーンショット 2014-01-12 14.57.29

この場合、硬く見せたい部分の頂点に、適当なボーン(一番近い物とか)からのウェイトをひとつだけ設定すると良いので、

  1. 硬く見せたい部分の頂点郡を選択
  2. 選択した頂点のボーンウェイトのみを全て削除(頂点ウェイトにはボーン以外のものもあり得るのでそれは削除しない)
  3. プロパティ→オブジェクトデータ→頂点グループから適当なボーンのものをひとつ選んでウェイト1.0を適用

とやると目的が達成できそうなんですが、blenderにはどうも上記2を実行するUIがないみたいです。
頂点一つ一つ選択しながら全部のボーンウェイトの×ボタン(上の画像参照)を一つ一つ押せばいいんですが、頂点数が多いとやってられません。
なので、上の手順2の部分を自動で行うPythonの関数を書いてみました。
blenderのPythonコンソール(対話モード)に貼りつけて実行することを想定してるのでちょっとコメントの書き方が変になっております。
(対話モードでは関数中に空行があるとそこで関数が終わりだと認識されてしまうので空行が無いようにしています)

def clear_deform_weights():
    #  アクティブなオブジェクトを取得
    obj = bpy.context.active_object
    if not obj:
        return
    bpy.ops.object.mode_set(mode='OBJECT')
    #
    # ボーン名を得る
    bnames = [b.name for b in obj.parent.pose.bones]
    #
    # bmeshの生成
    import bmesh
    bm = bmesh.new()
    bm.from_mesh(obj.data)
    #
    # 頂点ウェイト(変形)のレイヤーを取得
    dflay = bm.verts.layers.deform.active
    if not dflay:
        return
    #
    # 選択中の頂点ウェイト(変形)削除する
    for v in bm.verts:
        if v.select:
            for dvk, dvv in v[dflay].items():
                if obj.vertex_groups[dvk].name in bnames:
                    del v[dflay][dvk]
    #
    # 反映
    bm.to_mesh(obj.data)
    bpy.ops.object.mode_set(mode='EDIT')

blender v2.69 のMac版で動作確認してます。
編集モードでボーンウェイトを削除したい頂点を選んでから、上記の関数を実行するという使い方を想定しています。
アーマチュアが対象のオブジェクトの親になってて(下記画像のような状態)、ボーン名と対応する頂点グループの名前が同じであるという前提で書いたコードですので、それ以外の場合では正しく動かないと思われますが、まぁ普通に作ってたら多分この前提は満たすはずじゃないかなとうっすらと何となく思います。
また、blenderの内部の仕組みとか全然知らずに書いたのでもしかしたら副作用があったりUIの状態によってはおかしな事になったりするかもしれません。
スクリーンショット 2014-01-12 15.29.08

余談

各ボーンウェイトの右にある×ボタン(最初の画像参照)にマウスカーソルをのせると分かるんですが、

bpy.ops.object.vertex_weight_delete(weight_group = 頂点グループのインデックス)

というメソッドでも1つのボーンウェイトを1つの頂点から削除出来ます。
なので、これを1つの頂点の全てのボーンウェイトに対して呼び出す、ってのをさらに選択された複数の頂点に対して行う、という方法が使えるなら同じことが実現できます。
こちらの方法を最初考えて実装してみたんですが、この方法では頂点を複数選んでても1つ分しかボーンウェイトが削除されずダメでした。

blenderにはオブジェクトや面や辺や頂点の選択に関して、selectとactiveという考え方があります。(私がUIやAPIをざっと見た感想ですが)
select状態に関しては複数のオブジェクトなり頂点なりが同時になることが出来ますが、active状態に関しては同時に1つのものしかなることが出来ません。
スクリーンショット 2014-01-12 15.47.35
この画像は頂点の場合についての例です。
3頂点がselect状態にありますが、active状態なのは1つだけ(白色のやつ)になります。
基本、最後に選択したものがactiveになるようです。

で、vertex_weight_deleteメソッドはactiveな頂点についてしか処理出来ない上に、blenderのPython APIには頂点をactive状態にするものが無いみたいなので、こちらの方法は諦めました。
APIからselectすると最後のが自動的にactiveになるかというとそういう事も無いみたいです。

以前、blenderのソースは独特な書き方がされているよという話は聞いたことがありましたが、今回Python APIを使ってみてその独特具合の片鱗を見た気がします。

コメントを残す

メールアドレスが公開されることはありません。



※画像をクリックして別の画像を表示

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください