サクサクPython Scripting TIPS

Blender Advent Calendar2016 12/10の記事です。

みんな!アドオン作ってる?

ということでアドオンやスクリプト作成をより便利で楽ちんにする話です。

例によって俺はアマチュアスクリプターなのでコーディング規則がヤバかったりいろいろ問題をはらんでたりするかもしれませんが

あとまぁ普段ゴリゴリにコーディングしてる人には今更な内容かもしれません。

コードで便利

スクリプトを書くときにかなり頻出するコードがあるわけですが、これが結構長文で何度もうつのが非常にめんどくさい。どっかにメモっておいて毎回ペーストするんでもなかなかにめんどい。

例えばこういう表現。

obj = bpy.context.objects.active

とか、

bpy.context.objects.active = obj

obj.select = True

とか、

あるいは

for obj in bpy.context.selected_objects:

if obj.type == "MESH":

bpy.context.scene.objects.active = obj

for mod in obj.modifiers:

if mod.type=="MIRROR":

bpy.ops.object.modifier_apply (modifier=mod.name)

みたいな処理はよく書くことになるし、

for obj in bpy.data.objects:

obj.select = False

なんて超頻出ですよね。(頻出じゃない?)

あるいは、そんな使わないけど手続きとかマニュアルひっぱるのがめんどい表現とか。

そういうのはあらかじめ関数化してまとめておくのが便利ですね。

import文

とりあえずまずはimport文について

知ってる人はここは飛ばしましょう。上記の頻出文書く人は知ってる気はしますが。

アドオンとか、スクリプトを書いているとよくimport文を書きますな。これはどういう機能かというと、他のコードファイルから内容をひっぱってくるという機能。

Blenderのテキストエディタの中でかく

import bpy

は、bpyというモジュールから機能をひっぱってくるよ、という意味です。

大体のモジュールは.pyファイルで記述されていて、Blender内で登録されている所定のフォルダに配置されています。

そこに自分で書いたファイルを配置しておけば、スクリプトを書くときに利用できるわけですね。

例えば、

(プログラムのフォルダ)/Blender Foundation/Blender/2.78/scripts/addons/

内に、

mytools.py

を配置すれば、他のスクリプトからは

import mytools

とすれば、mytools内の関数等を利用できます。

mytools.test()

というかんじで利用する。

mytools.をいちいち書くのがめんどくさい場合は

from mytools import *

とすれば、省略できて、

test()

といきなりかけますね。

ただし、変数とかクラス、関数の名前が使うスクリプトとかぶっているとエラーとか誤動作の原因になるので注意が必要です。

便利コード

ということで前説が終わったところでサクサクにPythonスクリプトをかくための便利なコードを適当に紹介していきます。

記事末尾でファイル配布してます。

def activate(obj):

bpy.context.scene.objects.active = obj

obj.select = True

return obj

指定オブジェクトをアクティブオブジェクトにする。

返り値でそのオブジェクトをそのまま返すと、active(obj).data...ってかんじでメソッドチェーン的にもかける。

あんまりそんなことしない気もするけど。

def active():

return bpy.context.scene.objects.active

アクティブオブジェクトを取得する。

bpyから書くと長いので…。

def get_selected_list(type = "ALL"):

list = []

for obj in bpy.context.selected_objects:

if type == "ALL":

list.append(obj)

else:

if obj.type == type:

list.append(obj)

return list

ちょっと命名がひどいかもしれないけど。

選択中のオブジェクトをリストにコピーして取得する。

リストにコピーというのがポイントで、bpy.context.selected_objectsはobj.selectを変えると内容が変わってしまう。

処理中に一旦選択を解除したりしたいんだけど、でも対象オブジェクト群は変更したくない、ということはままある。ので、リストにコピーして取得する。

ついでに、タイプでフィルタリングできるようにしてある。

とりあえずばーっとオブジェクトを選択して、その中のメッシュだけに処理をしたい!という雑なオペレーションに便利。

def select(objects):

for obj in objects:

obj.select = True

get_selected_list()と組み合わせて威力を発揮するのがこれ!

リストとして取得したオブジェクトの選択を一発でできて便利!

def deselect():

for obj in bpy.data.objects:

obj.select = False

select()があるならdeselect()もあるよね。

ショートカットAに割り振られてるbpy.ops.object.select_all()だと、非表示オブジェクトとか非表示レイヤーのオブジェクトはスルーされてしまっていまいち不安が残ることがあるので、bpy.data.objectsからたどることですべての選択をオフにする。

def find(name):

result = []

for obj in bpy.data.objects:

if name in obj.name:

result.append(obj)

return result

名前で検索して該当オブジェクトをリストで返す。

def mode(modeto):

if active() != None:

bpy.ops.object.mode_set(mode=modeto, toggle=False)

オブジェクトのモードをサクッと切り替え!

ちょっとエディットモードとかオブジェクトモードに切り替えたいってだけなのに、長文を書かなきゃいけないのはバカバカしいよなぁ。ということで。

用例

以上のようなコードを用意しておけば、例えば「選択オブジェクトの法線をすべて自動で外側に向けたい」なんていう処理が、

↓こんなかんじでかけるわけです。

import mytools

for obj in get_selected_list("MESH"):

mode("OBJECT")

activate(obj)

mode("EDIT")

bpy.ops.mesh.select_all(action='SELECT')

bpy.ops.mesh.normals_make_consistent(inside=False)

「選択オブジェクトのモディファイアを全部適用したい!」

import mytools

for obj in get_selected_list():

activate(obj)

for mod in obj.modifiers:

try:

bpy.ops.object.modifier_apply(modifier=mod.name)

except:

#ダメだった=パラメータが不正で有効になってないのでmod除去

bpy.ops.object.modifier_remove(modifier=mod.name)

「メッシュデフォームの再バインドを選択オブジェクトに対して全部やりたい」

import mytools

for obj in get_selected_list("MESH"):

activate(obj)

for mod in obj.modifiers:

if mod.type == "MESH_DEFORM":

if mod.is_bound:

bpy.ops.object.meshdeform_bind(modifier=mod.name)

bpy.ops.object.meshdeform_bind(modifier=mod.name)

というかんじで、サクサクっとコードをかけるようになります。なんか後半便利要素少ない気がしなくもないけど。

人にコードを配布する時に、ライブラリ用のファイルも一緒に配布しなければならないという難点があるっちゃあります。

ともかく、便利ライブラリを作って充実させていくことはそれ自体楽しいので、もりっと書いておくと楽しいですよ!

次はPentanさんの「NPRのためのCyclesマテリアルの基礎知識(釣りタイトル)」です!→Blender Advent Calendar2016