ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【Blender/python】Blenderの自作アドオンの作り方(その3)

rikoubou.hatenablog.com

 前回はアドオンの保存場所と2通りのデバッグ方法を確認しました。
 今回はいよいよアドオンの中身について触れていきます。また自分が作成したアドオンのソースコードについても紹介しつつ説明していきたいと思います。


1:アドオンの基本的な構成
 より詳しい説明は参考にさせていただいた以下のページを参照してください。
https://nutti.gitbooks.io/introduction-to-add-on-development-in-blender/content/body/chapter_02/01_Basic_of_Add-on_Development.html

 アドオンを自作する時の基本的な構成は以下のようになります。

・example.py

import bpy # ①BlenderのpythonAPI

# ②自作アドオンについての説明
bl_info = {
    "name": "sample: add-onTest",
    "author": "ssss",
    "version": (2, 0),
    "blender": (2, 79, 0),
    "location": "location",
    "description": "sample",
    "warning": "warning",
    "support": "TESTING",
    "wiki_url": "http://hogehoge.com",
    "tracker_url": "http://hogehoge2.com",
    "category": "Object"
}

# ③追加したいアドオンのクラス
class AddSphere(bpy.types.Operator):

    bl_idname = "object.add_shhere_object" # ID
    bl_label = "Sphere" # メニューに追加されるラベル
    bl_description = "Add Sphere" # 関数の説明
    bl_options = {'REGISTER', 'UNDO'} # 処理の属性

    # ④実際の処理
    def execute(self, context):
        bpy.ops.mesh.primitive_ico_sphere_add()
        print("Add Sphere End") # コンソールにログ出力
        self.report({'INFO'}, "Add Sphere End") # ⑤Blenderのログに出力
        return {'FINISHED'}

# ⑥メニューを構築する関数
def menu_fn(self, context):
    self.layout.separator()
    self.layout.operator(AddSphere.bl_idname)


# ⑦アドオン有効化時の処理
def register():
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_object.append(menu_fn)
    print("Add-on:ON")


# ⑧アドオン無効化時の処理
def unregister():
    bpy.types.VIEW3D_MT_object.remove(menu_fn)
    bpy.utils.unregister_module(__name__)
    print("Add-on:OFF")

# ⑨メイン関数
if __name__ == "__main__":
    register()

 ではソースコードのそれぞれの解説です。

BlenderのpythonAPI

import bpy # ①BlenderのpythonAPI

 これはBlenderpython APIを読み込んで使えるようにしています。この一文がなければBlenderの機能をpythonを使って制御できないので必須になります。

②自作アドオンについての説明

# ②自作アドオンについての説明
bl_info = {
    "name": "sample: add-onTest",
    "author": "ssss",
    "version": (2, 0),
    "blender": (2, 79, 0),
    "location": "location",
    "description": "sample",
    "warning": "warning",
    "support": "TESTING",
    "wiki_url": "http://hogehoge.com",
    "tracker_url": "http://hogehoge2.com",
    "category": "Object"
}

 これは「User Preferences」画面のアドオンに表示される各情報の記述です。各キーの内容は以下のようになります。

キー 内容
name 文字列 アドオンの名前
author 文字列 アドオンの作者名
version タプル アドオンのバージョン
blender タプル アドオンが動作する最古のBlenderバージョン
location 文字列 アドオンが提供する機能が存在する場所
description 文字列 アドオンの説明
warning 文字列 アドオン使用時の注意点やバグ情報
support 文字列 アドオンのサポートレベル
wiki_url 文字列 アドオンに関するドキュメントURL
tracker_url 文字列 アドオンのサポートサイトURL
category 文字列 アドオンのカテゴリー

 それぞれがAdd-onsの画面とどう対応しているかは以下を参照してください。
f:id:rikoubou:20180419091726p:plain

③追加したいアドオンのクラス

# ③追加したいアドオンのクラス
class AddSphere(bpy.types.Operator):

    bl_idname = "object.add_shhere_object" # ID
    bl_label = "Sphere" # メニューに追加されるラベル
    bl_description = "Add Sphere" # 関数の説明
    bl_options = {'REGISTER', 'UNDO'} #処理の属性

    # 実際の処理を行う関数
    def execute(self, context):
        # 処理を記述する
        return {'FINISHED'}

 追加したいアドオンのオペレータクラスです。Blenderでは何かしらの処理をするのにオベレータクラスを作成して処理を記述します。
 実際の処理を行う関数の前にクラス変数を定義しています。このクラス変数は以下のような内容です。

変数名 内容
bl_idname 文字列 Blender内部で扱うID。(カテゴリ).(任意の文字列)という形式。※大文字は使えないようなので注意!
bl_label 文字列 メニューに追加されるラベル
bl_description 文字列 関数の説明
bl_options 集合型 処理の属性

 それぞれがどこに対応しているかは以下の画像を参照してください。
f:id:rikoubou:20180419093106p:plain

