LoginSignup
28
20

More than 1 year has passed since last update.

BlenderのPythonスクリプトのコードが2.80で動かなかった時に見る記事(2021.01.26改稿)

Last updated at Posted at 2019-11-01

※頂いたコメントを元にソースコードを改良しました(2021.01.26)※

 おはPython! 皆様、今日も元気にモデリングしてますか!

 私はといえば、BlenderをPythonスクリプトで動かそうとしたところ、謎のエラーに苦しめられていました。悪戦苦闘した結果、その原因はAPIの仕様変更にあることがわかりました。現在ネット上に公開されているコード(≒2.79以前用)はほとんどコピペしただけでは動かないと考えてよいでしょう。

 そこで、現在公開されているコードについて、ここをこうすれば2.80でも動くよ! という形を直したものを公開しようと思います。変更前と変更後のコードを並べた後で、変更があった個所には該当リファレンスを参照してこのように変更されていますよ、という形で解説していこうと思います。キーとなる変更ポイントはどのコードでもだいたい同じなので、おそらく他のコードでエラーに見舞われた人の役にも立つかと思われます。

 コードを書いてくださった方々は何も悪くないのですが、仕様変更があったので仕方がありません。

第1例

Blender × Pythonでお気楽3DCG!

image.png

 以上のようなメッシュを作成するスクリプトです。

コード

オリジナル

2.79

import bpy
import math

#reset objects
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(True)

#world
bpy.context.scene.world.horizon_color=(0.0,0.0,0.0)

#plane_add
for i in range(0,100):
    bpy.ops.mesh.primitive_plane_add(radius = (i*1.1/100),location=(0,0,0),rotation=(math.pi*1/2,math.pi*i*8.2/360,math.pi*i*10/360))

for item in bpy.context.scene.objects:
    if item.type == 'MESH':
        bpy.context.scene.objects.active = bpy.data.objects[item.name]
        bpy.ops.object.modifier_add(type='WIREFRAME')
        bpy.context.object.modifiers['Wireframe'].thickness = 0.0025
        bpy.context.object.modifiers['Wireframe'].use_boundary = True

#lamp add
bpy.ops.object.lamp_add(type='HEMI',location=(0.0,0.0,2.0))

#camera add
bpy.ops.object.camera_add(location=(5.0,0.0,0.0))
bpy.data.objects['Camera'].rotation_euler = (math.pi*1/2, 0, math.pi*1/2)

#render
bpy.context.scene.render.resolution_x = 1000
bpy.context.scene.render.resolution_y = 1000
bpy.context.scene.render.resolution_percentage = 100
bpy.context.scene.camera = bpy.context.object
bpy.context.scene.render.image_settings.file_format = 'PNG'
bpy.data.scenes["Scene"].render.filepath = "tmp/plane.png"
bpy.ops.render.render(write_still=True)

変更後 ※2020.01.26 改稿※

2.80
import bpy
import math

#reset objects
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(True)

#world
bpy.context.scene.world.node_tree.nodes["Background"].inputs["Color"].default_value = (0,0,0,1)

#plane_add
for i in range(0,100):
    bpy.ops.mesh.primitive_plane_add(size = (i*1.1/100),location=(0,0,0),rotation=(math.pi*1/2,math.pi*i*8.2/360,math.pi*i*10/360))

for item in bpy.context.scene.objects:
    if item.type == 'MESH':
        bpy.context.view_layer.objects.active = bpy.data.objects[item.name]
        bpy.ops.object.modifier_add(type='WIREFRAME')
        bpy.context.object.modifiers['Wireframe'].thickness = 0.0025
        bpy.context.object.modifiers['Wireframe'].use_boundary = True

#lamp add
bpy.ops.object.light_add(location=(0.0,0.0,2.0))

#camera add
bpy.ops.object.camera_add(location=(5.0,0.0,0.0))
bpy.data.objects['Camera'].rotation_euler = (math.pi*1/2, 0, math.pi*1/2)

