広告
広告

Blender 2.79 のスクリプトを 2.80 にアップデートする

カテゴリ:blender
cc4.0

この記事は 2018 年 12 月 01 日時点でのReference/Release Notes/2.80/Python APIの翻訳だ。

目次

Blender 2.80 における Python API の変更

アドオン API

シーンとオブジェクト API

メッシュ API

ユーザーインターフェイス API

描画 API

タイマー API

ヘルパーモジュール

キーワード強制引数(Keyword Only Arguments)

行列の乗算

オペレーターアップデート

プロパティアップデート

Blender 2.80 における Python API の変更

Blender 2.80 は過去バージョンの Python API と互換性がない。アドオンやスクリプトを 2.80 で実行するにはアップデートが必要になる。

注意: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 のコードで広く使われているものだ)。この命名規則はクラスの bl_idname に適用される。クラス内で bl_idname を定義しない場合はクラス名にも適用される。

その命名規則はわかりにくいが [大文字アルファベット][大文字アルファベット 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_

いくつか有効な bl_idname の例をあげる。

  • OBJECT_OT_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()
# 2.8x
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 ではオブジェクトの 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 のタプルが欲しい場合は、以下のようにする。

(dup for dup in context.depsgraph.object_instances if dup.parent and dup.parent.original == context.object)

depsgraph は今のところ Context または ViewLayer から取得できる。しかし 'render' depsgraph を取得する方法がまだない。depsgraph の API はいまだに実装途中だ。

メッシュ 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)
# 2.8x
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)はルートをひとつしか持つべきではない。これは自動でチェックされないのでスクリプト側で処理する必要がある。

ユーザーインターフェイス 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) を参照。

ヘルパーモジュール

シェーダーノードのインポート/エクスポート

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)
# 2.8x
noise.noise(position, noise_basis='BLENDER')

ノイズ型名

ノイズ型名は以下の文字列に変更された。

2.7x2.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
# 2.8x
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)
# 2.8x
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
# 2.8x
mat = Matrix()
vec = Vector()
result = mat @ vec

Blender 2.80 以降で 2.7x の記法を使うと以下の警告が出る。

TypeError: Element-wise multiplication: not supported between 'xxx' and 'yyy' types

オペレーターアップデート

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']

関連記事

Process/Addons/Guidelines/UpdatingScripts

Blender 2.8 API Changes

Blender 2.8x / Python, Proposed Changes

Blender 記事の目次


広告
広告