Blender 2.79 のスクリプトを 2.80 にアップデートする
この記事は 2019 年 08 月 15 日時点でのReference/Release Notes/2.80/Python APIの翻訳だ。
目次
- 座標系
- 削除されたモジュール
- リネーム
- キーワード強制引数(Keyword Only Arguments)
- 行列の乗算
- テキストのインポート
- オペレーターアップデート
- プロパティアップデート
- Blender のバージョンの比較
- 外部リンク
Blender 2.80 における Python API の変更
Blender 2.80 は過去バージョンの Python API と互換性がない。アドオンやスクリプトを 2.80 で実行するにはアップデートが必要になる。
アドオン API
バージョン情報
bl_info ディクショナリの "blender" キーの値は (2, 80, 0) となる。(2, 8, 0) や (2, 79, 0) は例外が投げられる。
# 正しい bl_info bl_info = { "name" : "Addon name", "author" : "author", "version" : (1, 0), "blender" : (2, 80, 0), "location" : "", "description" : "", "warning" : "", "wiki_url" : "", "tracker_url" : "", "category" : "" }
バージョン情報が間違っている場合、以下の例外が投げられる。
Exception: Add-on 'ADDON_NAME' has not been upgraded to 2.8, ignoring
モジュールの登録
bpy.utils.register_module は不要な管理コストがかかるため削除された。代わりにクラスをタプルまたはリストにして直接登録する。
# クラスの登録コード classes = ( FooClass, BarClass, BazClass, ) def register(): from bpy.utils import register_class for cls in classes: register_class(cls) def unregister(): from bpy.utils import unregister_class for cls in reversed(classes): unregister_class(cls)
上記のコードを使い回さなくて済むように、bpy.utils.register_classes_factory が用意されている。
classes = ( FooClass, BarClass, BazClass, ) register, unregister = bpy.utils.register_classes_factory(classes)
大量のクラスがある場合は、Patch to help remove register_module use in 2.8 を使えばクラスのリストを生成できる。
クラスの登録
詳細はPython API, changes to type registration in 2.8(T52599)を参照。
アクセス(bpy.types)
アドオンによって登録されたクラスは bpy.types からアクセスできなくなった。代わりにモジュールをインポートすることで、直接それらにアクセスできるようになった。
ただし [Header, Menu, Operator, Panel, UIList] のサブクラスは bpy.types からアクセスできる。
命名規則
Blender 2.7x ではクラス名の衝突が起こりやすかった。これを防ぐため Blender 2.8x ではクラス名に制約ができた。これらの命名規則は Blender のコードで広く使われているものだ。
その命名規則はわかりにくいが [大文字アルファベット][大文字アルファベット or 数字 or _]_{セパレーター}_[アルファベット or 数字 or _] だ。正規表現で書けば以下のようになる。
[A-Z][A-Z0-9_]*_(HT|MT|OT|PT|UL)_[A-Za-z0-9_]+
セパレーターは以下のようになる。
- Header -> _HT_
- Menu -> _MT_
- Operator -> _OT_
- Panel -> _PT_
- UIList -> _UL_
いくつか有効なクラス名の例をあげる。
- OBJECT_OT_fancy_tool(bl_idname = "object.fancy_tool")
- SOME_HEADER_HT_my_header
- PANEL123_PT_myPanel
この命名規則に従わない場合、起動時(startup:おそらく Blender 起動時とアドオン有効時)に警告が表示される。以下に警告例を示す。
Warning: 'Oscurart Files Tools' doesn't contain '_PT_' with prefix & suffix Warning: 'Oscurart Overrides' doesn't contain '_PT_' with prefix & suffix Warning: 'Oscurart Animation Tools' doesn't contain '_PT_' with prefix & suffix
クラスプロパティの登録
bpy.props のプロパティを持つクラスは Python アノテーション(PEP 526)(日本語の解説)を使うように変更された。代入に "=" ではなく ":" を使う。
# 2.7x class MyOperator(Operator): value = IntProperty()
class MyOperator(Operator):
value: IntProperty()
Blender 2.80 以降で 2.7x の記法を使うと以下の警告が出る。
Warning: class Foo "contains a properties which should be an annotation!"
シーンとオブジェクト API
ビューレイヤーとコレクション
2.8x のコレクションは 2.7x のグループと 20 個しかなかったレイヤーとを置き換える。コレクション内のオブジェクトはシーンのコレクション階層にリンクされている場合、エンプティやパーティクルの位置にインスタンス化できたり、シーン内のオブジェクトを管理したりできる。
シーンは必ずマスターコレクションをひとつ持つ。これはシーンのコレクション階層の根だ。マスターコレクションはシーンデータで管理されるので、データベース(bpy.data.collections)には現れない。
2.7x ではオブジェクトのレイヤー情報はオブジェクトが持っていた。2.8x ではコレクションにリンクする必要がある。
# 2.7x C.object.layers[1] = True # 2.8x # コレクションをシーンにリンク col = D.collections.new("Collection 2") C.scene.collection.children.link(col) # オブジェクトをコレクションにリンク col.objects.link(C.object)
2.8x のビューレイヤーは 2.7x のレンダーレイヤーを拡張したものだ。ビューレイヤーはシーン内のオブジェクト・レンダリングするオブジェクト・コレクションの可視をコントロールする。
ビューレイヤーはシーンのコレクション階層を複製する。しかし LayerCollection は特定のコレクションを除外できる。シーンにリンクされているコレクションは全てのビューレイヤーでデフォルトでアクティブになる。
# 2.7x C.scene.render.layers.active.layers[1] = False # 2.8x # アクティブビューレイヤーから "Collection 2" を除外する C.window.view_layer.layer_collection.children["Collection 2"].exclude = True
2.8x ではアクティブウインドウからアクティブビューレイヤーを取得する。アクティブシーンからは取得できない。
シーンアップデート
# 2.7x scene = bpy.context.scene scene.update() # 2.8x layer = bpy.context.view_layer layer.update()
オブジェクトの選択と表示/非表示
2.7x ではオブジェクトの select プロパティを操作することで表示/非表示を操作できた。2.8x ではこれらは削除されており、代わりに get/set メソッドを使うようになった。
obj = bpy.context.object # 2.7x if (not obj.select): obj.select = True # 2.8x if (not obj.select_get()): obj.select_set(True)
詳細はselect_set(state, view_layer=None)を参照。
hide プロパティは hide_viewport に変更された。その方がほかの hide プロパティと一貫性があるためだ。
o = bpy.context.object # 2.7x o.hide = True o.hide_render = True o.hide_select = True # 2.8x o.hide_viewport = True o.hide_render = True o.hide_select = True
オブジェクトが可視状態であってもコレクションが不可視である場合には、可視状態のオブジェクトは見えないことに注意する必要がある。
オブジェクトの追加
2.7x ではオブジェクトはシーンにリンクしていた。2.8x ではコレクションにリンクする。ビューレイヤーとコレクションを参照。
scene = bpy.context.scene # 2.7x scene.objects.link(o) # 2.8x scene.collection.objects.link(o)
オブジェクトは複数のコレクションにリンクできる。しかし実態はひとつなので、複数あるコレクションの内のどれかひとつのオブジェクトが不可視になると、他のコレクション内の該当オブジェクトも不可視になる。これはコレクション自体を不可視にしても同じだ。
注意:2.8x では scene.objects のアクセスは読み出しのみに変更された。scene.objects でシーン内のインスタンス化されたオブジェクトすべてにアクセスできるが、編集はできない。
インスタンス化(Instancing)
Dupli Instances へのアクセス方法は完全に別物になった。Blender 2.7x ではひとつのオブジェクトから生成されたすべてのインスタンスにアクセスできた。
# 2.7x ob = context.object ob.dupli_list_create(scene, 'PREVIEW') for dup in ob.dupli_list: pass # dupli_list が有効な間はあらゆる操作が合法 # ... ob.dupli_list_clear() # これより後は dupli objects へのアクセスはエラー(invalid)
Blender 2.8x では depsgraph context の内部でのみインスタンスにアクセスできる。なのでインスタンスを作成する必要はなく、ループを使ってアクセスするだけだ。ただし depsgraph を取得する必要がある。
depsgraph = context.depsgraph for dup in depsgraph.object_instances: pass # dup を使って何かする。ただし参照を取得してはならない!
警告:取得した dupli のあらゆる参照(ID を含む)を保存してはならない。ループの外だけでなく、ループの次のアイテムへの持越しもよくない。そのデータは実行時に生成され、ループの次のアイテムを処理するさいに無効になる。さらに ID RNA ポインタは評価された(生成された)ものを参照するため、何らかの値(行列やベクトルなど)を保存する場合は、その値を複製する必要がある。
もうひとつの大きな違いがある。Blender 2.7x ではオブジェクトに dupli が紐づけられていたが、Blender 2.8x では current depsgraph に大量のインスタンスが格納されるようになった。大量のインスタンスの中に 'リアル' オブジェクトも含まれる。
# 2.8x depsgraph = context.depsgraph for dup in depsgraph.object_instances: if dup.is_instance: # リアル dupli インスタンス(実行時に生成されたオブジェクト) obj = dup.instance_object.original parent = dup.parent.original mat = dup.matrix_world.copy() else: # 普通のオブジェクト obj = dup.object.original # obj, parent, mat を使って何かする。これらの値はローカルコピーもしくはオリジナルデータブロックだ。
アクティブなオブジェクトの dupli のタプルが欲しい場合は、以下のようにする。
depsgraph = context.evaluated_depsgraph_get() for ob_inst in depsgraph.object_instances: if ob_inst.parent and ob_inst.parent.original == context.object: # ob_inst にアクセスする.
depsgraph は今のところ Context または ViewLayer から取得できる。しかし 'render' depsgraph を取得する方法がまだない。depsgraph の API はいまだに実装途中だ。
マルチオブジェクトモード
Blender 2.80 では複数のオブジェクトをエディットモードやポーズモードで同時に編集できるようになった。なのでオペレーターはアクティブなモードですべてのオブジェクトが更新しなければならない。
アクティブなモードを取得するには context.mode を使う。このモードでオブジェクトにアクセスするには以下の context プロパティが使える。
- context.objects_in_mode:このモードのすべてのオブジェクトを列挙する
- context.objects_in_mode_unique_data:複数のオブジェクトが同じデータを共有している場合、データは重複して列挙されない。一般的にはこちらを使ったほうが良い。
ベースとコンテクストオーバーライド
scene.object_bases が削除されたので、Python API から ObjectBase にアクセスできなくなった。なのでコンテクストをオーバーライドするさいに、いくつかのオペレーターのために以下のベースを設定する必要がなくなった。
- visible_bases
- selectable_bases
- editable_bases
- selected_editable_bases
- active_base
メッシュ API
メッシュテッセレーション
テッセレートされた面を取得する古い機能は API から削除された。それは n ゴンをサポートする前の、三角面と四角面しか扱えなかった時代の名残だ。
代わりに mesh polygons を使う。メッシュのテッセレートを必要とするエクスポーターやレンダラーは loop triangles が代わりに使える。
# 2.7x mesh.calc_tessface() for face in mesh.tessfaces: for vert_index in face.vertices: print(mesh.vertices[vert_index].co) # 2.8x mesh.calc_loop_triangles() for tri in mesh.loop_triangles: for vert_index in tri.vertices: print(mesh.vertices[vert_index].co)
Loop triangles はメッシュの三角分割のみをサポートしている。四角面が必要な場合は、同じポリゴンの連続した loop triangles を四角面として解釈できる。
UV と頂点色も loop triangles を介してアクセスするようになった。
# 2.7x for uv_layer in mesh.tessface_uv_textures: for face in mesh.tessfaces: face_uvs = uv_layer.data[face.index] for uv in face_uvs: print(uv) # 2.8x for uv_layer in mesh.uv_layers: for tri in mesh.loop_triangles: for loop_index in tri.loops: print(uv_layer.data[loop_index].uv)
BMesh
Operator Enumerators and Flags
Blender 2.7x では BMesh の operator enumerators/flags は整数で指定していた。Blender 2.8x では文字列で指定する。
# 2.7x bmesh.ops.mirror(bm, geom=verts, axis=1)
bmesh.ops.mirror(bm, geom=verts, axis='Y')
Skin Root Vertices の設定
API を使って Mesh skin vertex roots (スキンモディフィアの「ルートをマーク」)を設定できるようになった。
l = bm.verts.layers.skin.verify() v = bm.verts[0] v[l].use_root = True
それぞれの独立したメッシュ(mesh island)はルートをひとつしか持つべきではない。これは自動でチェックされないのでスクリプト側で処理する必要がある。
Preference API
ユーザー設定(User Preference)はプリファレンス(Preference)に改名された。Python API もそれに合わせて変更されている。
# 2.7x bpy.context.user_preferences # 2.8x bpy.context.preferences
残りのパラメータはBlender 2.80: Preferences API を参照。
ユーザーインターフェイス API
ツールバー
ツールバーはアクティブツールのみ配置するように変更された。ツールバーにツールを配置していたアドオンをアップデートする方法はいくつかある:
- サイドバーに配置する
- 新しいアクティブツールシステム用に書き直すと、ツールバーに配置できる
- コマンドのようなアドオンはメニューに配置できる。設定はポップアップウインドウで行う。これはエディターヘッダーメニューや W キーのコンテクストメニューに配置することもできる。
- モード特有の設定はツール設定パネルに追加することもできる。ツール設定パネルはトップバーやプロパティエディターツール設定タブにある。
ボタンとパネル
シングルカラムレイアウト
ほとんどの Blender のユーザーインターフェイスはシングルカラムレイアウトを使うように変更された。シングルカラムレイアウトを使うには以下のようにする。
def draw(self, context): layout = self.layout layout.use_property_split = True # シングルカラムレイアウトを有効にする view = context.scene.view_settings layout.prop(view, "view_transform") layout.prop(view, "look")
サブパネル
パネルはサブパネルを持てるようになった。サブパネルはコンテンツをまとめたり、あまり使わない機能や高度な機能を隠したりするのに使える。親パネルを参照する 'bl_parent_id' を追加することでサブパネルを作成できる。
# 親パネル class RENDER_PT_color_management(RenderButtonsPanel, Panel): bl_label = "Color Management" ... # サブパネル class RENDER_PT_color_maangement_curves(RenderButtonsPanel, Panel): bl_label = "Use Curves" bl_parent_id = "RENDER_PT_color_management" ...
アイコン
アイコンは変更点が多すぎる。変更リストはIconsを参照。
描画 API
OpenGL Core Profile
Blender 2.80 では OpenGL core profile を使う。これは Eevee やビューポートのレンダリングに必要な機能をすべて含んでいる。しかしこの OpenGL バージョンは、immediate モードや固定機能パイプラインといったよく使われる機能が削除されている。そのため簡単な描画機能のために多くの API 呼び出しが必要になる。
低レイヤーの bgl モジュールを直接使うのではなく、gpu モジュールの使用を推奨する。gpu モジュールは将来 Vulkan が採用されたときでも変更されにくい、抽象度の高い API を提供している。
gpu モジュールで提供されていないより低レイヤーの機能が必要な場合は、Getting Started の OpenGL core profile を移植する方法を参照。
GPU モジュール
GPU モジュールを使うにあたりバッチとシェーダオブジェクトとを理解することが重要だ。バッチは描画の単位だ。たいていはマテリアルひとつあたりバッチがひとつ必要になる。シェーダはジオメトリの描画方法をきめる。GLSL でシェーダを描くこともできるし、gpu モジュールのユーティリティシェーダを使うこともできる。
詳細やサンプルは GPU Shader Module (gpu) を参照
タイマー API
ユースケース:
- x 秒ごとに関数を実行する
- x 秒以内に関数を実行する
- 異なる間隔で同じ関数を複数回呼び出す
詳細は Application Timers (bpy.app.timers) を参照。
アニメーション&リグ API
PoseBone.bone の振る舞いの修正
PoseBone.bone プロパティは正しく id_data の結果を Armature ID にセットするようになった。以前はアーマチュアオブジェクトへのポインタを保持しているだけだった(1c106e18)。
# 2.7x >>> bpy.data.objects[0].pose.bones[0].bone bpy.data.objects['Armature'].pose.bones["Bone"].bone # 2.8x >>> bpy.data.objects[0].pose.bones[0].bone bpy.data.armatures['Armature'].bones["Bone"]
この結果として object.pose.[...].bone.driver_add() は object.animation_data ではなく object.data.animation_data にドライバーを設定するようになった。
ボーン行列ユーティリティ
ボーンとポーズボーンとに、ボーンロールと Bendy ボーンの形状との計算を補助するメソッドが追加された(a58f0eea)
- Bone.MatrixFromAxisRoll()
- Bone.AxisRollFromMatrix()
- pose_bone.bbone_segment_matrix()
- pose_bone.compute_bbone_handles()
ボーンにポーズ座標への変換行列を計算するメソッドが追加された(48a3f97b)
- bone.convert_local_to_pose()
キーフレームユーティリティ
anim_data.nla_tweak_strip_time_to_scene() で NLA ストリップとシーン時間(scene time)とを変換できる(27d097e9)。
object.keyframe_inser() で使えるフラグが追加された(ebc44aae)。
- INSERTKEY_REPLACE, INSERTKEY_AVAILABLE, INSERTKEY_CYCLE_AWARE
fcurve.is_empty プロパティが追加された。これを使うと、キーフレームや有用なモディフィアがないためにアニメーションに影響がないカーブを簡単に検出できる(b05038fe)。現在はそのようなカーブはアニメーションを評価するコードによって無視される。なので不要なカーブのクリーンアップは複雑な編集を行うオペレーターが終了するまで延期できる。そのオペレーターが現在のフレームを変更したりカーブの再評価を引き起こしたとしても特別な処理は不要だ。
そのほか
hook.vertex_indices, hook.vertex_indices_set() で、フックモディフィアのバインドされている頂点インデックス配列に直接アクセスできるようになった。(3f788eac)
object.shape_key_clear() でオブジェクトのすべてのシェイプキーを削除できるようになった。(c7ec6bb7)
from rna_prop_ui import rna_idprop_ui_create で、既定値・値制限・ UI 設定を一度に設定できるカスタムプロパティを作成できるようになった。(40dd9156)
ヘルパーモジュール
シェーダーノードのインポート/エクスポート
Blender レンダーが削除されたのですべてのマテリアルはノードベースになった。しかし外部ファイルのシェーダは大抵、固定されたパラメータを持っているだけだ。そのようなシェーダのインポート/エクスポートを補助するため bpy_extras.node_shader_utils が追加された。これは cycles_shader_compat の置き換えだ。
2.7x とは以下の点で違っている:
- インポートとエクスポートとをサポートする
- 大量のシンプルなシェーダの組み合わせでなく、プリンシプルシェーダを基本とする
- プリンシプルシェーダを使わないカスタムシェーダは無視される。その場合デフォルト値がエクスポートされる。
- クランプのような値の変換は行わない。変換や変形は個々のアドオンが行う
- アクセサを使うことで以下のような機能が実現できる
- 必要なノードの遅延作成
- シェーダをエクスポートする際に書き込みしてしまうことを防ぐ
コード例:
import bpy from bpy_extras.node_shader_utils import PrincipledBSDFWrapper from bpy_extras.image_utils import load_image # Import mat = bpy.data.materials.new(name="Material") mat.use_nodes = True principled = PrincipledBSDFWrapper(mat, is_readonly=False) principled.base_color = (0.8, 0.8, 0.5) principled.specular_texture.image = load_image("/path/to/image.png") # Export principled = PrincipledBSDFWrapper(mat, is_readonly=True) base_color = principled.base_color specular_texture = principled.specular_texture if specular_texture and specular_texture.image: specular_texture_filepath = principled.specular_texture.image.filepath
比較的シンプルな使用例として Blender 2.8x の OBJ のインポートエクスポートアドオンを参照するといい。この画像はフリーの OBJ 猫モデルをインポートしたときに生成されたノードマテリアルだ。
ノイズ
Noise and Metric Enumerators
Blender 2.7x では Enumerator はモジュールレベルの定数だった。Blender 2.8x では文字列になる。
# 2.7x noise.noise(position, noise_basis=noise.types.BLENDER)
noise.noise(position, noise_basis='BLENDER')
ノイズ型名
ノイズ型名は以下の文字列に変更された。
2.7x | 2.8x |
noise.types.BLENDER | 'BLENDER' |
noise.types.STDPERLIN | 'PERLIN_ORIGINAL' |
noise.types.NEWPERLIN | 'PERLIN_NEW' |
noise.types.VORONOI_F1 | 'VORONOI_F1' |
noise.types.VORONOI_F2 | 'VORONOI_F2' |
noise.types.VORONOI_F3 | 'VORONOI_F3' |
noise.types.VORONOI_F4 | 'VORONOI_F4' |
noise.types.VORONOI_F2F1 | 'VORONOI_F2F1' |
noise.types.VORONOI_CRACKLE | 'VORONOI_CRACKLE' |
noise.types.CELLNOISE | 'CELLNOISE' |
座標系(Orientation)
既定のカスタム座標系(orientation)をもつ基底クラスを作る orientatioin_helper_factory は削除された。代わりにより Python 風な orientation_helper デコレーターが追加されている。
# 2.7x from bpy_extras.io_utils import ( ImportHelper, orientation_helper_factory, ) IOFBXOrientationHelper = orientation_helper_factory("IOFBXOrientationHelper", axis_forward='-Z', axis_up='Y') class ImportFBX(bpy.types.Operator, ImportHelper, IOFBXOrientationHelper): pass
from bpy_extras.io_utils import (
ImportHelper,
orientation_helper,
)
@orientation_helper(axis_forward='-Z', axis_up='Y')
class ImportFBX(bpy.types.Operator, ImportHelper):
pass
削除されたモジュール
extensions_framework モジュールは削除された。
リネーム
いくつかの用語はユーザーインターフェイス上で変更された。
- ランプ -> ライト
- グループ -> コレクション
- デュプリ -> インスタンス
- レンダーレイヤー -> ビューレイヤー
- ユーザー設定 -> プリファレンス
キーワード強制引数(Keyword Only Arguments)
キーワード引数はキーワードが必要となり、引数の位置から推測させることができなくなった。
# 2.7x context.scene.frame_set(10, 0.25) noise.noise(position, noise.types.BLENDER)
context.scene.frame_set(10, subframe=0.25)
noise.noise(position, noise_basis='BLENDER')
行列の乗算
行列の乗算には "*" が使われていたが、Blender 2.8x からは "@" を使う(PEP 465)。これは以下の組み合わせに適用される。
- Vector * Vector
- Quaternion * Vector
- Matrix * Vector
- Vector * Matrix
- Matrix * Matrix
"*" 演算子はアダマール積(要素ごとの乗算)に割り当てられる予定だ。
# 2.7x mat = Matrix() vec = Vector() result = mat * vec
mat = Matrix()
vec = Vector()
result = mat @ vec
Blender 2.80 以降で 2.7x の記法を使うと以下の警告が出る。
TypeError: Element-wise multiplication: not supported between 'xxx' and 'yyy' types
テキストのインポート
モジュールとして Blender テキストデータをインポートする能力は削除された(dc8dd24351)。モジュールとしてテキストデータブロックを取得したければ as_module() が使える。
my_module = bpy.data.text["my_module"].as_module()
オペレーターアップデート
select_by_layer は削除され view3d.viewnumpad(view3d.view_axid, view3d.view_camera) に置き換えられた。
プロパティアップデート
SpaceView3D.viewport_shader は SpaceView3D.shading.type に置き換えられた。
そのほかの変更
percentage キーワードは factor に改名された。
# 2.7x box.split(percentage=0.6, align=False) # 2.8x box.split(factor=0.6, align=False)
アクティブオブジェクトの設定は view_layer から行う
# 2.7x bpy.context.scene.objects.active = bpy.data.objects['Cube'] # 2.8x bpy.context.view_layer.objects.active = bpy.data.objects['Cube']
Blender のバージョンの比較
文字列としてバージョンを取得
bpy.app.version_string
バージョンの比較(2.80 より古いバージョンの検出)
if (2, 80, 0) > bpy.app.version: print("This version is older than 2.80.")
オブジェクトの選択とアクティブ化
o = 何らかのオブジェクトの取得 bpy.context.view_layer.objects.active = o o.select_set(True) bpy.context.view_layer.update() # ループでオブジェクトを取得する場合前回のオブジェクトの選択を解除 old_object.select_set(False)
外部リンク
Process/Addons/Guidelines/UpdatingScripts
Blender 2.8x / Python, Proposed Changes
2.80 Cheat Sheet for updating add-ons