# Understanding the Treasure Hunt sample

## Code walkthrough

This walkthrough shows you how the iOS sample Treasure Hunt handles the following tasks:

- Implement a
  [`UIViewController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/)
  to host
  [`GVRCardboardView`](https://developers.google.com/vr/ios/reference/interface_g_v_r_cardboard_view).

- Define a renderer to implement the
  [`GVRCardboardViewDelegate`](https://developers.google.com/vr/ios/reference/protocol_g_v_r_cardboard_view_delegate-p)
  protocol.

- Add a render loop using the
  [`CADisplayLink`](https://developer.apple.com/library/prerelease/ios/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/index.html)
  object.

- Handle inputs.

### Implement a UIViewController to host GVRCardboardView

The TreasureHunt app implements a
[`UIViewController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/),
the `TreasureHuntViewController` class. This UIViewController class has an
instance of [`GVRCardboardView`](https://developers.google.com/vr/ios/reference/interface_g_v_r_cardboard_view)
class. An instance of the `TreasureHuntRenderer` class is created and set as a
[`GVRCardboardViewDelegate`](https://developers.google.com/vr/ios/reference/protocol_g_v_r_cardboard_view_delegate-p)
for the `GVRCardboardView`. In addition, the app provides a
render loop, the `TreasureHuntRenderLoop` class, that drives the
[`-render`](https://developers.google.com/vr/ios/reference/interface_g_v_r_cardboard_view#method-detail)
method of the GVRCardboardView.

    - (void)loadView {
      _treasureHuntRenderer = [[TreasureHuntRenderer alloc] init];
      _treasureHuntRenderer.delegate = self;

      _cardboardView = [[GVRCardboardView alloc] initWithFrame:CGRectZero];
      _cardboardView.delegate = _treasureHuntRenderer;
      ...
      _cardboardView.vrModeEnabled = YES;
      ...
      self.view = _cardboardView;
    }

### Define a renderer to implement the GVRCardboardViewDelegate protocol

[`GVRCardboardView`](https://developers.google.com/vr/ios/reference/interface_g_v_r_cardboard_view)
provides a drawing surface for your rendering. It coordinates the drawing with
your rendering code through the
[`GVRCardboardViewDelegate`](https://developers.google.com/vr/ios/reference/protocol_g_v_r_cardboard_view_delegate-p)
protocol. To achieve this, the `TreasureHuntRenderer` class implements
`GVRCardboardViewDelegate`:

    #import "GVRCardboardView.h"

    /** TreasureHunt renderer. */
    @interface TreasureHuntRenderer : NSObject<GVRCardboardViewDelegate>

    @end

### Implement the GVRCardboardViewDelegate protocol

To draw the GL content onto
[`GVRCardboardView`](https://developers.google.com/vr/ios/reference/interface_g_v_r_cardboard_view), `TreasureHuntRenderer` implements the
[`GVRCardboardViewDelegate`](https://developers.google.com/vr/ios/reference/protocol_g_v_r_cardboard_view_delegate-p)
protocol:

    @protocol GVRCardboardViewDelegate<NSObject>

    - (void)cardboardView:(GVRCardboardView *)cardboardView
             didFireEvent:(GVRUserEvent)event;

    - (void)cardboardView:(GVRCardboardView *)cardboardView
         willStartDrawing:(GVRHeadTransform *)headTransform;

    - (void)cardboardView:(GVRCardboardView *)cardboardView
         prepareDrawFrame:(GVRHeadTransform *)headTransform;

    - (void)cardboardView:(GVRCardboardView *)cardboardView
                  drawEye:(GVREye)eye
        withHeadTransform:(GVRHeadTransform *)headTransform;

    - (void)cardboardView:(GVRCardboardView *)cardboardView
       shouldPauseDrawing:(BOOL)pause;

    @end

Implementations for the `willStartDrawing`, `prepareDrawFrame`, and `drawEye`
methods are described below.

#### Implement willStartDrawing

To perform one-time GL state initialization, implement
[`-cardboardView:willStartDrawing:`](https://developers.google.com/vr/ios/reference/protocol_g_v_r_cardboard_view_delegate-p#method-detail).
Use this opportunity to load shaders, initialize scene geometry, and bind to GL
parameters. We also initialize an instance of the
[`GVRCardboardAudioEngine`](https://developers.google.com/vr/ios/reference/interface_g_v_r_audio_engine)
class here:

    - (void)cardboardView:(GVRCardboardView *)cardboardView
         willStartDrawing:(GVRHeadTransform *)headTransform {
      // Load shaders and bind GL attributes.
      // Load mesh and model geometry.
      // Initialize GVRCardboardAudio engine.
      _cardboard_audio_engine =
      [[GVRCardboardAudioEngine alloc]initWithRenderingMode:
          kRenderingModeBinauralHighQuality];
      [_cardboard_audio_engine preloadSoundFile:kSampleFilename];
      [_cardboard_audio_engine start];
      ...
      [self spawnCube];
    }

#### Implement prepareDrawFrame

To set up rendering logic before the individual eyes are rendered, implement
[`-cardboardView:prepareDrawFrame:`](https://developers.google.com/vr/ios/reference/protocol_g_v_r_cardboard_view_delegate-p#method-detail).
Any per-frame operations specific to this rendering should happen here. This is
a good place to update your model and clear the GL state for drawing. The app
computes the head orientation and updates the audio engine.

    - (void)cardboardView:(GVRCardboardView *)cardboardView
         prepareDrawFrame:(GVRHeadTransform *)headTransform {
      GLKMatrix4 head_from_start_matrix = [headTransform headPoseInStartSpace];
      // Update audio listener's head rotation.
      const GLKQuaternion head_rotation =
          GLKQuaternionMakeWithMatrix4(GLKMatrix4Transpose(
          [headTransform headPoseInStartSpace]));
      [_cardboard_audio_engine setHeadRotation:head_rotation.q[0]
                                             y:head_rotation.q[1]
                                             z:head_rotation.q[2]
                                             w:head_rotation.q[3]];
      // Update the audio engine.
      [_cardboard_audio_engine update];

      // Clear the GL viewport.
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      glEnable(GL_DEPTH_TEST);
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glEnable(GL_SCISSOR_TEST);
    }

#### Implement drawEye

The
[`drawEye`](https://developers.google.com/vr/ios/reference/protocol_g_v_r_cardboard_view_delegate-p#method-detail)
delegate provides the core of the rendering code, similar to building a regular
OpenGL ES application.

The following snippet shows how to implement `drawEye`
to get the view transformation matrix for each eye and the perspective
transformation matrix. Note that this method gets called for each eye. If the
`GVRCardboardView` does not have VR mode enabled, then eye is set to the center
eye. This is useful for monoscopic rendering, to provide a non-VR view of the
3D scene.

    - (void)cardboardView:(GVRCardboardView *)cardboardView
                  drawEye:(GVREye)eye
        withHeadTransform:(GVRHeadTransform *)headTransform {
      // Set the viewport.
      CGRect viewport = [headTransform viewportForEye:eye];
      glViewport(viewport.origin.x, viewport.origin.y, viewport.size.width,
          viewport.size.height);
      glScissor(viewport.origin.x, viewport.origin.y, viewport.size.width,
          viewport.size.height);

      // Get the head matrix.
      const GLKMatrix4 head_from_start_matrix =
          [headTransform headPoseInStartSpace];

      // Get this eye's matrices.
      GLKMatrix4 projection_matrix = [headTransform
          projectionMatrixForEye:eye near:0.1f far:100.0f];
      GLKMatrix4 eye_from_head_matrix =
          [headTransform eyeFromHeadMatrix:eye];

      // Compute the model view projection matrix.
      GLKMatrix4 model_view_projection_matrix =
          GLKMatrix4Multiply(projection_matrix,
          GLKMatrix4Multiply(eye_from_head_matrix, head_from_start_matrix));

      // Render from this eye.
      [self renderWithModelViewProjectionMatrix:model_view_projection_matrix.m];
    }

After returning from this call,
[`GVRCardboardView`](https://developers.google.com/vr/ios/reference/interface_g_v_r_cardboard_view)
renders the scene to the display.

### Add a render loop using CADisplayLink

The rendering needs to be driven by a render loop using
[`CADisplayLink`](https://developer.apple.com/library/prerelease/ios/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/index.html).
The TreasureHunt app provides a sample render loop: `TreasureHuntRenderLoop`.
This needs to call the
[`-render`](https://developers.google.com/vr/ios/reference/interface_g_v_r_cardboard_view#method-detail)
method of the
[`GVRCardboardView`](https://developers.google.com/vr/ios/reference/interface_g_v_r_cardboard_view)
class. This is handled in the
[`-viewWillAppear:`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instm/UIViewController/viewWillAppear:)
and
[`- viewDidDisappear:`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instm/UIViewController/viewDidDisappear:)
methods of the TreasureHuntViewController class:

    - (void)viewWillAppear:(BOOL)animated {
      [super viewWillAppear:animated];

      _renderLoop = [[TreasureHuntRenderLoop alloc]
       initWithRenderTarget:_cardboardView selector:@selector(render)];
    }

    - (void)viewDidDisappear:(BOOL)animated {
      [super viewDidDisappear:animated];

      [_renderLoop invalidate];
      _renderLoop = nil;
    }

### Handling inputs

The Google VR SDK detects events fired when the user presses the viewer button.

To provide custom behavior when these events occur, implement the
[`-cardboardView:didFireEvent:`](https://developers.google.com/vr/ios/reference/protocol_g_v_r_cardboard_view_delegate-p#method-detail)
delegate method.

    - (void)cardboardView:(GVRCardboardView *)cardboardView
             didFireEvent:(GVRUserEvent)event {
      switch (event) {
        case kGVRUserEventBackButton:
        // If the view controller is in a navigation stack or
        // over another view controller, pop or dismiss the
        // view controller here.
        break;
        case kGVRUserEventTrigger:
         NSLog(@"User performed trigger action");
         // Check whether the object is found.
         if (_is_cube_focused) {
           // Vibrate the device on success.
           AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
           // Generate the next cube.
           [self spawnCube];
         }
         break;
      }
    }