2017年6月4日日曜日

HoloLens開発 SpatialUnderstanding事始め 空間検知と空間内の情報(天井、床、壁)の判定まで

お疲れ様です。ZuQ9->Nnです。
久しぶりのHoloLens開発の進捗。
今回はSpatialUnderstandingです。

SpatialUnderstandingはクラス名、メソッド名が長くまた公式のサンプル
HoloToolkit-Examples > SpatialUnderstanding > SpatialUnderstanding-FeatureOverviewの
中のコードもかなり多めで解析が困難です。
ですから、まず空間検知と空間内の情報(天井、床、壁)の判定までを行っていきます。

今回は、Unity 5.6.1f1 (64-bit)
HoloToolKit-UnityはGitHubの最新を取得して行いました。

Unityのプロジェクトを作成し、
HoloToolKit-UnityとHoloToolkit-Testsのフォルダーを追加してください。

UnityにHoloToolKitのメニューが表示されますので
Configure > Apply HoloLens Project Settingsを選択
表示されたメニューをそのままApply > YES

プロジェクトの設定が終わったら再びUnityにHoloToolKitのメニューから
Configure > Apply HoloLens Capability Settingsを選択
Spatial PerceptionをチェックしApply

次に、 HierarchyのMain cameraを右クリック
メニューからDelelteで削除します

Project内のHoloToolkit > Input > PrefabのHoloLensCamera

続いてInputManager、

HoloToolkit > Input > Prefab > Cursorの中のCursor

HoloToolkit > SpatialMapping > Preabsの中のSpatialMapping

HoloToolkit > SpatialUnderstanding > Preabsの中のSpatialUnderstandingを

それぞれHierarchyに追加

HierarchyのSpatialMappingを選択し、Inspectorの
Object Surface Obsever Room Modelのプロパティをクリック
MediumRoomWithHomeFurnitureを選択しセット

Spatial Mapping ManagerのDraw Visual Meshesのチェックを外します。


この段階でいったんUnityを実行してみると。
こんな感じて空間検知が実行されメッシュが表示されます。

SpatialMapping同様、空間検知だけならノーコーディングでできてしまいます。

次にHierarchyのCreateから 3DObject > 3D Textを選択し追加
3D TextのInspectorで見やすい位置、設定にしてください。

今回はTransformのpositionをx:0 y:0 z;40
Text MehのAnchorをMiddle Cneter AlignmentをCenter
Font Sizeを30に設定しました。(ここはご自由にお好きな設定にしてください)


つづいて3D TextのInspector Add ComponentでTaglongと

BillboardのScriptを追加します

ProjectのCreateからC# Script、スクリプトの名前を
SpatialUnserstandingTestとします。

SpatialUnserstandingTestの中身はこんな感じ

using UnityEngine;
using HoloToolkit.Unity;
using System;

public class SpatialUnserstandingTest : MonoBehaviour {

    public TextMesh textMesh = null;
    public float kMinAreaForComplete = 50.0f;
    public float kMinHorizAreaForComplete = 25.0f;
    public float kMinWallAreaForComplete = 10.0f;

    private void LateUpdate()
    {
        if (DoesScanMeetMinBarForCompletion)
        {
            SpatialUnderstanding.Instance.RequestFinishScan();
        }

        Vector3 rayPos = Camera.main.transform.position;
        Vector3 rayVec = Camera.main.transform.forward * 10.0f;

        IntPtr raycastResultPtr = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticRaycastResultPtr();
        SpatialUnderstandingDll.Imports.PlayspaceRaycast(
            rayPos.x,
            rayPos.y,
            rayPos.z,
            rayVec.x,
            rayVec.y,
            rayVec.z,
            raycastResultPtr);

        SpatialUnderstandingDll.Imports.RaycastResult rayCastResult = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticRaycastResult();
        textMesh.text = rayCastResult.SurfaceType.ToString();
    }
    private bool DoesScanMeetMinBarForCompletion
    {
        get
        {
            if ((SpatialUnderstanding.Instance.ScanState != SpatialUnderstanding.ScanStates.Scanning) ||
                (!SpatialUnderstanding.Instance.AllowSpatialUnderstanding))
            {
                return false;
            }

            IntPtr statsPtr = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceStatsPtr();
            if (SpatialUnderstandingDll.Imports.QueryPlayspaceStats(statsPtr) == 0)
            {
                return false;
            }

            SpatialUnderstandingDll.Imports.PlayspaceStats stats = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceStats();
            if ((stats.TotalSurfaceArea > kMinAreaForComplete) ||
                (stats.HorizSurfaceArea > kMinHorizAreaForComplete) ||
                (stats.WallSurfaceArea > kMinWallAreaForComplete))
            {
                return true;
            }
            return false;
        }
    }
}

