Blenderのエリアのコンテキストに依存する操作を、「bpy.context.temp_override」を用いて一時的にコンテキストを上書きすることでスクリプト上で自動化します。
ここでは、任意のピボットを指定したリサイズ処理を例にあげて、操作方法をまとめておきます。
動作確認環境 : Blender 4.2
コンテキストの取得と上書き
まず、コンテキストの扱いについて基本的なことをまとめます。
Blenderのコンテキストとは
エリアやリージョン、オブジェクトやモード、シーンの情報のことです。bpyによるある操作は、特定のコンテキストに依存しています。
例えば、頂点や面、辺を編集しようとする場合にモード設定が「Editモード」である必要がありますが、「Objectモード」で編集を行うようなスクリプトを記載するとコンテキストエラーとなります。
エラーの例 :
RuntimeError: Operator bpy.ops.mesh.loopcut_slide.poll() failed, context is incorrect
コンテキストの取得
- エリア:3D View
- リージョン:WINDOW
Python :
def get_override(
area_type='VIEW_3D'
, region_type='WINDOW'
):
for area in bpy.context.screen.areas:
if area.type == area_type:
for region in area.regions:
if region.type == region_type:
override = {'area': area, 'region': region}
return override
# エラーメッセージ表示
raise RuntimeError(f"Wasn't able to find {region_type} in area {area_type}. Make sure it's open while executing script.")
環境の初期化時の処理に入れておくと便利です。
▼関連記事
一時的なコンテキストの上書き
エリアとリージョンのコンテキストを一時的に上書きして、「bpy.context.scene.tool_settings.transform_pivot_point」を用いてピボットを設定したうえで要素のリサイズを行います。
指定できるピボットは以下です。
POINT!
参考:- BOUNDING_BOX_CENTER
- 選択したオブジェクトの境界ボックスの中心
- CURSOR
- 3Dカーソル
- INDIVIDUAL_ORIGINS
- 各オブジェクトの原点
- MEDIAN_POINT
- 選択したオブジェクトの中点
- ACTIVE_ELEMENT
- アクティブオブジェクト
https://docs.blender.org/api/current/bpy.types.ToolSettings.html
Python :
# 上書きしたコンテキストでオペレーターを実行
with bpy.context.temp_override(**override):
# ピボット設定
bpy.context.scene.tool_settings.transform_pivot_point = 'INDIVIDUAL_ORIGINS'
# 要素サイズ変更
bpy.ops.transform.resize(
value=(0.23, 1, 1),
orient_type='GLOBAL'
)
# 上書きしたコンテキストでオペレーターを実行
with bpy.context.temp_override(**override):
# ピボット設定
bpy.context.scene.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
# 要素サイズ変更
bpy.ops.transform.resize(
value=(0.9, 1, 1),
orient_type='GLOBAL'
)
ここで、一時的なコンテキストの上書きを行わない場合、適切なピボットで要素がリサイズされません。(なぜそうなるのかはわかっていません。)
コンテキスト上書きを用いたピボット設定の使用例
上記を用いたオブジェクトの編集例を示します。
差し込んだ面に対して、
- 個々の面の中心でリサイズ ('INDIVIDUAL_ORIGINS')
- オブジェクトの中心でリサイズ ('MEDIAN_POINT')
Python :
def Base_create():
bpy.ops.object.mode_set(mode='OBJECT')
# 立方体を追加
bpy.ops.mesh.primitive_cube_add(
size=1
, location=(0, 0, 1)
, scale=(0.09,0.05,0.008)
)
# 名前設定
bpy.context.object.name = "cube_00"
# Mode切り替え
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_mode(type='EDGE')
# ループカット
bpy.ops.mesh.loopcut_slide(
MESH_OT_loopcut={
"number_cuts":1
, "smoothness":0
, "falloff":'INVERSE_SQUARE'
, "object_index":0
, "edge_index":5
}
, TRANSFORM_OT_edge_slide={
"value":0
, "single_side":False
, "use_even":False
}
)
# Mode切り替え
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_mode(type='FACE')
# 要素選択
element_select(
element_list=[5, 6]
, select_mode="FACE"
, object_name_list=["cube_00"]
)
# 面の差し込み
bpy.ops.mesh.inset(
thickness=0.008
, depth=0
, use_individual=True
)
# 上書きしたコンテキストでオペレーターを実行
with bpy.context.temp_override(**override):
# ピボット設定
bpy.context.scene.tool_settings.transform_pivot_point = 'INDIVIDUAL_ORIGINS'
# 要素サイズ変更
bpy.ops.transform.resize(
value=(0.23, 1, 1),
orient_type='GLOBAL'
)
# 上書きしたコンテキストでオペレーターを実行
with bpy.context.temp_override(**override):
# ピボット設定
bpy.context.scene.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
# 要素サイズ変更
bpy.ops.transform.resize(
value=(0.9, 1, 1),
orient_type='GLOBAL'
)
Base_create()
以上