OpenVR driver を作るメモ

[OpenVR](https://github.com/ValveSoftware/openvr)では独自にHMDやコントローラーやトラッカーを実装できます。
![tracker.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59664/464bc223-009e-3005-52c2-1e0f66c38786.jpeg)

`OpenVR v1.9.16`

本記事では Steam を `C:/Steam` にインストールしています。適当に読み替えてください。

# driver_sample の動かし方

https://github.com/ValveSoftware/openvr/tree/master/samples/driver_sample

## driver のフォルダ構成

```
+ sample(driver名と同じにする)
  + driver.vrdrivermanifest https://github.com/ValveSoftware/openvr/blob/master/samples/bin/drivers/sample/driver.vrdrivermanifest
  + bin
    + win64
      + driver_sample.dll(driver_{driver名}.dllという命名規則)
  + resources https://github.com/ValveSoftware/openvr/tree/master/samples/bin/drivers/sample/resources
```

となっていて、https://github.com/ValveSoftware/openvr/tree/master/samples/driver_sample をビルドして `driver_sample.dll` を得れば必要なファイルは揃います。

## driver を SteamVR に認識させる

### driver フォルダにコピーする

`C:\Steam\steamapps\common\SteamVR\drivers`

にsampleフォルダをコピーします。

### vrpathreg.exe でフォルダを SteamVR に登録する

こちらが推奨のようです。

```
> cd C:\Steam\common\SteamVR\bin\win64
> vrpathreg.exe
PS C:\Steam\steamapps\common\SteamVR\bin\win64> .\vrpathreg.exe
Runtime path = C:\Steam\steamapps\common\SteamVR
Config path = C:\Steam\config
Log path = C:\Steam\logs
> vrpathreg.exe --help
Commands:
  show - Display the current paths
  setruntime <path> - Sets the runtime path
  setthis - Sets the runtime path to the runtime that vrpathreg lives in
  setconfig <path> - Sets the config path
  setlog <path> - Sets the log path
  adddriver <path> - Adds an external driver
  removedriver <path> - Removes an external driver
> vrpathreg.exe adddriver {driver.vrdrivermanifestのあるフォルダ}
> vrpathreg.exe
Runtime path = C:\Steam\steamapps\common\SteamVR
Config path = C:\Steam\config
Log path = C:\Steam\logs
External Drivers:
        C:\openvr\samples\bin\drivers\sample
```

## 実験する

SteamVR を起動する。
動かない場合、
OpenVR に暗黙の前提があります。

* HMD(driver) はひとつ
* Controller, Tracker などは、HMDと同じドライバーから供給される

なので、ハードウェアのHMDが接続済みの環境では、そっちのdriverがロードされて driver_sample がロードされません。
ここで、 `driver.vrdrivermanifest` を修正します。

```
	"alwaysActivate": true,
```

https://github.com/ValveSoftware/openvr/wiki/DriverManifest

> alwaysActivate: If this is true this driver will be activated even if the active HMD is from another driver. Defaults to false 

この設定を変更してSteamVRを再起動すると、driver_sample に含まれている `NullHmd` と `Controller` のうち `Controller` が既存の driver に追加してロードすることができます。

![tracker.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59664/464bc223-009e-3005-52c2-1e0f66c38786.jpeg)

`Can't add device sample.1234: Can't add a second HMD`

HMDは依然としてロードできません(2個目のHMDが可能かは調査中)。

# Debug
## ログの見方

`C:\Steam\logs\vrserver.txt`

もしくは

![webconsole.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59664/eccea224-0427-bb89-68e3-f827cffb3ba6.jpeg)

ソースコード上で、

```cpp
DriverLog("Using settings values\n");
```

とすると

```
Sat Jan 25 2020 17:09:44.065 - sample: Using settings values
```

と出てきます。

## デバッガのアタッチ
VSCode の場合。

```json:.vscode/launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(Windows) 接続",
            "type": "cppvsdbg",
            "request": "attach",
            "processId": "${command:pickProcess}"
        }
    ]
}
```

F5 で Debug を開始すると、プロセス一覧が出てくるので、
`vrserver.exe` を選択する。

IServerTrackedDeviceProvider::RunFrame で breakpoint を止めることができた。

# OpenVR driverの構成
https://github.com/ValveSoftware/openvr/wiki/Driver-Documentation

## dll(driverの本体)

`HmdDriverFactory` を公開する。

```c++
#include <openvr_driver.h>
extern vr::IServerTrackedDeviceProvider* GetInstance();

extern "C" __declspec(dllexport) void *HmdDriverFactory(const char *pInterfaceName, int *pReturnCode)
{
	if (0 == strcmp(vr::IServerTrackedDeviceProvider_Version, pInterfaceName))
	{
		// singletone的なvr::IServerTrackedDeviceProvider*を返す
		return GetInstance();
	}
	if (pReturnCode)
	{
		*pReturnCode = vr::VRInitError_Init_InterfaceNotFound;
	}
	return nullptr;
}
```

内容は決まっていてだいたいこんな感じ。
Server 以外に WatchDog も返すことができるのだけど、必要になってからでよさそう。

## verserver.exe と driver のロード

vrserver.exe が driver を制御するプロセスのようです。

* SteamVRを起動すると verserver.exe が起動される
* verserver.exe にデバッガをアタッチすると driver に break point しかけられる
* VR_Init. vrserver.exe に接続する。起動していなければ起動する
* VR_Shutdown. vrserver.exe を shutdown する。個別のアプリは呼ばなくてよい

ドライバのロード

`C:\Steam\steamapps\common\SteamVR\drivers`

と

vrpathreg.exe で登録された外部パスの中から順番にロードして、hmd が有効なものが見つかったそれをアクティブにする。
通常はこれで、アクティブなドライバがひとつになってロード終了。

もし `driver.vrdrivermanifest` 

```
	"alwaysActivate": true,
```

な driver であれば処理を続行という挙動の様子。

### HMD

active なドライバが１つだけ有効にできる？

### コントローラー

WIP

### Tracker

WIP

## IServerTrackedDeviceProvider
IServerTrackedDeviceProvider::Init関数で、ITrackedDeviceServerDriver をシステムに登録する。
複数登録してもよい。
ITrackedDeviceServerDriver は HMD、Controller, Tracker といった個々のデバイスを指す。

というより、

* カメラ情報(画角、フレームレートなどの情報)得たり、画像を出力できる Tracker => HMD
* ボタン入力やHaptics出力できる Tracker => Controller 

という感じで基本にTrackerがあって、Trackerに機能を追加していく感じ。

### Init 
* 設定
* デバイスの登録

### RunFrame 
* イベントを処理する
* デバイスのRunFrameを呼び出す(してもよい)

## ITrackedDeviceServerDriver

### GetPose
トラッカー(HMD, Controller)の初期姿勢を返す。

### コントローラーの実装

#### ボタン入力

#### haptics出力

### GetComponent
ITrackedDeviceServerDriver に特定の機能があるときに実装する。

#### IVRDisplayComponent
HMDのパラメーターを返す

#### IVRDriverDirectModeComponent
ハードウェアへの出力を実装

#### IVRCameraComponent
HMDに付属している外部カメラの映像？

#### IVRIOBuffer
謎。

#### IVRVirtualDisplay

レンダリング画像を、録画したり、配信したりする用途。Wireless HMD など。

> TrackedDeviceClass_DisplayRedirect - Accessories that aren't necessarily tracked themselves, but may redirect video output from other tracked devices

https://github.com/ValveSoftware/virtual_display

https://github.com/ValveSoftware/virtual_display/blob/master/driver.vrdrivermanifest

```
"redirectsDisplay": true,
```

ロード順の制御？

https://steamcommunity.com/app/250820/discussions/3/2733047810442171975/

# VR設定

`C:\Steam\steamapps\common\SteamVR\resources\settings\defalut.vrsettings`

* http://shop-0761.hatenablog.com/entry/2018/01/08/034418
* https://syan0.hatenadiary.org/entry/20180316/1521199910
* http://ymremio.hatenablog.com/entry/2018/09/07/001155
* [Vive TrackerをHMDなしで使うメモ](http://haquxx.com/vive-tracker%E3%82%92hmd%E3%81%AA%E3%81%97%E3%81%A7%E4%BD%BF%E3%81%86%E3%83%A1%E3%83%A2/)

# 各デバイスの役割割り当て問題
普通に接続したときには HMD, LeftController, RightController という基本構成になるが、
3rdController とか 2ndHMD があったとしてどのように役割を割り当てるのかという問題。

## アプリが使い方を知っている
* 3rdController をMR合成用のカメラ位置として使う

## アプリが使い方を知らない
* 3rdController で既存のコントローラーを乗っ取る

https://github.com/ValveSoftware/openvr/wiki/TrackingOverrides

TODO

## OpenVR Action Input

[Steam VR Input System(OpenVR Action Input)についてのメモ](https://qiita.com/gpsnmeajp/items/e423c699dde7aecb25cc)

https://github.com/ValveSoftware/openvr/wiki/Input-Profiles

# OpenVR driver 実装

* https://github.com/matzman666/OpenVR-InputEmulator