スカートだけを消し去るBlenderスクリプト（2.8）が完成した

**※Blenderのバージョンは2.8です※**

　正確には**任意の文字列を含むボーンのウェイトが乗った頂点を消し去る**スクリプトなのですが、面白いのでとりあえずスカートを前面に出してみました。改変における実用性（？）もあるかもしれないし。BlenderのAPIともそこそこ格闘したので、そのあたりの解説も加えています。

#適用の様子

　適用前

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/d9602000-86cd-b484-0641-dd2c263dcb4e.png)


　適用後

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/ff39815a-ebe0-a2a0-1b71-829bcfaf6406.png)

　Hairを対象にした場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/d50497c2-d165-3bb5-2541-64bf29ce085f.png)

　Chestを対象にした場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/66fb2579-3598-49b5-e3d2-0fe76da65473.png)

# ソースコード

```python
import bpy

for ob in bpy.data.objects:
    try:
        me = ob.to_mesh()
        bpy.context.view_layer.objects.active = ob
        vg = ob.vertex_groups
        for v in bpy.data.meshes[me.name].vertices:
            v.select = False
            for g in v.groups:
                if "Skirt" in vg[g.group].name or "skirt" in vg[g.group].name: #ここに消したいボーンの文字列を指定
                    v.select = True 
        bpy.ops.object.mode_set(mode='EDIT', toggle=False)
        bpy.ops.mesh.delete(type='VERT')
        bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
        print(ob.name + "... completed")
    except:
        print(ob.name + "... no mesh!")
```

#解説

　ウェイトは頂点グループとの対応関係で管理されています。ある頂点が、あるボーンの名前を持った頂点グループに含まれている場合、それはウェイトを持っているとみなされます。すべての頂点を探索し、`skirt`か`Skirt`を含む頂点グループに含まれているかを判断、そうであった場合は削除しています。

　まず、すべてのオブジェクトを探索します。

```python
for ob in bpy.data.objects:
```

　次に、オブジェクトが含まれるメッシュデータを手に入れます。このあたりのデータ構造については、拙作[Blenderのデータ構造を辿り、fbxから頂点座標を抽出する](https://qiita.com/hibit/items/8cc9b002caaa786c8630)でも解説しておりますので、合わせて読んでいただければ幸いです。

```python
    me = ob.to_mesh()
```

　また、この命令は、メッシュを持たないオブジェクト（`Camera`とか`Armature`とか）の場合エラーを吐いてしまうので、`try:`節によりエスケープ処理しています。あまりスマートなやり方ではないですね。

```
    bpy.context.view_layer.objects.active = ob
```

　この時、目的のオブジェクトをアクティブ状態にします。2.7時代までに使えていた`bpy.context.scene.objects.active`は使えなくなっているのでご注意ください。`bpy_prop_collection: attribute "active" not found`というエラーメッセージが出る場合はこれの可能性が高いです。

```python
    vg = ob.vertex_groups
```

 次に、オブジェクトに含まれる頂点グループのリストを取得します。これはメッシュではなくオブジェクトに含まれているので注意です。`Hips`や`Chest`などといった頂点グループの名前はここに含まれます。`bpy.data.objects[x].vertex_groups[x].name`といった形で名前を取得できます。

```python
        for v in bpy.data.meshes[me.name].vertices:
```

　次に、頂点をすべて探索します。頂点データはメッシュに含まれます。`bpy.data.meshes[x].vertices`といった形でリストを取得できます。最初は、先程定義した`me`から頂点データを抜き出そうとしたのですが、別idの変数を生成してしまっているらしく、`me`をいじってもオブジェクトの頂点が変化しないということが起こったので、やむなく`meshes['メッシュ名']`という形でメッシュデータを参照しています。`objects[]`も`meshes[]`も、数値で参照してもよいですし、辞書型のように名前で参照することもできます。

```python
            v.select = False
            for g in v.groups:
                if "Skirt" in vg[g.group].name or "skirt" in vg[g.group].name: #ここに消したいボーンの文字列を指定
                    v.select = True 
```

　すべての頂点に対して、`Skirt`もしくは`skirt`という文字を含む頂点グループがあるか、ということを調べています。ここら辺は正規表現を活用すればもっとクールに色々な条件をカバーできるでしょう。ある頂点は複数種類の頂点グループに含まれる可能性があるので（といっても、現実的には1個か2個の例が圧倒的に多いですが）、更にループを回して調べる必要があります。ある頂点から、それを含む頂点グループには`bpy.data.meshes[x].vertices[x].groups`でアクセスできるので、`for g in v.groups:`でそれを探索します。

　この後がややこしいのですが、`bpy.data.meshes[x].vertices[x].groups.group`で頂点グループの**番号**がわかるので、それを以前求めていた`bpy.data.objects[x].vertex_groups`に参照させることによって、頂点グループの名前を求めることができます。まわりくどい！

　後は、指定した文字列を含めば`bpy.data.meshes[x].vertices[x].select`を`True`にし、そうでなければ`False`にして、

```python
        bpy.ops.object.mode_set(mode='EDIT', toggle=False)
        bpy.ops.mesh.delete(type='VERT')
        bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
```

　この命令により選択部分を消去します。

#トラブルシューティング

　素の状態でこのスクリプトを実行すると、なぜかオブジェクトのメッシュがすべて消えてしまうことがあるようです。これは、初期状態ではすべての頂点を選択している（そして**なぜかそれを変更できない**）ことが原因のようで、一回編集モードにして頂点選択を解除すれば変更できるようになります。なんだこの仕様……。オブジェクトモードで、メッシュがあるオブジェクトを選択した状態で`a`を押して全選択　-> `Tab`を押して編集モードに移行 -> 全ての頂点が選択されている状態になっているので、`a`を押して選択解除 -> `Tab`でオブジェクトモードに戻る、という操作をすれば大丈夫です。このあたりの原因は要研究です。詳しい方いれば対処法お願いします……。

#お世話になったページ

[Blender PythonのMeshデータアクセスのチートシート](Blender PythonのMeshデータアクセスのチートシート)
[Blenderで利用可能なpythonスクリプトを作る その１３（メッシュ情報から頂点の選択）](https://bluebirdofoz.hatenablog.com/entry/2019/06/02/043117)