TypeScriptとThreeJSで遊ぶ(Oculus Rift)

この記事はTypeScript Advent Calendarの15日目の記事です。

この記事では、TypeScriptでthree.jsとOcurusRiftを扱う方法について書きます。
取り扱う対象の性質により（？）、内容がオタク成分高めですが、ご容赦ください。

## OculusRiftの感想
数か月前、[OcurusRift DK2](https://www.oculus.com/ja/)を買いました。
無論、目的は[Steam](http://store.steampowered.com/) のゲームを立体視で遊ぶことです。
どうもネットの話題を見ていると、あの[Half-Life 2 が立体視で遊べる](https://www.youtube.com/watch?v=-RehCTRrWM0)らしいのです。
これは買わざるを得ません。

注文して数ヶ月後実物が届き、さっそくHalf-Life 2を起動してみます。
「おぉ、[NPC](http://ja.wikipedia.org/wiki/%E3%83%8E%E3%83%B3%E3%83%97%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E3%82%AD%E3%83%A3%E3%83%A9%E3%82%AF%E3%82%BF%E3%83%BC)の背丈が実感できる！[ゴードンさん](http://ja.wikipedia.org/wiki/%E3%82%B4%E3%83%BC%E3%83%89%E3%83%B3%E3%83%BB%E3%83%95%E3%83%AA%E3%83%BC%E3%83%9E%E3%83%B3)背が高い！」
...しかし残念なことに、気分が悪くなってしまい5分と遊べませんでした。
おそらく、Oculusで[FPS](http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%83%BC%E3%82%B9%E3%83%88%E3%83%91%E3%83%BC%E3%82%BD%E3%83%B3%E3%83%BB%E3%82%B7%E3%83%A5%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B2%E3%83%BC%E3%83%A0)を遊べるのは[フィギュアスケーター](http://www.ntv.co.jp/megaten/library/date/06/01/0115.html)だけなのではないでしょうか。
[OculusVR社のCEO](http://en.wikipedia.org/wiki/Palmer_Luckey)が「Oculus は今までのゲームを対応させるのでなく、専用に作らないとダメだ」というようなコメントをしていたらしいですが、これはまさしく本当の事でした。

気を取り直して、3D動画を見てみることにしました。
[StereoScopic Player](http://www.3dtv.at/Index_en.aspx)を使って、映画鑑賞... と思ったのですが、3D対応の映画ソフトが手元にありません。
近所のヨドバシに買いに行ったのですが、なんと[3D対応のブルーレイ](http://www.amazon.co.jp/s/?ie=UTF8&keywords=%E3%83%96%E3%83%AB%E3%83%BC%E3%83%AC%E3%82%A4+3%EF%BD%84&tag=googhydr-22&index=dvd&jp-ad-ap=0&hvadid=23902730465&hvpos=1t1&hvexid=&hvnetw=g&hvrand=3005704890227555392&hvpone=&hvptwo=&hvqmt=b&hvdev=c&ref=pd_sl_6fd9vcydyt_b)の多くは価格が5,000円近くするのです。
しかも、違法なコピープロテクト解除ツールを使わないとそもそもStereoScopicPlayer に取り込めないようです。
現実は甘くなかった...。

その後、[vorpX](http://www.vorpx.com/)なども使っていくつかのゲームで OculusRift を試してみたのですが、現状では主に以下の2つの問題点を感じました。

1. 前述のように、FPSのような視野の移動が激しいものだと酔ってしまう。
2. ピントの合う範囲が狭い。視野内の中央から70% くらいの範囲しかピントが合いません。最近のゲームは情報量が多く、おおよそのゲームが画面の端にHUDを配置していることを考えると、これはなかなか厳しい条件です。

Kinect にも言えるのですが、信じられないような斬新なハードウェアでも、それをゲームに生かして普通に遊べるようにするのははなかなか難しいのだなあと実感しました。
それでもOculusRiftはとても魅力的な事は確かなので、今後の進歩を期待したいです。


## 開発を試してみる
前置きが長くなってしまいましたが、このままではOculusRift がただの箱になってしまうので、TypeScriptで遊んでみることにしました。
そもそも DevelopmentKit なので開発に使用するのが本来の用途なのですが...。
なお、世の中の一般的なムードでは、OculusRift のアプリ制作にはUnity を使用することが多いようです(僕もおそらくそのほうが良いと思います)。
しかし、JSでもOculusRiftを扱えないわけではなく、とりあえず趣味的にいじるのであれば特に問題なく動作はするようです。

### three.js での Oculus 対応
3DアプリケーションをOculusRift に対応させるには、具体的には以下の2つの処理を追加する必要があります。

1. 画面出力を、[サイドバイサイド方式](http://www3.jvckenwood.com/dvmain/gs-td1/sp/about_3d.html)の左右に分割された映像にする。
2. OculusRift からの[入力（頭の位置）](http://doope.jp/2014/0131477.html)を、画面に反映させる。

1 については、three.js のポストプロセッシング処理によって簡単に対応できます。公式サイトに既に[サンプル](http://threejs.org/examples/#webgl_effects_oculusrift)がありますが、[OculusRiftEffect.js](https://github.com/mrdoob/three.js/blob/master/examples/js/effects/OculusRiftEffect.js) を出力時に挟み込むだけで、Oculus向けに調整されたサイドバイサイド映像が出力されます。ただし、自前で改造しないと[EffectComposer](https://github.com/mrdoob/three.js/blob/master/examples/js/postprocessing/EffectComposer.js) など他のポストプロセッシング処理とは併用できないようです。

2 については、ローカルマシンにサーバを建てて、Oculusの入力を受け取り、WebSocket 経由でthree.js に値を渡す必要があります。独自に実装するとそれなりに難しそうですが、[すでにJava製のサーバを作っている人がいました](http://laht.info/dk2-sensor-data-websocket-server/)ので、こちらを使うだけで済みます。

### デモ

今回作成してみたデモはこちらです。
http://gyohk.github.io/threejs-typescript-oculusdk2/

OculusRift をお持ちの場合、いちおう[こちらのリポジトリ](https://github.com/gyohk/threejs-typescript-oculusdk2)にソースコードが入っているので、実際に試すこともできるはずです(ac2014_demo ブランチです)。


デモは[板野ミサイル](http://dic.nicovideo.jp/a/%E6%9D%BF%E9%87%8E%E3%82%B5%E3%83%BC%E3%82%AB%E3%82%B9)を想起させる(?)見た目になっていますが、実はいくつかのサンプルを繋ぎ合わせているだけです。
ネタばらしをすると、この辺です。
http://jabtunes.com/labs/3d/bezierlights/
http://threejs.org/examples/#webgl_particles_sprites
http://threejs.org/examples/#webgl_geometry_dynamic

コードについては（単にいくつかのサンプルからの移植なので...）、それほど特別なことはありません。
ただ、TypeScriptコードの最初にある、以下の記述は少し特別です。

```
declare module THREE {
    var OculusRiftEffect: any;
    var DK2Controls: any;
}

module app {
    "use strict";

... (以下略)
```

three.js のプログラムを書く場合、本体のコードだけではなく、プラグインとして example/ のコードを利用することが多いと思います。
TypeScript の型定義ファイル three.d.ts には(型定義が肥大してしまい、キリがないため)そちらの内容は含まれていません。
そのためプラグインに含まれるクラス名を any で定義することにより回避する、という手段が必要になります。

### typescript の使用感
生JSだと、複数のサンプルコードを繋ぎ合わせる際、気づかないうちに変数名がバッティングしてしまったりして相当な苦行です(実際、色々なコードを試してみると無駄な変数や定義の重複などを頻繁に発見します)。
しかし、TypeScript を使うと、いったん個々のサンプルをJS -> TypeScript に移植してしまえば、コンパイラの導きに従うだけで、割と機械的にできてしまいます。
もちろん補完も効きますし、特にthree.js のようなAPI数の多い巨大なライブラリでは altJS のメリットは大きいと感じています。

## まとめ
* 現状では、OculusRift はプログラミングして遊ばないとただの箱です。
* [three.js の豊富なサンプル](http://threejs.org/examples/)を改造して立体視で見てみるだけでも数ヶ月は楽しめます。ぜひお時間のあるとき、TypeScriptで試してみてください。
* あと、もしOculusで遊べる楽しいゲームを御存じの方がいらっしゃいましたら、是非教えてください... ([AaaaaAAaaaAAAaaAAAAaAAAAA!!!](http://store.steampowered.com/app/15560/?l=japanese) 以外で)。