Home | History | Annotate | Download | only in com.example.android.wearable.watchface
      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      */
     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      */
    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      */
    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      */
    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          */
    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          */
    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. */
    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