④実際の処理

    # ④実際の処理
    def execute(self, context):
        bpy.ops.mesh.primitive_ico_sphere_add()
        print("Add Sphere End") # コンソールにログ出力
        self.report({'INFO'}, "Add Sphere End")
        return {'FINISHED'}

 ③のクラス内に「def execute(self, context)」関数を作成してそこにBlenderで行いたい処理を記述します。この関数の引数は以下のようになっています。

引数 説明
self 呼びだされた execute() メソッドを定義しているクラス オペレータクラスのインスタンス
context bpy_types.Context execute() メソッド実行時のコンテキスト

「bpy.ops.mesh.primitive_ico_sphere_add()」という一行で球体を作成しています。他にどういう処理ができるかはBlenderのpythonAPIを参照してください。

 関数の最後は「return {'FINISHED'}」と処理を確定させる値を返しています。詳しい説明は冒頭で上げた参考資料のページを参照してください。
 またprint関数を記述することによりコンソールに表示させることができます。

Blenderのログに出力

self.report({'INFO'}, "Add Sphere End") # ⑤Blenderのログに出力

 この記述で前回説明したBlenderのコンソールに情報を表示させることができます。

⑥メニューを構築する関数

# ⑥メニューを構築する関数
def menu_fn(self, context):
    self.layout.separator()
    self.layout.operator(AddSphere.bl_idname)

 最初の2行は定型文で「self.layout.operator(クラス名.bl_idname)」でオベレータクラスで作成したメニューを追加します。

⑦アドオン有効化時の処理

# ⑦アドオン有効化時の処理
def register():
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_object.append(menu_fn)
    print("Add-on:ON")

「bpy.utils.register_module」はBlender内で引数のモジュールを使えるようにするための関数です。メイン関数である「__name__」を指定しています。
 2行目の「bpy.types.VIEW3D_MT_object.append」で引数のものをメニューに追加をしています。引数にメニューを追加する「menu_fn」関数を指定しています。
 ここでの「VIEW3D_MT_object」は以下の画像のところに追加するという意味です。詳しい内容はBlenderのpythonAPIから調べてください。
f:id:rikoubou:20180419094656p:plain
 最後にprint関数でアドオンが有効になったことをコンソールに表示させています。

⑧アドオン無効化時の処理

# ⑧アドオン無効化時の処理
def unregister():
    bpy.types.VIEW3D_MT_object.remove(menu_fn)
    bpy.utils.unregister_module(__name__)
    print("Add-on:OFF")

「bpy.types.VIEW3D_MT_object.remove」で追加したメニューを削除しています。
 2行目でBlenderでモジュールを使えないようにしています。
 最後にprint関数でアドオンが無効になったことをコンソールに表示させています。

⑨メイン関数

# ⑨メイン関数
if __name__ == "__main__":
    register()

 メイン関数です。定型文だと思ってつけておいてください。

 以上が基本的なアドオンの構成になります。


■今回作成したアドオン
 アドオンの説明が終わったのでようやく今回自作したアドオンの紹介です。今までの説明からソースコードの内容はなんとなくわかると思います。

 今回作成したアドオンの内容としてはBlenderの以下の部分(目のアイコンもしくはカメラのアイコン)をまとめてキーフレームに登録したり削除したりする」というものです。
f:id:rikoubou:20180419013446p:plain

 作成したアドオンのソースコードは以下の通りです。

・insertDeleteHideAndRender.py

# 選択されているオブジェクトの表示/非表示、レンダー時の表示/非表示を
# まとめてキーフレームに登録するためのアドオン

import bpy # BlenderのpythonAPI

bl_info = {
    "name": "Insert/Delete hide and render", # アドオン一覧に表示される名前
    "author": "whip",  # 作者
    "version": (1, 0), # アドオンのバージョン
    "blender": (2, 79, 0), # 対応するBlenderのバージョン
    "location": "",
    "description": "Insert/Delete keyframe of hide or render", # アドオンの説明
    "warning": "",
    "support": "TESTING", # アドオンの分類
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object" # カテゴリー
}

# Restrict renderingの項目をキーフレームに登録するクラス
class insertRestrictRendering(bpy.types.Operator):
    bl_idname = "object.insert_restrict_rendering" # ID
    bl_label = "Insert Restrict rendering" # メニューに表示される名前
    bl_description = "Insert keyframe of Restrict rendering of selected objects" # 関数の説明
    bl_options = {'REGISTER', 'UNDO'} # オブション

    # 実際の処理
    def execute(self, context):
        selected_objs = context.selected_objects # 選択されているオブジェクトを全て取得
        currentFrame = bpy.context.scene.frame_current # 現在のキーフレームを取得
        self.report({'INFO'}, "frame:%s" % currentFrame) # Blenderのログに出力
        # 取得したオブジェクト分繰り返す
        for obj in selected_objs:
            obj.keyframe_insert('hide_render',frame=currentFrame) # レンダー時の表示/非表示を登録
            self.report({'INFO'}, "%s render:%s" % (obj.name, obj.hide_render)) # Blenderのログに出力
        return {'FINISHED'}

