本日は Blender2.8 の調査枠です。
Blender 2.8 の Python API ドキュメントを少しずつ読みつつ試していきます。
前回記事の続きです。
bluebirdofoz.hatenablog.com
Blender 2.8 Python API Documentation
以下のページを日本語訳しつつ実際に試して記事を進めていきます。
docs.blender.org
docs.blender.org
今日は「落とし穴」の「古いデータ」です。
古いデータ
Pythonの値を変更し、更新された値にすぐにアクセスしたい場合があります。
例えば、オブジェクトの bpy.types.Object.location を変更し、bpy.types.Object.matrix_world からすぐにその変更にアクセスする場合です。
これは期待通りには機能しません。
import bpy # 選択中のオブジェクトのロケーションを(1, 2, 3)に変更する bpy.context.object.location = 1, 2, 3 # オブジェクトのワールド座標を取得する matrix = bpy.context.object.matrix_world # 標準出力に取得した座標を表示する print(matrix)
オブジェクトの最終的な変換を計算する際の計算を考えてみます。
これには次のものが含まれます。
・アニメーションの関数曲線。
・ドライバーとそのPython式
・制約
・親オブジェクトとその全てのFカーブ/制約など
プロパティが変更されるたびに高負荷な再計算を避けるために、Blenderは必要になるまで実際の計算を延期します。
しかしスクリプトの実行中に、更新された値にアクセスしたい場合があります。
この場合、値を変更した後に bpy.types.ViewLayer.update を呼び出す必要があります。
次に例を示します。
bpy.context.object.location = 1, 2, 3 bpy.context.view_layer.update()
これで、すべての依存データ(子オブジェクト、修飾子、ドライバーなど)が再計算されます。
アクティブビューレイヤー内のスクリプトで値を使用できるようになりました。
import bpy # 選択中のオブジェクトのロケーションを(1, 2, 3)に変更する bpy.context.object.location = 1, 2, 3 # 値を更新する bpy.context.view_layer.update() # オブジェクトのワールド座標を取得する matrix = bpy.context.object.matrix_world # 標準出力に取得した座標を表示する print(matrix)
スクリプト中に再描画できますか?
これに対する公式の答えは「いいえ」または「あなたはそれをすべきでない」です。
スクリプトが実行されている間、Blenderは終了するまで待機し、終了するまで事実上ロックされます。
この状態では、Blenderはユーザーの入力に対して再描画または応答しません。
通常、Blenderで配布されるスクリプトは長期間実行されない傾向があるため、これはそれほど問題ではありません。
しかしスクリプトの実行には時間がかかることがあり、ビューポートで何が起こっているのかを見るのは素晴らしいことです。
Blenderをループでロックして再描画するツールは推奨されません。
ツールの実行中、インターフェイスのさまざまな部分を更新するBlendersの機能と競合するためです。
したがって、ここでの解決策はモーダル演算子、つまり -modal()関数を定義する演算子を記述することです。
テキストエディタでモーダル演算子テンプレートを参照してください。
モーダル演算子はユーザー入力で実行するか、独自のタイマーをセットアップして実行します。
イベントを処理したり、キーマップや他のモーダル演算子で処理するためにパススルーすることもできます。
モーダル演算子の例として、変換、ペインティング、フライモード、およびファイル選択があります。
モーダル演算子の作成は再描画するだけの簡単な for ループよりも多くの労力を必要とします。
しかしより柔軟性があり、Blenderの設計との統合性が高いです。
それでもPythonから再描画したい
どうしても必要ならば可能です。
しかし、このハックを使用するスクリプトはBlenderに含めることは考慮されず、これによる問題はバグとはみなされません。
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
import bpy import time # 処理を5回ループする for i in range(5): # Z軸方向に徐々に上昇する bpy.context.object.location[2] = bpy.context.object.location[2] + 1.0 # 値を更新する bpy.context.view_layer.update() # 1秒間処理をスリープする time.sleep(1) # 再描画を行う bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)