最後に、HierarchyのCreateからCreate EmptyでからのGameObjectを作成

作成したGameObjectのAdd Componentで先ほど作成した
SpatialUnserstandingTestとのスクリプトをアタッチ

SpatialUnserstandingTestのText MeshにNew Textを設定

これでUnityを実行、マウスでぐりぐり視線を動かすとこんな感じで
Testに空間の情報が表示されます。
video

それでは、SpatialMappingとSpatialUnserstandingとの相違点は何かといことですが
僕の理解では下記の点かとおもいます。(間違っていた場合はご指摘ください。)

*メッシュの形状が異なる
   これは一目瞭然なのですが、SpatialMappingは、かなり、
    ガタガタなメッシュが表示されるのに対し
 SpatialUnserstandingは場所によっては、
    とても綺麗なグリッド状のメッシュが表示されます。

*メッシュの構築プロセスが異なる
    これは文章で説明するとわかりにくいのですが、SpatialMappingは実行して
    時間がたてば行動しなくてもメッシュの情報が構築されていきます。
     SpatialUnserstandingは動画で示した通り、実行しただけでは、
     少ししかメッシュが表示されず
     積極的に視線を動かしていくことでメッシュの情報が構築されていきます。

*SpatialUnserstandingは直接、空間内の情報(天井、壁、床)を判別できる。
     SpatialMappingはSurfaceMeshesToPlanes.csのMakePlanesメソッドを実行し
     SurfacePlaneオブジェクトを生成しないと空間内の天井、壁、床を判別できませんが
     SpatialUnserstandingは視線の先の情報を直接判別することができます。

*空間内で判別できる情報がSpatialUnserstandingの方が多い
     SpatialMappingは、SurfacePlane.csにPlaneTypesのenumで定義されている
     Wall、Floor、Ceiling、Table、Unknownの5種類しか判別できないのに対し
     SpatialUnserstandingは、SpatialUnderstandingDll.csの
     内部クラスRaycastResultのSurfaceTypesのenumで定義されている
     Invalid、Other、Floor、FloorLike、Platform、Ceiling、WallExternal、WallLikeの8種類です

*SpatialUnserstandingでは定義以外の空間内情報を独自定義することができる
     MicrosoftのSpatialUnserstanding公式のサンプルでは
  ChairやCouch(ソファーのような長椅子)を独自定義し利用しています。

*SpatialUnserstandingでは空間内にオブジェクトを生成する便利関数が存在する
     HoloToolkit > SpatialMapping > Script内に
 SpatialUnderstandingDllObjectPlacement.csのスクリプトが用意されていて
     Create_OnFloor、Create_OnWall、Create_OnCeilingという、
     いかにもオブジェクトの生成を助けるような便利関数が存在しています。

これらを見ていくと、今回おこなった空間検知、空間内の情報の判別を行った後
最後のお便利関数を利用してオブジェクトを配置してくといった
処理の流れになるかと思います。

SpatialUnderstandingは、やはりコードも長く複雑な印象が強く
苦手意識が強くなりますが、今回くらいの内容であれば
意外と怖くないかも?っておもってくれる開発者が多くなるといいなぁと考えています。

SpatialUnderstandingは情報も少なかったので
個人的に参考にした記事のリンクも張っておきます

Indubitable Development
HoloLens Tutorial – Spatial Understanding

El Bruno
#Hololens – Spatial Understanding vs Spatial Mapping, and a step-by-step on how to use it

Mike Taulty
Hitchhiking the HoloToolkit-Unity, Leg 14–More with Spatial Understanding
https://mtaulty.com/2017/05/03/hitchhiking-the-holotoolkit-unity-leg-14-more-with-spatial-understanding/