※Blenderのバージョンは2.8です※
正確には任意の文字列を含むボーンのウェイトが乗った頂点を消し去るスクリプトなのですが、面白いのでとりあえずスカートを前面に出してみました。改変における実用性(?)もあるかもしれないし。BlenderのAPIともそこそこ格闘したので、そのあたりの解説も加えています。
適用の様子
適用前
適用後
Hairを対象にした場合
Chestを対象にした場合
ソースコード
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を含む頂点グループに含まれているかを判断、そうであった場合は削除しています。
まず、すべてのオブジェクトを探索します。
for ob in bpy.data.objects:
次に、オブジェクトが含まれるメッシュデータを手に入れます。このあたりのデータ構造については、拙作Blenderのデータ構造を辿り、fbxから頂点座標を抽出するでも解説しておりますので、合わせて読んでいただければ幸いです。
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というエラーメッセージが出る場合はこれの可能性が高いです。
vg = ob.vertex_groups
次に、オブジェクトに含まれる頂点グループのリストを取得します。これはメッシュではなくオブジェクトに含まれているので注意です。HipsやChestなどといった頂点グループの名前はここに含まれます。bpy.data.objects[x].vertex_groups[x].nameといった形で名前を取得できます。
for v in bpy.data.meshes[me.name].vertices:
次に、頂点をすべて探索します。頂点データはメッシュに含まれます。bpy.data.meshes[x].verticesといった形でリストを取得できます。最初は、先程定義したmeから頂点データを抜き出そうとしたのですが、別idの変数を生成してしまっているらしく、meをいじってもオブジェクトの頂点が変化しないということが起こったので、やむなくmeshes['メッシュ名']という形でメッシュデータを参照しています。objects[]もmeshes[]も、数値で参照してもよいですし、辞書型のように名前で参照することもできます。
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にして、
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スクリプトを作る その13(メッシュ情報から頂点の選択)



