Skip to content

Most visited

Recently visited

navigation
WatchFace / Wearable / src / com.example.android.wearable.watchface /

Gles2ColoredTriangleList.java

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
}