1 /* 2 * Copyright 2018 Google LLC All Rights Reserved. 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 17 package com.google.skar; 18 19 import android.graphics.Matrix; 20 21 /** 22 * Provides static methods for matrix manipulation needed to draw in ARCore with Canvas. 23 * Input matrices are assumed to be 4x4 android.opengl.Matrix types (16-float arrays in column-major 24 * order). 25 * Output matrices are 3x3 android.graphics.Matrix types. 26 */ 27 28 public class CanvasMatrixUtil { 29 30 /******************* PUBLIC FUNCTIONS ***********************/ 31 32 /** 33 * Returns an android.graphics.Matrix that can be used on a Canvas to draw a 2D object in 34 * perspective. Object will be rotated towards the XZ plane and will appear to stick on Planes. 35 * Undefined behavior when any of the matrices are not of size 16, or are null. 36 * 37 * @param model 4x4 model matrix of the object to be drawn (global/world) 38 * @param view 4x4 camera view matrix (brings objects to camera origin and 39 * orientation) 40 * @param projection 4x4 projection matrix 41 * @param viewPortWidth width of viewport of GLSurfaceView 42 * @param viewPortHeight height of viewport of GLSurfaceView 43 * @return 3x3 matrix that puts a 2D objects in perspective on a Canvas 44 */ 45 createPerspectiveMatrix(float[] model, float[] view, float[] projection, float viewPortWidth, float viewPortHeight)46 public static Matrix createPerspectiveMatrix(float[] model, float[] view, float[] projection, 47 float viewPortWidth, float viewPortHeight) { 48 float[] viewPort = createViewportMatrix(viewPortWidth, viewPortHeight); 49 float[] planeStickRotation = createXYtoXZRotationMatrix(); 50 float[][] matrices = {planeStickRotation, model, view, projection, viewPort}; 51 return createMatrixFrom4x4Array(matrices); 52 } 53 54 /** 55 * Returns a 16-float matrix in column-major order that represents a viewport matrix given 56 * the width and height of the viewport. 57 * 58 * @param width width of viewport 59 * @param height height of viewport 60 */ 61 createViewportMatrix(float width, float height)62 public static float[] createViewportMatrix(float width, float height) { 63 float[] viewPort = new float[16]; 64 android.opengl.Matrix.setIdentityM(viewPort, 0); 65 android.opengl.Matrix.translateM(viewPort, 0, width / 2, height / 2, 0); 66 android.opengl.Matrix.scaleM(viewPort, 0, width / 2, -height / 2, 0); 67 return viewPort; 68 } 69 70 /** 71 * Returns a 16-float matrix in column-major order that is used to rotate objects from the 72 * XY plane to the XZ plane. This is useful given that objects drawn on the Canvas are on the 73 * XY plane. 74 * In order to get objects to appear as if they are sticking on planes/ceilings/walls, we need 75 * to rotate them from the XY plane to the XZ plane. 76 */ 77 createXYtoXZRotationMatrix()78 public static float[] createXYtoXZRotationMatrix() { 79 float[] rotation = new float[16]; 80 android.opengl.Matrix.setIdentityM(rotation, 0); 81 android.opengl.Matrix.rotateM(rotation, 0, 90, 1, 0, 0); 82 return rotation; 83 } 84 85 86 /** 87 * Returns an android.graphics.Matrix resulting from a 16-float matrix array in column-major 88 * order. 89 * Undefined behavior when the array is not of size 16 or is null. 90 * 91 * @param m4 16-float matrix in column-major order 92 */ 93 createMatrixFrom4x4(float[] m4)94 public static Matrix createMatrixFrom4x4(float[] m4) { 95 float[] m3 = matrix4x4ToMatrix3x3(m4); 96 return createMatrixFrom3x3(m3); 97 } 98 99 /** 100 * Returns an android.graphics.Matrix resulting from the concatenation of 16-float matrices 101 * in column-major order from left to right. 102 * e.g: m4Array = {m1, m2, m3} --> returns m = m3 * m2 * m1 103 * Undefined behavior when the array is empty, null, or contains arrays not of size 9 (or null) 104 * 105 * @param m4Array array of 16-float matrices in column-major order 106 */ 107 createMatrixFrom4x4Array(float[][] m4Array)108 public static Matrix createMatrixFrom4x4Array(float[][] m4Array) { 109 float[] result = multiplyMatrices4x4(m4Array); 110 return createMatrixFrom4x4(result); 111 } 112 113 /** 114 * Returns 4-float array resulting from the multiplication of a Vector of 4 floats 115 * with a 4x4 float Matrix. The return is essentially m4 * v4, with perspective-divide applied 116 * if perspectiveDivide is true 117 * @param m4 16-float matrix in column-major order 118 * @param v4 4-float vector 119 * @param perspectiveDivide if true, divide return value by the w-coordinate 120 * @return 4-float array resulting from the multiplication 121 */ 122 multiplyMatrixVector(float[] m4, float[] v4, boolean perspectiveDivide)123 public static float[] multiplyMatrixVector(float[] m4, float[] v4, boolean perspectiveDivide) { 124 float[] result = new float[4]; 125 android.opengl.Matrix.multiplyMV(result, 0, m4, 0, v4, 0); 126 if (perspectiveDivide) { 127 return new float[] {result[0] / result[3], result[1] / result[3], 128 result[2] / result[3], 1}; 129 } 130 131 return new float[] {result[0], result[1], result[2], result[3]}; 132 } 133 134 /** 135 * Returns a 16-float matrix in column-major order resulting from the multiplication of matrices 136 * e.g: m4Array = {m1, m2, m3} --> returns m = m3 * m2 * m1 137 * Undefined behavior when the array is empty, null, or contains arrays not of size 9 (or null) 138 * 139 * @param m4Array array of 16-float matrices in column-major order 140 */ 141 multiplyMatrices4x4(float[][] m4Array)142 public static float[] multiplyMatrices4x4(float[][] m4Array) { 143 float[] result = new float[16]; 144 android.opengl.Matrix.setIdentityM(result, 0); 145 float[] rhs = result; 146 for (int i = 0; i < m4Array.length; i++) { 147 float[] lhs = m4Array[i]; 148 android.opengl.Matrix.multiplyMM(result, 0, lhs, 0, rhs, 0); 149 rhs = result; 150 } 151 return result; 152 } 153 154 /******************* PRIVATE FUNCTIONS ***********************/ 155 156 /** 157 * Returns an android.graphics.Matrix resulting from a 9-float matrix array in row-major order. 158 * Undefined behavior when the array is not of size 9 or is null. 159 * 160 * @param m3 9-float matrix array in row-major order 161 */ 162 createMatrixFrom3x3(float[] m3)163 private static Matrix createMatrixFrom3x3(float[] m3) { 164 Matrix m = new Matrix(); 165 m.setValues(m3); 166 return m; 167 } 168 169 /** 170 * Returns a 9-float matrix in row-major order given a 16-float matrix in column-major order. 171 * This will drop the Z column and row. 172 * Undefined behavior when the array is not of size 9 or is null. 173 * 174 * @param m4 16-float matrix in column-major order 175 */ 176 matrix4x4ToMatrix3x3(float[] m4)177 private static float[] matrix4x4ToMatrix3x3(float[] m4) { 178 float[] m3 = new float[9]; 179 180 int j = 0; 181 for (int i = 0; i < 7; i = i + 3) { 182 if (j == 2) { 183 j++; //skip row #3 184 } 185 m3[i] = m4[j]; 186 m3[i + 1] = m4[j + 4]; 187 m3[i + 2] = m4[j + 12]; 188 j++; 189 } 190 return m3; 191 } 192 } 193