「数字->新名」の対応を元に、「Bone_L.数字」を「新名_L」に改名する(正規表現)
Pythonを使ってBlenderでできる事をまとめています。解説もあります。
Pythonの難易度: ★★★★★
動機
左右対称のリグを作るとき、ボーンの名前に左右を表す_Lや_Rが付いていると便利です。
しかし、X-Axis Mirrorを有効にボーン編集をしても、Bone_[LかR].[三桁数字]というボーンが作られてしまいます。(「001」のように0埋めがされた数字も三桁数字と表現しています)
これをBone.[三桁数字]_[LかR]に名付け直せると便利ですね。
また、.001が付いているボーンはSholderに、.002ならArmのように、数字に基づいて改名してみましょう。
細かい仕様
- 名称変更を施したいアーマチュアがオブジェクトモードでアクティブだとします。
- アーマチュアのボーンの名称は「
Bone_L.[三桁数字]/Bone_R.[三桁数字]/その他」のいずれかが含まれています。この内、.[三桁数字]は省略されている時もありますが.000と同じとみなします。 -
["Sholder", "Arm", ...]のような、新しいボーンの名称がリストの形式で定義されています。(以下、name) -
Bone_L.iは[nameのi番目の要素]_Lに、Bone_R.iは[nameのi番目の要素]_Rに改名してください。ただし、len(name) <= iのiに関してはBone.i_Lのようにして下さい。 -
_Lや_Rの一方しかない場合も、改名してください。 - その他のボーンは名前を変えないでください。
サンプルコード
name = ['Sholder', 'Arm', 'Hand', 'Finger']
import re
pattern = re.compile(r"Bone_(?P<lr>L|R)($|\.(?P<count>\d{3}))")
import bpy
obj = bpy.context.active_object
if obj.type != "ARMATURE":
raise Exception("active object is not an armature")
bones = obj.data.bones
for bone in bones:
match = re.match(pattern, bone.name)
if match is None:
continue
bone_lr = match.groupdict()["lr"]
if match.groupdict()["count"]:
bone_count = int(match.groupdict()["count"])
else:
bone_count = 0
if bone_count < len(name):
trunk = name[bone_count]
else:
trunk = "Bone.{0:3d}".format(bone_count)
bone.name = "{0}_{1}".format(trunk, bone_lr)
上記サンプルコードを、Blender内蔵のテキストエディタに貼り付けた後、Alt-Pを実行してください。
解説
正規表現
文字列があるパターン(aから始まるとか)に一致しているかを調べる方法に正規表現というのがあります。
詳細はググって欲しいですが、a+b*のように普通の文字に特殊文字を混ぜることでパターンを定義します。
たとえば、a+b*というのは「aを1個以上のあとbを0個以上含むパターン」を指します。
そのため、aaaabbb,a,abはa+b*という正規表現に一致しますし、cbは一致しません。
今回、Bone_(L|R)(^|\.\d{3})という正規表現にマッチしています。(?P<lr>などは後で説明するので一旦無視してください)
Bone_(L|R)(^|\.\d{3})はどのようなパターンを指すのでしょうか?
(L|R)は「LかRのどちらか」というパターンを指します。つまり、Bone_(L|R)というパターンはBone_LかBone_Rのどちらかが一致します。
同様に、$は「文字列の終端」を、\.は「.という文字1」を、\dは「0~9の数字のどれか一文字」を、{3}というのは「直前の文字3文字」を指します。
したがって、Bone_(L|R)($|\.\d{3})というのは「BoneのあとにLかRがあって、そこで文字列が終了するか、.とそのあと数字を3文字」というのを指します。まさにBone_L.001などが一致しますね。
正規表現をpythonで使うにはreという標準モジュールを使います。
まず、pattern = re.compile(正規表現)で正規表現をコンパイルします。あまり気にしないでください。
match = re.match(pattern,文字列)を実行すると、matchには専用のオブジェクト(一致した場合)かNone(一致しなかった場合)が代入されます。
ここで、先ほど飛ばした?P<lr>を説明します。
これは、match.groupdict()と一緒に使うことで「一致した文字列を後から辞書型で取得」できます。
例えば、文字列がBone_L.001だった場合を考えてみましょう。
すると、match.groupdict()["lr"]はLです。
このように?P<キー>パターンを指定すると、パターンに一致した文字列を後から辞書型で取得できます。
bpy.context.active_object
PythonからBlenderを使うにはbpyというライブラリを使います。
例えば、bpy.dataにはBlender内の様々な変数(オブジェクトのインスタンス)が、bpy.contextにはBlenderの現在の状態(選択中のオブジェクトなど)が格納されています。
選択中のオブジェクトを取得するにはbpy.context.selected_objectsが使えます。
今回はアクティブなオブジェクト(最後に選択したオブジェクト)が対象なので、bpy.context.active_objectを使用します。
-
.というのは「なにか一文字」を指します。 ↩