MRが楽しい

MRやVRについて学習したことを書き残す

Blenderのpythonテンプレートを読み解く その2(background_job.py)

本日は Blender の小ネタ枠です。
Blenderpythonテンプレートを読み解いて、どんな機能を持つテンプレートなのか確認します。
今回は「background_job」テンプレートです。
f:id:bluebirdofoz:20200619002326j:plain

pythonテンプレートの利用方法

[テキストエディタ]ビューを開き、メニューから テンプレート -> python を開きます。
様々な機能を持った python コードのテンプレートが利用できます。
f:id:bluebirdofoz:20200619002336j:plain

background_job.py

スクリプトコマンドライン実行のサンプルスクリプトです。
テキストオブジェクト、カメラ、ライトを作成して画像やプロジェクトを保存します。
利用の際はテンプレートをスクリプトファイルとして保存して、コマンドラインから以下のように実行します。

blender --background --factory-startup --python $HOME/background_job.py -- --text="Hello World" --render="/tmp/hello" --save="/tmp/hello.blend

f:id:bluebirdofoz:20200619002347j:plain

    • text 引数でテキストオブジェクトの文字列を指定します。本引数は必須です。

更に --render 引数と --save 引数を指定して処理を実行すると、レンダリング画像とプロジェクトファイルが出力されます。
f:id:bluebirdofoz:20200619002359j:plain

以下はテンプレートの内容にコメントを追加したものです。
・background_job.py

# このスクリプトは、コマンドラインから Blender を実行する方法の例です。
# タスクを自動化する参考例になります。
# この例ではテキストオブジェクト、カメラ、ライトを作成して画像やプロジェクトを保存します。
# また、コマンドライン引数をスクリプトで解析する方法も示しています。
#
# 例えば以下のように実行します。
#   blender --background --factory-startup --python $HOME/background_job.py -- \
#          --text="Hello World" \
#          --render="/tmp/hello" \
#          --save="/tmp/hello.blend"
#
# メモ:
# '--factory-startup' 
#   '--factory-startup'はユーザーのデフォルト設定がシーン生成を妨げないために使用されます。
#
# '--' causes blender to ignore all following arguments so python can use them.
#   pythonが'--'以降の引数を使用できるように、Blender に'--'以降の引数を全て無視させます。
#
# 詳細については blender --help を参照してください。
#
# This script is an example of how you can run blender from the command line
# (in background mode with no interface) to automate tasks, in this example it
# creates a text object, camera and light, then renders and/or saves it.
# This example also shows how you can parse command line options to scripts.
#
# Example usage for this test.
#  blender --background --factory-startup --python $HOME/background_job.py -- \
#          --text="Hello World" \
#          --render="/tmp/hello" \
#          --save="/tmp/hello.blend"
#
# Notice:
# '--factory-startup' is used to avoid the user default settings from
#                     interfering with automated scene generation.
#
# '--' causes blender to ignore all following arguments so python can use them.
#
# See blender --help for details.


import bpy


