/**
   * Init CameraManager gl texture id, camera SurfaceTexture, bind to EXTERNAL_OES, and redirect
   * camera preview to the surfaceTexture.
   *
   * @throws IOException when camera cannot be open
   */
  private void initCameraSurface() throws IOException {

    // Gen openGL texture id
    int texture[] = new int[1];
    GLES10.glGenTextures(1, texture, 0);
    mCameraTextureId = texture[0];

    if (mCameraTextureId == 0) {
      throw new RuntimeException("Cannot create openGL texture (initCameraSurface())");
    }

    // Camera preview is redirected to SurfaceTexture.
    // SurfaceTexture works with TEXTURE_EXTERNAL_OES, so we bind this textureId so that camera
    // will automatically fill it with its video.
    GLES10.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mCameraTextureId);

    // Can't do mipmapping with camera source
    GLES10.glTexParameterf(
        GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR);
    GLES10.glTexParameterf(
        GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR);

    // Clamp to edge is the only option
    GLES10.glTexParameterf(
        GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE);
    GLES10.glTexParameterf(
        GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE);

    // create a SurfaceTexture associated to this openGL texture...
    mCameraSurfaceTex = new SurfaceTexture(mCameraTextureId);
    mCameraSurfaceTex.setDefaultBufferSize(640, 480);

    // ... and redirect camera preview to it
    mCameraManager.setPreviewSurface(mCameraSurfaceTex);

    // Setup viewfinder
    mViewFinder = new TexturedPlane(mViewFinderSize);
    mViewFinder.setTexture(
        BitmapDecoder.safeDecodeBitmap(mContext.getResources(), VIEWFINDER_RESSOURCE_ID));
    mViewFinder.recycleTexture();
    mViewFinder.translate(0, 0, VIEWFINDER_DISTANCE);
    mViewFinder.setAlpha(VIEWFINDER_ATTENUATION_ALPHA);
  }
  private void reinitCameraSurface() throws IOException {
    // for an unknown reason, the camera preview is not in correct direction by default. Need to
    // rotate it
    final int screenRotation =
        ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE))
            .getDefaultDisplay()
            .getRotation();
    mCameraRoll = 270;
    mTargetsLock.lock();
    switch (screenRotation) {
      case Surface.ROTATION_0:
        mCameraRoll += 0.0f;
        mContours = mContours34;
        break;
      case Surface.ROTATION_90:
        mCameraRoll += 90.0f;
        mContours = mContours43;
        break;
      case Surface.ROTATION_180:
        mCameraRoll += 180.0f;
        mContours = mContours34;
        break;
      default:
        mCameraRoll += 270.0f;
        mContours = mContours43;
        break;
    }
    ;
    mTargetsLock.unlock();

    mCameraRoll %= 360;

    // create a new TexturedPlane, that holds the camera texture.
    mCameraSurface = new TexturedPlane(mCameraSize, CAMERA_RATIO);
    mCameraSurface.setTexture(mCameraTextureId);
    mCameraSurface.setAlpha(CAMERA_SURFACE_ALPHA);
    // for unknown reason, the preview is not in correct orientation
    mCameraSurface.rotate(0, 0, mCameraRoll);
    mCameraSurface.translate(0, 0, CAMERA_DISTANCE);
  }
  @Override
  public void onDrawFrame(GL10 gl) {
    // draws the skybox
    if (mUseSkybox) super.onDrawFrame(gl);

    // refresh camera texture
    if (mCameraFrameAvaible) {
      mCameraSurfaceTex.updateTexImage();
      mCameraFrameAvaible = false;
    }
    mCameraSurfaceTex.setOnFrameAvailableListener(this);
    // draw camera surface
    gl.glEnable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
    mCameraSurface.draw(gl, mViewMatrix);
    gl.glDisable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);

    // draw the viewFinder
    mViewFinder.draw(gl, mViewMatrix);

    // launch memory cleanup??
    Runtime info = Runtime.getRuntime();
    long freeMem = info.freeMemory() / 1048576L;
    if (freeMem < MEMORY_CLEANUP_THRESHOLD) mHasToFreeMemory = true;
    else mHasToFreeMemory = false;

    // the snapshots that are in FOV
    mSnapshotsLock.lock();
    for (Snapshot3D snap : mSnapshots) {
      float distance = this.getSnapshotDisnance(snap);

      if (mHasToFreeMemory && distance > AUTO_UNLOADTEXTURE_ANGLE) {
        snap.unloadGLTexture(gl);
      } else if (this.getSnapshotDisnance(snap) < AUTO_LOADTEXTURE_ANGLE) {
        snap.loadGLTexture(gl);
      }

      if (distance > 120.0f) snap.setVisible(false);
      else {
        snap.setVisible(true);
        snap.draw(gl, super.getRotationMatrix());
      }
    }
    mSnapshotsLock.unlock();

    // ... and then all markers with newly computed alpha
    float d;

    // draw markers
    if (mUseMarkers) {
      mTargetsLock.lock();
      for (Snapshot3D dot : mDots) {
        d = getSnapshotDisnance(dot);
        if (d > 60.0f) {
          dot.setVisible(false);
        } else {
          dot.setVisible(true);
          // Set alpha based on camera distance to the point
          d = d * mMarkersAttenuationFactor / 360.0f;
          d = (d > 1.0f ? 1.0f : d);
          dot.setAlpha(1.0f - d);
          dot.draw(gl, super.getRotationMatrix());
        }
      }
      mTargetsLock.unlock();
    }
    if (mUseContours) {
      mTargetsLock.lock();
      for (Snapshot3D contour : mContours) {
        d = getSnapshotDisnance(contour);

        if (d > 60.0f) {
          contour.setVisible(false);
        } else {
          contour.setVisible(true);
          // Set alpha based on camera distance to the point
          d = d * mMarkersAttenuationFactor / 360.0f;
          d = (d > 1.0f ? 1.0f : d);
          contour.setAlpha(1.0f - d);
          contour.draw(gl, super.getRotationMatrix());
        }
      }
      mTargetsLock.unlock();
    }
  }
 /* ********
  * ACCESSORS
  * ********/
 public void setCamPreviewVisible(boolean visible) {
   mCameraSurface.setVisible(visible);
 }