# Restrict renderingの項目をキーフレームから削除するクラス
class deleteRestrictRendering(bpy.types.Operator):
    bl_idname = "object.delete_restrict_rendering" # ID
    bl_label = "Delete Restrict rendering" # メニューに表示される名前
    bl_description = "Delete keyframe of Restrict rendering of selected objects" # 関数の説明
    bl_options = {'REGISTER', 'UNDO'} # オブション

    # 実際の処理
    def execute(self, context):
        selected_objs = context.selected_objects # 選択されているオブジェクトを全て取得
        currentFrame = bpy.context.scene.frame_current # 現在のキーフレームを取得
        # 取得したオブジェクト分繰り返す
        for obj in selected_objs:
            # 登録されているかの判定
            if obj.animation_data is not None and obj.animation_data.action is not None:
                obj.keyframe_delete('hide_render',frame=currentFrame) # レンダー時の表示/非表示を削除
        self.report({'INFO'}, "frame:%s render deleted" % currentFrame) # Blenderのログに出力
        return {'FINISHED'}

# Visibllityの項目をキーフレームに登録するクラス
class insertVisibllity(bpy.types.Operator):
    bl_idname = "object.insert_visibllity" # ID
    bl_label = "Insert Visibllity" # メニューに表示される名前
    bl_description = "Insert keyframe of Visibllity of selected objects" # 関数の説明
    bl_options = {'REGISTER', 'UNDO'} # オブション

    # 実際の処理
    def execute(self, context):
        selected_objs = context.selected_objects # 選択されているオブジェクトを全て取得
        currentFrame = bpy.context.scene.frame_current # 現在のキーフレームを取得
        self.report({'INFO'}, "frame:%s" % currentFrame) # Blenderのログに出力
        # 取得したオブジェクト分繰り返す
        for obj in selected_objs:
            obj.keyframe_insert('hide',frame=currentFrame) # 表示/非表示を登録
            self.report({'INFO'}, "%s hide:%s" % (obj.name, obj.hide)) # Blenderのログに出力
        return {'FINISHED'}

# Visibllityの項目をキーフレームから削除するクラス
class deleteVisibllity(bpy.types.Operator):
    bl_idname = "object.delete_visibllity" # ID
    bl_label = "Delete Visibllity" # メニューに表示される名前
    bl_description = "Delete keyframe of Visibllity of selected objects" # 関数の説明
    bl_options = {'REGISTER', 'UNDO'} # オブション

    def execute(self, context):
        selected_objs = context.selected_objects # 選択されているオブジェクトを全て取得
        currentFrame = bpy.context.scene.frame_current # 現在のキーフレームを取得
        # 取得したオブジェクト分繰り返す
        for obj in selected_objs:
            # 登録されているかの判定
            if obj.animation_data is not None and obj.animation_data.action is not None:
                obj.keyframe_delete('hide',frame=currentFrame) # 表示/非表示を削除
        self.report({'INFO'}, "frame:%s hide deleted" % currentFrame) # Blenderのログに出力
        return {'FINISHED'}

# メニューに追加する関数
def menu_fn(self, context):
    self.layout.separator()

    # 先に追加されたものが下にくる
    self.layout.operator(deleteVisibllity.bl_idname)
    self.layout.operator(insertVisibllity.bl_idname)
    self.layout.operator(deleteRestrictRendering.bl_idname)
    self.layout.operator(insertRestrictRendering.bl_idname)

# アドオンが有効化された時の関数
def register():
    bpy.utils.register_module(__name__) # 
    bpy.types.VIEW3D_MT_object.append(menu_fn)
    print("Insert/Delete hide and render:Available") # コンソールにログを出力

# アドオンが無効化された時の関数
def unregister():
    bpy.types.VIEW3D_MT_object.remove(menu_fn)
    bpy.utils.unregister_module(__name__)
    print("Insert/Delete hide and render:Anavailable") # コンソールにログを出力

# メイン関数
if __name__ == "__main__":
    register()

 使い方としては、オブジェクトを複数選択した状態で「Object」→「Insert Restrict rendering」を選択すると、選択したオブジェクトのレンダー時の表示/非表示を現在のキーフレームに登録することができます。
 同じようにオブジェクトを複数選択した状態で「Object」→「Insert Visibllity」を選択すると、選択したオブジェクトの画面上の表示/非表示を現在のキーフレームに登録することができます。
 またキーフレームに登録したものをまとめて削除する「Delete Restrict rendering」、「Delete Visibllity」もあります。

 このアドオンをBlenderに読み込ませて実際に使うと以下のようになります。


 以上がBlenderの自作アドオンを作ってみた備忘録です。
 pythonもそんなに書いたことない上に初めてBlenderのアドオンを作ってみたのでわからないことだらけでした。

 けれど今回やってみたことでBlenderのアドオンを作成する方法がわかったので「ちょっとこの処理を自動化したい」と思った場合には気軽に作れるようになったと思います。(作るのが楽とは言っていない)

 もしかしたら今回やろうとしていたことがBlenderのショートカットキーですでに実装されているのであれば、操作方法を教えていただきたいです。

 長々となってしまいましたが、自作アドオンの作り方についての記事はこれで終わりです。ありがとうございました。


・参考資料