#render
bpy.context.scene.render.resolution_x = 1000
bpy.context.scene.render.resolution_y = 1000
bpy.context.scene.render.resolution_percentage = 100
bpy.context.scene.camera = bpy.context.object
bpy.context.scene.render.image_settings.file_format = 'PNG'
bpy.data.scenes["Scene"].render.filepath = "tmp/plane.png"
bpy.ops.render.render(write_still=True)

変更箇所

2.79
    bpy.ops.mesh.primitive_plane_add(radius = (i*1.1/100),location=(0,0,0),rotation=(math.pi*1/2,math.pi*i*8.2/360,math.pi*i*10/360))
2.80
    bpy.ops.mesh.primitive_plane_add(size = (i*1.1/100),location=(0,0,0),rotation=(math.pi*1/2,math.pi*i*8.2/360,math.pi*i*10/360))

 平面のプリミティブを追加。スケールを示すradiussizeに。

該当リファレンス:https://docs.blender.org/api/current/bpy.ops.mesh.html?highlight=primitive%20plane#bpy.ops.mesh.primitive_plane_add

image.png

2.79
        bpy.context.scene.objects.active = bpy.data.objects[item.name]
2.80
        bpy.context.view_layer.objects.active = bpy.data.objects[item.name]

 アクティブなオブジェクトを選択。オリジナルではbpy.context.scene~となっていた部分をbpy.context.view_layer~とする必要があります。sceneのAPIも残っているのでどういう使い分けかは正直よくわかりませんが、ここら辺はリファレンスのこのあたりを読んで勉強するしかなさそうです。

2.79
bpy.ops.object.lamp_add(type='HEMI',location=(0.0,0.0,2.0))
2.80
bpy.ops.object.light_add(location=(0.0,0.0,2.0))

 ランプの追加。lampの名前をlightに変更。またHEMI(半球)タイプのライトも消滅。

image.png

2.79
#world
bpy.context.scene.world.horizon_color=(0.0,0.0,0.0)
2.80
#world
bpy.context.scene.world.node_tree.nodes["Background"].inputs["Color"].default_value = (0,0,0,1)

 これについては代替APIが見つかりませんでした。GUIでは

image.png

 ここを操作することで変更できます。一応bpy.context.scene.world.colorというAPIがあるのですが、ここを指定しても変わらず。ここは力及ばずです。再現実装したい人は手動で変更してください。すいません……。

 (2021.01.26追記)コメントで正しいAPIを教えていただきました。(追記終)

第2例

blenderのpythonスクリプト入門してみた_その01

image.png

 以上のようなメッシュを、プリミティブを使わずに生成するスクリプトです。

ソースコード

オリジナル

2.79
import bpy

# デフォルトのCubeを削除
def delete_all():
    for item in bpy.context.scene.objects:
        bpy.context.scene.objects.unlink(item)

    for item in bpy.data.objects:
        bpy.data.objects.remove(item)

    for item in bpy.data.meshes:
        bpy.data.meshes.remove(item)

    for item in bpy.data.materials:
        bpy.data.materials.remove(item)

delete_all()

# 頂点座標を定義
coords=[
    (-1.0, -1.0, -1.0),
    ( 1.0, -1.0, -1.0),
    ( 1.0,  1.0, -1.0),
    (-1.0,  1.0, -1.0),
    ( 0.0,  0.0,  1.0)
]

# この添字を使って面を定義
# 各面は4つの整数の並びで定義
# 三角形の面は最初の頂点と4つ目の頂点が同じになる必要
faces=[
    (2,1,0,3),
    (0,1,4,0),
    (1,2,4,1),
    (2,3,4,2),
    (3,0,4,3)
]

# 新規メッシュを作成
me          = bpy.data.meshes.new("PyramidMesh")
# メッシュでオブジェクトを作成
ob          = bpy.data.objects.new("Pyramid", me)
# オブジェクトを 3D カーソルの位置に配置
ob.location = bpy.context.scene.cursor_location
# オブジェクトをシーンにリンク
bpy.context.scene.objects.link(ob)
# メッシュの頂点、辺、面を埋めまる
me.from_pydata(coords,[],faces)
# 新たなデータでメッシュを更新
me.update(calc_edges=True)

