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