「数字->新名」の対応を元に、「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
を使用します。
-
.
というのは「なにか一文字」を指します。 ↩