Home | History | Annotate | Download | only in Renderer
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.cts.verifier.sensors.sixdof.Renderer;
     17 
     18 import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.CameraStreamManager;
     19 import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.DrawParameters;
     20 import com.android.cts.verifier.sensors.sixdof.Renderer.Renderable.CameraPreviewRenderable;
     21 import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
     22 import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.PoseProvider;
     23 
     24 import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.MATRIX_4X4;
     25 import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.ORIENTATION_360_ANTI_CLOCKWISE;
     26 import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
     27 import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
     28 import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
     29 
     30 import android.content.Context;
     31 import android.content.res.Configuration;
     32 import android.content.res.Resources;
     33 import android.opengl.GLES20;
     34 import android.opengl.GLSurfaceView;
     35 import android.opengl.Matrix;
     36 import android.util.Log;
     37 import android.view.Display;
     38 import android.view.Surface;
     39 import android.view.WindowManager;
     40 
     41 import javax.microedition.khronos.egl.EGLConfig;
     42 import javax.microedition.khronos.opengles.GL10;
     43 
     44 /**
     45  * Abstract class that connects to Android Camera to use as an OpenGL texture.
     46  */
     47 public abstract class BaseRenderer implements GLSurfaceView.Renderer {
     48     private static final String TAG = "BaseRenderer";
     49     private static final int ORIENTATION_COUNT = 4;
     50 
     51     protected float[] mViewMatrix = new float[MATRIX_4X4];
     52     protected float[] mOrthogonalViewMatrix = new float[MATRIX_4X4];
     53     protected float[] mProjectionMatrix = new float[MATRIX_4X4];
     54 
     55     protected float[] mOrthogonalProjectionMatrix = new float[MATRIX_4X4];
     56     protected float[] mFrustrumProjectionMatrix = new float[MATRIX_4X4];
     57 
     58     protected DrawParameters mDrawParameters;
     59 
     60     protected float[] mCameraCoordinates;
     61 
     62     protected PoseProvider mPoseProvider;
     63     protected boolean mIsValid = false;
     64 
     65     protected CameraPreviewRenderable mCameraPreview;
     66     protected double mLastRGBFrameTimestamp = -1;
     67 
     68     private int mCameraPreviewRotation = 0;
     69 
     70     protected int mOpenGlRotation = 0;
     71     protected float[] mOpenGlUpVector;
     72 
     73     private Context mContext;
     74 
     75     public BaseRenderer(Context context) {
     76         mContext = context;
     77         mOpenGlRotation = getDeviceRotation(context);
     78         mOpenGlUpVector = getUpVector(mOpenGlRotation);
     79         mCameraPreviewRotation = CameraStreamManager.getRotation(context, mOpenGlRotation);
     80     }
     81 
     82     @Override
     83     public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
     84         // Set the background clear color to black.
     85         GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
     86 
     87         // Enable depth testing
     88         GLES20.glEnable(GLES20.GL_DEPTH_TEST);
     89 
     90         mCameraPreview = new CameraPreviewRenderable();
     91 
     92         resetViewMatrix();
     93     }
     94 
     95     protected void resetViewMatrix() {
     96         // Position the eye in front of the origin.
     97         final float eyeX = 0.0f;
     98         final float eyeY = 0.0f;
     99         final float eyeZ = 0.0f;
    100 
    101         // We are looking toward the distance
    102         final float lookX = 0.0f;
    103         final float lookY = 0.0f;
    104         final float lookZ = -5.0f;
    105 
    106         // Set our up vector. This is where our head would be pointing were we holding the camera.
    107         float[] upVector = getUpVector(mCameraPreviewRotation);
    108 
    109         // Set the view matrix.
    110         Matrix.setLookAtM(mViewMatrix, 0,
    111                 eyeX, eyeY, eyeZ,
    112                 lookX, lookY, lookZ,
    113                 upVector[X], upVector[Y], upVector[Z]);
    114         Matrix.setLookAtM(mOrthogonalViewMatrix, 0,
    115                 eyeX, eyeY, eyeZ,
    116                 lookX, lookY, lookZ,
    117                 upVector[X], upVector[Y], upVector[Z]);
    118     }
    119 
    120     @Override
    121     public void onSurfaceChanged(GL10 glUnused, int width, int height) {
    122         // Set the OpenGL viewport to the same size as the surface.
    123         GLES20.glViewport(0, 0, width, height);
    124 
    125         // Create a new perspective projection matrix. The height will stay the same
    126         // while the width will vary as per aspect ratio.
    127         // This project matrix does not take into account the camera intrinsics and should not be
    128         // used for AR purposes.
    129         final float ratio = (float) width / height;
    130         float left = -ratio;
    131         float right = ratio;
    132         float bottom = -1.0f;
    133         float top = 1.0f;
    134         final float near = 1.0f;
    135         final float far = 10.0f;
    136 
    137         boolean invertAxis = false;
    138 
    139         switch (mCameraPreviewRotation) {
    140             case MathsUtils.ORIENTATION_0:
    141             case MathsUtils.ORIENTATION_180_ANTI_CLOCKWISE:
    142             case MathsUtils.ORIENTATION_360_ANTI_CLOCKWISE:
    143                 break;
    144             case MathsUtils.ORIENTATION_90_ANTI_CLOCKWISE:
    145             case MathsUtils.ORIENTATION_270_ANTI_CLOCKWISE:
    146                 // Invert aspect ratio.
    147                 invertAxis = true;
    148                 bottom = -ratio;
    149                 top = ratio;
    150                 left = -1.0f;
    151                 right = 1.0f;
    152                 break;
    153             default:
    154                 // Unexpected orientation, error out.
    155                 throw new RuntimeException("Unexpected orientation that cannot be dealt with!");
    156         }
    157 
    158         mCameraCoordinates = getCameraCoordinates(left, right, bottom, top);
    159 
    160         // Give camera preview reference to the context so that it can connect to the camera.
    161         mCameraPreview.initialiseCameraPreview(mCameraCoordinates, invertAxis, mContext);
    162 
    163         Matrix.orthoM(mOrthogonalProjectionMatrix, 0, left, right, bottom, top, near, far);
    164         Matrix.frustumM(mFrustrumProjectionMatrix, 0, left, right, bottom, top, near, far);
    165 
    166         mProjectionMatrix = mOrthogonalProjectionMatrix;
    167 
    168         mDrawParameters = new DrawParameters();
    169 
    170         mIsValid = true;
    171     }
    172 
    173     @Override
    174     public void onDrawFrame(GL10 glUnused) {
    175         GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    176         doPreRenderingSetup();
    177         doCoreRendering();
    178         doTestSpecificRendering();
    179     }
    180 
    181     private void doCoreRendering() {
    182         mDrawParameters.update(mViewMatrix, mProjectionMatrix);
    183         mCameraPreview.draw(mDrawParameters);
    184     }
    185 
    186     protected synchronized void updateCameraTexture() {
    187         mLastRGBFrameTimestamp = mCameraPreview.updateTexture();
    188     }
    189 
    190     /**
    191      * Setup up view and projecttion matrices to be the ones you want for this draw call.
    192      */
    193     protected abstract void doPreRenderingSetup();
    194 
    195     /**
    196      * Do rendering that is unique to each test.
    197      */
    198     protected abstract void doTestSpecificRendering();
    199 
    200     /**
    201      * Where to position the camera preview on the screen. Can be overridden by sub classes.
    202      */
    203     protected float[] getCameraCoordinates(float left, float right, float bottom, float top) {
    204         switch (mCameraPreviewRotation) {
    205             case MathsUtils.ORIENTATION_0:
    206             case MathsUtils.ORIENTATION_180_ANTI_CLOCKWISE:
    207             case MathsUtils.ORIENTATION_360_ANTI_CLOCKWISE:
    208                 // Normal aspect ratio.
    209                 return new float[]{
    210                         left, top, 0.0f,
    211                         left, bottom, 0.0f,
    212                         right, top, 0.0f,
    213                         left, bottom, 0.0f,
    214                         right, bottom, 0.0f,
    215                         right, top, 0.0f,
    216                 };
    217             case MathsUtils.ORIENTATION_90_ANTI_CLOCKWISE:
    218             case MathsUtils.ORIENTATION_270_ANTI_CLOCKWISE:
    219                 // Inverted aspect ratio.
    220                 return new float[]{
    221                         bottom, right, 0.0f,
    222                         bottom, left, 0.0f,
    223                         top, right, 0.0f,
    224                         bottom, left, 0.0f,
    225                         top, left, 0.0f,
    226                         top, right, 0.0f,
    227                 };
    228             default:
    229                 // Unexpected orientation, error out.
    230                 throw new RuntimeException("Unexpected orientation that cannot be dealt with!");
    231         }
    232     }
    233 
    234 
    235     /**
    236      * Saves PoseProvider object for later so we can connect to camera at appropriate time.
    237      */
    238     public void connectCamera(PoseProvider poseProvider, Context context) {
    239         // Save these for later so we can connect to camera after mCameraPreview has been
    240         // initialised. Also used to setup extrinsics in ComplexMovementRenderer.
    241         mPoseProvider = poseProvider;
    242         mContext = context;
    243     }
    244 
    245     public void disconnectCamera() {
    246         mCameraPreview.disconnectCamera();
    247     }
    248 
    249     public void onDestroy() {
    250         mPoseProvider = null;
    251         mContext = null;
    252 
    253         if (mCameraPreview != null) {
    254             mCameraPreview.destroy();
    255             mCameraPreview = null;
    256         }
    257     }
    258 
    259     private static float[] getUpVector(int rotation) {
    260         float [] upVector = new float[MathsUtils.VECTOR_3D];
    261 
    262         switch (rotation) {
    263             case MathsUtils.ORIENTATION_0:
    264             case ORIENTATION_360_ANTI_CLOCKWISE:
    265                 upVector[X] = 0.0f;
    266                 upVector[Y] = 1.0f;
    267                 upVector[Z] = 0.0f;
    268                 break;
    269             case MathsUtils.ORIENTATION_90_ANTI_CLOCKWISE:
    270                 upVector[X] = -1.0f;
    271                 upVector[Y] = 0.0f;
    272                 upVector[Z] = 0.0f;
    273                 break;
    274             case MathsUtils.ORIENTATION_180_ANTI_CLOCKWISE:
    275                 upVector[X] = 0.0f;
    276                 upVector[Y] = -1.0f;
    277                 upVector[Z] = 0.0f;
    278                 break;
    279             case MathsUtils.ORIENTATION_270_ANTI_CLOCKWISE:
    280                 upVector[X] = 1.0f;
    281                 upVector[Y] = 0.0f;
    282                 upVector[Z] = 0.0f;
    283                 break;
    284             default:
    285                 // Unexpected orientation, error out.
    286                 throw new RuntimeException("Unexpected orientation that cannot be dealt with!");
    287         }
    288 
    289         return upVector;
    290     }
    291 
    292     public static int getDeviceRotation(Context context) {
    293         WindowManager windowManager = (WindowManager)
    294                 context.getSystemService(Context.WINDOW_SERVICE);
    295         final Display display = windowManager.getDefaultDisplay();
    296         int naturalOrientation = Configuration.ORIENTATION_LANDSCAPE;
    297         int configOrientation = context.getResources().getConfiguration().orientation;
    298         switch (display.getRotation()) {
    299             case Surface.ROTATION_0:
    300             case Surface.ROTATION_180:
    301                 // We are currently in the same basic orientation as the natural orientation.
    302                 naturalOrientation = configOrientation;
    303                 break;
    304             case Surface.ROTATION_90:
    305             case Surface.ROTATION_270:
    306                 // We are currently in the other basic orientation to the natural orientation.
    307                 naturalOrientation = (configOrientation == Configuration.ORIENTATION_LANDSCAPE) ?
    308                         Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
    309                 break;
    310             default:
    311                 // Unexpected orientation, error out.
    312                 throw new RuntimeException("Unexpected orientation that cannot be dealt with!");
    313         }
    314 
    315         // Since the map starts at portrait, we need to offset if this device's natural orientation
    316         // is landscape.
    317         int indexOffset = 0;
    318         if (naturalOrientation == Configuration.ORIENTATION_LANDSCAPE) {
    319             indexOffset = 1;
    320         }
    321 
    322         // Get rotation as a clockwise rotation.
    323         int currentRotation = ORIENTATION_COUNT - display.getRotation();
    324 
    325         // Check for reverse rotation direction and currentRotation if required.
    326         try {
    327             if (context.getResources().getBoolean(context.getResources().getSystem().getIdentifier(
    328                     "config_reverseDefaultRotation", "bool", "android"))) {
    329                 currentRotation = display.getRotation();
    330             }
    331         } catch (Resources.NotFoundException e) {
    332             // If resource is not found, assume default rotation and continue.
    333             Log.d(TAG, "Cannot determine device rotation direction, assuming default");
    334         }
    335 
    336         int currentOrientation = (currentRotation + indexOffset);
    337         int defaultOrientation = indexOffset;
    338 
    339         int difference = (currentOrientation - defaultOrientation) % ORIENTATION_COUNT;
    340         difference = difference * 90;
    341 
    342         return difference;
    343     }
    344 }
    345