※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(メッシュ情報から頂点の選択)