はじめに
Unityでつけたボーンポーズを出力して、
Blenderのボーンポーズに適用するスクリプトを作成しました。
Blenderのボーンウェイトの調整の時、
Unityの物理演算で設定されたポーズにしたかったのが目的です。
手順
以下方針で行います。
次のようなサンプルで説明します。
注意
rootボーンのポーズを設定しても無視されます
Unity
- MiniJSON.csをダウンロード
- AssertsにPluginsフォルダを作成し、MiniJSON.csを配置
- 以下スクリプトを作成しGameObjectにAddComponentする
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; using System; using MiniJSON; public class ExportUnityBones : MonoBehaviour { [SerializeField] List<GameObject> bones = null; [SerializeField] string filename = "UnityBonePoses.json"; // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.Space)) { // localToWorldMatrix -> List var bonesList = new Dictionary<string, List<List<float>>>(); foreach (GameObject bone in bones) { Matrix4x4 H = bone.transform.localToWorldMatrix; var rows = new List<List<float>>(); foreach (int i in Enumerable.Range(0, 4)) { var row = new List<float>(); foreach (int j in Enumerable.Range(0, 4)) { row.Add(H[i, j]); } rows.Add(row); } bonesList.Add(bone.name, rows); } Debug.Log($"Export:{System.IO.Path.GetFullPath(filename)}"); string json = Json.Serialize(bonesList); using (var f = new System.IO.StreamWriter(filename)) { f.Write(json); } } } }
- 出力したいボーン親を右クリック>Select Children で子を選択
- ExportUnityBones.csのBonesにドラッグアンドドロップ
- ExportUnityBones.csのFilenameに出力ファイル名を指定
- Playしてポーズを設定した後スペースキーを押すと出力
Blender
- Scripting Workspaceに切り替え
- 以下スクリプトを作成
import bpy from mathutils import Matrix import json import os def main(): filename = 'UnityBonePoses.json' H_BBld_BUnt = Matrix([ [-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ]) H_BUnt_BBld = H_BBld_BUnt.inverted() print('import '+ os.path.abspath(filename)) with open(filename) as f: unity_bones = json.load(f) bpy.context.view_layer.objects.active = bpy.data.objects['Armature'] bpy.ops.object.mode_set(mode='EDIT') edit_bones = bpy.data.objects['Armature'].data.edit_bones pose_bones = bpy.data.objects['Armature'].pose.bones for bone_name, unity_bone_matrix in unity_bones.items(): if bone_name not in pose_bones: continue bone = pose_bones[bone_name] parent_bone = bone.parent if not parent_bone or parent_bone.name not in unity_bones: continue child_edit_bone = edit_bones[bone_name] parent_edit_bone = child_edit_bone.parent H_WBld_BBldEdtChdHed = child_edit_bone.matrix H_BBldEdtChdHed_WBld = H_WBld_BBldEdtChdHed.inverted() H_WBld_BBldEdtPntHed = parent_edit_bone.matrix H_BBldEdtPntHed_WBld = H_WBld_BBldEdtPntHed.inverted() H_BBldEdtPntHed_BBldEdtChdHed = H_BBldEdtPntHed_WBld @ H_WBld_BBldEdtChdHed T_BBldEdtPntHed_BBldEdtPntTil = Matrix.Translation(H_BBldEdtPntHed_BBldEdtChdHed.to_translation()) H_BBldPosChdOrg_BBldPosPntTil = H_BBldEdtChdHed_WBld @ H_WBld_BBldEdtPntHed @ T_BBldEdtPntHed_BBldEdtPntTil H_WUnt_BUntPosChdHed = Matrix(unity_bone_matrix) H_BUntPosPntHed_WUnt = Matrix(unity_bones[parent_bone.name]).inverted() H_BUntPosPntHed_BUntPosChdHed = H_BUntPosPntHed_WUnt @ H_WUnt_BUntPosChdHed T_BUntPosPntTil_BUntPosPntHed = Matrix.Translation(-1 * H_BUntPosPntHed_BUntPosChdHed.to_translation()) H_BBldPosPntTil_BBldPosChdHed = H_BBld_BUnt @ T_BUntPosPntTil_BUntPosPntHed @ H_BUntPosPntHed_BUntPosChdHed @ H_BUnt_BBld H_BBldPosChdOrg_BBldPosChdHed = H_BBldPosChdOrg_BBldPosPntTil @ H_BBldPosPntTil_BBldPosChdHed bone.matrix_basis = H_BBldPosChdOrg_BBldPosChdHed bpy.context.view_layer.update() # update bone pose bpy.ops.object.mode_set(mode='POSE') main() print('end\n')
- filenameにUnityで出力したファイルを指定
- Run Scriptを押すとPose ModeのボーンがUnityで設定したポーズになる
解説
UnityとBlender間の座標変換式について解説します。
座標変換については、https://yu00.hatenablog.com/entry/2022/07/11/103954
を参照ください。
略字
- : ワールド座標
- : ボーンローカル座標
- : Blender
- : Unity
- : Blender Edit Mode
- : Blender Pose Mode
- : Parent Bone
- : Child Bone
- : Bone Head
- : Bone Tail
基本方針
適用順によってポーズが変わらないように、
Blenderのbpy.types.PoseBone.matrix_basis
に適用します。
Pose Mode Parent Tail Bone座標 から見て
ポーズ適用前の座標をPose Mode Child Origin Bone座標 、
ポーズ適用後の座標をPose Mode Child Head Bone座標 とすると、
matrix_basisは、Pose Mode Child Head Bone座標 から
Pose Mode Child Origin Bone座標 への同次変換行列
です。
ただしPose Mode Parent Tail Bone座標 は Pose Mode Parent Head Bone座標 から
Pose Mode Child Head Bone座標 原点へ平行移動した座標とします。
また、と
は一致します。
また、Unityで出力したtransform.localToWorldMatrix
は、
, に対応します。
Unity, Blenderボーン座標の基底変換
- : Blenderボーン座標から見たUnityボーン座標の基底
- : Blenderボーン座標 の基底
- : Blenderボーン座標から見た点p
- : Unityボーン座標から見た点p
- : Blenderボーン座標からUnityボーン座標への同次変換行列
Unity, Blenderボーン座標の座標変換
(備考)Unity, Blenderワールド座標の基底変換
- : Blenderワールド座標から見たUnityワールド座標の基底
- : Blenderワールド座標 の基底
- : Blenderワールド座標から見た点p
- : Unityワールド座標から見た点p
- : Unityワールド座標からBlenderワールド座標への同次変換行列
(備考)Unity同次変換行列
- : Unityローカル座標からUnityワールド座標への同次変換行列(
transform.localToWorldMatrix
) - : 移動同次変換行列(
transform.position
) - : 回転同次変換行列(
transform.rotation
) - : スケール同次変換行列(
transform.localScale
)
参考
- https://light11.hatenadiary.com/entry/2019/01/24/223705
- https://docs.unity3d.com/ja/2018.4/ScriptReference/Quaternion.Euler.html