package com.vizit;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.microedition.khronos.egl.EGLConfig;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import java.nio.IntBuffer;
import android.util.Log;
import android.opengl.Matrix;
import android.graphics.PointF;
/**
* Render a simple triangle using the simplest possible OpenGL ES 2.
*/
public class TriangleRenderer
implements GLSurfaceView.Renderer
{
private final String appName = "ESMotion";
// Vertex buffer for the second triangle.
private int anotherBuffer;
/** How many bytes per float. */
private final int bytesPerFloat = 4;
/** R, G, B, A for color at each vertex. */
private final int colorElementSize = 4;
private float[] moreVertices;
/** Number of elements per vertex. */
private final int nElements = 3;
/** Each position element has three values, x,y, and z coordinates. */
private final int positionElementSize = 3;
/** Offset of the position data. */
private final int positionOffset = 0;
private int program;
/** Translation for the second triangle has floating point x and y components. */
private PointF translation = new PointF();
// Vertex buffer for the first triangle.
private int vertexBuffer;
private float[] vertices;
/** Translate from world space to the view. */
private float[] viewMatrix;
public TriangleRenderer()
{
vertices = new float[] {
// Vertex coordinate, 3 floats (XYZ)
-1.0f, -1.0f, 0.0f,
// Vertex color, four floats (RGBA)
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f
};
moreVertices = new float[] {
-1.0f, -1.0f, 0.5f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.5f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.5f,
1.0f, 0.0f, 0.0f, 1.0f
};
}
/**
* Compile a shader of the given type.
* @param shaderSource A string containing GLSL code for the shader.
* @param shaderType The type of shader: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER for OpenGL ES 2.
*
* @return The compiled shader.
*/
protected int compileShader(String shaderSource, int shaderType)
{
IntBuffer logStatus;
int shader;
String compileLog;
int[] compileStatus;
shader = GLES20.glCreateShader(shaderType);
// Pass in the shader source.
GLES20.glShaderSource(shader, shaderSource);
// Compile the shader.
GLES20.glCompileShader(shader);
// Get the compilation status.
compileStatus = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
// Error compile status, get the relevant log.
if (compileStatus[0] != GLES20.GL_TRUE)
{
logStatus = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
GLES20.glGetShaderiv(shader, GLES20.GL_INFO_LOG_LENGTH, logStatus);
if (logStatus.get(0) > 0)
{
compileLog = GLES20.glGetShaderInfoLog(shader);
Log.d(appName, "Shader compilation failed with, " + compileLog);
}
else
{
// Workaround for issue 9953, where GL_INFO_LOG_LENGTH is zero.
Log.d(appName, "Shader compilation failed for an unknown reason.");
}
GLES20.glDeleteShader(shader);
shader = 0;
}
return shader;
}
/**
* Given the handles to two previously compiled shaders, assemble them into a program.
*
* @param vertexShader The handle for the vertex shader for this program.
* @param fragmentShader The handle for ths fragment shader for this program.
*
* @return int A handle on the linked program.
*/
protected int createProgram(int vertexShader, int fragmentShader)
{
String loaderLog;
IntBuffer logStatus;
int program;
program = GLES20.glCreateProgram();
GLES20.glAttachShader(program, vertexShader);
// Bind the fragment shader to the program.
GLES20.glAttachShader(program, fragmentShader);
GLES20.glLinkProgram(program);
// Get the link status.
final int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
// If the link failed, delete the program.
if (linkStatus[0] != GLES20.GL_TRUE)
{
logStatus = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
GLES20.glGetProgramiv(program, GLES20.GL_INFO_LOG_LENGTH, logStatus);
if (logStatus.get(0) > 0)
{
loaderLog = GLES20.glGetProgramInfoLog(program);
Log.d(appName, "Program linking failed with, " + loaderLog);
}
else
{
// Workaround for issue 9953, where GL_INFO_LOG_LENGTH is zero.
Log.d(appName, "Program linking failed for an unknown reason.");
}
GLES20.glDeleteProgram(program);
program = 0;
}
GLES20.glUseProgram(program);
return program;
}
/**
* Generate a matrix corresponding to an eye at eye(x,y,z) looking toward
* look (x,y,z), with the up direction up (x,y,z).
*
* @return A matrix that will transform the scene according to the given view.
*/
public float[] generateViewMatrix()
{
final float[] viewMatrix = new float[16];
// Position the eye behind the origin.
final float eyeX = 0.0f;
final float eyeY = 0.0f;
final float eyeZ = 0.0f;
// We are looking in the -z direction
final float lookX = 0.0f;
final float lookY = 0.0f;
final float lookZ = -1.0f;
// Up is the y axis.
final float upX = 0.0f;
final float upY = 1.0f;
final float upZ = 0.0f;
// Set the view matrix. This matrix can be said to represent the camera position.
Matrix.setLookAtM(viewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
return viewMatrix;
}
/**
* Load vertex data into a buffers so that it can be read into vertex shader attributes.
*
* @param vertexData An array of floats containing per vertex data.
*/
protected int createBuffer(float[] vertexData)
{
int[] vertexBuffer = new int[1];
/** Store our model data in a float buffer. */
FloatBuffer vertexBufferData;
// The OpenGL Driver expects floating point data in native byte order.
vertexBufferData = ByteBuffer.allocateDirect(vertexData.length * bytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
vertexBufferData.put(vertexData).position(0);
// Generate a single buffer handle into element 0 of the array.
GLES20.glGenBuffers(1, vertexBuffer, 0);
// Binding an object in Open GL creates it, and makes it the target of subsequent manipulations.
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBuffer[0]);
// loads the current buffer, the vertexBuffer found above, with the vertex data.
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexBufferData.capacity() * bytesPerFloat,
vertexBufferData, GLES20.GL_STATIC_DRAW);
return vertexBuffer[0];
}
/**
* Invoked after the rendering surface is setup - the OpenGL context is created.
* It is now safe to compile shaders, build programs, setup vertex buffers, etc.
*/
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
{
// Enable depth tests to draw only nearer objects, less depth.
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LEQUAL);
GLES20.glDepthMask(true);
final String vertexShaderSource = "attribute vec3 position;"
+ "attribute vec4 color;"
+ "uniform mat4 modelViewMatrix;"
+ ""
+ "varying vec4 vColor;"
+ ""
+ "void main()"
+ "{"
+ " gl_Position = modelViewMatrix * vec4(position, 1);"
+ " vColor = color;"
+ "}";
// The fragment shader can be thought of for now as doing per pixel computations. It is the
// fragment shader the colors each pixel in a 3d scene.
final String fragmentShaderSource = "precision mediump float;"
+ ""
+ "varying vec4 vColor;"
+ ""
+ "void main()"
+ "{"
+ " gl_FragColor = vColor;"
+ "}";
int vertexShader = compileShader(vertexShaderSource, GLES20.GL_VERTEX_SHADER);
int fragmentShader = compileShader(fragmentShaderSource, GLES20.GL_FRAGMENT_SHADER);
program = createProgram(vertexShader, fragmentShader);
vertexBuffer = createBuffer(vertices);
anotherBuffer = createBuffer(moreVertices);
viewMatrix = generateViewMatrix();
}
/**
* Height or width changed, usually portrait <=> landscape.
*/
@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height)
{
// Resize our drawing surface to the new height and width.
GLES20.glViewport(0, 0, width, height);
}
/**
* Setup pointer for attribute to read data into shader attribute.
* @param vertexBuffer The FloatBuffer within this program space containing the data to be read.
* @param program A handle on the GLSL program that will consume the data.
* @param attribute A String giving the name of the shader attribute associated with this data.
* @param size The number of entries from the buffer that make up each attribute value.
* @param type The type of each entry.
* @param stride An in giving the begin-begin spacing of values in bytes. 0 for adjacent values.
* @param offset An in giving the offset into the FloatBuffer where we begin reading data.
*/
protected void bindBuffer(int vertexBuffer, int program, String attribute, int size, int type, int stride, int offset)
{
int attributeLocation;
attributeLocation = GLES20.glGetAttribLocation(program, attribute);
// Binding an object makes it the target of subsequent manipulations.
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBuffer);
GLES20.glVertexAttribPointer(attributeLocation, size, type, false, stride, offset);
GLES20.glEnableVertexAttribArray(attributeLocation);
}
/**
* Supply a 4x4 floating point matrix to the given program.
* @param program A handle on the target program.
* @param matrix A Float array containing the matrix elements in column major order.
* @param name A String giving the name of the uniform/
*/
protected void bindMatrixUniform(int program, float[] matrix, String name)
{
int uniformLocation = GLES20.glGetUniformLocation(program, name);
// Location, count (1 matrix), transposed, values, offset
GLES20.glUniformMatrix4fv(uniformLocation, 1, false, matrix, 0);
}
/**
* Draws each frame. Invoked when the surface first becomes drawable,
* and again whenever motion is detected.
* @param glUnused
*/
@Override
public void onDrawFrame(GL10 glUnused)
{
int stride;
GLES20.glClearColor(.9255f, .8941f, .8275f, 1.0f);
GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
bindMatrixUniform(program, viewMatrix, "modelViewMatrix");
stride = bytesPerFloat*(colorElementSize + positionElementSize);
bindBuffer(vertexBuffer, program, "position", positionElementSize,
GLES20.GL_FLOAT, stride, positionOffset*bytesPerFloat);
bindBuffer(vertexBuffer, program, "color", colorElementSize,
GLES20.GL_FLOAT, stride, (positionOffset + positionElementSize)*bytesPerFloat);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, nElements);
// Take into account translations of the second triangle, if any. (does NOT need be be recreated here).a
float[] newModelView = new float[16];
// load newModelView with x and y translation from the detected motion.
Matrix.translateM(newModelView, 0, viewMatrix, 0, translation.x, translation.y, 0);
bindMatrixUniform(program, newModelView, "modelViewMatrix");
bindBuffer(anotherBuffer, program, "position", positionElementSize,
GLES20.GL_FLOAT, stride, positionOffset*bytesPerFloat);
bindBuffer(anotherBuffer, program, "color", colorElementSize,
GLES20.GL_FLOAT, stride, (positionOffset + positionElementSize)*bytesPerFloat);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, nElements);
}
/**
* Invoked by the surfaced view with normalized change in x and y
* @param deltaX A float giving how far the touch moved in the x direction.
* @param deltaY A float giving how far the touch moved in the y direction.
*/
public void motion(float deltaX, float deltaY)
{
translation.x += deltaX;
translation.y += deltaY;
}
}
Impliments onTouchEvent in MyGLSurfaceView to respond to touch events.
TriangleRenderer now provides a ModelViewMatrix to position the elements within the scene. The ModelView is updated for the second triangle to allow it to be moved out from behind the larger triangle.