# バックグラウンド実行処理
def example_function(text, save_path, render_path):
    # オブジェクトを削除する
    # Clear existing objects.
    bpy.ops.wm.read_factory_settings(use_empty=True)

    # 現在のシーンの参照を取得する
    scene = bpy.context.scene

    # フォントデータを作成する
    txt_data = bpy.data.curves.new(name="MyText", type='FONT')

    # Text Object
    # 作成したフォントデータをオブジェクトとして作成する
    txt_ob = bpy.data.objects.new(name="MyText", object_data=txt_data)
    # 作成したオブジェクトをシーンに追加する
    scene.collection.objects.link(txt_ob)   # add the data to the scene as an object
    # コマンドライン引数で指定された文字列を指定する
    txt_data.body = text         # the body text to the command line arg given
    # 文字列の配置を中央寄せに変更する
    txt_data.align_x = 'CENTER'  # center text

    # Camera
    # カメラデータを作成する
    cam_data = bpy.data.cameras.new("MyCam")
    # 作成したカメラデータをオブジェクトとして作成する
    cam_ob = bpy.data.objects.new(name="MyCam", object_data=cam_data)
    # 作成したオブジェクトをシーンに追加する
    scene.collection.objects.link(cam_ob)  # instance the camera object in the scene
    # シーンのアクティブカメラとして作成したカメラを指定する
    scene.camera = cam_ob       # set the active camera
    # カメラ位置を(0.0, 0.0, 10.0)の位置に設定する
    cam_ob.location = 0.0, 0.0, 10.0

    # Light
    # ライトデータを作成する
    light_data = bpy.data.lights.new("MyLight", 'POINT')
    # 作成したライトデータをオブジェクトとして作成する
    light_ob = bpy.data.objects.new(name="MyLight", object_data=light_data)
    # 作成したオブジェクトをシーンに追加する
    scene.collection.objects.link(light_ob)
    # ライト位置を(2.0, 2.0, 5.0)の位置に設定する
    light_ob.location = 2.0, 2.0, 5.0

    # 状態を更新する
    bpy.context.view_layer.update()

    # save引数でプロジェクトの出力パスが指定されているか確認する
    if save_path:
        # 出力パスにプロジェクトを保存する
        bpy.ops.wm.save_as_mainfile(filepath=save_path)

    # render引数でレンダリング画像の出力パスが指定されているか確認する
    if render_path:
        # 出力パスにレンダリング画像を保存する
        render = scene.render
        render.use_file_extension = True
        render.filepath = render_path
        bpy.ops.render.render(write_still=True)


# メイン関数
# コマンドライン引数の解析を実施する
def main():
    # 各種インポート
    # コマンドライン引数取得のため、sys をインポートする
    import sys       # to get command line args
    # オプションを解析してヘルプメッセージを出力するため、argparse をインポートする
    import argparse  # to parse options for us and print a nice help message

    # コマンドライン引数の"--"の後に Blender に渡される引数を取得します。
    # "--"以降の引数は Blender に無視されるため、スクリプトは独自の引数を受け取ることができます。
    # get the args passed to blender after "--", all of which are ignored by
    # blender so scripts may receive their own arguments
    argv = sys.argv

    # "--"引数の存在を確認する
    if "--" not in argv:
        # "--"引数が存在しない場合、引数は渡されなかったものとする。
        argv = []  # as if no args are passed
    else:
        # "--"引数が存在する場合、"--"引数の後の引数を取得する。
        argv = argv[argv.index("--") + 1:]  # get all args after "--"

    # --help 引数が指定されている場合、または引数が指定されていない場合、このヘルプを出力します
    # When --help or no args are given, print this help
    usage_text = (
        "Run blender in background mode with this script:"
        "  blender --background --python " + __file__ + " -- [options]"
    )

    # 引数解析(argparse)クラスの作成
    parser = argparse.ArgumentParser(description=usage_text)

    # オプションの利用例:--text、--render、--save
    # 利用可能なタイプ:string、int、long、choice、float、comple
    # Example utility, add some text and renders or saves it (with options)
    # Possible types are: string, int, long, choice, float and complex.

    # --text引数の定義
    parser.add_argument(
        "-t", "--text", dest="text", type=str, required=True,
        help="This text will be used to render an image",
    )
    # --save引数の定義
    parser.add_argument(
        "-s", "--save", dest="save_path", metavar='FILE',
        help="Save the generated file to the specified path",
    )
    # --render引数の定義
    parser.add_argument(
        "-r", "--render", dest="render_path", metavar='FILE',
        help="Render an image to the specified path",
    )

    # 引数解析を行う
    args = parser.parse_args(argv)  # In this example we won't use the args

    # 引数の存在確認を行う
    if not argv:
        # 引数が存在しない場合はヘルプメッセージを表示して処理を行わない
        parser.print_help()
        return

    # text引数の存在確認を行う
    if not args.text:
        # text引数が指定されていない場合はヘルプメッセージを表示して処理を行わない
        print("Error: --text=\"some string\" argument not given, aborting.")
        parser.print_help()
        return

    # 処理を実行する
    # Run the example function
    example_function(args.text, args.save_path, args.render_path)

    # 完了メッセージを表示する
    print("batch job finished, exiting")

# スクリプト実行時の処理
if __name__ == "__main__":
    # メイン関数の呼び出し
    main()