protected double getHeadTranslationScale(IDelegateView view) {
   Position eyePosition = view.getEyePosition();
   DrawContext dc = view.getDC();
   if (dc == null) {
     return Math.abs(eyePosition.elevation);
   }
   double altitude = ViewUtil.computeElevationAboveSurface(dc, eyePosition);
   return Math.abs(altitude);
 }
 @Override
 public Matrix computeProjection(
     IDelegateView view, Angle horizontalFieldOfView, double nearDistance, double farDistance) {
   if (!shouldRenderForHMD()) {
     return view.computeProjection(horizontalFieldOfView, nearDistance, farDistance);
   }
   return RiftUtils.toMatrix(
       Hmd.getPerspectiveProjection(eyeFov[eye], (float) nearDistance, (float) farDistance, true));
 }
  @Override
  public void beforeComputeMatrices(IDelegateView view) {
    if (shouldRenderForHMD()) {
      // double hfovRadians = Math.atan(eyeFov[eye].LeftTan) + Math.atan(eyeFov[eye].RightTan);
      // double vfovRadians = Math.atan(eyeFov[eye].UpTan) + Math.atan(eyeFov[eye].DownTan);
      // view.setFieldOfView(Angle.fromRadians(hfovRadians));

      // TODO hmmm, something about the reported Oculus FOV above and the World Wind's View FOV
      // don't mix very well, so set it manually to a high FOV to ensure all tiles are rendered:
      view.setFieldOfView(Angle.fromDegrees(130));
    }
  }
  @Override
  public void draw(IDelegateView view, DrawContext dc, DrawableSceneController sc) {
    GL2 gl = dc.getGL().getGL2();
    init(gl);

    if (distortionShader.isCreationFailed()) {
      view.draw(dc, sc);
      return;
    }

    Rectangle oldViewport = view.getViewport();

    hmd.beginFrameTiming(++frameCount);
    {
      Posef[] eyePoses = hmd.getEyePoses(frameCount, eyeOffsets);
      // RiftLogger.logPose(eyePoses);

      renderEyes = true;
      frameBuffer.bind(gl);
      {
        sc.clearFrame(dc);

        for (int i = 0; i < ovrEye_Count; i++) {
          int eye = hmd.EyeRenderOrder[i];
          Posef pose = eyePoses[eye];
          this.eyePoses[eye].Orientation = pose.Orientation;
          this.eyePoses[eye].Position = pose.Position;

          this.eye = eye;

          gl.glViewport(
              eyeRenderViewport[eye].Pos.x,
              eyeRenderViewport[eye].Pos.y,
              eyeRenderViewport[eye].Size.w,
              eyeRenderViewport[eye].Size.h);

          sc.applyView(dc);
          sc.draw(dc);
        }
      }
      frameBuffer.unbind(gl);
      renderEyes = false;

      OGLStackHandler oglsh = new OGLStackHandler();
      oglsh.pushAttrib(gl, GL2.GL_ENABLE_BIT);
      oglsh.pushClientAttrib(gl, GL2.GL_CLIENT_VERTEX_ARRAY_BIT);
      try {
        gl.glViewport(0, 0, hmd.Resolution.w, hmd.Resolution.h);
        gl.glDisable(GL2.GL_DEPTH_TEST);

        gl.glEnable(GL2.GL_TEXTURE_2D);
        gl.glActiveTexture(GL2.GL_TEXTURE0);
        gl.glBindTexture(GL2.GL_TEXTURE_2D, frameBuffer.getTexture().getId());
        for (int eyeNum = 0; eyeNum < ovrEye_Count; eyeNum++) {
          OvrMatrix4f[] timeWarpMatricesRowMajor = new OvrMatrix4f[2];
          hmd.getEyeTimewarpMatrices(eyeNum, eyePoses[eyeNum], timeWarpMatricesRowMajor);
          distortionShader.use(
              gl,
              uvScaleOffset[eyeNum][0].x,
              -uvScaleOffset[eyeNum][0].y,
              uvScaleOffset[eyeNum][1].x,
              1 - uvScaleOffset[eyeNum][1].y,
              timeWarpMatricesRowMajor[0].M,
              timeWarpMatricesRowMajor[1].M);

          gl.glClientActiveTexture(GL2.GL_TEXTURE0);
          gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
          gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY);
          gl.glEnableClientState(GL2.GL_COLOR_ARRAY);

          gl.glBindBuffer(
              GL2.GL_ARRAY_BUFFER, distortionObjects[eyeNum][DistortionObjects.vbo.ordinal()]);
          {
            int stride = 10 * 4;
            gl.glVertexPointer(4, GL2.GL_FLOAT, stride, 0);
            gl.glTexCoordPointer(2, GL2.GL_FLOAT, stride, 4 * 4);
            gl.glColorPointer(4, GL2.GL_FLOAT, stride, 6 * 4);

            gl.glBindBuffer(
                GL2.GL_ELEMENT_ARRAY_BUFFER,
                distortionObjects[eyeNum][DistortionObjects.ibo.ordinal()]);
            {
              gl.glDrawElements(GL2.GL_TRIANGLES, indicesCount, GL2.GL_UNSIGNED_INT, 0);
            }
            gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0);
          }
          gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);

          distortionShader.unuse(gl);
        }
      } finally {
        oglsh.pop(gl);
      }
    }
    hmd.endFrameTiming();

    // apply the old viewport, and ensure that the view is updated for the next picking round
    gl.glViewport(oldViewport.x, oldViewport.y, oldViewport.width, oldViewport.height);
    sc.applyView(dc);

    view.firePropertyChange(
        AVKey.VIEW, null, view); // make the view draw repeatedly for oculus rotation
  }
 @Override
 public void pick(IDelegateView view, DrawContext dc, DrawableSceneController sc) {
   view.pick(dc, sc);
   sc.clearFrame(dc);
   fixViewportCenterPosition(dc, sc);
 }
 @Override
 public Matrix computeModelView(IDelegateView view) {
   pretransformedModelView = view.computeModelView();
   return transformModelView(pretransformedModelView, view);
 }