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