/**
   * Creates a new CaptureRenderer, based on an Inside3dRenderer with the given mesh as Skybox.
   *
   * @param context - Context of the application.
   * @param skybox
   * @param cameraManager
   */
  public CaptureRenderer(Context context, Cube skybox, CameraManager cameraManager) {
    // based on Inside3dRenderer. We are inside a skybox.
    super(context);
    mSkybox = skybox;
    super.setSurroundingMesh(mSkybox);
    super.setFovDeg(DEFAULT_FOV_DEG);

    // init attributes
    mCameraManager = cameraManager;
    mCameraManager.addSnapshotEventListener(this);
    mContext = context;

    mMarkerBitmap = BitmapDecoder.safeDecodeBitmap(mContext.getResources(), MARKER_RESSOURCE_ID);
    mContourBitmap = BitmapDecoder.safeDecodeBitmap(mContext.getResources(), CONTOUR_RESSOURCE_ID);

    // if auto samplig enabled
    if (mSampleRate == 0) {
      mSampleRate = (int) mCameraManager.getCameraResolution();
      mSampleRate = (int) (ceilPowOf2(mSampleRate) / SNAPSHOT_AUTO_SAMPLE_RATIO);
    }

    mViewMatrix = new float[16];
    Matrix.setIdentityM(mViewMatrix, 0);

    // create dots and snapshot lists
    mSnapshots = new ArrayList<Snapshot3D>();
    mDots = new LinkedList<Snapshot3D>();
    mContours43 = new LinkedList<Snapshot3D>();
    mContours34 = new LinkedList<Snapshot3D>();
    mContours = mContours43;
    mTargetsLock = new ReentrantLock();
    mSnapshotsLock = new ReentrantLock();
  }
  /**
   * Build a Snapshot3D from the given snapshot, and put it in the 3D view at its pithc, yaw and
   * roll.
   *
   * @param pictureData - the picture byteArray to fill the snapshot3D with.
   * @param snapshot
   * @return
   */
  private Snapshot3D putSnapshot(byte[] pictureData, Snapshot snapshot) {

    // build a snapshot3d from the snapshot2d
    Snapshot3D snap = new Snapshot3D(mSnapshotsSize, CAMERA_RATIO, snapshot);
    // fill the snapshot with the byteArray, faster than reading its data from SD.
    snap.setSampleRate(mSampleRate);
    snap.setTexture(BitmapDecoder.safeDecodeBitmap(pictureData, mSampleRate));
    snap.setZoom(mSnapshotZoom);
    snap.recycleTexture();

    // put the snapshot at its place.
    snap.translate(0.0f, 0.0f, SNAPSHOTS_DISTANCE);
    snap.setVisible(true);
    mSnapshotsLock.lock();
    mSnapshots.add(snap);
    mSnapshotsLock.unlock();

    return snap;
  }
  /**
   * 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);
  }