Oculus GoとALVRとNOLO-CV1でSteam VR Gameを遊ぶ

※2018/12/01追記
Noloから正式にOculus GO対応の[Streaming Serviceアプリ](https://ja-jp.facebook.com/NOLO.Virtual.Reality/)が出たので本手順は不要となりました!

# はじめに
[ALVR](https://github.com/polygraphene/ALVR)と[NOLO-CV1](https://www.nolovr.com/index?lang=en_US)を連携してSteam VR Gameを遊べる環境を構築する手順をまとめました。動機は下記。

- Oculus GoでALVR動かしたら思いのほか高品質で感動する。作者に感謝！
- 6DofとちゃんとしたコントローラーでSteam VRのゲームがやりたくなりNOLO-CV1を購入するがALVRがNOLO-CV1に対応してなくて愕然とする
- [VRidgeとRiftcatを使ってみるも](https://yukiosugiyama.com/2018/06/19/post-2228/)画質や遅延が酷くて使い物にならず絶望する
- NOLOの中の人が[ALVRの中の人にコンタクトを取っているよう](https://github.com/polygraphene/ALVR/issues/142)で期待しつつも、ALVRの中の人が[6Dof対応に消極的](https://github.com/polygraphene/ALVR/issues/8)なようなので不安が募る
- [Driver4VRとか](https://github.com/polygraphene/ALVR/issues/79)[VirtualHereUSB使え](https://github.com/polygraphene/ALVR/issues/24)とかキーワードは出てくるけど手順がよくわからん
- Oculus Santa Cruzが来るまで待てない
- もんもんとしてたら夢で[サッポロビールの作るしかない歌](https://www.youtube.com/watch?v=6zr6pWwHACU)を見たのでとりあえず自分で作ろうと奮起する
- コントローラの遅延はあるがBeat SaberのHardモードがクリアできる所までは来たので手順をまとめる事にした

おそらくDriver4VRとか使うのが王道な気もしますが、もっと良い＆簡単な方法をご存知の方は教えてください！

# 概要
ALVRは[FreePIE](http://andersmalmgren.github.io/FreePIE/)に対応しているようなのでALVRとNOLOをFreePIE経由で連携させる事を思い立ちましたが、NOLOとFreePIEとの連携方法がググってもよく分かりませんでした。
そのためNOLO用のFreePIEプラグインを自分で作りました。ソースは下記に置いてます。ネットで拾ったサンプルコードのコピペで作った習作ですので汚いコードですがご容赦ください。
- [NoloFreePIEPlugin](https://github.com/ahinore/NoloFreePIEPlugin)
- [NoloFreePIEClient](https://github.com/ahinore/NoloFreePIEClient)

NoloFreePIEClient(Androidアプリ)でOculus Goに接続したNOLOのセンサーデータをUDP経由でホストPCに転送し、NoloFreePIEPluginで受信してFreePIE内でNOLOの各種センシングデータを使えるようにしています。
上記とALVRのFreePIEプラグインを連携させる事により、ALVR内のコントローラーをNOLOで制御できるようになります。
※ALVRのAsynchronous Timewarpを生かすためNOLOで取得したHMDのローテーションデータは使わずに捨てる

ただALVRのソースを読んだ限りではHaptic Feedbackのイベントを拾っていないようで、上記だけだとNOLOのHaptic Feedback機能が使えません。そのためALVR側にも手を入れて、Haptic FeedbackイベントをFreePIEに渡せるようにしました。ソースは下記に置いてます。
- [ALVR with haptic feedback functionality](https://github.com/ahinore/ALVR/tree/feature/hapticfeedback)

一応ちゃんと動いたのでALVRにプルリクも送りましたが、初プルリクで不作法な感じ＆的外れな実装かもしれないのでマージされるかは未知数です。Haptic Feedbackが欲しい方は上記リポジトリのソースをコンパイルしてALVRを起動してみてください。
※Driver4VRとか使うといらないのかもしれない。
※VRidgeとRiftcatはHaptic対応してたのでもうちょい画質とレイテンシーがまともになれば。。。

Window10 x64、GTX1080の環境でテストしてます。

# 手順
下記手順ではNOLOのセンサーをOculus Goに接続する前提でNoloFreePIEClientのapkをOculus Goにインストールしてますが、NOLOの位置情報をPCに送信するだけなので、センサーの接続＆apkインストール先はOculus Goじゃなくても良いです。
別のAndroid端末持ってる方はそちらにセンサーを接続してapkをインストールした方がOculus Goにつなぐよりも電池の持ちがよくなりますし、Vysorも不要となるので良い気がします。

### 事前準備
1. ホストPCでadbコマンドを使えるようにする ※[こちら](https://sp7pc.com/google/android/android-howto/34263)を参照
2. Oculus Goをデベロッパーモードにする ※[こちら](https://www.scriptlife.jp/contents/programming/2018/05/10/oculus-go-adb/)を参照
3. Oculus GoとホストPCにALVRをインストールしSteam VRが動くようにする ※[こちら](http://www.itmedia.co.jp/news/articles/1806/15/news071.html)を参照
4. ホストPCに[FreePIE](http://andersmalmgren.github.io/FreePIE/)をインストールする
5. FreePIEのインストールディレクトリに[System.Reactive.dll](https://github.com/ahinore/NoloFreePIEPlugin/blob/master/NoloFreePIEPlugin/bin/Release/System.Reactive.dll)を配置する
6. FreePIEのプラグインディレクトリに[NoloFreePIEPlugin.dll](https://github.com/ahinore/NoloFreePIEPlugin/blob/master/NoloFreePIEPlugin/bin/Release/NoloFreePIEPlugin.dll)を配置する
7. FreePIEのプラグインディレクトリに[ALVRFreePIE.dll](https://github.com/polygraphene/ALVR/releases)を配置する ※ALVR-portable-v2.3.0.zipに入ってます
8. ホストPCに[Vysor](https://www.vysor.io/)をインストールする
9. Oculus GoをUSBでホストPCに接続し、下記コマンドで[NoloFreePIEClientのapk](https://github.com/ahinore/NoloFreePIEClient/blob/master/app/release/NoloFreePIEClient_v1.0_20180905_225447.apk)をインストールする ※apkは適当な位置に保存
 `adb install NoloFreePIEClient_v1.0_20180905_225447.apk`

10. NOLOのベースステーション、HMDセンサー、コントローラーを[ペアリング](https://www.youtube.com/watch?v=wrEywuWb8zQ)する

### 遊ぶ際の手順
1. Oculus GoをUSBでホストPCに接続する
2. Oculus GoのIPを調べ接続ポートを5555に設定する
 `adb shell ip addr show wlan0`
 ` adb tcpip 5555`

3. USBケーブルを外しadb connectでOculus Goにネットワーク経由で接続する
 `adb connect <2で調べたIP>:5555`

4. VysorでOculus Goに接続する
![4.jpg](https://qiita-image-store.s3.amazonaws.com/0/14831/4cdfe06e-0a4d-d4a8-0925-1c14bf4588bc.jpeg)
5. NOLOのHMDセンサーをOculus Goに接続する ※接続に成功すると音が鳴ります
6. 下記コマンドでNoloFreePIEClientを起動する
 `adb shell am start -n net.platyrhynchos.nolofreepieclient/net.platyrhynchos.nolofreepieclient.MainActivity`

7. Vysor経由でNoloFreePIEClientにホストPCのIPアドレスを設定しStartボタンを押す
![7.jpg](https://qiita-image-store.s3.amazonaws.com/0/14831/a7cdf4d3-7bb3-5267-e880-66b1169366f9.jpeg)
8. USB接続のダイアログが出たら、チェックボックスにチェックを入れタブを3回おしてOKボタンを押す ※OKボタンは見えないですが右下当たりにいるはず
![8.jpg](https://qiita-image-store.s3.amazonaws.com/0/14831/d7dfd567-61b3-d15f-8d6a-b74224a2d2cf.jpeg)
9. トラッキングが開始された事を確認する
![9.jpg](https://qiita-image-store.s3.amazonaws.com/0/14831/40d12045-cd85-b114-ef31-da72731877b3.jpeg)
10. Vysorの〇ボタンでホームに戻る
![10.jpg](https://qiita-image-store.s3.amazonaws.com/0/14831/26f1de8a-a6d4-6a6c-e00a-2e8a2ae45365.jpeg)
11. Oculus GoのALVR Clientを起動する
12. ホストPCのALVR＆Steam VRを起動し、ALVR Clientと接続する
13. NOLOのベースステーション、コントローラの電源を入れ、LEDが緑色になる事を確認する
![13.jpg](https://qiita-image-store.s3.amazonaws.com/0/14831/b93d57b8-3dd5-e28d-b252-9168adc35493.jpeg)
14. FreePIEを起動し、[NOLOとALVRを連携するスクリプト](https://github.com/ahinore/NoloFreePIEPlugin/blob/master/FreePIEScript_ALVR_NOLO.py)を読み込んで実行する
![14.jpg](https://qiita-image-store.s3.amazonaws.com/0/14831/5a962a1e-37ea-c4b5-477b-52a315fa63e5.jpeg)
15. Steam VR内でHMD、コントローラーが6Dofで動作する事を確認する
![15.jpg](https://qiita-image-store.s3.amazonaws.com/0/14831/a17e9ee7-5fc6-3e01-d907-4d133dabc47c.jpeg)
16. HMDセンサーの高さをSteam VRキャリブレーション時の高さに合わせ、HMDセンサー上部のボタンを押してキャリブレーションする
17. NOLOコントローラーをベースステーションに向けて電源ボタンをダブルクリックしキャリブレーションする
18. Steam VRのゲームで遊ぶ 
※Vysorは閉じておいた方がNetworkトラフィック減って良いかも
19. 止める時はVysorで再接続して右下の㈢ボタンを押して、NoloFreePIEClientのタスクを×ボタンで終了する

# 課題
- ヘッドトラッキングの位置情報がNOLOのセンサー位置を基準とするため、Oculus Goの上部にセンサーを設置すると頭を傾げる等の動きでポジションが大きめに動いてしまい違和感がある。VR空間内の頭の中央辺りが基準となる様に補正が必要。慣れないと酔うので何とかしたい。
- HMDとかコントローラーの位置が現実とズレているような気がするので補正が必要そう。でも遊んでると慣れるので気にしなければOK。
- コントローラーを動かしていると向きがズレる事がままある。特にセンサーデータの加工はしていないのでNOLOの問題な気がする。ズレたらベースステーションにコントローラーを向けて電源ボタンダブルクリックで補正可能。
- タッチパッドクリック時の挙動が怪しい気がする
- Haptic Eventの値をどうやってNOLOのHapticPuls関数に渡せば正解なのか分からないので、イベントが発生した事だけ拾って値は適当に設定してる
- Oculus Goの電池が40%以下になっている場合、NOLOセンサーを接続しても正常に認識してくれない。[Oculus Goの仕様上仕方がない](http://forums.nolovr.com/discussion/633/tutorial-for-oculus-go-nolo-cv1-steamvr-gameplay)らしいので使う前にフル充電する必要あり。一度認識すれば電池切れになるまで動かせる。
- NoloFreePIEClientのUIが手抜きのため操作にVysorが必要。でも[Oculus Goの仕様上USB接続時のダイアログ操作](https://forums.oculusvr.com/developer/discussion/65503/oculus-go-native-permission-dialog)でどうしてもVysorが必要になるのでやむなし
- 通信部分がUDPで指定IPに文字列をCSVで送りまくったりソケットのハンドリングが微妙だったりと雑な作り。ゲームの通信処理カッチリ作ってるサンプル何処かに無いかな
- Beat SaberのExpertモードをクリアできるようにするためにはもうちょいコントローラーの遅延を押さえる必要があるけど、何処がボトルネックか分からない。たぶん通信部分とかFreePIE部分だろうけど、この辺りのプロファイリングとか最適化はどうしたら良いのだろうか。NOLOのセンサーデータ取得部分だったらお手上げ
- C#側の通信処理をネットで拾ったサンプルのコピペで作ったらSystem.Reactive.dllが必要となってしまった。再配布方法が不明だったのでリポジトリに入れてしまったが良いのだろうか。このDLLだけ拾うの厳しそうだしどうやって配布するのが正解なんだろう
- 手順が複雑すぎるけど公式対応までの繋ぎだから我慢

# 参考文献
- [ALVR](https://github.com/polygraphene/ALVR)
- [FreePIEプラグイン開発方法](https://github.com/AndersMalmgren/FreePIE/wiki/Plugin-development)
- [Hapticイベントの拾い方](https://github.com/ValveSoftware/openvr/blob/master/samples/driver_sample/driver_sample.cpp)
- [Hapticイベントの所属コンポーネント判別方法](https://github.com/ValveSoftware/openvr/issues/792)