MRTKを使ってHoloLensの空間メッシュを取得する

こんにちは。HoloLensチームの野元です。
先日、待ちに待ったHoloLens2が発売されましたね!手元にHoloLens2が来るのを今か今かと待ち望んでいる今日この頃です。(笑)

HoloLensを使用すると空間をスキャンしてメッシュデータを取得することが出来ます。
HoloLens1ではHTK(HoloToolKit)のSpatialMappingを使ってメッシュデータを取得していましたが、HoloLens2ではMRTK(MixedRealityToolkit)のSpatialAwarenessを使用しメッシュデータの取得する必要があります。(HoloLens1でもMRTKは使えます。)
HoloLens2がいつ手に入ってもいいように、MRTKを使用したメッシュデータの取得方法を見ていくことにします。

MRTK is 何?

MRTKは、Mixed Reality Toolkitの略称であり、Virtual Reality (VR) 及び Augmented Reality (AR) の Mixed Reality エクスペリエンスを構築するためのクロスプラットフォームツールキットのことです。
HoloLensアプリケーションを作る際には、MRTKがないと始まらないレベルで重要です。
詳しく知りたい方は、MRTK公式ドキュメントをご覧ください。

検証環境

  • Windows 10 SDK (10.0.18362.0)
  • Unity 2018.4.2.f1
  • Visual Studio 2019
  • Microsoft Mixed Reality Toolkit v2.1.0

方法その1. メッシュデータを全件取得する

下記の通りにすると、HoloLensがスキャンしたメッシュデータを一括で取得することできます。

// SpatialAwarenessSystemをIMixedRealityDataProviderAccessにキャストしてオブザーバーを取得します
var access = CoreServices.SpatialAwarenessSystem as IMixedRealityDataProviderAccess;

// 利用可能な最初のメッシュオブザーバーを取得します。通常、登録されているのは1つだけです。
var observer = access.GetDataProvider< IMixedRealitySpatialAwarenessMeshObserver>();

// 既知のすべてのメッシュをループします
foreach (SpatialAwarenessMeshObject meshObject in observer.Meshes.Values)
{
  // ここでメッシュが取れます
    Mesh mesh = meshObject.Filter.mesh;
  // 以降はお好みの処理をどうぞ!
}

方法その2. メッシュデータの差分を取得する

下記の通りにすると、HoloLensがスキャンしたメッシュデータを逐一差分で取得することができます。

// タイプを簡素化します
using SpatialAwarenessHandler = IMixedRealitySpatialAwarenessObservationHandler< SpatialAwarenessMeshObject>;

public class MeshObservationExample : MonoBehaviour, SpatialAwarenessHandler
{
    private bool isRegistered = false;

    private void OnEnable()
    {
        RegisterEventHandlers();
    }

    private void OnDisable()
    {
        UnregisterEventHandlers();
    }

    /// SpatialAwarenessSystemイベントを登録します
    private void RegisterEventHandlers()
    {
        if (!isRegistered && (CoreServices.SpatialAwarenessSystem != null))
        {
            CoreServices.SpatialAwarenessSystem.RegisterHandler< SpatialAwarenessHandler>(this);
            isRegistered = true;
        }
    }

    /// SpatialAwarenessSystemイベントを登録解除します
    private void UnregisterEventHandlers()
    {
        if (isRegistered && (CoreServices.SpatialAwarenessSystem != null))
        {
            CoreServices.SpatialAwarenessSystem.UnregisterHandler< SpatialAwarenessHandler>(this);
            isRegistered = false;
        }
    }

    /// メッシュが追加されたときに呼ばれます
    public void OnObservationAdded(MixedRealitySpatialAwarenessEventData< SpatialAwarenessMeshObject> eventData)
    {
        // メッシュ追加時の処理を記述します
    }

    /// メッシュが更新されたときに呼ばれます
    public void OnObservationUpdated(MixedRealitySpatialAwarenessEventData< SpatialAwarenessMeshObject> eventData)
    {
        // メッシュ更新時の処理を記述します
    }

    /// メッシュが削除されたときに呼ばれます
    public void OnObservationRemoved(MixedRealitySpatialAwarenessEventData< SpatialAwarenessMeshObject> eventData)
    {
        // メッシュ削除時の処理を記述します
    }
}

まとめ

周辺環境は常に変化していくと思うので、HoloLensの空間スキャンデータの差分をハンドリングする方がベターがと思います。
さらに詳しく知りたい方は、SpatialAwarenessの項目をご覧ください。