GearVR FrameworkでHello World

ちょっと見ないうちに[GearVR Framework](http://gearvrf.org)のWebサイトがリニューアルされていたりバージョンが4.0まで上がっていたりしたので、GearVR FrameworkでHello Worldする手順をもう一度確認してみました。

## プロジェクトの作成

普通にAndroidプロジェクトを作成してください。Minimum SDKは最低でも21以上にしてください。Empty Activityのテンプレートを選び、Generate Layout FileとBackwards Compatibility (AppCompat)の**チェックを外して**プロジェクトを作ると楽です。

プロジェクトができたらプロジェクト直下にある*build.gradle*に少し修正を加えます。

```groovy:%projectRoot%/build.gradle
...

allprojects {
    repositories {
        jcenter()

        // ↓これを追加
        maven {
            url "https://oss.sonatype.org/content/repositories/snapshots/"
        }
    }
}
```

次に、アプリケーションモジュールの*build.gradle*に

```groovy

android {
    ...
    defaultConfig {
        ...
        // ↓これを追加しないと実行時にエラーになる場合がある
        ndk {
            abiFilters 'armeabi-v7a'
        }
    }
}

// ↓追加
ext.gearvrfVersion = '4.0.0'
project.ext.jomlVersion = "1.9.1-SNAPSHOT"

dependencies {
    // ↓以下のライブラリを全て追加
    compile "org.joml:joml-android:$jomlVersion"
    compile "org.gearvrf:framework:$gearvrfVersion"
    compile "org.gearvrf:backend_daydream:$gearvrfVersion"
    compile "org.gearvrf:backend_oculus:$gearvrfVersion"
    compile 'com.google.vr:sdk-base:1.80.0'
}
```

## アプリケーションの実装

通常のAndroidアプリでは`(AppCompat)Activity`を継承したクラスを作るところから始めますが、GearVR Frameworkでは`GVRActivity`を継承します。`GVRActivity`の中でVRレンダリング用の諸々の処理をしてくれるので、アプリケーション開発者はそれらのことを意識する必要はありません。
また、UIの初期表示を設定するのに通常は`Activity.onCreate()`の中で`Activity.setContentView()`を呼びますが、これも`GVRActivity`の処理の中で自動的に設定されるため、自分では何もしなくて良いです。

代わりにGearVR Frameworkでは`GVRActivity.onCreate()`の中でアプリケーションのメイン処理を行うクラスを指定する`GVRActivity.setMain()`を呼び出します。ここに渡すのは`GVRMain`を継承したクラスのインスタンスで、アプリケーション初期化時や毎フレームの画面更新処理を行います。実際のアプリケーションの処理のほとんどはこちらのクラスに書かれることになります。

それでは簡単なアプリを実装してみます。

```java:MainActivity.java
public class MainActivity extends GVRActivity {
    private GVRMain main = new MainScene();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setMain(main);
    }
}
```

↑で設定している`MainScene`の中身です。

```java:MainScene.java
class MainScene extends GVRMain {

    // アプリケーションの初期化時に一度だけ呼ばれる。
    @Override
    public void onInit(GVRContext gvrContext) {
        super.onInit(gvrContext);

        // アプリアイコンをテクスチャに設定したキューブを作成する。
        GVRSceneObject obj = new GVRCubeSceneObject(gvrContext);
        obj.getTransform().setPositionZ(-5.0f); // -Z は前方向
        obj.getRenderData().setMaterial(new GVRMaterial(gvrContext));
        obj.getRenderData().getMaterial().setMainTexture(gvrContext.getAssetLoader().loadTexture(new GVRAndroidResource(gvrContext, R.mipmap.ic_launcher_round)));

        // 動きを付ける(後述)
        obj.attachComponent(new Rotator(gvrContext));

        // 作成したキューブオブジェクトをシーンに追加する
        gvrContext.getMainScene().addSceneObject(obj);
    }
}
```

上で`obj.attachComponent(new Rotator(gvrContext))`という処理がありました。シーン中のオブジェクトに対して何らかの動きをつけたりする場合、`GVRBehavior`を継承したクラスを作り、この中で処理を記述します。そしてオブジェクトに`attachComponent`で登録することで毎フレームの更新通知を受け取ったりすることができるようになります。**ちょうどUnityのComponentのような感じ**で使えます。

```java:Rotator.java
class Rotator extends GVRBehavior {

    public Rotator(GVRContext gvrContext) {
        super(gvrContext);
    }

    @Override
    public void onDrawFrame(float frameTime) {
        // 毎秒45度ずつ、Y軸を中心に回転する
        getTransform().rotateByAxis(frameTime * 45.0f, 0.0f, 1.0f, 0.0f);
    }
}
```

## Gear VRで実行する

ここまでできたら、アプリをGear VRで動かしてみましょう。Gear VRでカスタムアプリを動かすためには[osigファイル](https://qiita.com/niusounds/items/167b5a7108313c6e31c2)が必要なので、あらかじめ作っておいて*app/src/main/assets*の中に入れておきます。
次にAndroidManifestを編集します。

```xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.eje_c.gvrftest">

    <!-- ↓android:themeは削除する -->
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:resizeableActivity="false"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true">

        <!-- ↓Gear VRモードでアプリを動かすために必要 -->
        <meta-data
            android:name="com.samsung.android.vr.application.mode"
            android:value="vr_only" />

        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|screenSize|keyboard|keyboardHidden"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
```

ここまでできたらアプリをビルドして実行しましょう。

![1.png](https://qiita-image-store.s3.amazonaws.com/0/56668/7ce1aad9-0852-1fbb-2d03-e5c930959ec1.png)
![2.png](https://qiita-image-store.s3.amazonaws.com/0/56668/905b3a7b-64b2-561b-d3c7-5a6892debb4e.png)

## Daydreamで実行する

GearVR Frameworkのすごいところは、**一度コードを書けばGear VRとDaydream両対応のアプリにできることです。**Daydreamで動かすために必要なのは、AndroidManifestの`<intent-filter>`に`<category android:name="com.google.intent.category.DAYDREAM" />`を追加するだけです。また、Gear VRに必要だったosigや`<meta-data>`の追加は必要ありません。

```xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...>

    <application
        ...>

        <activity
            ...>
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
                <!-- ↓追加する -->
                <category android:name="com.google.intent.category.DAYDREAM" />
                <!-- ↓これも追加すると、Daydream Readyの端末以外でもCardboardで使える -->
                <category android:name="com.google.intent.category.CARDBOARD" />
            </intent-filter>
        </activity>
    </application>

</manifest>
```

![1.png](https://qiita-image-store.s3.amazonaws.com/0/56668/d937b745-c043-e883-2cc1-6208aa7db30a.png)
![2.png](https://qiita-image-store.s3.amazonaws.com/0/56668/5cec13ca-7e7c-966f-afde-21825a697b29.png)

## 単眼モードで実行する

GearVR Frameworkは立体視の二眼モードだけでなく**ハコスコなどの単眼ビュワー向けに単眼レンダリングをすることもできます。**ただしこのモードで動作するのはGalaxy系のGear VRが動作する端末だけなようです。そして**osigも必要です。**Gear VRアプリじゃないのに…

単眼モードでレンダリングするためには、*app/src/main/assets/gvr.xml*というファイルを作成して(名前は後で指定するコードと揃っていれば何でも良い)、中身を以下のようにします。

```xml:gvr.xml
<?xml version="1.0" encoding="UTF-8"?>
<lens name="N4">
    <vr-app-settings>
        <mono-mode-parms monoMode="true" />
    </vr-app-settings>
</lens>
```

そして`MainActivity`のコードで`setMain`を呼び出している箇所の第二引数にこのファイル名を渡します。

```java:MainActivity.java
public class MainActivity extends GVRActivity {
    private GVRMain main = new MainScene();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setMain(main, "gvr.xml"); // <- 第二引数を追加
    }
}
```

それから、AndroidManifest.xmlに`<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only" />`の指定をしていた場合は、これを削除します。

![1.png](https://qiita-image-store.s3.amazonaws.com/0/56668/d7b2bf88-ce22-f62f-3be5-c3b3a8bff0ad.png)

これより先は公式ページの[チュートリアル](http://gearvrf.org/tutorials/simple_vr_app/)をご覧ください！