HoloLensでFilepickerから選択したVRMファイルを読み込む


はじめに

HoloLensでVRMファイルを読み込んで表示してみました。
VRMファイルはFilePickerを使ってフォルダから選択できるようにしました。

サンプルプロジェクトこちら ⇒ VRMRuntimeLoaderSampleForHoloLens

いくつかハマりどころがありましたが、ポイントは以下の2点です。

  • Player SettingsのScripting BackendをIL2CPPに設定する。
  • IL2CPPビルドでファイルを読み込む時はWWWクラスを使う。 (WWWクラスを使わないとDirectoryNotFoundExceptionが発生する)


開発・検証環境

  • UniVRM v0.43
  • Unity 2017.4.9f1
  • Visual Studio 2017
  • HoloLens RS5 Preview (OS Build: 17720.1000)

事前準備・設定

UniVRMのインポート

UniVRM v0.43のunitypackageをダウンロードしてインポートする。

IL2CPPビルドの設定

Player Settings/Other SettingsのScripting BackendをIL2CPPに設定する。

Capabilitiesの設定

VRMファイルが置いてあるフォルダにアクセスできるようにCapabilitiesを設定する。
今回はObjects3Dフォルダを使うため、Player Settings/Publishing Settings/CapabilitiesのObjects3Dにチェックを入れておく。

VRMファイルを選択してランタイムロードする

サンプルスクリプト

以下のようなスクリプトを空のゲームオブジェクトにアタッチします。

VRMRuntimeLoaderUsingFilePicker.cs
using System.Collections;
using UnityEngine;
using System.IO;

#if !UNITY_EDITOR && UNITY_WSA_10_0
using System;
using System.Threading.Tasks;
using Windows.Storage.Pickers;
#endif

public class VRMRuntimeLoaderUsingFilePicker : MonoBehaviour
{
    public GameObject VRMRoot;

    void Start ()
    {
#if !UNITY_EDITOR && UNITY_WSA_10_0

        UnityEngine.WSA.Application.InvokeOnUIThread(async () =>
        {
            var openPicker = new FileOpenPicker();
            openPicker.SuggestedStartLocation = PickerLocationId.Objects3D;
            openPicker.FileTypeFilter.Add(".vrm");

            var file = await openPicker.PickSingleFileAsync();
            UnityEngine.WSA.Application.InvokeOnAppThread(() => 
            {
                if(file != null)
                {
                    StartCoroutine(LoadVrmCoroutine(file.Path));
                }
            }, false);
        }, false);

#elif UNITY_EDITOR

        string path = Application.dataPath + "/Models/" + "default.vrm";
        StartCoroutine(LoadVrmCoroutine(path));

#endif
    }

    IEnumerator LoadVrmCoroutine(string path)
    {
        var www = new WWW("file://" + path);
        yield return www;
        VRM.VRMImporter.LoadVrmAsync(www.bytes, OnLoaded);
    }

    void OnLoaded(GameObject vrm)
    {
        if(VRMRoot != null)
        {
            vrm.transform.SetParent(VRMRoot.transform, false);
        }
    }
}

ファイル選択

FileOpenPickerを使うことで、ファイルエクスプローラーを表示してファイルを選択することができます。
FileOpenPickerは、UnityのMainThreadでは利用できないため、UIThreadで動作させます。
選択されたファイルのパスをファイル読み込み用のメソッドに渡します。

"#if"の書き方はいくつかありますが、WINDOWS_UWPと書いてもIL2CPPで動くようです。
(参考:HoloLensのUnity内でFilepicker使ってみた


#if !UNITY_EDITOR && UNITY_WSA_10_0

        UnityEngine.WSA.Application.InvokeOnUIThread(async () =>
        {
            var openPicker = new FileOpenPicker();
            openPicker.SuggestedStartLocation = PickerLocationId.Objects3D;
            openPicker.FileTypeFilter.Add(".vrm");

            var file = await openPicker.PickSingleFileAsync();
            UnityEngine.WSA.Application.InvokeOnAppThread(() => 
            {
                if(file != null)
                {
                    StartCoroutine(LoadVrmCoroutine(file.Path));
                }
            }, false);
        }, false);

#elif UNITY_EDITOR

        string path = Application.dataPath + "/Models/" + "default.vrm";
        StartCoroutine(LoadVrmCoroutine(path));

#endif

VRMファイルの読み込み

WWWクラスでファイルを読み込み、byte配列をVRM.VRMImporter.LoadVrmAsyncに渡します。
IL2CPPビルドの場合、WWWクラスを使わないとDirectoryNotFoundExceptionが発生してファイルが読み込めませんでした。

また、VRMオブジェクトの位置や向きを調整できるように、OnLoadedメソッドでSetParentしています。
SetParentの第2引数(worldPositionStays)をfalseにしておくと調整可能です。(trueだとワールド座標系の位置・回転・スケールがキープされる)


    IEnumerator LoadVrmCoroutine(string path)
    {
        var www = new WWW("file://" + path);
        yield return www;
        VRM.VRMImporter.LoadVrmAsync(www.bytes, OnLoaded);
    }

    void OnLoaded(GameObject vrm)
    {
        if(VRMRoot != null)
        {
            vrm.transform.SetParent(VRMRoot.transform, false);
        }
    }

参考情報