夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

HoloLens 2でRecordServiceを使用する その③

本日はMRTK調査枠です。

MRTKでは[Input Recording Service]と呼ばれる機能が実装されています。

[Input Recording Service]はユーザーの動きを記録・保存する機能で[Input Simulation Service]を用いてUnityエディタ内で再生することができます。

今回はこれを勉強しながらアプリ内でユーザーの動きを録画してみます。

その①はドキュメントを読み解きながら各サービスの概要をつかみました。

redhologerbera.hatenablog.com

その②では実際にアプリで記録を行いUnityエディタ内で再生を行いました。

redhologerbera.hatenablog.com

今回はコードを読み解きながらどのようなデータを記録しているのか見ていきます。

〇InputRecordingControls

記録を行う際に呼び出されるコンポーネント

Mixed Reality Toolkit Foundation/SDK/Features/UX/Scripts/Utilities内の[InputRecordingControls.cs]の[StartRecording()]になります。

f:id:Holomoto-Sumire:20220104200319j:plain

github.com

public void StartRecording()
{
eyeGazeProvider = CoreServices.InputSystem.EyeGazeProvider;
    IsRecording = true;
    frameRate = InputRecordingProfile.FrameRate;
    frameInterval = 1f / frameRate;
    nextFrame = Time.time + frameInterval;

    if (UseBufferTimeLimit)
    {
        PruneBuffer();
    }
    if (!unlimitedRecordingStartTime.HasValue)
    {
        unlimitedRecordingStartTime = Time.time;
    }

    OnRecordingStarted?.Invoke();
}

ここではフレームレートなどを定義し[IsRecording]をTrueに変えています。

これにより[LateUpdate]無いの処理が走り[RecordKeyframe()]が実行されます。

 public override void LateUpdate()
        {
            if (IsEnabled && IsRecording && Time.time > nextFrame)
            {
                EndTime = Time.time;
                nextFrame += frameInterval * (Mathf.Floor((Time.time - nextFrame) * frameRate) + 1f);

                if (UseBufferTimeLimit)
                {
                    PruneBuffer();
                }

                RecordKeyframe();
            }
        }

[RecordKeyframe()]で実際の記録が行われています。

ここで各Inputごとの処理関数を実行しています。例えばHandTrackingの記録を行う場合[RecordInputHandData(Handedness.Left)]が実行されます。

private void RecordKeyframe()
        {
            float time = Time.time;
            var profile = InputRecordingProfile;

            recordingBuffer.NewKeyframe(time);

            if (profile.RecordHandData)
            {
                RecordInputHandData(Handedness.Left);
                RecordInputHandData(Handedness.Right);
            }

            MixedRealityPose cameraPose;

            if (profile.RecordCameraPose && CameraCache.Main)
            {
                cameraPose = new MixedRealityPose(CameraCache.Main.transform.position, CameraCache.Main.transform.rotation);
                recordingBuffer.SetCameraPose(cameraPose);
            }
            else
            {
                cameraPose = new MixedRealityPose(Vector3.zero, Quaternion.identity);
            }

            if (profile.RecordEyeGaze)
            {
                if (eyeGazeProvider != null)
                {
                    recordingBuffer.SetGazeRay(eyeGazeProvider.LatestEyeGaze);
                }
                else
                {
                    recordingBuffer.SetGazeRay(new Ray(cameraPose.Position, cameraPose.Forward));
                }
            }
        }

[RecordInputHandData]を見ていきます。

private void RecordInputHandData(Handedness handedness)
        {
            float time = Time.time;
            var profile = InputRecordingProfile;

            var hand = HandJointUtils.FindHand(handedness);
            ...
            if (isTracked)
            {
                for (int i = 0; i < ArticulatedHandPose.JointCount; ++i)
                {
                    if (hand.TryGetJoint((TrackedHandJoint)i, out MixedRealityPose jointPose))
                    {
                        recordingBuffer.SetJointPose(handedness, (TrackedHandJoint)i, jointPose);
                    }
                }
            }
        }

ここで記録を行っている部分は次の一行になります。

  recordingBuffer.SetJointPose(handedness, (TrackedHandJoint)i, jointPose);

SetJointPoseではHandTrackingによって取得される各Jointが記録されていることがわかります。

f:id:Holomoto-Sumire:20211228221216p:plain

以上でRecordServiceを読み解いていきました。

実際はバイナリーデータで書き出されるためもう少し読み解いていきたいです。