夜風のMixedReality

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

OpenXRのMRTKサンプルをチェックする その③ メインシーンを読み解く

本日は引き続きOpenXRのMRTKサンプルをチェックしていきます。

OpenXR-Unity-MixedReality-SamplesではOpenXRにのっとったHoloLens 2向けのサンプルプロジェクトを見ることができます。

 

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

またHoloLens 2のHolographic Remotingを試すことができます。

Holographic RemotingはHoloLens 2のアプリを外部PCのリソースを使用することで空け、ハイエンドな体験をできるようにするための仕組みです。

今回はこの機能を自身のアプリケーションで使用することを主目的にサンプルを読み解いています。

〇シーン構成

プロジェクト実行時に最初に読み込まれるMainMenuを除いていきます。

MainMenuシーンはSharedSubsceneシーンと同時に読み込み、実行されます。

SharedSubsceneシーンにはMixedRealityToolkitXRRigの二つがあり、これはすべてのシーンにおいて基準となるデバイス関連のシーンです。

SharedSubsceneシーンにMainMenuシーンやその他のシーンがオーバーレイされるようにしてプロジェクト内の体験が行われています。

MainMenuシーンは3つのオブジェクトで構成されています。

・AppRemoting

 HolographicRemotingの機能を管理するオブジェクト

・SampleSceneUtilities

 

 手の描画に使用するスフィアやUIなどが格納されているオブジェクトです。

・Sample Main Menu

 シーンを切り替えるための各種メニューです。

〇HolographicRemoting

AppRemotingここでは前述したようにHolographicRemoting関連の機能があります。

・FlatUI

PCアプリで開かれるHoloLens 2との接続に使用するUIです。

・Tappable Cube

接続解除に使用されるボタンです。

これらのパーツは親オブジェクトであるAppRemotingオブジェクトにアタッチされているApp Remoting Sampleコンポーネントで管理されています。

App Remoting SampleコンポーネントではAwake()関数で実行されている環境を確認し、PCアプリとして実行されている場合のみShowConnection2DUI()関数を実行しリモート用のUIを表示させています。

        private void Awake()
        {
            // This is intended for app remoting and shouldn't run in the editor
            // UnityEditorでの接続を想定していないためEditorでは接続用のUIの表示は行わない
            if (Application.isEditor)
            {
                DisableConnection2DUI();
                return;
            }


            SubsystemManager.GetInstances(XRDisplaySubsystems);
            foreach (XRDisplaySubsystem xrDisplaySubsystem in XRDisplaySubsystems)
            {
                // If a running XR display is found, assume an XR headset is attached.
                // In this case, don't display the UI, since the app has already launched
                // into an XR experience and it's too late to connect remoting.
     //XRデバイスで実行されている場合もリモート接続を行わないためUIを削除
                if (xrDisplaySubsystem.running)
                {
                    var connectionValid = Remoting.AppRemoting.TryGetConnectionState(out Remoting.ConnectionState connectionState, out Remoting.DisconnectReason disconnectReason);
                    if (!connectionValid || connectionState == Remoting.ConnectionState.Disconnected)
                    {
                        DisableConnection2DUI();
                    }
                    else
                    {
                        HideConnection2DUI();
                    }

                    return;
                }
            }

            ShowConnection2DUI();
        }
  //ボタンと接続用のUIのオブジェクトをアクティブ化
        private void ShowConnection2DUI()
        {
            SetObjectActive(immersiveUI, false);
            SetObjectActive(flatUI, true);
        }

Update関数ではRemoting.AppRemoting.TryGetConnectionState(out Remoting.ConnectionState connectionState, out Remoting.DisconnectReason disconnectReason)によってマイフレーム接続状況を確認しています。

これはMRTK OpenXR.Remotingで提供される関数でRemoting.ConnectionStateには接続状況に応じてenam型で

    Disconnected,
    Connecting,
    Connected,

