# Google VR NDK Controller

Using the Controller API requires no additional build system setup. The
functionality is present in `common.aar` and `libgvr.so`, which are already
being linked with your app.

> [!IMPORTANT]
> **Important:** the code assumes you will be using Android NDK rev 10e. We have not tested this code using other versions of the NDK. If you are using another version of the NDK and notice compilation or linking problems, try switching to NDK rev 10e.

## Using the ControllerApi

The main entrypoint for the controller API is the
[ControllerApi](https://developers.google.com/vr/android/ndk/reference/class/gvr/controller-api) class.

Create one at the start of your app:

    // Get your gvr_context pointer from the Java GvrLayout
    // (call gvrLayout.getGvrApi().getNativeGvrContext()).
    jlong j_native_gvr_context = .....;

    // Reinterpret it to a gvr_context*. This pointer is owned by
    // the GvrLayout. Do not destroy it.
    gvr_context* context = reinterpret_cast<gvr_context*>(j_native_gvr_context);

    // Initialize default options for the controller API.
    int32_t options = gvr::ControllerApi::DefaultOptions();

    // If you need accelerometer and gyro, enable them (they are not on
    // by default). Touch, buttons and orientation are on by default.
    options |= GVR_CONTROLLER_ENABLE_GYRO;
    options |= GVR_CONTROLLER_ENABLE_ACCEL;

    // Create the ControllerApi and ControllerState objects.
    std::unique_ptr<gvr::ControllerApi> controller_api(new ControllerApi);

    // Initialize it. Notice that we pass the gvr_context pointer.
    if (!controller_api->Init(options, gvr_context)) {
      // Handle failure. Do not proceed in case of failure (calling other
      // controller_api methods without a successful Init will crash with
      // an assert failure.
      return;
    }

    // Create a persistent controller state object. You must call Update() on
    // this each frame to get the current controller state.
    gvr::ControllerState controller_state;

    // If your app is in the resumed state (the Activity got onResume()), resume
    // the controller API now (if not, wait until you get onResume()).
    controller_api->Resume();

> [!NOTE]
> **Note:** Passing a `gvr_context*` to the controller API, although recommended, is not strictly required. You can pass `NULL` instead, and the effect will be that performing the recenter gesture in the controller will NOT automatically recenter the headset. In that case, you will have to recenter the headset yourself when the controller is recentered.

## Pausing and Resuming

You should pause and resume the Controller Client as well as `GvrApi`'s head
tracking when your app gets paused and resumed. When your app gets paused, call
the corresponding pause methods:

    gvr_api->PauseTracking();
    controller_api->Pause();

When your app resumes, call the corresponding resume methods:

    gvr_api->ResumeTracking();
    controller_api->Resume();

## Get the Controller State

When rendering a frame in your app, you can get the controller's current state
via a simple polling interface that lets you access the current state:

    void OnDrawFrame() {
      HandleController();
      DoFunPhysics();
      RenderAwesomeGraphics();
    }

    void HandleController() {
      // Read controller state, equivalent to gvr_controller_state_update().
      controller_state.Update(*controller_api);

      // To access the controller's state:
      // state.GetApiStatus() - status of the controller API
      // state.GetConnectionState() - connection state of the controller
      // state.GetOrientation() - the current orientation of the controller
      //    This is a quaternion in Start Space
      //    (X points to the right, Y points up, Z points back).
      // state.GetGyro() - current gyroscope reading
      // state.GetAccel() - current accelerometer reading
      // state.IsTouching() - whether the user is touching the touchpad
      // state.GetTouchPos() - current touch position
      //
      // Get button states:
      // state.GetButtonState(gvr::kControllerButtonApp)
      // state.GetButtonState(gvr::kControllerButtonClick)
      //
      // Transient events:
      // state.GetTouchDown() - user just started touching the touchpad
      // state.GetTouchUp() - user just stopped touching the touchpad
      //
      // Check if the app button was just released:
      // state.GetButtonUp(gvr::kControllerButtonApp)
      //
      // Check if the click button was just released:
      // state.GetButtonUp(gvr::kControllerButtonClick)
    }

The **transient event flags** (`GetTouchDown()`, `GetTouchUp()`,
`GetButtonDown()`, `GetButtonUp()`, `GetRecentered()`, etc) are called transient
because they will be true for only one call of `Update()` after the event
happens, then will revert back to false the next time you call `Update()`. Thus,
when they are true, they mean that the corresponding event happened since you
last called `Update()`.

## Start Space and Controller Space

The coordinate space for orientation, gyro and accelerometer are now
standardized and is now consistent across the headset and the controller. The
headset poses and the orientation of the controller are reported in what is
called **Start Space**.

The controller's orientation is represented as a quaternion. The quaternion
represents the controller's pose in Start Space. In other words, it specifies a
rotation from Controller Space to Start Space. Start Space is a cartesian
coordinate system with the following characteristics:

### Start Space

![](https://developers.google.com/static/vr/images/android/ndk/start-space.png)

- Start Space is **right-handed** , so when a viewer is positioned on the
  positive side of the Z axis and **looking at the origin**, they will see
  that the Y axis is 90 degrees counter-clockwise from the X axis
  (illustrated below).

- The Y axis points upwards (against gravity).

- The X and Z axes lie on the ground plane.

- Relative to the controller's pose when tracking starts, the X axis points
  **to the right** , and the Z axis points **back**.

- The axes do **NOT** move with the controller. They remain fixed.

Some examples of orientations are shown below:

| Pose^1^ | Resulting rotation | Expected quaternion^2^ ||||
| Pose^1^ | Resulting rotation | X | Y | Z | W |
|---|---|---|---|---|---|
| Initial pose, lying flat on table. | None (identity) | 0 | 0 | 0 | 1 |
| Flat on table, rotated 90 deg to the left | 90 degrees about Y axis | 0 | 0.7071 | 0 | 0.7071 |
| Flat on table, rotated 90 deg to the right | -90 degrees about Y axis | 0 | -0.7071 | 0 | 0.7071 |
| Flat on table, rotated 180 degrees (opposite to initial pose) | 180 degrees about Y axis | 0 | 1 | 0 | 0 |
| Pointing straight up | 90 degrees about X axis | 0.7071 | 0 | 0 | 0.7071 |
| Pointing straight down | -90 degrees about X axis | -0.7071 | 0 | 0 | 0.7071 |
| Rolled 90 degrees to the left | 90 degrees about Z axis | 0 | 0 | 0.7071 | 0.7071 |
| Rolled 90 degrees to the right | -90 degrees about Z axis | 0 | 0 | -0.7071 | 0.7071 |

(1) All poses are given relative to the initial pose; they are not cumulative
operations.

(2) \[X, Y, Z\] is the vector component, W is the scalar component. Note that (X,
Y, Z, W) expresses the same rotation as (-X, -Y, -Z, -W).

The **accelerometer** and **gyroscope** are reported in **Controller Space**,
which is a coordinate system that moves with the controller. In other words, the
axis are defined relative to the body of the controller and they move as the
controller moves.

### Controller Space

![](https://developers.google.com/static/vr/images/android/ndk/controller-space.png)

- Controller Space has the axes arranged in the same way as Start Space, but
  the coordinate system **moves with the controller**.

- The X axis always points to the right of the controller.

- The Y axis always points perpendicularly from the top surface of the
  controller.

- The Z axis always points along the controller's body, opposite to the
  controller's "forward" direction.

Here are some example of accelerometer readings:

| Pose\* | Expected accelerometer reading in m/s\^2 |||
| Pose\* | X | Y | Z |
|---|---|---|---|
| Lying flat on table, any heading. | 0 | 9.8 | 0 |
| Flipped on its back, on table. | 0 | -9.8 | 0 |
| Pointing straight up | 0 | 0 | -9.8 |
| Pointing straight down | 0 | 0 | 9.8 |
| Rolled 90 degrees to the left | 9.8 | 0 | 0 |
| Rolled 90 degrees to the right | -9.8 | 0 | 0 |

The gyroscope expresses a rotation in radians/second about each axis. The
rotation's sign follows the right-hand rule: a positive number means it's
rotating clockwise when the viewer sights along that axis. Here are some
example gyroscope readings:

| Motion | Expected gyroscope reading in radians/sec |||
| Motion | X | Y | Z |
|---|---|---|---|
| No motion | 0 | 0 | 0 |
| Flat on table, turning clockwise | 0 | negative | 0 |
| Flat on table, turning counter-clockwise | 0 | positive | 0 |
| Increasing pitch (moving nose up) | positive | 0 | 0 |
| Decreasing pitch (moving nose down) | negative | 0 | 0 |
| Rolling left | 0 | 0 | positive |
| Rolling right | 0 | 0 | negative |

## Recentering

The recentering gesture is automatically handled by the system. To recenter:

1. Press the HOME button (don't release it yet).

2. Point straight ahead, with the controller level with the horizon.

3. Continue to hold HOME for at least 1 second, then release it.

When the user recenters the controller, the API will start reporting head poses
and controller poses relative to the new center. You will know that a recenter
has happened because the `state.GetRecentered()` value will be true for one
frame.

Recentering only affects the controller and headset's heading (yaw) not the
pitch or roll. The pitch and roll are always defined by gravity.

> [!NOTE]
> **Note:** if you passed `NULL` as the `gvr_context*` pointer when initializing the Controller API, the headset will NOT be recentered automatically with the controller. In that case, you may want to manually recenter the headset when `state.GetRecentered()` is `true`.

## Monitor for Errors

On every frame, you should check `state.GetApiStatus()` and
`state.GetConnectionState()`. They tell you about the health of the connection
to the controller.

If `gvr::ControllerApiStatus` is anything other than `gvr::kControllerApiOk`,
something is permanently wrong with the client and requires user action to fix.

The `gvr::ControllerConnectionState`, on the other hand, is less serious: it
just tells you what the connection state of the controller is:

- **kControllerDisconnected**: Controller is disconnected
- **kControllerScanning**: Scanning for controller
- **kControllerConnecting**: Connecting to controller
- **kControllerConnected**: Controller is connected

## Reserved Buttons

Even though the API currently reports them, the following buttons are reserved
for system use and should not have any functionality in applications:

- **HOME** - reserved for launching system UIs and
  recentering.

- **VOLUME UP/DOWN** - fixed functionality,
  implemented by system.

The following buttons can be used freely by the application and have no planned
system functionality:

- **APP** button.

- **CLICK** button (touchpad click).