1 /* 2 * Copyright (C) 2014 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 17 package com.example.android.wearable.watchface; 18 19 import java.nio.ByteBuffer; 20 import java.nio.ByteOrder; 21 import java.nio.FloatBuffer; 22 23 import android.opengl.GLES20; 24 import android.opengl.GLU; 25 import android.opengl.GLUtils; 26 import android.util.Log; 27 28 /** 29 * A list of triangles drawn in a single solid color using OpenGL ES 2.0. 30 */ 31 public class Gles2ColoredTriangleList { 32 private static final String TAG = "GlColoredTriangleList"; 33 34 /** Whether to check for GL errors. This is slow, so not appropriate for production builds. */ 35 private static final boolean CHECK_GL_ERRORS = false; 36 37 /** Number of coordinates per vertex in this array: one for each of x, y, and z. */ 38 private static final int COORDS_PER_VERTEX = 3; 39 40 /** Number of bytes to store a float in GL. */ 41 public static final int BYTES_PER_FLOAT = 4; 42 43 /** Number of bytes per vertex. */ 44 private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * BYTES_PER_FLOAT; 45 46 /** Triangles have three vertices. */ 47 private static final int VERTICE_PER_TRIANGLE = 3; 48 49 /** 50 * Number of components in an OpenGL color. The components are:<ol> 51 * <li>red 52 * <li>green 53 * <li>blue 54 * <li>alpha 55 * </ol> 56 */ 57 private static final int NUM_COLOR_COMPONENTS = 4; 58 59 /** Shaders to render this triangle list. */ 60 private final Program mProgram; 61 62 /** The VBO containing the vertex coordinates. */ 63 private final FloatBuffer mVertexBuffer; 64 65 /** 66 * Color of this triangle list represented as an array of floats in the range [0, 1] in RGBA 67 * order. 68 */ 69 private final float mColor[]; 70 71 /** Number of coordinates in this triangle list. */ 72 private final int mNumCoords; 73 74 /** 75 * Creates a Gles2ColoredTriangleList to draw a triangle list with the given vertices and color. 76 * 77 * @param program program for drawing triangles 78 * @param triangleCoords flat array of 3D coordinates of triangle vertices in counterclockwise 79 * order 80 * @param color color in RGBA order, each in the range [0, 1] 81 */ Gles2ColoredTriangleList(Program program, float[] triangleCoords, float[] color)82 public Gles2ColoredTriangleList(Program program, float[] triangleCoords, float[] color) { 83 if (triangleCoords.length % (VERTICE_PER_TRIANGLE * COORDS_PER_VERTEX) != 0) { 84 throw new IllegalArgumentException("must be multiple" 85 + " of VERTICE_PER_TRIANGLE * COORDS_PER_VERTEX coordinates"); 86 } 87 if (color.length != NUM_COLOR_COMPONENTS) { 88 throw new IllegalArgumentException("wrong number of color components"); 89 } 90 mProgram = program; 91 mColor = color; 92 93 ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT); 94 95 // Use the device hardware's native byte order. 96 bb.order(ByteOrder.nativeOrder()); 97 98 // Create a FloatBuffer that wraps the ByteBuffer. 99 mVertexBuffer = bb.asFloatBuffer(); 100 101 // Add the coordinates to the FloatBuffer. 102 mVertexBuffer.put(triangleCoords); 103 104 // Go back to the start for reading. 105 mVertexBuffer.position(0); 106 107 mNumCoords = triangleCoords.length / COORDS_PER_VERTEX; 108 } 109 110 /** 111 * Draws this triangle list using OpenGL commands. 112 * 113 * @param mvpMatrix the Model View Project matrix to draw this triangle list 114 */ draw(float[] mvpMatrix)115 public void draw(float[] mvpMatrix) { 116 // Pass the MVP matrix, vertex data, and color to OpenGL. 117 mProgram.bind(mvpMatrix, mVertexBuffer, mColor); 118 119 // Draw the triangle list. 120 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mNumCoords); 121 if (CHECK_GL_ERRORS) checkGlError("glDrawArrays"); 122 } 123 124 /** 125 * Checks if any of the GL calls since the last time this method was called set an error 126 * condition. Call this method immediately after calling a GL method. Pass the name of the GL 127 * operation. For example: 128 * 129 * <pre> 130 * mColorHandle = GLES20.glGetUniformLocation(mProgram, "uColor"); 131 * MyGLRenderer.checkGlError("glGetUniformLocation");</pre> 132 * 133 * If the operation is not successful, the check throws an exception. 134 * 135 * <p><em>Note</em> This is quite slow so it's best to use it sparingly in production builds. 136 * 137 * @param glOperation name of the OpenGL call to check 138 */ checkGlError(String glOperation)139 private static void checkGlError(String glOperation) { 140 int error = GLES20.glGetError(); 141 if (error != GLES20.GL_NO_ERROR) { 142 String errorString = GLU.gluErrorString(error); 143 if (errorString == null) { 144 errorString = GLUtils.getEGLErrorString(error); 145 } 146 String message = glOperation + " caused GL error 0x" + Integer.toHexString(error) + 147 ": " + errorString; 148 Log.e(TAG, message); 149 throw new RuntimeException(message); 150 } 151 } 152 153 /** 154 * Compiles an OpenGL shader. 155 * 156 * @param type {@link GLES20#GL_VERTEX_SHADER} or {@link GLES20#GL_FRAGMENT_SHADER} 157 * @param shaderCode string containing the shader source code 158 * @return ID for the shader 159 */ loadShader(int type, String shaderCode)160 private static int loadShader(int type, String shaderCode){ 161 // Create a vertex or fragment shader. 162 int shader = GLES20.glCreateShader(type); 163 if (CHECK_GL_ERRORS) checkGlError("glCreateShader"); 164 if (shader == 0) { 165 throw new IllegalStateException("glCreateShader failed"); 166 } 167 168 // Add the source code to the shader and compile it. 169 GLES20.glShaderSource(shader, shaderCode); 170 if (CHECK_GL_ERRORS) checkGlError("glShaderSource"); 171 GLES20.glCompileShader(shader); 172 if (CHECK_GL_ERRORS) checkGlError("glCompileShader"); 173 174 return shader; 175 } 176 177 /** OpenGL shaders for drawing solid colored triangle lists. */ 178 public static class Program { 179 /** Trivial vertex shader that transforms the input vertex by the MVP matrix. */ 180 private static final String VERTEX_SHADER_CODE = "" + 181 "uniform mat4 uMvpMatrix;\n" + 182 "attribute vec4 aPosition;\n" + 183 "void main() {\n" + 184 " gl_Position = uMvpMatrix * aPosition;\n" + 185 "}\n"; 186 187 /** Trivial fragment shader that draws with a fixed color. */ 188 private static final String FRAGMENT_SHADER_CODE = "" + 189 "precision mediump float;\n" + 190 "uniform vec4 uColor;\n" + 191 "void main() {\n" + 192 " gl_FragColor = uColor;\n" + 193 "}\n"; 194 195 /** ID OpenGL uses to identify this program. */ 196 private final int mProgramId; 197 198 /** Handle for uMvpMatrix uniform in vertex shader. */ 199 private final int mMvpMatrixHandle; 200 201 /** Handle for aPosition attribute in vertex shader. */ 202 private final int mPositionHandle; 203 204 /** Handle for uColor uniform in fragment shader. */ 205 private final int mColorHandle; 206 207 /** 208 * Creates a program to draw triangle lists. For optimal drawing efficiency, one program 209 * should be used for all triangle lists being drawn. 210 */ Program()211 public Program() { 212 // Prepare shaders. 213 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE); 214 int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE); 215 216 // Create empty OpenGL Program. 217 mProgramId = GLES20.glCreateProgram(); 218 if (CHECK_GL_ERRORS) checkGlError("glCreateProgram"); 219 if (mProgramId == 0) { 220 throw new IllegalStateException("glCreateProgram failed"); 221 } 222 223 // Add the shaders to the program. 224 GLES20.glAttachShader(mProgramId, vertexShader); 225 if (CHECK_GL_ERRORS) checkGlError("glAttachShader"); 226 GLES20.glAttachShader(mProgramId, fragmentShader); 227 if (CHECK_GL_ERRORS) checkGlError("glAttachShader"); 228 229 // Link the program so it can be executed. 230 GLES20.glLinkProgram(mProgramId); 231 if (CHECK_GL_ERRORS) checkGlError("glLinkProgram"); 232 233 // Get a handle to the uMvpMatrix uniform in the vertex shader. 234 mMvpMatrixHandle = GLES20.glGetUniformLocation(mProgramId, "uMvpMatrix"); 235 if (CHECK_GL_ERRORS) checkGlError("glGetUniformLocation"); 236 237 // Get a handle to the vertex shader's aPosition attribute. 238 mPositionHandle = GLES20.glGetAttribLocation(mProgramId, "aPosition"); 239 if (CHECK_GL_ERRORS) checkGlError("glGetAttribLocation"); 240 241 // Enable vertex array (VBO). 242 GLES20.glEnableVertexAttribArray(mPositionHandle); 243 if (CHECK_GL_ERRORS) checkGlError("glEnableVertexAttribArray"); 244 245 // Get a handle to fragment shader's uColor uniform. 246 mColorHandle = GLES20.glGetUniformLocation(mProgramId, "uColor"); 247 if (CHECK_GL_ERRORS) checkGlError("glGetUniformLocation"); 248 } 249 250 /** 251 * Tells OpenGL to use this program. Call this method before drawing a sequence of 252 * triangle lists. 253 */ use()254 public void use() { 255 GLES20.glUseProgram(mProgramId); 256 if (CHECK_GL_ERRORS) checkGlError("glUseProgram"); 257 } 258 259 /** Sends the given MVP matrix, vertex data, and color to OpenGL. */ bind(float[] mvpMatrix, FloatBuffer vertexBuffer, float[] color)260 public void bind(float[] mvpMatrix, FloatBuffer vertexBuffer, float[] color) { 261 // Pass the MVP matrix to OpenGL. 262 GLES20.glUniformMatrix4fv(mMvpMatrixHandle, 1 /* count */, false /* transpose */, 263 mvpMatrix, 0 /* offset */); 264 if (CHECK_GL_ERRORS) checkGlError("glUniformMatrix4fv"); 265 266 // Pass the VBO with the triangle list's vertices to OpenGL. 267 GLES20.glEnableVertexAttribArray(mPositionHandle); 268 if (CHECK_GL_ERRORS) checkGlError("glEnableVertexAttribArray"); 269 GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, 270 false /* normalized */, VERTEX_STRIDE, vertexBuffer); 271 if (CHECK_GL_ERRORS) checkGlError("glVertexAttribPointer"); 272 273 // Pass the triangle list's color to OpenGL. 274 GLES20.glUniform4fv(mColorHandle, 1 /* count */, color, 0 /* offset */); 275 if (CHECK_GL_ERRORS) checkGlError("glUniform4fv"); 276 } 277 } 278 } 279