public void beforeDrawFrame() {
   this.mDrawingFrame = true;
   if (this.mFovsChanged || this.mTextureFormatChanged) {
     this.updateTextureAndDistortionMesh();
   }
   GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, this.mOriginalFramebufferId);
   GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, this.mFramebufferId);
 }
 private int createProgram(final String vertexSource, final String fragmentSource) {
   final int vertexShader = this.loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
   if (vertexShader == 0) {
     return 0;
   }
   final int pixelShader = this.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
   if (pixelShader == 0) {
     return 0;
   }
   int program = GLES20.glCreateProgram();
   if (program != 0) {
     GLES20.glAttachShader(program, vertexShader);
     this.checkGlError("glAttachShader");
     GLES20.glAttachShader(program, pixelShader);
     this.checkGlError("glAttachShader");
     GLES20.glLinkProgram(program);
     final int[] linkStatus = {0};
     GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
     if (linkStatus[0] != 1) {
       Log.e("DistortionRenderer", "Could not link program: ");
       Log.e("DistortionRenderer", GLES20.glGetProgramInfoLog(program));
       GLES20.glDeleteProgram(program);
       program = 0;
     }
   }
   return program;
 }
 public void undistortTexture(final int textureId) {
   if (this.mRestoreGLStateEnabled) {
     if (this.mChromaticAberrationCorrectionEnabled) {
       this.mGLStateBackupAberration.readFromGL();
     } else {
       this.mGLStateBackup.readFromGL();
     }
   }
   if (this.mFovsChanged || this.mTextureFormatChanged) {
     this.updateTextureAndDistortionMesh();
   }
   GLES20.glViewport(
       0, 0, this.mHmd.getScreenParams().getWidth(), this.mHmd.getScreenParams().getHeight());
   GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
   GLES20.glDisable(GLES20.GL_CULL_FACE);
   GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
   GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
   if (this.mChromaticAberrationCorrectionEnabled) {
     GLES20.glUseProgram(this.mProgramHolderAberration.program);
   } else {
     GLES20.glUseProgram(this.mProgramHolder.program);
   }
   GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
   GLES20.glScissor(
       0, 0, this.mHmd.getScreenParams().getWidth() / 2, this.mHmd.getScreenParams().getHeight());
   this.renderDistortionMesh(this.mLeftEyeDistortionMesh, textureId);
   GLES20.glScissor(
       this.mHmd.getScreenParams().getWidth() / 2,
       0,
       this.mHmd.getScreenParams().getWidth() / 2,
       this.mHmd.getScreenParams().getHeight());
   this.renderDistortionMesh(this.mRightEyeDistortionMesh, textureId);
   if (this.mRestoreGLStateEnabled) {
     if (this.mChromaticAberrationCorrectionEnabled) {
       this.mGLStateBackupAberration.writeToGL();
     } else {
       this.mGLStateBackup.writeToGL();
     }
   }
 }
 private void updateTextureAndDistortionMesh() {
   final ScreenParams screen = this.mHmd.getScreenParams();
   final CardboardDeviceParams cdp = this.mHmd.getCardboardDeviceParams();
   if (this.mProgramHolder == null) {
     this.mProgramHolder = this.createProgramHolder();
   }
   if (this.mProgramHolderAberration == null) {
     this.mProgramHolderAberration = (ProgramHolderAberration) this.createProgramHolder(true);
   }
   final float textureWidthTanAngle = this.mLeftEyeViewport.width + this.mRightEyeViewport.width;
   final float textureHeightTanAngle =
       Math.max(this.mLeftEyeViewport.height, this.mRightEyeViewport.height);
   final int[] maxTextureSize = {0};
   GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
   final int textureWidthPx =
       Math.min(Math.round(textureWidthTanAngle * this.mXPxPerTanAngle), maxTextureSize[0]);
   final int textureHeightPx =
       Math.min(Math.round(textureHeightTanAngle * this.mYPxPerTanAngle), maxTextureSize[0]);
   float xEyeOffsetTanAngleScreen =
       (screen.getWidthMeters() / 2.0f - cdp.getInterLensDistance() / 2.0f)
           / this.mMetersPerTanAngle;
   final float yEyeOffsetTanAngleScreen =
       (cdp.getVerticalDistanceToLensCenter() - screen.getBorderSizeMeters())
           / this.mMetersPerTanAngle;
   this.mLeftEyeDistortionMesh =
       this.createDistortionMesh(
           this.mLeftEyeViewport,
           textureWidthTanAngle,
           textureHeightTanAngle,
           xEyeOffsetTanAngleScreen,
           yEyeOffsetTanAngleScreen);
   xEyeOffsetTanAngleScreen =
       screen.getWidthMeters() / this.mMetersPerTanAngle - xEyeOffsetTanAngleScreen;
   this.mRightEyeDistortionMesh =
       this.createDistortionMesh(
           this.mRightEyeViewport,
           textureWidthTanAngle,
           textureHeightTanAngle,
           xEyeOffsetTanAngleScreen,
           yEyeOffsetTanAngleScreen);
   this.setupRenderTextureAndRenderbuffer(textureWidthPx, textureHeightPx);
   this.mFovsChanged = false;
 }
 private void checkGlError(final String op) {
   final int error;
   if ((error = GLES20.glGetError()) != 0) {
     final String s = "DistortionRenderer";
     final String value = String.valueOf(String.valueOf(op));
     Log.e(
         s,
         new StringBuilder(21 + value.length())
             .append(value)
             .append(": glError ")
             .append(error)
             .toString());
     final String value2 = String.valueOf(String.valueOf(op));
     throw new RuntimeException(
         new StringBuilder(21 + value2.length())
             .append(value2)
             .append(": glError ")
             .append(error)
             .toString());
   }
 }
 private int createTexture(
     final int width, final int height, final int textureFormat, final int textureType) {
   final int[] textureIds = {0};
   GLES20.glGenTextures(1, textureIds, 0);
   GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
   GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
   GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
   GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
   GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
   GLES20.glTexImage2D(
       GLES20.GL_TEXTURE_2D,
       0,
       textureFormat,
       width,
       height,
       0,
       textureFormat,
       textureType,
       (Buffer) null);
   return textureIds[0];
 }
 private int loadShader(final int shaderType, final String source) {
   int shader = GLES20.glCreateShader(shaderType);
   if (shader != 0) {
     GLES20.glShaderSource(shader, source);
     GLES20.glCompileShader(shader);
     final int[] compiled = {0};
     GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
     if (compiled[0] == 0) {
       Log.e(
           "DistortionRenderer",
           new StringBuilder(37)
               .append("Could not compile shader ")
               .append(shaderType)
               .append(":")
               .toString());
       Log.e("DistortionRenderer", GLES20.glGetShaderInfoLog(shader));
       GLES20.glDeleteShader(shader);
       shader = 0;
     }
   }
   return shader;
 }
 public DistortionMesh(
     final Distortion distortionRed,
     final Distortion distortionGreen,
     final Distortion distortionBlue,
     final float screenWidth,
     final float screenHeight,
     final float xEyeOffsetScreen,
     final float yEyeOffsetScreen,
     final float textureWidth,
     final float textureHeight,
     final float xEyeOffsetTexture,
     final float yEyeOffsetTexture,
     final float viewportXTexture,
     final float viewportYTexture,
     final float viewportWidthTexture,
     final float viewportHeightTexture) {
   super();
   this.mArrayBufferId = -1;
   this.mElementBufferId = -1;
   final float[] vertexData = new float[14400];
   short vertexOffset = 0;
   for (int row = 0; row < ROWS; ++row) {
     for (int col = 0; col < COLS; ++col) {
       final float uTextureBlue =
           col / 39.0f * (viewportWidthTexture / textureWidth) + viewportXTexture / textureWidth;
       final float vTextureBlue =
           row / 39.0f * (viewportHeightTexture / textureHeight)
               + viewportYTexture / textureHeight;
       final float xTexture = uTextureBlue * textureWidth - xEyeOffsetTexture;
       final float yTexture = vTextureBlue * textureHeight - yEyeOffsetTexture;
       final float rTexture = (float) Math.sqrt(xTexture * xTexture + yTexture * yTexture);
       final float textureToScreenBlue =
           (rTexture > 0.0f) ? (distortionBlue.distortInverse(rTexture) / rTexture) : 1.0f;
       final float xScreen = xTexture * textureToScreenBlue;
       final float yScreen = yTexture * textureToScreenBlue;
       final float uScreen = (xScreen + xEyeOffsetScreen) / screenWidth;
       final float vScreen = (yScreen + yEyeOffsetScreen) / screenHeight;
       final float rScreen = rTexture * textureToScreenBlue;
       final float screenToTextureGreen =
           (rScreen > 0.0f) ? distortionGreen.distortionFactor(rScreen) : 1.0f;
       final float uTextureGreen =
           (xScreen * screenToTextureGreen + xEyeOffsetTexture) / textureWidth;
       final float vTextureGreen =
           (yScreen * screenToTextureGreen + yEyeOffsetTexture) / textureHeight;
       final float screenToTextureRed =
           (rScreen > 0.0f) ? distortionRed.distortionFactor(rScreen) : 1.0f;
       final float uTextureRed =
           (xScreen * screenToTextureRed + xEyeOffsetTexture) / textureWidth;
       final float vTextureRed =
           (yScreen * screenToTextureRed + yEyeOffsetTexture) / textureHeight;
       final float vignetteSizeTexture = VIGNETTE_SIZE_TAN_ANGLE / textureToScreenBlue;
       final float dxTexture =
           xTexture
               + xEyeOffsetTexture
               - clamp(
                   xTexture + xEyeOffsetTexture,
                   viewportXTexture + vignetteSizeTexture,
                   viewportXTexture + viewportWidthTexture - vignetteSizeTexture);
       final float dyTexture =
           yTexture
               + yEyeOffsetTexture
               - clamp(
                   yTexture + yEyeOffsetTexture,
                   viewportYTexture + vignetteSizeTexture,
                   viewportYTexture + viewportHeightTexture - vignetteSizeTexture);
       final float drTexture = (float) Math.sqrt(dxTexture * dxTexture + dyTexture * dyTexture);
       float vignette;
       if (DistortionRenderer.this.mVignetteEnabled) {
         vignette = 1.0f - clamp(drTexture / vignetteSizeTexture, 0.0f, 1.0f);
       } else {
         vignette = 1.0f;
       }
       vertexData[vertexOffset + 0] = 2.0f * uScreen - 1.0f;
       vertexData[vertexOffset + 1] = 2.0f * vScreen - 1.0f;
       vertexData[vertexOffset + 2] = vignette;
       vertexData[vertexOffset + 3] = uTextureRed;
       vertexData[vertexOffset + 4] = vTextureRed;
       vertexData[vertexOffset + 5] = uTextureGreen;
       vertexData[vertexOffset + 6] = vTextureGreen;
       vertexData[vertexOffset + 7] = uTextureBlue;
       vertexData[vertexOffset + 8] = vTextureBlue;
       vertexOffset += COMPONENTS_PER_VERT;
     }
   }
   this.nIndices = 3158;
   final short[] indexData = new short[this.nIndices];
   short indexOffset = 0;
   vertexOffset = 0;
   for (int row2 = 0; row2 < 39; ++row2) {
     if (row2 > 0) {
       indexData[indexOffset] = indexData[indexOffset - 1];
       ++indexOffset;
     }
     for (int col2 = 0; col2 < 40; ++col2) {
       if (col2 > 0) {
         if (row2 % 2 == 0) {
           ++vertexOffset;
         } else {
           --vertexOffset;
         }
       }
       indexData[indexOffset] = vertexOffset;
       ++indexOffset;
       indexData[indexOffset] = (short) (vertexOffset + 40);
       ++indexOffset;
     }
     vertexOffset += 40;
   }
   final FloatBuffer vertexBuffer =
       ByteBuffer.allocateDirect(vertexData.length * BYTES_PER_FLOAT)
           .order(ByteOrder.nativeOrder())
           .asFloatBuffer();
   vertexBuffer.put(vertexData).position(0);
   final ShortBuffer indexBuffer =
       ByteBuffer.allocateDirect(indexData.length * BYTES_PER_SHORT)
           .order(ByteOrder.nativeOrder())
           .asShortBuffer();
   indexBuffer.put(indexData).position(0);
   final int[] bufferIds = new int[2];
   GLES20.glGenBuffers(2, bufferIds, 0);
   this.mArrayBufferId = bufferIds[0];
   this.mElementBufferId = bufferIds[1];
   GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.mArrayBufferId);
   GLES20.glBufferData(
       GLES20.GL_ARRAY_BUFFER,
       vertexData.length * BYTES_PER_FLOAT,
       (Buffer) vertexBuffer,
       GLES20.GL_STATIC_DRAW);
   GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, this.mElementBufferId);
   GLES20.glBufferData(
       GLES20.GL_ELEMENT_ARRAY_BUFFER,
       indexData.length * BYTES_PER_SHORT,
       (Buffer) indexBuffer,
       GLES20.GL_STATIC_DRAW);
   GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
   GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
 }
 private ProgramHolder createProgramHolder(final boolean aberrationCorrected) {
   ProgramHolder holder;
   GLStateBackup state;
   if (aberrationCorrected) {
     holder = new ProgramHolderAberration();
     holder.program = this.createProgram(VERTEX_SHADER_ABERRATION, FRAGMENT_SHADER_ABERRATION);
     if (holder.program == 0) {
       throw new RuntimeException("Could not create aberration-corrected program");
     }
     state = this.mGLStateBackupAberration;
   } else {
     holder = new ProgramHolder();
     holder.program = this.createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
     if (holder.program == 0) {
       throw new RuntimeException("Could not create program");
     }
     state = this.mGLStateBackup;
   }
   holder.aPosition = GLES20.glGetAttribLocation(holder.program, "aPosition");
   this.checkGlError("glGetAttribLocation aPosition");
   if (holder.aPosition == -1) {
     throw new RuntimeException("Could not get attrib location for aPosition");
   }
   state.addTrackedVertexAttribute(holder.aPosition);
   holder.aVignette = GLES20.glGetAttribLocation(holder.program, "aVignette");
   this.checkGlError("glGetAttribLocation aVignette");
   if (holder.aVignette == -1) {
     throw new RuntimeException("Could not get attrib location for aVignette");
   }
   state.addTrackedVertexAttribute(holder.aVignette);
   if (aberrationCorrected) {
     ((ProgramHolderAberration) holder).aRedTextureCoord =
         GLES20.glGetAttribLocation(holder.program, "aRedTextureCoord");
     this.checkGlError("glGetAttribLocation aRedTextureCoord");
     if (((ProgramHolderAberration) holder).aRedTextureCoord == -1) {
       throw new RuntimeException("Could not get attrib location for aRedTextureCoord");
     }
     ((ProgramHolderAberration) holder).aGreenTextureCoord =
         GLES20.glGetAttribLocation(holder.program, "aGreenTextureCoord");
     this.checkGlError("glGetAttribLocation aGreenTextureCoord");
     if (((ProgramHolderAberration) holder).aGreenTextureCoord == -1) {
       throw new RuntimeException("Could not get attrib location for aGreenTextureCoord");
     }
     state.addTrackedVertexAttribute(((ProgramHolderAberration) holder).aRedTextureCoord);
     state.addTrackedVertexAttribute(((ProgramHolderAberration) holder).aGreenTextureCoord);
   }
   holder.aBlueTextureCoord = GLES20.glGetAttribLocation(holder.program, "aBlueTextureCoord");
   this.checkGlError("glGetAttribLocation aBlueTextureCoord");
   if (holder.aBlueTextureCoord == -1) {
     throw new RuntimeException("Could not get attrib location for aBlueTextureCoord");
   }
   state.addTrackedVertexAttribute(holder.aBlueTextureCoord);
   holder.uTextureCoordScale = GLES20.glGetUniformLocation(holder.program, "uTextureCoordScale");
   this.checkGlError("glGetUniformLocation uTextureCoordScale");
   if (holder.uTextureCoordScale == -1) {
     throw new RuntimeException("Could not get attrib location for uTextureCoordScale");
   }
   holder.uTextureSampler = GLES20.glGetUniformLocation(holder.program, "uTextureSampler");
   this.checkGlError("glGetUniformLocation uTextureSampler");
   if (holder.uTextureSampler == -1) {
     throw new RuntimeException("Could not get attrib location for uTextureSampler");
   }
   return holder;
 }
 private int setupRenderTextureAndRenderbuffer(final int width, final int height) {
   if (this.mTextureId != -1) {
     GLES20.glDeleteTextures(1, new int[] {this.mTextureId}, 0);
   }
   if (this.mRenderbufferId != -1) {
     GLES20.glDeleteRenderbuffers(1, new int[] {this.mRenderbufferId}, 0);
   }
   if (this.mFramebufferId != -1) {
     GLES20.glDeleteFramebuffers(1, new int[] {this.mFramebufferId}, 0);
   }
   this.mTextureId = this.createTexture(width, height, this.mTextureFormat, this.mTextureType);
   this.mTextureFormatChanged = false;
   this.checkGlError("setupRenderTextureAndRenderbuffer: create texture");
   final int[] renderbufferIds = {0};
   GLES20.glGenRenderbuffers(1, renderbufferIds, 0);
   GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderbufferIds[0]);
   GLES20.glRenderbufferStorage(
       GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, width, height);
   this.mRenderbufferId = renderbufferIds[0];
   this.checkGlError("setupRenderTextureAndRenderbuffer: create renderbuffer");
   final int[] framebufferIds = {0};
   GLES20.glGenFramebuffers(1, framebufferIds, 0);
   GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferIds[0]);
   this.mFramebufferId = framebufferIds[0];
   GLES20.glFramebufferTexture2D(
       GLES20.GL_FRAMEBUFFER,
       GLES20.GL_COLOR_ATTACHMENT0,
       GLES20.GL_TEXTURE_2D,
       this.mTextureId,
       0);
   GLES20.glFramebufferRenderbuffer(
       GLES20.GL_FRAMEBUFFER,
       GLES20.GL_DEPTH_ATTACHMENT,
       GLES20.GL_RENDERBUFFER,
       renderbufferIds[0]);
   final int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
   if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
     final String s = "Framebuffer is not complete: ";
     final String value = String.valueOf(Integer.toHexString(status));
     throw new RuntimeException((value.length() != 0) ? s.concat(value) : new String(s));
   }
   GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
   return framebufferIds[0];
 }
 private void renderDistortionMesh(final DistortionMesh mesh, final int textureId) {
   ProgramHolder holder;
   if (this.mChromaticAberrationCorrectionEnabled) {
     holder = this.mProgramHolderAberration;
   } else {
     holder = this.mProgramHolder;
   }
   GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mesh.mArrayBufferId);
   GLES20.glVertexAttribPointer(holder.aPosition, 2, GLES20.GL_FLOAT, false, 36, 0 * 4);
   GLES20.glEnableVertexAttribArray(holder.aPosition);
   GLES20.glVertexAttribPointer(holder.aVignette, 1, GLES20.GL_FLOAT, false, 36, 2 * 4);
   GLES20.glEnableVertexAttribArray(holder.aVignette);
   GLES20.glVertexAttribPointer(holder.aBlueTextureCoord, 2, GLES20.GL_FLOAT, false, 36, 7 * 4);
   GLES20.glEnableVertexAttribArray(holder.aBlueTextureCoord);
   if (this.mChromaticAberrationCorrectionEnabled) {
     GLES20.glVertexAttribPointer(
         ((ProgramHolderAberration) holder).aRedTextureCoord,
         2,
         GLES20.GL_FLOAT,
         false,
         36,
         3 * 4);
     GLES20.glEnableVertexAttribArray(((ProgramHolderAberration) holder).aRedTextureCoord);
     GLES20.glVertexAttribPointer(
         ((ProgramHolderAberration) holder).aGreenTextureCoord,
         2,
         GLES20.GL_FLOAT,
         false,
         36,
         5 * 4);
     GLES20.glEnableVertexAttribArray(((ProgramHolderAberration) holder).aGreenTextureCoord);
   }
   GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
   GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
   GLES20.glUniform1i(this.mProgramHolder.uTextureSampler, 0);
   GLES20.glUniform1f(this.mProgramHolder.uTextureCoordScale, this.mResolutionScale);
   GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mesh.mElementBufferId);
   GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, mesh.nIndices, GLES20.GL_UNSIGNED_SHORT, 0);
 }
 public void afterDrawFrame() {
   GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, this.mOriginalFramebufferId.array()[0]);
   this.undistortTexture(this.mTextureId);
   this.mDrawingFrame = false;
 }