UnityとUE4モーションデータを出力してみた(VMCtoMOP)
概要
前回の記事にThreeDPoseTracker(Unity) -> VMCtoMOP(Unity) -> MopUE4という形でUnityツールの骨格認識経由でUE4のモデルを動かしましたが、リリース版のものを使ったので、中身はどういう感じか分かりませんでした。
モーション周りの勉強のため、プロジェクト上でモーションデータ取り出したいと思います。最新の ThreeDPoseTracker(Unity) は現時点 Close Source になっているらしいですが、VMCtoMOPはOpen Sourceなので、VMCtoMOPのプロジェクトを立ち上げ、ソース改造し、モーション(VMCとMOP両方)を出力してみたいと思います。
マシン環境
CPU : Intel i7-6920HQ
GPU : AMD Radeon Pro 460
Memory : 16GB
System : Windows 10 (with .NET 5 installed)
IDE : Visual Studio 2019 Community
Editor : Visual Studio Code
Terminal : Windows Terminal (or Command Prompt)
Engine : Unity 2020.3.17f1 Personal
事前準備
GitHub: VMCtoMOP by HAL9HARUKU まず上記のプロジェクトをダウンロードします(zipファイルかgit cloneかどちでも大丈夫)、。展開(Clone)したものをUnity Hubで開いて、Safe Modeを無視し、Ignoreを押してプロジェクトを開きます。
VMCtoMOP 依存ライブラリなどを参照することで、下記のパッケージも用意します: - TextMeshPro 3.0.4 (Unity 公式) - UniVRM v0.66.0 - uOSC v0.0.2 - UniRx Ver 7.1.0 - UniTask Ver.2.2.5 - VRoid: Vivi
Unity Packages
Assets -> Import Package -> Custom Packageでダウンロードした四つパッケージをプロジェクトに追加します。
TextMeshPro
TextMeshProは公式のPackage ManagerでInstallできます。
それからMainSceneを開くと、TMP Importerのことが聞かれます。Import TMP Essentialsを選択してください。
VRoid Vivi
版権などの問題だと思いますが、キャラクターモデルViviはプロジェクトに含まれていません。外部リンクVRoid: ViviでViviのVRMファイルをダウンロードします(サイトのアカウント登録が必要)。
先ほどUniVRMが追加されたので、メニューにVRM0ボタンが表示されます。VRM0 -> ImportでダウンロードしたViviのVRMファイルをプロジェクトに追加します。
もともとシーンにあったViviを削除して、追加されたViviをシーンに入れて、新しいViviを Manager -> Manager(Script) -> Animator と Manager -> Mop Sender(Script) -> Animator の参照にします(Drag&Drop)。
シーンにあるViviを選択し、InspectorのとこでVRM Meta(Script) -> Information -> Versionにワーニングが出ていて、適当な数字を入れれば大丈夫です。
Viviのポジションを(0,0,0)にしてください。
ソースの改造
.json
の形で高速、大量なデータの書き込みは問題が発生してますので(解決したら修正します)、.txt
の形でモーションデータを保存します。
モーションが発生するタイミングはTime.deltaTime
で加算し、記録されます。
VMCモーションデータ(for Unity)の出力
まずOscServer.csにソースコードを追加し、VMCモーションデータをアウトプットします。
using System.IO; public void Run(int port) { // ......省略 CreateFileAndWriter(); } private void Update() { // lock(lockObject_) // { // while (parser_.messageCount > 0) // { // var message = parser_.Dequeue(); // onDataReceived.Invoke(message); OutputVmcMotion(message); // } // } time += Time.deltaTime; } FileStream fileStream = null; StreamWriter writer = null; float time = .0f; private void CreateFileAndWriter() { var dirInfo = Directory.CreateDirectory("D:/MotionData/"); string fileName = "MotionVmc" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".txt"; fileStream = File.Create(dirInfo.FullName + fileName); writer = new StreamWriter(fileStream, System.Text.Encoding.UTF8); } private void OutputVmcMotion(Message message) { if(message.address == "/VMC/Ext/Root/Pos" || message.address == "/VMC/Ext/Bone/Pos") { string address = message.address; string name = (string)message.values[0]; Vector3 pos = new Vector3((float)message.values[1], (float)message.values[2], (float)message.values[3]); Quaternion rot = new Quaternion((float)message.values[4], (float)message.values[5], (float)message.values[6], (float)message.values[7]); writer.WriteLine($"a:{address}, n:{name}, p:{pos}, r:{rot}, t:{time}"); } } private void OnDestroy() { writer?.Close(); }
CreateFileAndWriter()
で現時点をファイル名(txt)としてD:/MotionData/
にVMCモーションデータを保存するファイルを作って、ファイルに書き込むStreamWriterを準備します。
OutputVmcMotion(Message message)
でVMCのモーションデータをa:{address}, n:{name}, p:{pos}, r:{rot}, t:{time}
の形で作ったファイルに保存します。
ファイルに書き込むことが終ったら(プログラムが中断されたら)OnDestroy()
にStreamWriter.Close()
でファイルを解放してください。
MOPモーションデータ(for UE4)の出力
Unreal Engine用のMOPモーションデータを保存するため、MopSender.csを改造します。
MopSender.cs
using System; using System.IO; private void Start() { //......省略 CreateFileAndWriter(); } private void OnDestroy() { //......省略 writer?.Close(); } private void SendMotion() { //......省略 time += Time.deltaTime; } private void PostBodySize() { for (var parameterIndex = 0; parameterIndex < BoneParameterNum; ++parameterIndex) { //......省略 OutputMopBodySize(message); } } private void PostBones() { // var sb = new System.Text.StringBuilder(); for (var parameterIndex = 0; parameterIndex < BoneParameterNum; ++parameterIndex) { //......省略 uOSC.Message message = new uOSC.Message($"/Mop/BoneControl/{BoneNameList[parameterIndex]}", -location.x * 100.0f, location.z * 100.0f, location.y * 100.0f, -rotation.x, rotation.z, rotation.y, rotation.w); this.sendBundle.Add(message); OutputMopBone(message); } } FileStream fileStream = null; StreamWriter writer = null; float time = .0f; private void CreateFileAndWriter() { var dirInfo = Directory.CreateDirectory("D:/MotionData/"); string fileName = "MotionMop" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".txt"; fileStream = File.Create(dirInfo.FullName + fileName); writer = new StreamWriter(fileStream, System.Text.Encoding.UTF8); } private void OutputMopBodySize(uOSC.Message message) { string name = message.address; Vector3 size = new Vector3( Convert.ToSingle(message.values[0]), Convert.ToSingle(message.values[1]), Convert.ToSingle(message.values[2])); string str = $"n:{name}, s:{size}, t:{time}"; writer.WriteLine(str); } private void OutputMopBone(uOSC.Message message) { string name = message.address; Vector3 pos = new Vector3( Convert.ToSingle(message.values[0]), Convert.ToSingle(message.values[1]), Convert.ToSingle(message.values[2])); Quaternion rot = new Quaternion( Convert.ToSingle(message.values[3]), Convert.ToSingle(message.values[4]), Convert.ToSingle(message.values[5]), Convert.ToSingle(message.values[6])); string str = $"n:{name}, p:{pos}, r:{rot}, t:{time}"; writer.WriteLine(str); }
CreateFileAndWriter()
で現時点をファイル名(txt)としてD:/MotionData/
にMOPモーションデータを保存するファイルを作って、ファイルに書き込むStreamWriterを準備します。
Unreal Engineに転送するMOPモーションデータはBody Size(size)とBone Data(position, rotation)二種類があるらしいので、OutputMopBodySize(uOSC.Message message)
と OutputMopBone(uOSC.Message message)
を用いいします。VMCの場合はaddress
とname
に分けていますが、MOPは骨パスはname
にまとめているらしいです。
Body Sizeはn:{name}, s:{size}, t:{time}
、Bone Dataはn:{name}, p:{pos}, r:{rot}, t:{time}
という感じにフォマードし、D:/MotionData/
に記録されます。
プロジェクト実行
前回の記事の流れて(モーションデータの記録だけなので、UE4の部分は起動しなくても大丈夫)、VMCtoMOPリリース版の代わりに、今回作ったUnityプロジェクトを実行します。
実行されたら、スクリーンショットの感じでVMCとMOP両方のモーションデータが保存されるはずです。
最後
モーションデータを保存できて、画面上Key Annotationの描画やモーションの機械学習などができるかもしれないと思います。ただ、実際使いたいモーションデータのフォマードに合わせて一部追加改造が必要です。
自分がモーションについて知識が少ないので、またVMCとMOPについて詳しく調べたいと思います。
以上です。