#07 VRMお手軽ポーズのjsonをUnityで読み込む
YATTA!駆動開発の第7回。プロトタイプ版 VRMお手軽ポーズのポーズデータのjsonファイルをUnity上でVRMモデルに適用する。
https://gyazo.com/f1b5a7df1986070302cb4a075fc0a1cfhttps://gyazo.com/a3228910606f9f0d1218eb3cad2f5f4e
基本情報
前回:#06 VRMお手軽ポーズ/IK調整などなど
次回:#08 VRMお手軽ポーズ 高解像度撮影 MaterialDebugモード
pixiv/three-vrmのVRMPoseをUnity上のVRMモデルに適用する
ソースコード
ポーズJSONデータの取得
TODO : pixiv/three-vrmでポーズデータを出力する
プロトタイプ版 VRMお手軽ポーズではVRMモデルを扱うためにpixiv/three-vrmというライブラリを使用しています。
pixiv/three-vrmではVRMに対応したポーズデータをVRMPoseとして扱うことができます。
VRMPoseは以下のような構造になっています。
code:json
{
ボーン名 : {
rotation: 0.000, 0.000, 0.00, 1.00 ,
position: 0.000, 0.000, 0.000
},
}
ボーン名:Enumeration HumanoidBoneName
rotationは各ボーンの回転のクォータニオンです。
positionは省略可能です。
VRMへのget/setは以下の関数を使用します。
VRM.humanoid.getPose() :VRMpose
VRM.humanoid.setPose(poseObject: VRMPose): void
VRMPoseをpositionを削除しjsonとして出力しています。
code:js
const blob = new Blob(JSON.stringify(pose, null, ' '), { type: 'application\/json' });
UnityでのJSONデータの読み込み
json の構造を記述する
ファイルパスを指定してstringとして読みこむ
JsonUtilityを使ってインスタンス化
JsonUtilityではあらかじめ読み込むJSONを記述する必要があります。
読み込むJSONデータの例
code:json
{
chest:{
rotation:0.0,0.0,0.0,1.0
},
...
}
これのJSONの構造を定義するクラスを作成します。
ネストされている要素はSerializableなクラス作成してメンバに含めれば良いみたいです。
code:cs
System.Serializable
public class PoseItem
{
public BoneItem chest;
...
}
System.Serializable
public class BoneItem
{
public float[] rotation;
}
json -> PoseItem として読み込みます。
インスペクタからfilePathを設定
jsonファイルを文字列として読み込み
JsonUtility.FromJson<PoseItem>でPoseItemのインスタンス化
code:cs
public string filePath;
ContextMenu("load json")
public void reload()
{
string json = File.ReadAllText(filePath);
PoseItem poseItem = JsonUtility.FromJson<PoseItem>(json);
// applyPose(poseItem);
}
VRMモデルにポーズを適用
スクリプトをアタッチしたVRMモデルに対してAnimator.GetBoneTransformを使って関節角を変更します。
foreachでPoseItemの各BoneItemを取得して関節角を適用する処理を行います。
全てのBoneItemに対して処理を行うためにType.GetFields()をしています・・
ほんとうにこれでいいんですかね
code:c#
private Animator humanoidAnimator;
private void applyPose(PoseItem poseItem)
{
humanoidAnimator = GetComponent<Animator>();
Type type = poseItem.GetType();
foreach (FieldInfo field in type.GetFields())
{
BoneItem boneItem = (BoneItem)field.GetValue(poseItem);
if (boneItem.rotation != null)
{
applyBoneRotaiton(field.Name, boneItem.rotation);
}
}
}
VRMPoseのボーン名とUnityのHumanBodyBonesが対応していることを利用します。
VRMPoseのボーン名はlowerCamelCaseなのでUpperCamelCaseに変換し名前でHumanBodyBoneを指定します。
Animator.GetBoneTransform(bone)で関節のtransformを取得し角度を代入します。
three.jsとUnityで座標系が違うのでそのままクォータニオンを代入せずに変換する必要があります。
code:cs
private void applyBoneRotaiton(string name, float[] rotation)
{
if (humanoidAnimator != null)
{
string boneName = Char.ToUpper(name0) + name.Substring(1);
HumanBodyBones bone =(HumanBodyBones)Enum.Parse(typeof(HumanBodyBones), boneName);
var boneTrans = humanoidAnimator.GetBoneTransform(bone);
if (boneTrans != null)
{
boneTrans.localRotation = new Quaternion(-rotation0, -rotation1, rotation2, rotation3);
}
}
}
filePathをinspectorからドラッグ&ドロップで設定できるようにする
filePathを入力するのが面倒なのでインスペクタからD&Dで設定できるようにします。
やり方は
Unity Inspector Editor上にDrag&Dropしたい
そのままです。
ついでにpose読み込みを行うようにしました。
code:cs
...
m_FilePath.stringValue = AssetDatabase.GetAssetPath(draggedObject);
VRMPoseImporter targetComp = target as VRMPoseImporter;
targetComp.loadJson(AssetDatabase.GetAssetPath(draggedObject));
...
使い方
https://gyazo.com/f1b5a7df1986070302cb4a075fc0a1cf
(UniVRMによるVRMの読み込みは導入済みとする)
ソースコードをダウンロード
VRMPoseImporterEditor.csをEditorフォルダに配置
VRMPoseImporter.csをVRMのモデルにアタッチ
プロトタイプ版 VRMお手軽ポーズのポーズのjsonファイルをインポートした後、インスペクタからドラッグ&ドロップ
サンプルポーズ:ヨシッ
サンプルポーズ:Tポーズ
問題がなければポーズが変わるはずです。
参考
VRMPose
HumanBodyBones
Humanoidの3DモデルをQuaternionで直接動かす方法
JsonUtility をつかって Unity で JSON を取り扱う方法
Unity Inspector Editor上にDrag&Dropしたい
おわり。
次回は#08 VRMお手軽ポーズ 高解像度撮影 MaterialDebugモード
#エディタ拡張