[UE5] Control Rig をPythonから使う
Pythonでコントロールリグを云々する時のメモです。
アセットの取得
プロジェクト内のアセットを取得するとき
unreal.EditorAssetLibrary.load_asset( asset_path )
asset_path
にはいろいろな書き方があてはめられます。
asset_path = "/Script/ControlRigDeveloper.ControlRigBlueprint'/Game/Characters/Mannequins/Rigs/CR_Mannequin_Body.CR_Mannequin_Body'"
asset_path = "/Game/Characters/Mannequins/Rigs/CR_Mannequin_Body.CR_Mannequin_Body"
asset_path = "/Game/Characters/Mannequins/Rigs/CR_Mannequin_Body"
コンテンツブラウザで選択中のアセットを取得するとき
sel_obj = unreal.EditorUtilityLibrary.get_selected_assets()[0]
開いている コントロールリグBP を取得
cr_bp = unreal.ControlRigBlueprint.get_currently_open_rig_blueprints()[0]
コントロールリグのプレビューメッシュ関連
プレビューメッシュの取得
cr_bp.get_preview_mesh()
プレビューメッシュを割り当てる
sk_mesh = unreal.EditorAssetLibrary.load_asset( sk_asset_path )
cr_bp.set_preview_mesh(sk_mesh)
コントロールリグ・グラフの取得
graph = cr_bp.get_model()
コントロールリグ・グラフのノードの取得
グラフのノードを何でもいいので取得
graph.get_nodes()[0]
# <Object '/Game/Characters/Mannequins/Rigs/CR_Mannequin_Body.CR_Mannequin_Body:RigVMModel.RigUnit_BeginExecution_1' (0x00000A8658230480) Class 'RigVMUnitNode'>
ノード名から取得
graph.find_node('RigUnit_BeginExecution_1')
選択中のノード
node_name = graph.get_select_nodes()[0]
# LogPython: Name("RigUnit_BeginExecution_1")
get_select_nodes
でノードが取れればいいんですが、ノードではなくノード名が得られるので、
それをfind_node
に渡します。
graph.find_node(graph.get_select_nodes()[0])
# <Object '/Game/Characters/Mannequins/Rigs/CR_Mannequin_Body.CR_Mannequin_Body:RigVMModel.RigUnit_BeginExecution_1' (0x00000A8658230480) Class 'RigVMUnitNode'>
ノードを調べる
ノードのID
node145 = graph.get_nodes()[145]
node145.get_node_index()
# 145
ノード名
node.get_name()
# 'RigUnit_BeginExecution_1'
node.get_node_title()
# 'Forwards Solve'
ノードの位置
シーン内での位置(トランスフォーム)ではなく、ノードツリー上の位置です。XYで受け取れます。
node.get_position()
# <Struct 'Vector2D' (0x0000024875CA7620) {x: -8224.000000, y: -11360.000000}>
ノードのパス
node.get_path_name()
# '/Game/Characters/Mannequins/Rigs/CR_Mannequin_Body.CR_Mannequin_Body:RigVMModel.RigUnit_BeginExecution_1'
なにに使えるかいまいちわかりません。もしかしたらノードではなくピン側で使う想定のものかも。
ドキュメントでノードを調べる
Pythonドキュメントを「RigVM」で検索し、
検索結果を「node」で検索すると各種ノードについて調べられます。
Sequenceノードについて
5.1から旧Sequenceノードが非推奨になり、実行ピンが可変長になった新しいSequenceノードが登場しています。
内部的には、前者は「RigUnit_SequenceExecution」、後者は「RigUnit_SequenceAggregate」というようです。
リグ階層を取得
hierarchy = cr_bp.hierarchy
階層内のすべての要素を取得
リグ階層での「ボーン」「コントローラ」「Null」といった要素をまとめて「key」と呼びます。アニメーション作業中に打つキーと紛らわしい感じがしますが別物です。
hierarchy.get_all_keys()
# <Array object at 0x0000024875C9A4F0>
hierarchy.get_all_keys()[0]
# <Struct 'RigElementKey' (0x00000A868D8C94D0) {type: Bone, name: "root"}>
ボーン・コントローラ・Null以外にどういったタイプがあるかはRigElementTypeで確認できます。
選択中のキーを取得
hierarchy.get_selected_keys()[0]
# <Struct 'RigElementKey' (0x00000A86B0E1DEE0) {type: Bone, name: "root"}>
リグ階層を操作する
リグ階層のためのコントローラーオブジェクトを取得して、そこから操作を開始します。
(ここでいうコントローラーは、リグ要素としてのコントローラーとは違うPythonオブジェクトとしてのコントローラーです)
hierarchy_controller = hierarchy.get_controller()
# or
hierarchy_controller = cr_bp.get_hierarchy_controller()
hierarchy_controller.add_bone('root', '', unreal.Transform(location=[0.000000,0.000000,0.000000],rotation=[0.000000,0.000000,-0.000000],scale=[1.000000,1.000000,1.000000]), False, unreal.RigBoneType.IMPORTED)
hierarchy_controller.add_control('global_ctrl', '', control_settings_global_ctrl, unreal.RigHierarchy.make_control_value_from_euler_transform(unreal.EulerTransform(location=[0.000000,0.000000,0.000000],rotation=[0.000000,0.000000,0.000000],scale=[1.000000,1.000000,1.000000])))
hierarchy_controller.add_null('head_fk_space', unreal.RigElementKey(type=unreal.RigElementType.NULL, name='chest_space'), unreal.Transform(location=[40.598420,3.662945,0.000029],rotation=[-90.000000,90.000000,-80.547132],scale=[1.000000,1.000000,1.000000]), False)
add_bone
add_control
add_null
さらに、RigControlは作成時にはコントロールセッティングを与える必要があります。
control_settings_global_ctrl = unreal.RigControlSettings()
control_settings_global_ctrl.animation_type = unreal.RigControlAnimationType.ANIMATION_CONTROL
control_settings_global_ctrl.control_type = unreal.RigControlType.EULER_TRANSFORM
control_settings_global_ctrl.display_name = 'None'
control_settings_global_ctrl.draw_limits = True
control_settings_global_ctrl.shape_color = unreal.LinearColor(1.000000, 0.964687, 0.000000, 1.000000)
control_settings_global_ctrl.shape_name = 'Hexagon_Thin'
control_settings_global_ctrl.shape_visible = True
control_settings_global_ctrl.is_transient_control = False
control_settings_global_ctrl.limit_enabled = [unreal.RigControlLimitEnabled(False, False), unreal.RigControlLimitEnabled(False, False), unreal.RigControlLimitEnabled(False, False), unreal.RigControlLimitEnabled(False, False), unreal.RigControlLimitEnabled(False, False), unreal.RigControlLimitEnabled(False, False), unreal.RigControlLimitEnabled(False, False), unreal.RigControlLimitEnabled(False, False), unreal.RigControlLimitEnabled(False, False)]
control_settings_global_ctrl.minimum_value = unreal.RigHierarchy.make_control_value_from_euler_transform(unreal.EulerTransform(location=[0.000000,0.000000,0.000000],rotation=[0.000000,0.000000,0.000000],scale=[1.000000,1.000000,1.000000]))
control_settings_global_ctrl.maximum_value = unreal.RigHierarchy.make_control_value_from_euler_transform(unreal.EulerTransform(location=[0.000000,0.000000,0.000000],rotation=[0.000000,0.000000,0.000000],scale=[1.000000,1.000000,1.000000]))
control_settings_global_ctrl.primary_axis = unreal.RigControlAxis.X
hierarchy_controller.add_control('global_ctrl', '', control_settings_global_ctrl, unreal.RigHierarchy.make_control_value_from_euler_transform(unreal.EulerTransform(location=[0.000000,0.000000,0.000000],rotation=[0.000000,0.000000,0.000000],scale=[1.000000,1.000000,1.000000])))
選択中のキーからコントロールセッティングを取得するにはこう
hierarchy_controller.get_control_settings(hierarchy.get_selected_keys()[0])
# <Struct 'RigControlSettings' (0x00000A86CEDC7E00) {animation_type: AnimationControl, control_type: EulerTransform, display_name: "", primary_axis: X, limit_enabled: ((),(),(),(),(),(),(),(),()), draw_limits: True, minimum_value: {}, maximum_value: {}, shape_visible: True, shape_visibility: UserDefined, shape_name: "Hexagon_Thin", shape_color: {r: 1.000000, g: 0.964687, b: 0.000000, a: 1.000000}, is_transient_control: False, control_enum: None, customization: {available_spaces: , removed_spaces: }, driven_controls: , group_with_parent_control: False}>
ちなみにリグ要素のキーではない実体はこちらなようです
RigBone
RigControl RigSpaceでもこれらを取得する方法がいまいちわかりませんでした。castも出来ない様子。
root_bone.cast(unreal.RigBone)
LogPython: Error: Traceback (most recent call last):
LogPython: Error: File "<string>", line 1, in <module>
LogPython: Error: TypeError: RigElementKey: Cannot cast type 'RigBone' to 'RigElementKey'
リグコントローラーオブジェクトの取得
さきほど紹介した階層のコントローラーと同じく、Pythonオブジェクトとしてのリグコントローラーです。本名は「RigVMController」
ノードの選択や追加、ピン接続、undoなど、コントロールリグを開いているときにエディタで行う操作を一通り実行できます。
コントロールしたいコントロールリグBPを取得するところから開始します。
cr_bp = unreal.ControlRigBlueprint.get_currently_open_rig_blueprints()[0]
# cr_bp = unreal.EditorAssetLibrary.load_asset( cr_bp_path )
crtl = unreal.ControlRigBlueprintLibrary.get_controller(cr_bp)
# or
func_lib = cr_bp.get_local_function_library()
crtl = cr_bp.get_controller(func_lib)
公式ドキュメントを確認する。
記事を書いた後で気づいたんですが、
最高です。
事前にこのページを見つけていたらこの記事は書かなかったです笑
特に最高な個所をいくつか抜粋します。
開いているコントロールリグをPythonスクリプトとして取得する
右上の「クラス設定」から「py」とでも検索し、
Pythonログ設定 > Python Log Settings > Commands
の「Pythonスクリプトのコピー」を実行します。
そして適宜エディタにペーストして下さい。
これで、いま開いているコントロールリグをPythonで作成するにはどうするか、その全貌が得られます。
たとえばUE5 Mannequinであれば10142行ほどのコードが得られました。
ある意味コントロールリグのアスキー版みたいな感じですね。
最高。
エディタでの操作ログをPythonスクリプトとして得る。
「メッセージログ」ウィンドウ を見てみると「コントロールリグのPythonログ」というカテゴリがあります。※「コントロールリグのログ」と紛らわしいので注意。
コントロールリグをエディタで操作すると、そのログがPythonスクリプトとして流れてきます。
例えば、左下のリグ階層で新規にNullを作った場合はこう、
hierarchy_controller.add_null('NewNull', unreal.RigElementKey(type=unreal.RigElementType.BONE, name='tip_r'), unreal.Transform(location=[0.000000,0.000000,0.000000],rotation=[0.000000,0.000000,-0.000000],scale=[1.000000,1.000000,1.000000]), False)
それをリネームしたらこう
hierarchy_controller.rename_element(unreal.RigElementKey(type=unreal.RigElementType.NULL, name='NewNull'), 'test_null')
ただし、左上のビューポート上でコントローラを操作したりすると、ぶわーっと同じようなログが流れますのでちょっと注意です。
ほか
ほかには、コントロールリグを開いたときにPythonを実行してもらう、みたいな仕込みもできるようです。
MayaでいうuserSetupとかPre Render MELみたいな感じですかね。
試していません。
Discussion