のいずれかが返されます。

またRemoting.DisconnectReason disconnectReasonは接続が切断された場合なぜ切断されたかのログを返します。

これもenam型で定義されています

    None,
    Unknown,
    NoServerCertificate,
    HandshakePortBusy,
    HandshakeUnreachable,
    HandshakeConnectionFailed,
    AuthenticationFailed,
    RemotingVersionMismatch,
    IncompatibleTransportProtocols,
    HandshakeFailed,
    TransportPortBusy,
    TransportUnreachable,
    TransportConnectionFailed,
    ProtocolVersionMismatch,
    ProtocolError,
    VideoCodecNotAvailable,
    Canceled,
    ConnectionLost,
    DeviceLost,
    DisconnectRequest,
    HandshakeNetworkUnreachable,
    HandshakeConnectionRefused,
    VideoFormatNotAvailable,
    PeerDisconnectRequest,
    PeerDisconnectTimeout,
    SessionOpenTimeout,
    RemotingHandshakeTimeout,
    InternalError,
    HandshakePermissionDenied,

接続が確認された場合次の処理を行っているようです。

      private void Update()
        {
            //...
            var connectionStateValid = Remoting.AppRemoting.TryGetConnectionState(out Remoting.ConnectionState connectionState, out Remoting.DisconnectReason disconnectReason);

            if (connectionStateValid)
            {
                if (m_connectionState != connectionState || disconnectReason != m_disconnectReason)
                {
                    m_connectionState = connectionState;
                    m_disconnectReason = disconnectReason;

                    if (m_appRemotingMode == AppRemotingMode.connect)
                    {
                        Debug.Log($"Connection state changed : {ip}:{connectPort}, {connectionState}, {m_disconnectReason}");
                    }
                    else if (m_appRemotingMode == AppRemotingMode.listen)
                    {
                        Debug.Log($"Connection state changed : {hostIp}:{listenPort}, {connectionState}, {m_disconnectReason}");
                    }

                    switch (m_connectionState)
                    {
                        case Remoting.ConnectionState.Connected:
                            HideConnection2DUI();
                            break;
                        case Remoting.ConnectionState.Connecting:
                            ShowConnection2DUI();
                            break;
                        case Remoting.ConnectionState.Disconnected:
                            ShowConnection2DUI();
                            break;
                    }
                }
            }
            else
            {
                m_connectionState = Remoting.ConnectionState.Disconnected;
            }

            string commonMessage = "Welcome to App Remoting! Provide Ip address & click Connect or click Listen";

            string connectMessage = string.IsNullOrWhiteSpace(ip)
                    ? $"No IP address was provided to {nameof(Remoting.AppRemoting)}."
                        : m_connectionState == Remoting.ConnectionState.Connected
                            ? $"Connected to {ip}:{connectPort}."
                            : m_connectionState == Remoting.ConnectionState.Connecting
                                ? $"Connecting to {ip}:{connectPort}..."
                                : $"Disconnected to {ip}:{connectPort}. Reason is {m_disconnectReason}";
            string listenMessage = m_connectionState == Remoting.ConnectionState.Connected
                            ? $"Connected on {hostIp}."
                            : m_connectionState == Remoting.ConnectionState.Disconnected && m_listenCompleted
                                ? $"Disconnected on {hostIp}:{listenPort}. Reason is {m_disconnectReason}"
                                : $"Listening to incoming connection on {hostIp}";

            switch (m_appRemotingMode)
            {
                case AppRemotingMode.none:
                    outputText.text = commonMessage;
                    break;
                case AppRemotingMode.connect:
                    outputText.text = connectMessage;
                    break;
                case AppRemotingMode.listen:
                    outputText.text = listenMessage;
                    break;
            }
        }

今回はメインシーンであるMainMenuシーンを見て、軽くHolographicRemotingの冒頭のコードをさらってみました。

次回は続きから深堀していきます。