blob: 2441c65915e2f267a2275eceeb7803e50603a5dd [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.wearable.watchface;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.opengl.GLES20;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.util.Log;
/**
* A list of triangles drawn in a single solid color using OpenGL ES 2.0.
*/
public class Gles2ColoredTriangleList {
private static final String TAG = "GlColoredTriangleList";
/** Whether to check for GL errors. This is slow, so not appropriate for production builds. */
private static final boolean CHECK_GL_ERRORS = false;
/** Number of coordinates per vertex in this array: one for each of x, y, and z. */
private static final int COORDS_PER_VERTEX = 3;
/** Number of bytes to store a float in GL. */
public static final int BYTES_PER_FLOAT = 4;
/** Number of bytes per vertex. */
private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * BYTES_PER_FLOAT;
/** Triangles have three vertices. */
private static final int VERTICE_PER_TRIANGLE = 3;
/**
* Number of components in an OpenGL color. The components are:<ol>
* <li>red
* <li>green
* <li>blue
* <li>alpha
* </ol>
*/
private static final int NUM_COLOR_COMPONENTS = 4;
/** Shaders to render this triangle list. */
private final Program mProgram;
/** The VBO containing the vertex coordinates. */
private final FloatBuffer mVertexBuffer;
/**
* Color of this triangle list represented as an array of floats in the range [0, 1] in RGBA
* order.
*/
private final float mColor[];
/** Number of coordinates in this triangle list. */
private final int mNumCoords;
/**
* Creates a Gles2ColoredTriangleList to draw a triangle list with the given vertices and color.
*
* @param program program for drawing triangles
* @param triangleCoords flat array of 3D coordinates of triangle vertices in counterclockwise
* order
* @param color color in RGBA order, each in the range [0, 1]
*/
public Gles2ColoredTriangleList(Program program, float[] triangleCoords, float[] color) {
if (triangleCoords.length % (VERTICE_PER_TRIANGLE * COORDS_PER_VERTEX) != 0) {
throw new IllegalArgumentException("must be multiple"
+ " of VERTICE_PER_TRIANGLE * COORDS_PER_VERTEX coordinates");
}
if (color.length != NUM_COLOR_COMPONENTS) {
throw new IllegalArgumentException("wrong number of color components");
}
mProgram = program;
mColor = color;
ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT);
// Use the device hardware's native byte order.
bb.order(ByteOrder.nativeOrder());
// Create a FloatBuffer that wraps the ByteBuffer.
mVertexBuffer = bb.asFloatBuffer();
// Add the coordinates to the FloatBuffer.
mVertexBuffer.put(triangleCoords);
// Go back to the start for reading.
mVertexBuffer.position(0);
mNumCoords = triangleCoords.length / COORDS_PER_VERTEX;
}
/**
* Draws this triangle list using OpenGL commands.
*
* @param mvpMatrix the Model View Project matrix to draw this triangle list
*/
public void draw(float[] mvpMatrix) {
// Pass the MVP matrix, vertex data, and color to OpenGL.
mProgram.bind(mvpMatrix, mVertexBuffer, mColor);
// Draw the triangle list.
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mNumCoords);
if (CHECK_GL_ERRORS) checkGlError("glDrawArrays");
}
/**
* Checks if any of the GL calls since the last time this method was called set an error
* condition. Call this method immediately after calling a GL method. Pass the name of the GL
* operation. For example:
*
* <pre>
* mColorHandle = GLES20.glGetUniformLocation(mProgram, "uColor");
* MyGLRenderer.checkGlError("glGetUniformLocation");</pre>
*
* If the operation is not successful, the check throws an exception.
*
* <p><em>Note</em> This is quite slow so it's best to use it sparingly in production builds.
*
* @param glOperation name of the OpenGL call to check
*/
private static void checkGlError(String glOperation) {
int error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
String errorString = GLU.gluErrorString(error);
if (errorString == null) {
errorString = GLUtils.getEGLErrorString(error);
}
String message = glOperation + " caused GL error 0x" + Integer.toHexString(error) +
": " + errorString;
Log.e(TAG, message);
throw new RuntimeException(message);
}
}
/**
* Compiles an OpenGL shader.
*
* @param type {@link GLES20#GL_VERTEX_SHADER} or {@link GLES20#GL_FRAGMENT_SHADER}
* @param shaderCode string containing the shader source code
* @return ID for the shader
*/
private static int loadShader(int type, String shaderCode){
// Create a vertex or fragment shader.
int shader = GLES20.glCreateShader(type);
if (CHECK_GL_ERRORS) checkGlError("glCreateShader");
if (shader == 0) {
throw new IllegalStateException("glCreateShader failed");
}
// Add the source code to the shader and compile it.
GLES20.glShaderSource(shader, shaderCode);
if (CHECK_GL_ERRORS) checkGlError("glShaderSource");
GLES20.glCompileShader(shader);
if (CHECK_GL_ERRORS) checkGlError("glCompileShader");
return shader;
}
/** OpenGL shaders for drawing solid colored triangle lists. */
public static class Program {
/** Trivial vertex shader that transforms the input vertex by the MVP matrix. */
private static final String VERTEX_SHADER_CODE = "" +
"uniform mat4 uMvpMatrix;\n" +
"attribute vec4 aPosition;\n" +
"void main() {\n" +
" gl_Position = uMvpMatrix * aPosition;\n" +
"}\n";
/** Trivial fragment shader that draws with a fixed color. */
private static final String FRAGMENT_SHADER_CODE = "" +
"precision mediump float;\n" +
"uniform vec4 uColor;\n" +
"void main() {\n" +
" gl_FragColor = uColor;\n" +
"}\n";
/** ID OpenGL uses to identify this program. */
private final int mProgramId;
/** Handle for uMvpMatrix uniform in vertex shader. */
private final int mMvpMatrixHandle;
/** Handle for aPosition attribute in vertex shader. */
private final int mPositionHandle;
/** Handle for uColor uniform in fragment shader. */
private final int mColorHandle;
/**
* Creates a program to draw triangle lists. For optimal drawing efficiency, one program
* should be used for all triangle lists being drawn.
*/
public Program() {
// Prepare shaders.
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE);
// Create empty OpenGL Program.
mProgramId = GLES20.glCreateProgram();
if (CHECK_GL_ERRORS) checkGlError("glCreateProgram");
if (mProgramId == 0) {
throw new IllegalStateException("glCreateProgram failed");
}
// Add the shaders to the program.
GLES20.glAttachShader(mProgramId, vertexShader);
if (CHECK_GL_ERRORS) checkGlError("glAttachShader");
GLES20.glAttachShader(mProgramId, fragmentShader);
if (CHECK_GL_ERRORS) checkGlError("glAttachShader");
// Link the program so it can be executed.
GLES20.glLinkProgram(mProgramId);
if (CHECK_GL_ERRORS) checkGlError("glLinkProgram");
// Get a handle to the uMvpMatrix uniform in the vertex shader.
mMvpMatrixHandle = GLES20.glGetUniformLocation(mProgramId, "uMvpMatrix");
if (CHECK_GL_ERRORS) checkGlError("glGetUniformLocation");
// Get a handle to the vertex shader's aPosition attribute.
mPositionHandle = GLES20.glGetAttribLocation(mProgramId, "aPosition");
if (CHECK_GL_ERRORS) checkGlError("glGetAttribLocation");
// Enable vertex array (VBO).
GLES20.glEnableVertexAttribArray(mPositionHandle);
if (CHECK_GL_ERRORS) checkGlError("glEnableVertexAttribArray");
// Get a handle to fragment shader's uColor uniform.
mColorHandle = GLES20.glGetUniformLocation(mProgramId, "uColor");
if (CHECK_GL_ERRORS) checkGlError("glGetUniformLocation");
}
/**
* Tells OpenGL to use this program. Call this method before drawing a sequence of
* triangle lists.
*/
public void use() {
GLES20.glUseProgram(mProgramId);
if (CHECK_GL_ERRORS) checkGlError("glUseProgram");
}
/** Sends the given MVP matrix, vertex data, and color to OpenGL. */
public void bind(float[] mvpMatrix, FloatBuffer vertexBuffer, float[] color) {
// Pass the MVP matrix to OpenGL.
GLES20.glUniformMatrix4fv(mMvpMatrixHandle, 1 /* count */, false /* transpose */,
mvpMatrix, 0 /* offset */);
if (CHECK_GL_ERRORS) checkGlError("glUniformMatrix4fv");
// Pass the VBO with the triangle list's vertices to OpenGL.
GLES20.glEnableVertexAttribArray(mPositionHandle);
if (CHECK_GL_ERRORS) checkGlError("glEnableVertexAttribArray");
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
false /* normalized */, VERTEX_STRIDE, vertexBuffer);
if (CHECK_GL_ERRORS) checkGlError("glVertexAttribPointer");
// Pass the triangle list's color to OpenGL.
GLES20.glUniform4fv(mColorHandle, 1 /* count */, color, 0 /* offset */);
if (CHECK_GL_ERRORS) checkGlError("glUniform4fv");
}
}
}