変更後 ※2020.01.26 改稿※

2.80
import bpy

# デフォルトのCubeを削除
def delete_all():
    for col in bpy.data.collections:
        for item in col.objects:
            col.objects.unlink(item)
            bpy.data.objects.remove(item)

    for item in bpy.context.scene.collection.objects:
        bpy.context.scene.collection.objects.unlink(item)
        bpy.data.objects.remove(item)

    for item in bpy.data.meshes:
        bpy.data.meshes.remove(item)

    for item in bpy.data.materials:
        bpy.data.materials.remove(item)

delete_all()

# 頂点座標を定義
coords=[
    (-1.0, -1.0, -1.0),
    ( 1.0, -1.0, -1.0),
    ( 1.0,  1.0, -1.0),
    (-1.0,  1.0, -1.0),
    ( 0.0,  0.0,  1.0)
]

# この添字を使って面を定義
# 各面は4つの整数の並びで定義
# 三角形の面は最初の頂点と4つ目の頂点が同じになる必要
faces=[
    (2,1,0,3),
    (0,1,4,0),
    (1,2,4,1),
    (2,3,4,2),
    (3,0,4,3)
]

# 新規メッシュを作成
me          = bpy.data.meshes.new("PyramidMesh")
# メッシュでオブジェクトを作成
ob          = bpy.data.objects.new("Pyramid", me)
# オブジェクトを 3D カーソルの位置に配置
ob.location = bpy.context.scene.cursor.location
# オブジェクトをシーンにリンク
bpy.context.scene.collection.objects.link(ob)
# メッシュの頂点、辺、面を埋めまる
me.from_pydata(coords,[],faces)
# 新たなデータでメッシュを更新
me.update(calc_edges=True)

変更箇所

2.79
    for item in bpy.context.scene.objects:
        bpy.context.scene.objects.unlink(item)
2.80
    for col in bpy.data.collections:
        for item in col.objects:
            col.objects.unlink(item)
            bpy.data.objects.remove(item)

    for item in bpy.context.scene.collection.objects:
        bpy.context.scene.collection.objects.unlink(item)

 現在のシーンの中にあるオブジェクトに関する命令ですが、sceneの下に更にcollectionという階層が追加されたので、それを反映させる必要があります。sceneの直下にもobjectsがあって、それだとオブジェクトをlinkまたはunlinkする時にエラーが出る、というのが中々の初見殺しですね。AttributeError: 'bpy_prop_collection' object has no attribute 'link'というエラーに泣かされた人も多いのではないでしょうか。
(2021.01.26追記)context.scene.collectionはシーン直下にあるマスターコレクションを参照するようです。個別にコレクションが生成されている場合、更に個別コレクションごとに探索をする必要があります。(追記終)

2.79
ob.location = bpy.context.scene.cursor_location
2.80
ob.location = bpy.context.scene.cursor.location

 カーソル位置の指定。2.79以下ではcurosr_locationというAPIがありましたが、2.80ではcursorの下にlocationがある、という階層構造になってようです。

該当リファレンス:https://docs.blender.org/api/current/bpy.types.View3DCursor.html

image.png

2.79
bpy.context.scene.objects.link(ob)
2.80
bpy.context.scene.collection.objects.link(ob)

 先ほどのcollectionの話と同様。

使えるAPIをみつけるコツ

 このように、変更点がありすぎて必要となる情報もまだ少ない、という状況なので、使えるAPIは自分で探すことも必要になります。そのような時には、

 image.png

 BlenderのPythonコンソール上でCtrl+Spaceキー Tabキー(いつの間にかキーマップが変わっていました※。2.90以降?)を押すと候補となるコマンドの一覧が出てきますので(途中まで入力していればそれに応じて候補が絞られます)役に立つかもしれないです。

※2021年7月追記

 それでは皆様よきBlender 2.80ライフを。

28
20
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
20