見出し画像

頂点グループを調べる

オブジェクト・プロパティ画面でVertex ColorsコレクションとUV Mapsコレクションをいじってみたので、この流れでVertex Groupsコレクションを調査してみた。

頂点グループ

Blenderでは任意の頂点に名前をつけて管理することができる。この頂点の集まりをひとつのグループと見なして、頂点グループと呼んでいるようだ。

頂点グループはエディット・モードで頂点を選択し、オブジェクト・プロパティ画面からAssign(アサイン、割り当て)のボタンをクリックすることで指定することができる。

頂点グループが便利なのは、グループに含まれる頂点を一括して選択したり、選択解除したりできること。選択状態、非選択状態を論理値だと考えると、スクリプトでうまく操作を行えば、たくさんある頂点の選択状態を論理演算でコントロールできるようになる。

さっそく頂点グループにアクセスしてみる。これまでのVertex ColorsコレクションやUV Mapsコレクションと同じようにメッシュ・データ(bpy.data.meshes)から探したけれど、見つけることができなかった。どうやら頂点グループはオブジェクト・データからアクセスする必要があるようだ。

>>> bpy.data.objects["Cube"].vertex_groups[0].name
'Group'

頂点グループはメッシュ・データを描画するために直接参照する値ではない管理項目だから、bpy.data.meshesからアクセスするのではなく、bpy.data.objects参照するのだと考えることができる。

もしそうだとするなら、UIもオブジェクト・プロパティ画面に表示させないようにした方がわかりやすい気もするけれど、これはもはや慣例なのだろうから仕方がない。

頂点グループとの紐づけ

先ほど追加した頂点グループ「Group」にCubeオブジェクトの天面を構成する4個の頂点を割り当て、これらの頂点データへアクセスする方法を調べてみる。そのためにはまず、頂点と頂点グループを紐づける方法を知る必要があるので調べてみた。

インタラクティブ・コンソールで頂点グループ「Group」にアクセスして、自動補完をかけてみる。期待していたのは頂点グループを構成する頂点データを集めたverticesプロパティなんだけれど、どうやらここからは直接はアクセスできるようにはなっていないようだ。

これまでの調査で、Blenderの構造が基本的には逆参照型であることを思い知ったので、頂点グループへアクセスをしたい場合は、頂点データ側から参照する方式になっていると推測できる。

そこで、頂点データへアクセスし、それらしいプロパティをインタラクティブ・コンソールから探してみることにした。

自動補完されて表示された内容を眺めてみると、それらしい名前のプロパティにあるのは「groups」くらいしかないので、おそらくこのgroupsプロパティで合っているのだと思う。

名前が複数形なのでbpy_prop_collection型であると思うけれど、一応、データ型を調べてみる。

>>> type(bpy.data.meshes["Cube"].vertices[0].groups)
<class 'bpy_prop_collection'>

予想通り、bpy_porp_collectionになっているのでサブスクリプトを指定する。そうするとgroupプロパティがあるので、データ型を調べて確認する。

データ型を詳しく知りたいという場合はbl_rnaプロパティが役立つ。bl_rnaプロパティを使うと、そのデータがBlender上でどのような役割を与えられているのかを知ることができる。

生物におけるRNA(Ribonucleic acid、リボ核酸)は、タンパク質を構成するための情報を持った役割を持っている。

このRNAに擬え、Blender上で表現される数多のデータを構成する役割を担っているコアなデータをBlender RNAと呼ぶらしく、発想がとてもユニークだと思う。

bl_rnaプロパティへアクセスすると、groupsプロパティを構成するのは、VertexGroupElementというデータ型であることがわかる。

bl_rnaプロパティにはdescription(ディスクリプション、説明)プロパティがあり、このプロパティを参照すれば、プロパティの詳細を知ることができる。

>>> bpy.data.meshes["Cube"].vertices[0].groups[0].bl_rna
<bpy_struct, Struct("VertexGroupElement") at 0x00007FF76387F060>

