protected void initDistortion(GL2 gl) {
   // Configure Stereo settings.
   OvrSizei recommendedTex0Size = hmd.getFovTextureSize(ovrEye_Left, hmd.DefaultEyeFov[0], 1f);
   OvrSizei recommendedTex1Size = hmd.getFovTextureSize(ovrEye_Right, hmd.DefaultEyeFov[1], 1f);
   int x = recommendedTex0Size.w + recommendedTex1Size.w;
   int y = Math.max(recommendedTex0Size.h, recommendedTex1Size.h);
   OvrSizei renderTargetSize = new OvrSizei(x, y);
   frameBuffer.resize(gl, new Dimension(x, y));
   // Initialize eye rendering information.
   eyeRenderViewport[0].Pos = new OvrVector2i(0, 0);
   eyeRenderViewport[0].Size = new OvrSizei(renderTargetSize.w / 2, renderTargetSize.h);
   eyeRenderViewport[1].Pos = new OvrVector2i((renderTargetSize.w + 1) / 2, 0);
   eyeRenderViewport[1].Size = eyeRenderViewport[0].Size;
   distortionShader.create(gl);
   distortionObjects = new int[ovrEye_Count][DistortionObjects.count.ordinal()];
   for (int eyeNum = 0; eyeNum < ovrEye_Count; eyeNum++) {
     int distortionCaps =
         ovrDistortionCap_Chromatic | ovrDistortionCap_TimeWarp | ovrDistortionCap_Vignette;
     DistortionMesh meshData = hmd.createDistortionMesh(eyeNum, eyeFov[eyeNum], distortionCaps);
     DistortionVertex[] distortionVertices = new DistortionVertex[meshData.VertexCount];
     meshData.pVertexData.toArray(distortionVertices);
     {
       initDistortionVBOs(gl, eyeNum, distortionVertices);
     }
     short[] indicesData = meshData.pIndexData.getPointer().getShortArray(0, meshData.IndexCount);
     indicesCount = indicesData.length;
     {
       initDistortionIBO(gl, eyeNum, indicesData);
     }
     eyeRenderDescs[eyeNum] = hmd.getRenderDesc(eyeNum, eyeFov[eyeNum]);
     uvScaleOffset[eyeNum] =
         Hmd.getRenderScaleAndOffset(eyeFov[eyeNum], renderTargetSize, eyeRenderViewport[eyeNum]);
     eyeOffsets[eyeNum].x = eyeRenderDescs[eyeNum].HmdToEyeViewOffset.x;
     eyeOffsets[eyeNum].y = eyeRenderDescs[eyeNum].HmdToEyeViewOffset.y;
     eyeOffsets[eyeNum].z = eyeRenderDescs[eyeNum].HmdToEyeViewOffset.z;
   }
 }
  @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
  }