3Dデータ圧縮ライブラリDracoのUnityプラグインをHoloLensで動かす


# はじめに
Dracoで圧縮されたデータを読み込むUnityプラグインが用意されているので、UnityEditorとHoloLensで動かしてみました。
アセットの容量削減、クライアントとサーバー間のモデルデータ転送の高速化などに使えると思います。
サンプルプロジェクトはこちら ⇒ [DracoSampleForHoloLens](https://github.com/sotanmochi/DracoSampleForHoloLens)

![DracoSample.jpg](https://qiita-image-store.s3.amazonaws.com/0/162000/61506fd7-5007-6bde-d06f-d053aa817944.jpeg)

# Dracoについて
Dracoは、Google が開発している、3Dメッシュと点群データの圧縮/解凍のためのオープンソース・ライブラリです。
3Dモデルデータの保存や転送を改善することを目的に開発されています。
https://google.github.io/draco/

# テストデータ
OBJ形式のスタンフォードバニーをDracoで圧縮したデータを用意しました。Dracoで1/45程度まで圧縮されています。
サンプルプロジェクトの`Assets/Resources`に置いてあります。

- bunny_norm.obj (4,930,097 bytes)
- bunny.drc (108,924 bytes)

# Draco decoder plugin
マスターブランチの最新版のソースコードから HoloLens(UWP) 用に「dracodec_unity.dll」をビルドしました。（2018/05/28時点の最新版）
https://github.com/google/draco/tree/affc0ad8889e16ab34332192c57f7259ce61912f

HoloLens(UWP)用のDLLをビルドする時、CMakeとVisual Studioの設定に苦戦しましたが、それについては別の記事を書く予定です。
とりあえず使ってみたい場合は、サンプルプロジェクトのDLLをコピーしてください。

# 開発・検証環境
- Windows 10 Pro (OS build 16299.431)
- Unity 2017.4.2.f2
- Visual Studio 2017 (Version 15.6.6)
- HoloLens (OS build 10.0.17134.1000)

# サンプルプロジェクトのデプロイ方法
Unityから出力したソリューションファイルを開いて、Visual Studioからデプロイを実行する
![Build01.png](https://qiita-image-store.s3.amazonaws.com/0/162000/a9533c80-6290-36aa-a14b-2abd29d0c3ce.png)

アンセーフコードに関するエラーが発生する
![Build02.png](https://qiita-image-store.s3.amazonaws.com/0/162000/af3253ff-5b85-1e7e-5264-47ee321e3386.png)

Assembly-CSharpのプロパティで「アンセーフコードの許可」にチェックを入れる
![Build03.png](https://qiita-image-store.s3.amazonaws.com/0/162000/111d004f-adc2-4054-4079-3aa61e6580a8.png)

![Build04.png](https://qiita-image-store.s3.amazonaws.com/0/162000/b278e4af-9d16-3ecf-37fe-4912296a01b2.png)

再度、デプロイを実行する
![Build01.png](https://qiita-image-store.s3.amazonaws.com/0/162000/a9533c80-6290-36aa-a14b-2abd29d0c3ce.png)

## サンプルの実行結果
テストデータの「bunny.drc」と「bunny_norm.obj」を読み込んだ結果が表示されます。
モデルが左右反転していますが、Dracoのエンコーダーかデコーダーの処理で右手座標系と左手座標系が入れ替わったのでしょうか。

![DracoSample.jpg](https://qiita-image-store.s3.amazonaws.com/0/162000/61506fd7-5007-6bde-d06f-d053aa817944.jpeg)

# サンプルの解説
## Unityプロジェクトへのネイティブプラグインの追加
dracodec_unity.dllをPluginsフォルダに配置して、インポート設定を変更します。

HoloLens(UWP)用のDLLを「Plugins\WSA\x86」に配置する。設定は以下の通り。
![PluginUWP.png](https://qiita-image-store.s3.amazonaws.com/0/162000/74635732-ae42-2b85-fb85-371e5095c2b5.png)

UnityEditor用のDLLを「Plugins」に配置する。設定は以下の通り。
![PluginEditor.png](https://qiita-image-store.s3.amazonaws.com/0/162000/571451cb-4d33-ad43-5cc8-701f3d4633b1.png)

## Draco形式のデータを読み込む方法
Dracoのプロジェクトの中に「[DracoMeshLoader.cs](https://github.com/google/draco/blob/affc0ad8889e16ab34332192c57f7259ce61912f/unity/DracoMeshLoader.cs)」というUnityスクリプトが含まれているので、これを自分のプロジェクトにコピーして使います。

DracoMeshLoaderの使い方は以下のような感じです。読み込んだMeshをMeshFilterにセットすれば画面に描画できます。

```csharp
Mesh mesh = null;

List<Mesh> meshes = new List<Mesh>();
DracoMeshLoader dracoLoader = new DracoMeshLoader();

int numFaces = dracoLoader.LoadMeshFromAsset("bunny.drc", ref meshes);

if(numFaces > 0)
{
    mesh = meshes[0];
}
```

サンプルでは以下の2つのスクリプトを使って、画面に描画しています。

メッシュ読込用のクラス

```MeshLoader.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;

public class MeshLoader
{
    public static Mesh LoadMeshFromResources(string assetpath)
	{
        if(assetpath == null || assetpath.Equals(""))
        {
            Debug.Log("Assetpath is null or empty.");
            return null;
        }
        
        Mesh mesh = null;

        string extension = System.IO.Path.GetExtension(assetpath);
        if(extension.Equals(".drc"))
        {
            List<Mesh> meshes = new List<Mesh>();
    		DracoMeshLoader dracoLoader = new DracoMeshLoader();

    		int numFaces = dracoLoader.LoadMeshFromAsset(assetpath, ref meshes);

            if(numFaces > 0)
            {
                mesh = meshes[0];
            }
        }
        else
        {
            mesh = (Mesh)Resources.Load(assetpath, typeof(Mesh));
        }

        return mesh;
	}
}
```

オブジェクト描画用のスクリプト

```ResourceMeshObject.cs
using UnityEngine;

public class ResourceMeshObject : MonoBehaviour
{
	public string AssetName = "";

	void Start()
	{
		Mesh mesh = MeshLoader.LoadMeshFromResources(AssetName);

		MeshFilter filter = GetComponent<MeshFilter>();
		if(filter == null)
		{
			filter = gameObject.AddComponent<MeshFilter>();
		}
		filter.mesh = mesh;

		if(GetComponent<MeshRenderer>() == null)
		{
			MeshRenderer renderer = gameObject.AddComponent<MeshRenderer>();
			renderer.material = new Material(Shader.Find("Legacy Shaders/Diffuse"));
		}
	}
}
```

# まとめ
Dracoで圧縮されたスタンフォードバニーのモデルデータをHoloLensで読み込んで表示してみました。
実用的なアプリケーションには今のところ組み込めていませんが、アセットの容量削減、クライアントとサーバー間のモデルデータ転送の高速化などに使えると思います。
サンプルでは元のデータに対してモデルが左右反転していますが、Dracoの圧縮・解凍の処理で右手座標系と左手座標系が入れ替わったのではないかと考えられます。この点についても今後調査したいと思います。