>>> bpy.data.meshes["Cube"].vertices[0].groups[0].bl_rna.description
'Weight value of a vertex in a vertex group'

さて、続きに戻ろう。

インタラクティブ・コンソールで自動補完をかけ、頂点グループ「Group」に紐づきそうなプロパティを探す。RNA系のプロパティを除くと、groupプロパティとweightプロパティしか持っていないので、groupプロパティの一択となる。

このgroupプロパティへにアクセスしてみると0という値が返ってきた。

このgroupプロパティが本当に頂点グループ「Group」に紐づいているのかを調べる。これまでの流れで行けば、オブジェクト・プロパティのVertex Groupsへのアクセスは、辞書型でもよさそうなんだけど、文字列"Group"を指定するとエラーになってしまう。

>>> bpy.data.meshes["Cube"].vertices[0].groups["Group"]
Traceback (most recent call last):
 File "<blender_console>", line 1in <module>
KeyError: 'bpy_prop_collection[key]: key "Group" not found'

頂点グループのインデックスにしかアクセスできないとなると、別の方法で頂点グループと紐づいていることを確認する必要がある。

いま調べている頂点0番を、別の頂点グループにも割り当てて調べるのも悪くないけれど、お手軽に調べたいので、新しい頂点グループを追加し、頂点グループのインデックスを並べ替える。

ふたたびインタラクティブ・コンソールに戻り、頂点グループ番号を表示させてみると、インデックスが1に変わっていることを確認できた。

>>> bpy.data.meshes["Cube"].vertices[0].groups[0].group
1

ここまでの状況をイメージにまとめると、次のようになる。

しかし、なぜ頂点データから頂点グループ名を指定してアクセスできないのだろうか?

オブジェクト・プロパティ画面とにらめっこして気づいたのだけど、もしかして頂点グループはデータ・ブロックの順序を入れ替えることができるせいではないだろうか。

これまで登場した頂点カラーもUVマップも、じつはデータ・ブロックの順序を入れ替えることができない。

ただ、配列の要素は入れ替えることができるので、Blenderの手抜き実装なのではないか。仕方がないので、そういうものなんだと考えることにする。

頂点グループを構成する頂点へアクセスする

ここまでの調査で、頂点グループのデータと頂点データの構造がなんとか紐づいたので、これらのデータを紐づけたスクリプトを記述する。

頂点グループのデータはオブジェクト・データに紐づいているので、頂点グループ名で検索をかける。Pythonではリスト内の検索はindexメソッドを使うことができるけれど、bpy_prop_collection型のデータではfindメソッドというものが用意されているので、このfindメソッドを使う。

>>> bpy.data.objects["Cube"].vertex_groups.find("Group")
0

Pythonのリスト型を使ってindexメソッドを使う検索もできる。辞書型データはkeysプロパティからキーのリストを取得できるので、取得したリストに対してindexメソッドを呼べばいい。

>>> bpy.data.objects["Cube"].vertex_groups.keys().index("Group")
0

注意が必要なのは、リスト型のindexメソッドはキーが見つからない場合にValueErrorの例外を投げてくること。一方、findメソッドで検索した場合、キーが見つからなければ-1を返してくるのでハンドリングしやすい。

頂点グループの番号が取得できたら、各頂点データのgroupsプロパティ内を頂点グループ番号をキーにして検索する。検索に引っかかったものが、指定の頂点グループを構成する頂点ということになる。

import bpy

vertexGroupIndex = bpy.data.objects["Cube"].vertex_groups.find("Group")
vertices = [ vertex for vertex in bpy.data.meshes["Cube"].vertices if vertexGroupIndex in [ group.group for group in vertex.groups ] ]
for vertex in vertices:
   print(vertex.index)

スクリプトを実行すると、Cubeオブジェクトの天面に位置する、4個の頂点番号を取得することができた。

0
2
4
6

この記事が気に入ったらサポートをしてみませんか?